summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp33
-rw-r--r--StubLibraries.bp64
-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.java21
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java7
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java25
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java347
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/Agent.java57
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java7
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/Ledger.java5
-rw-r--r--core/api/current.txt125
-rw-r--r--core/api/module-lib-current.txt5
-rw-r--r--core/api/system-current.txt136
-rw-r--r--core/api/system-removed.txt8
-rw-r--r--core/api/test-current.txt69
-rw-r--r--core/java/Android.bp15
-rw-r--r--core/java/android/accessibilityservice/AccessibilityGestureEvent.java2
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java86
-rw-r--r--core/java/android/accessibilityservice/AccessibilityServiceInfo.java4
-rw-r--r--core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl2
-rw-r--r--core/java/android/animation/ValueAnimator.java7
-rw-r--r--core/java/android/app/ActivityClient.java23
-rw-r--r--core/java/android/app/ActivityManager.java2
-rw-r--r--core/java/android/app/ActivityThread.java58
-rw-r--r--core/java/android/app/AppOpsManager.java6
-rw-r--r--core/java/android/app/AutomaticZenRule.java8
-rw-r--r--core/java/android/app/BroadcastOptions.java62
-rw-r--r--core/java/android/app/ContextImpl.java16
-rw-r--r--core/java/android/app/GameManager.java12
-rw-r--r--core/java/android/app/GameState.aidl19
-rw-r--r--core/java/android/app/GameState.java179
-rw-r--r--core/java/android/app/GrantedUriPermission.java2
-rw-r--r--core/java/android/app/IActivityClientController.aidl12
-rw-r--r--core/java/android/app/IActivityManager.aidl4
-rw-r--r--core/java/android/app/ICompatCameraControlCallback.aidl30
-rw-r--r--core/java/android/app/IGameManagerService.aidl3
-rw-r--r--core/java/android/app/Instrumentation.java2
-rw-r--r--core/java/android/app/LoadedApk.java4
-rw-r--r--core/java/android/app/Notification.java1
-rw-r--r--core/java/android/app/NotificationChannelGroup.java2
-rw-r--r--core/java/android/app/OWNERS1
-rw-r--r--core/java/android/app/RemoteInputHistoryItem.java2
-rw-r--r--core/java/android/app/StatusBarManager.java25
-rw-r--r--core/java/android/app/SystemServiceRegistry.java31
-rw-r--r--core/java/android/app/TaskInfo.java88
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java104
-rw-r--r--core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java34
-rw-r--r--core/java/android/app/admin/ManagedProfileProvisioningParams.java56
-rw-r--r--core/java/android/app/admin/ProvisioningException.java90
-rw-r--r--core/java/android/app/assist/AssistStructure.java6
-rw-r--r--core/java/android/app/cloudsearch/OWNERS4
-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/usage/CacheQuotaHint.java2
-rw-r--r--core/java/android/app/usage/UsageStatsManager.java32
-rw-r--r--core/java/android/bluetooth/Attributable.java55
-rw-r--r--core/java/android/bluetooth/BluetoothA2dp.java513
-rwxr-xr-xcore/java/android/bluetooth/BluetoothA2dpSink.java186
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java10
-rw-r--r--core/java/android/bluetooth/BluetoothAvrcpController.java137
-rw-r--r--core/java/android/bluetooth/BluetoothCsipSetCoordinator.java260
-rw-r--r--core/java/android/bluetooth/BluetoothDevice.java3
-rw-r--r--core/java/android/bluetooth/BluetoothGatt.java10
-rw-r--r--core/java/android/bluetooth/BluetoothGattCharacteristic.java2
-rw-r--r--core/java/android/bluetooth/BluetoothGattDescriptor.java2
-rw-r--r--core/java/android/bluetooth/BluetoothGattIncludedService.java2
-rw-r--r--core/java/android/bluetooth/BluetoothGattService.java2
-rw-r--r--core/java/android/bluetooth/BluetoothHeadset.java566
-rw-r--r--core/java/android/bluetooth/BluetoothHeadsetClient.java582
-rw-r--r--core/java/android/bluetooth/BluetoothHeadsetClientCall.java3
-rw-r--r--core/java/android/bluetooth/BluetoothHearingAid.java307
-rw-r--r--core/java/android/bluetooth/BluetoothHidDevice.java265
-rw-r--r--core/java/android/bluetooth/BluetoothHidHost.java296
-rw-r--r--core/java/android/bluetooth/BluetoothLeAudio.java309
-rw-r--r--core/java/android/bluetooth/BluetoothManager.java35
-rw-r--r--core/java/android/bluetooth/BluetoothMap.java188
-rw-r--r--core/java/android/bluetooth/BluetoothMapClient.java252
-rw-r--r--core/java/android/bluetooth/BluetoothPan.java161
-rw-r--r--core/java/android/bluetooth/BluetoothPbap.java1
-rw-r--r--core/java/android/bluetooth/BluetoothPbapClient.java159
-rw-r--r--core/java/android/bluetooth/BluetoothSap.java185
-rw-r--r--core/java/android/bluetooth/BluetoothUtils.java41
-rw-r--r--core/java/android/bluetooth/BluetoothVolumeControl.java130
-rw-r--r--core/java/android/bluetooth/BufferConstraints.java2
-rw-r--r--core/java/android/bluetooth/le/BluetoothLeScanner.java15
-rw-r--r--core/java/android/bluetooth/le/PeriodicAdvertisingManager.java2
-rw-r--r--core/java/android/bluetooth/le/ScanFilter.java10
-rw-r--r--core/java/android/bluetooth/le/ScanResult.java2
-rw-r--r--core/java/android/companion/AssociationRequest.java195
-rw-r--r--core/java/android/companion/BluetoothDeviceFilter.java2
-rw-r--r--core/java/android/companion/BluetoothLeDeviceFilter.java2
-rw-r--r--core/java/android/content/AttributionSource.java43
-rw-r--r--core/java/android/content/BroadcastReceiver.java2
-rw-r--r--core/java/android/content/ContentProviderOperation.java2
-rw-r--r--core/java/android/content/Context.java9
-rw-r--r--core/java/android/content/Intent.java18
-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/ApplicationInfo.java67
-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/PackageParser.java44
-rw-r--r--core/java/android/content/pm/SharedLibraryInfo.java4
-rw-r--r--core/java/android/content/pm/ShortcutInfo.java52
-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/content/pm/parsing/ParsingPackage.java1
-rw-r--r--core/java/android/content/pm/parsing/ParsingPackageImpl.java42
-rw-r--r--core/java/android/content/pm/parsing/ParsingPackageUtils.java2
-rw-r--r--core/java/android/content/pm/parsing/ParsingUtils.java2
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedPermissionImpl.java2
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedProcess.java10
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedProcessImpl.java60
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedProcessUtils.java5
-rw-r--r--core/java/android/content/res/AssetManager.java17
-rw-r--r--core/java/android/content/res/Resources.java43
-rw-r--r--core/java/android/content/res/ResourcesImpl.java8
-rw-r--r--core/java/android/content/res/TypedArray.java47
-rw-r--r--core/java/android/graphics/fonts/FontUpdateRequest.java6
-rw-r--r--core/java/android/hardware/HardwareBuffer.java10
-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/CameraManager.java198
-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/location/GeofenceHardwareMonitorEvent.java2
-rw-r--r--core/java/android/hardware/location/NanoAppRpcService.aidl22
-rw-r--r--core/java/android/hardware/location/NanoAppRpcService.java159
-rw-r--r--core/java/android/hardware/location/NanoAppState.java69
-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/vcn/VcnConfig.java2
-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.java26
-rw-r--r--core/java/android/os/BatteryStats.java19
-rw-r--r--core/java/android/os/BatteryUsageStats.java2
-rw-r--r--core/java/android/os/Binder.java110
-rw-r--r--core/java/android/os/BinderProxy.java2
-rw-r--r--core/java/android/os/GraphicsEnvironment.java7
-rw-r--r--core/java/android/os/Message.java2
-rw-r--r--core/java/android/os/Parcel.java23
-rw-r--r--core/java/android/os/PowerComponents.java5
-rw-r--r--core/java/android/os/StrictMode.java2
-rw-r--r--core/java/android/os/Trace.java40
-rw-r--r--core/java/android/os/UserManager.java11
-rw-r--r--core/java/android/os/VibrationEffect.java2
-rw-r--r--core/java/android/os/VibratorInfo.java2
-rw-r--r--core/java/android/os/WorkSource.java2
-rw-r--r--core/java/android/os/storage/StorageManager.java83
-rw-r--r--core/java/android/os/storage/StorageVolume.java2
-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/ContactsContract.java10
-rw-r--r--core/java/android/provider/Settings.java6
-rw-r--r--core/java/android/provider/Telephony.java14
-rw-r--r--core/java/android/security/keymaster/KeymasterDefs.java19
-rw-r--r--core/java/android/service/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/cloudsearch/OWNERS4
-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/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/quickaccesswallet/GetWalletCardsResponse.java2
-rw-r--r--core/java/android/service/selectiontoolbar/ISelectionToolbarRenderService.aidl31
-rw-r--r--core/java/android/service/selectiontoolbar/OWNERS10
-rw-r--r--core/java/android/service/selectiontoolbar/SelectionToolbarRenderCallback.java53
-rw-r--r--core/java/android/service/selectiontoolbar/SelectionToolbarRenderService.java182
-rw-r--r--core/java/android/service/settings/suggestions/Suggestion.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/wallpaper/WallpaperService.java5
-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/telephony/TelephonyRegistryManager.java146
-rw-r--r--core/java/android/text/FontConfig.java6
-rw-r--r--core/java/android/text/StaticLayout.java12
-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.java6
-rw-r--r--core/java/android/util/IntArray.java8
-rw-r--r--core/java/android/util/MemoryIntArray.java2
-rw-r--r--core/java/android/view/DisplayInfo.java6
-rw-r--r--core/java/android/view/KeyboardShortcutInfo.java2
-rw-r--r--core/java/android/view/MotionEvent.java2
-rw-r--r--core/java/android/view/SurfaceControl.java135
-rw-r--r--core/java/android/view/View.java90
-rw-r--r--core/java/android/view/ViewDebug.java6
-rw-r--r--core/java/android/view/ViewRootImpl.java38
-rw-r--r--core/java/android/view/accessibility/AccessibilityEvent.java2
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java147
-rw-r--r--core/java/android/view/accessibility/AccessibilityWindowInfo.java2
-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/InputConnection.java31
-rw-r--r--core/java/android/view/inputmethod/InputMethodInfo.java34
-rw-r--r--core/java/android/view/selectiontoolbar/ISelectionToolbarCallback.aidl33
-rw-r--r--core/java/android/view/selectiontoolbar/ISelectionToolbarManager.aidl31
-rw-r--r--core/java/android/view/selectiontoolbar/OWNERS10
-rw-r--r--core/java/android/view/selectiontoolbar/SelectionContext.aidl22
-rw-r--r--core/java/android/view/selectiontoolbar/SelectionContext.java248
-rw-r--r--core/java/android/view/selectiontoolbar/SelectionToolbarManager.java89
-rw-r--r--core/java/android/view/selectiontoolbar/ShowInfo.aidl22
-rw-r--r--core/java/android/view/selectiontoolbar/ShowInfo.java169
-rw-r--r--core/java/android/view/selectiontoolbar/ToolbarMenuItem.aidl22
-rw-r--r--core/java/android/view/selectiontoolbar/ToolbarMenuItem.java211
-rw-r--r--core/java/android/view/selectiontoolbar/WidgetInfo.aidl22
-rw-r--r--core/java/android/view/selectiontoolbar/WidgetInfo.java168
-rw-r--r--core/java/android/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/ExpandableListView.java2
-rw-r--r--core/java/android/widget/RemoteViews.java2
-rw-r--r--core/java/android/window/ITaskOrganizerController.aidl3
-rw-r--r--core/java/android/window/TaskOrganizer.java15
-rw-r--r--core/java/android/window/TransitionRequestInfo.aidl1
-rw-r--r--core/java/android/window/TransitionRequestInfo.java230
-rw-r--r--core/java/android/window/WindowInfosListener.java35
-rw-r--r--core/java/android/window/WindowTokenClient.java10
-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/app/chooser/DisplayResolveInfo.java4
-rw-r--r--core/java/com/android/internal/inputmethod/EditableInputConnection.java2
-rw-r--r--core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java134
-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/os/AppFuseMount.java2
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java189
-rw-r--r--core/java/com/android/internal/os/CpuPowerCalculator.java10
-rw-r--r--core/java/com/android/internal/os/LongMultiStateCounter.java22
-rw-r--r--core/java/com/android/internal/os/MobileRadioPowerCalculator.java82
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java2
-rw-r--r--core/java/com/android/internal/power/MeasuredEnergyStats.java4
-rw-r--r--core/java/com/android/internal/statusbar/StatusBarIcon.java4
-rw-r--r--core/java/com/android/internal/telephony/ICarrierPrivilegesListener.aidl22
-rw-r--r--core/java/com/android/internal/telephony/ITelephonyRegistry.aidl8
-rw-r--r--core/java/com/android/internal/util/LatencyTracker.java16
-rw-r--r--core/java/com/android/internal/util/ScreenshotHelper.java6
-rw-r--r--core/jni/android_os_Trace.cpp22
-rw-r--r--core/jni/android_util_AssetManager.cpp8
-rw-r--r--core/jni/android_view_InputQueue.cpp3
-rw-r--r--core/jni/android_view_SurfaceControl.cpp62
-rw-r--r--core/jni/android_window_WindowInfosListener.cpp38
-rw-r--r--core/jni/com_android_internal_os_LongMultiStateCounter.cpp6
-rw-r--r--core/jni/include/android_runtime/android_view_InputQueue.h2
-rw-r--r--core/res/AndroidManifest.xml33
-rw-r--r--core/res/res/drawable/ic_add_supervised_user.xml34
-rw-r--r--core/res/res/values-af/strings.xml15
-rw-r--r--core/res/res/values-am/strings.xml15
-rw-r--r--core/res/res/values-ar/strings.xml17
-rw-r--r--core/res/res/values-as/strings.xml15
-rw-r--r--core/res/res/values-az/strings.xml15
-rw-r--r--core/res/res/values-b+sr+Latn/strings.xml15
-rw-r--r--core/res/res/values-be/strings.xml21
-rw-r--r--core/res/res/values-bg/strings.xml15
-rw-r--r--core/res/res/values-bn/strings.xml15
-rw-r--r--core/res/res/values-bs/strings.xml17
-rw-r--r--core/res/res/values-ca/strings.xml15
-rw-r--r--core/res/res/values-cs/strings.xml15
-rw-r--r--core/res/res/values-da/strings.xml15
-rw-r--r--core/res/res/values-de/strings.xml15
-rw-r--r--core/res/res/values-el/strings.xml15
-rw-r--r--core/res/res/values-en-rAU/strings.xml15
-rw-r--r--core/res/res/values-en-rCA/strings.xml15
-rw-r--r--core/res/res/values-en-rGB/strings.xml15
-rw-r--r--core/res/res/values-en-rIN/strings.xml15
-rw-r--r--core/res/res/values-en-rXC/strings.xml15
-rw-r--r--core/res/res/values-es-rUS/strings.xml21
-rw-r--r--core/res/res/values-es/strings.xml15
-rw-r--r--core/res/res/values-et/strings.xml15
-rw-r--r--core/res/res/values-eu/strings.xml15
-rw-r--r--core/res/res/values-fa/strings.xml15
-rw-r--r--core/res/res/values-fi/strings.xml15
-rw-r--r--core/res/res/values-fr-rCA/strings.xml16
-rw-r--r--core/res/res/values-fr/strings.xml15
-rw-r--r--core/res/res/values-gl/strings.xml15
-rw-r--r--core/res/res/values-gu/strings.xml15
-rw-r--r--core/res/res/values-hi/strings.xml15
-rw-r--r--core/res/res/values-hr/strings.xml15
-rw-r--r--core/res/res/values-hu/strings.xml15
-rw-r--r--core/res/res/values-hy/strings.xml15
-rw-r--r--core/res/res/values-in/strings.xml15
-rw-r--r--core/res/res/values-is/strings.xml15
-rw-r--r--core/res/res/values-it/strings.xml15
-rw-r--r--core/res/res/values-iw/strings.xml15
-rw-r--r--core/res/res/values-ja/strings.xml15
-rw-r--r--core/res/res/values-ka/strings.xml15
-rw-r--r--core/res/res/values-kk/strings.xml15
-rw-r--r--core/res/res/values-km/strings.xml15
-rw-r--r--core/res/res/values-kn/strings.xml15
-rw-r--r--core/res/res/values-ko/strings.xml15
-rw-r--r--core/res/res/values-ky/strings.xml15
-rw-r--r--core/res/res/values-lo/strings.xml15
-rw-r--r--core/res/res/values-lt/strings.xml15
-rw-r--r--core/res/res/values-lv/strings.xml19
-rw-r--r--core/res/res/values-mk/strings.xml15
-rw-r--r--core/res/res/values-ml/strings.xml15
-rw-r--r--core/res/res/values-mn/strings.xml15
-rw-r--r--core/res/res/values-mr/strings.xml15
-rw-r--r--core/res/res/values-ms/strings.xml15
-rw-r--r--core/res/res/values-my/strings.xml21
-rw-r--r--core/res/res/values-nb/strings.xml15
-rw-r--r--core/res/res/values-ne/strings.xml15
-rw-r--r--core/res/res/values-nl/strings.xml15
-rw-r--r--core/res/res/values-or/strings.xml15
-rw-r--r--core/res/res/values-pa/strings.xml15
-rw-r--r--core/res/res/values-pl/strings.xml15
-rw-r--r--core/res/res/values-pt-rBR/strings.xml23
-rw-r--r--core/res/res/values-pt-rPT/strings.xml15
-rw-r--r--core/res/res/values-pt/strings.xml23
-rw-r--r--core/res/res/values-ro/strings.xml15
-rw-r--r--core/res/res/values-ru/strings.xml15
-rw-r--r--core/res/res/values-si/strings.xml15
-rw-r--r--core/res/res/values-sk/strings.xml15
-rw-r--r--core/res/res/values-sl/strings.xml15
-rw-r--r--core/res/res/values-sq/strings.xml15
-rw-r--r--core/res/res/values-sr/strings.xml15
-rw-r--r--core/res/res/values-sv/strings.xml15
-rw-r--r--core/res/res/values-sw/strings.xml15
-rw-r--r--core/res/res/values-ta/strings.xml15
-rw-r--r--core/res/res/values-te/strings.xml21
-rw-r--r--core/res/res/values-th/strings.xml15
-rw-r--r--core/res/res/values-tl/strings.xml15
-rw-r--r--core/res/res/values-tr/strings.xml15
-rw-r--r--core/res/res/values-uk/strings.xml15
-rw-r--r--core/res/res/values-ur/strings.xml15
-rw-r--r--core/res/res/values-uz/strings.xml15
-rw-r--r--core/res/res/values-vi/strings.xml15
-rw-r--r--core/res/res/values-zh-rCN/strings.xml15
-rw-r--r--core/res/res/values-zh-rHK/strings.xml15
-rw-r--r--core/res/res/values-zh-rTW/strings.xml15
-rw-r--r--core/res/res/values-zu/strings.xml15
-rw-r--r--core/res/res/values/attrs.xml3
-rw-r--r--core/res/res/values/attrs_manifest.xml3
-rw-r--r--core/res/res/values/config.xml32
-rw-r--r--core/res/res/values/public.xml3
-rw-r--r--core/res/res/values/strings.xml2
-rw-r--r--core/res/res/values/symbols.xml5
-rw-r--r--core/tests/coretests/Android.bp1
-rw-r--r--core/tests/coretests/src/android/os/AidlTest.java22
-rw-r--r--core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java49
-rw-r--r--core/tests/coretests/src/android/view/MotionEventTest.java24
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java5
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java2
-rw-r--r--core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java182
-rw-r--r--core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java5
-rw-r--r--core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java19
-rw-r--r--data/etc/privapp-permissions-platform.xml3
-rw-r--r--data/etc/services.core.protolog.json6
-rw-r--r--graphics/java/android/graphics/BaseCanvas.java117
-rw-r--r--graphics/java/android/graphics/Picture.java15
-rw-r--r--graphics/java/android/graphics/RuntimeShader.java292
-rw-r--r--keystore/java/android/security/KeyStoreException.java433
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java5
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java24
-rw-r--r--libs/WindowManager/Shell/res/color/compat_background_ripple.xml (renamed from libs/WindowManager/Shell/res/color/size_compat_background_ripple.xml)0
-rw-r--r--libs/WindowManager/Shell/res/drawable/camera_compat_dismiss_button.xml33
-rw-r--r--libs/WindowManager/Shell/res/drawable/camera_compat_dismiss_ripple.xml (renamed from packages/CompanionDeviceManager/res/layout/profile_summary.xml)20
-rw-r--r--libs/WindowManager/Shell/res/drawable/camera_compat_treatment_applied_button.xml32
-rw-r--r--libs/WindowManager/Shell/res/drawable/camera_compat_treatment_applied_ripple.xml20
-rw-r--r--libs/WindowManager/Shell/res/drawable/camera_compat_treatment_suggested_button.xml53
-rw-r--r--libs/WindowManager/Shell/res/drawable/camera_compat_treatment_suggested_ripple.xml20
-rw-r--r--libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml4
-rw-r--r--libs/WindowManager/Shell/res/drawable/size_compat_restart_button_ripple.xml2
-rw-r--r--libs/WindowManager/Shell/res/layout/compat_mode_hint.xml4
-rw-r--r--libs/WindowManager/Shell/res/layout/compat_ui_layout.xml49
-rw-r--r--libs/WindowManager/Shell/res/values/dimen.xml6
-rw-r--r--libs/WindowManager/Shell/res/values/strings.xml13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java24
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java72
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java31
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java74
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java80
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java114
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java117
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java26
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java20
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java52
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java265
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenTransitions.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java150
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java17
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt50
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java84
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java9
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java36
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java14
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java12
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java126
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java93
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java157
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java123
-rw-r--r--libs/androidfw/AssetManager2.cpp21
-rw-r--r--libs/androidfw/TEST_MAPPING3
-rw-r--r--libs/androidfw/include/androidfw/AssetManager2.h6
-rw-r--r--libs/hwui/jni/Shader.cpp99
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp5
-rw-r--r--libs/hwui/renderthread/EglManager.cpp14
-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/AudioPortDeviceExt.aidl13
-rw-r--r--media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPortDeviceExt.aidl2
-rwxr-xr-xmedia/java/android/media/IAudioService.aidl4
-rw-r--r--media/java/android/media/MediaDescription.java6
-rw-r--r--media/java/android/media/MediaRoute2Info.java2
-rw-r--r--media/java/android/media/audiopolicy/AudioPolicy.java42
-rw-r--r--media/java/android/media/midi/MidiDeviceStatus.java2
-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/TvContentRatingSystemInfo.java4
-rw-r--r--media/java/android/media/tv/TvContract.java62
-rw-r--r--media/java/android/media/tv/TvInputInfo.java10
-rw-r--r--media/java/android/media/tv/interactive/ITvIAppClient.aidl5
-rw-r--r--media/java/android/media/tv/interactive/ITvIAppManager.aidl3
-rw-r--r--media/java/android/media/tv/interactive/ITvIAppService.aidl3
-rw-r--r--media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl4
-rw-r--r--media/java/android/media/tv/interactive/TvIAppManager.java133
-rw-r--r--media/java/android/media/tv/interactive/TvIAppService.java77
-rw-r--r--media/java/android/media/tv/interactive/TvIAppView.java45
-rw-r--r--media/java/android/media/tv/tuner/Tuner.java61
-rw-r--r--media/java/android/media/tv/tuner/dvr/DvrPlayback.java28
-rw-r--r--media/java/android/media/tv/tuner/dvr/DvrRecorder.java13
-rw-r--r--media/java/android/media/tv/tuner/filter/DownloadEvent.java14
-rw-r--r--media/java/android/media/tv/tuner/filter/DownloadSettings.java35
-rw-r--r--media/java/android/media/tv/tuner/filter/Filter.java3
-rw-r--r--media/java/android/media/tv/tuner/filter/MediaEvent.java30
-rw-r--r--media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java2
-rw-r--r--media/java/android/media/tv/tuner/filter/SectionEvent.java11
-rw-r--r--media/java/android/media/tv/tuner/filter/SectionSettings.java27
-rw-r--r--media/java/android/media/tv/tuner/filter/TsRecordEvent.java2
-rw-r--r--media/java/android/media/tv/tuner/frontend/FrontendStatus.java55
-rw-r--r--media/java/android/media/tv/tuner/frontend/ScanCallback.java7
-rw-r--r--media/jni/android_media_tv_Tuner.cpp228
-rw-r--r--media/jni/android_media_tv_Tuner.h16
-rw-r--r--media/jni/soundpool/Stream.cpp20
-rw-r--r--media/jni/soundpool/Stream.h9
-rw-r--r--media/jni/soundpool/StreamManager.cpp13
-rw-r--r--media/jni/tuner/DvrClient.cpp10
-rw-r--r--media/jni/tuner/DvrClient.h5
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java5
-rw-r--r--native/android/input.cpp4
-rw-r--r--native/android/libandroid.map.txt1
-rw-r--r--native/android/surface_control.cpp10
-rw-r--r--packages/CarrierDefaultApp/AndroidManifest.xml1
-rw-r--r--packages/CompanionDeviceManager/AndroidManifest.xml26
-rw-r--r--packages/CompanionDeviceManager/res/layout/activity_confirmation.xml81
-rw-r--r--packages/CompanionDeviceManager/res/layout/buttons.xml43
-rw-r--r--packages/CompanionDeviceManager/res/layout/device_chooser.xml39
-rw-r--r--packages/CompanionDeviceManager/res/layout/device_confirmation.xml30
-rw-r--r--packages/CompanionDeviceManager/res/layout/title.xml25
-rw-r--r--packages/CompanionDeviceManager/res/values-af/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-am/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-ar/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-as/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-az/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-be/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-bg/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-bn/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-bs/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-ca/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-cs/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-da/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-de/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-el/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-en-rAU/strings.xml14
-rw-r--r--packages/CompanionDeviceManager/res/values-en-rCA/strings.xml14
-rw-r--r--packages/CompanionDeviceManager/res/values-en-rGB/strings.xml14
-rw-r--r--packages/CompanionDeviceManager/res/values-en-rIN/strings.xml14
-rw-r--r--packages/CompanionDeviceManager/res/values-en-rXC/strings.xml14
-rw-r--r--packages/CompanionDeviceManager/res/values-es-rUS/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-es/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-et/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-eu/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-fa/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-fi/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-fr/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-gl/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-gu/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-hi/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-hr/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-hu/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-hy/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-in/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-is/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-it/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-iw/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-ja/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-ka/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-kk/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-km/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-kn/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-ko/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-ky/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-lo/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-lt/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-lv/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-mk/strings.xml14
-rw-r--r--packages/CompanionDeviceManager/res/values-ml/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-mn/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-mr/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-ms/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-my/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-nb/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-ne/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-nl/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-or/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-pa/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-pl/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-pt/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-ro/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-ru/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-si/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-sk/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-sl/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-sq/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-sr/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-sv/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-sw/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-ta/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-te/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-th/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-tl/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-tr/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-uk/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-ur/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-uz/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-vi/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values-zu/strings.xml20
-rw-r--r--packages/CompanionDeviceManager/res/values/dimens.xml7
-rw-r--r--packages/CompanionDeviceManager/res/values/strings.xml51
-rw-r--r--packages/CompanionDeviceManager/res/values/styles.xml29
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java519
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java623
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceFilterPair.java108
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java123
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java84
-rw-r--r--packages/ConnectivityT/OWNERS3
-rw-r--r--packages/ConnectivityT/framework-t/Android.bp51
-rw-r--r--packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java11
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/ConnectivityFrameworkInitializerTiramisu.java52
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/DataUsageRequest.java2
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java (renamed from core/java/android/net/EthernetManager.java)0
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkSpecifier.java (renamed from core/java/android/net/EthernetNetworkSpecifier.java)0
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/IEthernetManager.aidl (renamed from core/java/android/net/IEthernetManager.aidl)0
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/IEthernetServiceListener.aidl (renamed from core/java/android/net/IEthernetServiceListener.aidl)0
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/IIpSecService.aidl (renamed from core/java/android/net/IIpSecService.aidl)0
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/ITetheredInterfaceCallback.aidl (renamed from core/java/android/net/ITetheredInterfaceCallback.aidl)0
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/IpSecAlgorithm.java (renamed from core/java/android/net/IpSecAlgorithm.java)11
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/IpSecConfig.aidl (renamed from core/java/android/net/IpSecConfig.aidl)0
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/IpSecConfig.java (renamed from core/java/android/net/IpSecConfig.java)8
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java (renamed from core/java/android/net/IpSecManager.java)12
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/IpSecSpiResponse.aidl (renamed from core/java/android/net/IpSecSpiResponse.aidl)0
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/IpSecSpiResponse.java (renamed from core/java/android/net/IpSecSpiResponse.java)0
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/IpSecTransform.java (renamed from core/java/android/net/IpSecTransform.java)0
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/IpSecTransformResponse.aidl (renamed from core/java/android/net/IpSecTransformResponse.aidl)0
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/IpSecTransformResponse.java (renamed from core/java/android/net/IpSecTransformResponse.java)3
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/IpSecTunnelInterfaceResponse.aidl (renamed from core/java/android/net/IpSecTunnelInterfaceResponse.aidl)0
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/IpSecTunnelInterfaceResponse.java (renamed from core/java/android/net/IpSecTunnelInterfaceResponse.java)3
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/IpSecUdpEncapResponse.aidl (renamed from core/java/android/net/IpSecUdpEncapResponse.aidl)0
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/IpSecUdpEncapResponse.java (renamed from core/java/android/net/IpSecUdpEncapResponse.java)6
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.java6
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/UnderlyingNetworkInfo.java2
-rw-r--r--packages/ConnectivityT/framework-t/src/android/net/nsd/NsdManager.java90
-rw-r--r--packages/ConnectivityT/service/Android.bp34
-rw-r--r--packages/ConnectivityT/service/src/com/android/server/IpSecService.java (renamed from services/core/java/com/android/server/IpSecService.java)258
-rw-r--r--packages/ConnectivityT/service/src/com/android/server/net/IpConfigStore.java (renamed from services/core/java/com/android/server/net/IpConfigStore.java)32
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/Android.bp6
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java19
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java4
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java165
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java1
-rw-r--r--packages/Shell/AndroidManifest.xml3
-rw-r--r--packages/SystemUI/Android.bp33
-rw-r--r--packages/SystemUI/TEST_MAPPING9
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt13
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt10
-rw-r--r--packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java15
-rw-r--r--packages/SystemUI/proguard.flags12
-rw-r--r--packages/SystemUI/res-keyguard/drawable/super_lock_icon.xml5
-rw-r--r--packages/SystemUI/res-keyguard/drawable/unlocked_to_aod_lock.xml169
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml13
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_slice_view.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values/dimens.xml8
-rw-r--r--packages/SystemUI/res-keyguard/values/styles.xml3
-rw-r--r--packages/SystemUI/res/drawable/ic_signal_wifi_off.xml24
-rw-r--r--packages/SystemUI/res/layout/combined_qs_header.xml12
-rw-r--r--packages/SystemUI/res/layout/dream_overlay_container.xml61
-rw-r--r--packages/SystemUI/res/layout/internet_connectivity_dialog.xml16
-rw-r--r--packages/SystemUI/res/values-h800dp/dimens.xml2
-rw-r--r--packages/SystemUI/res/values/dimens.xml7
-rw-r--r--packages/SystemUI/res/xml/combined_qs_header_scene.xml13
-rw-r--r--packages/SystemUI/res/xml/qqs_header.xml19
-rw-r--r--packages/SystemUI/res/xml/qs_header.xml11
-rw-r--r--packages/SystemUI/res/xml/split_header.xml24
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl7
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java2
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java18
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java4
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java315
-rw-r--r--packages/SystemUI/src/com/android/keyguard/AnimatableClockView.kt370
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java20
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java25
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java13
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java10
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java13
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java25
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherPopupMenu.java8
-rw-r--r--packages/SystemUI/src/com/android/keyguard/LockIconViewController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIApplication.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/CommunalSourcePrimer.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeUi.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerView.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java140
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java86
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java172
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java62
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java105
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java81
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/PendingDrawnTasksContainer.kt82
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/RecommendationViewHolder.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt59
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipState.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/FgsManagerTile.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java56
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java133
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java42
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java99
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt49
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt97
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java72
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarDemoMode.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt173
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt83
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/condition/dagger/MonitorComponent.java (renamed from packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalConditionsMonitor.java)34
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java32
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/communal/CommunalManagerUpdaterTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java31
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java61
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java195
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt134
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java86
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt133
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt216
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java58
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java114
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java48
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java52
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java34
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt44
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java53
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt110
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt37
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java7
-rw-r--r--services/Android.bp2
-rw-r--r--services/accessibility/Android.bp14
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java6
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java118
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java39
-rw-r--r--services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java6
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java25
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java51
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java121
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java11
-rw-r--r--services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java6
-rw-r--r--services/backup/java/com/android/server/backup/UserBackupManagerService.java313
-rw-r--r--services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java4
-rw-r--r--services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java4
-rw-r--r--services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java8
-rw-r--r--services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java59
-rw-r--r--services/backup/java/com/android/server/backup/internal/BackupHandler.java15
-rw-r--r--services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java17
-rw-r--r--services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java8
-rw-r--r--services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java14
-rw-r--r--services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java12
-rw-r--r--services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java19
-rw-r--r--services/cloudsearch/OWNERS4
-rw-r--r--services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java369
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java5
-rw-r--r--services/companion/java/com/android/server/companion/PermissionsUtils.java23
-rw-r--r--services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java14
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java32
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java9
-rw-r--r--services/core/Android.bp3
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java184
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java26
-rw-r--r--services/core/java/com/android/server/am/AppProfiler.java6
-rw-r--r--services/core/java/com/android/server/am/BatteryExternalStatsWorker.java25
-rw-r--r--services/core/java/com/android/server/am/PhantomProcessList.java6
-rw-r--r--services/core/java/com/android/server/app/GameManagerService.java79
-rw-r--r--services/core/java/com/android/server/app/GameManagerShellCommand.java12
-rw-r--r--services/core/java/com/android/server/audio/AudioManagerShellCommand.java165
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java35
-rw-r--r--services/core/java/com/android/server/audio/MediaFocusControl.java46
-rw-r--r--services/core/java/com/android/server/clipboard/ClipboardService.java79
-rw-r--r--services/core/java/com/android/server/communal/CommunalManagerService.java8
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java62
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerShellCommand.java197
-rw-r--r--services/core/java/com/android/server/display/DisplayModeDirector.java6
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java5
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodBindingController.java81
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java20
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java158
-rw-r--r--services/core/java/com/android/server/location/LocationManagerService.java8
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java8
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java9
-rw-r--r--services/core/java/com/android/server/location/injector/Injector.java3
-rw-r--r--services/core/java/com/android/server/location/injector/LocationAttributionHelper.java122
-rw-r--r--services/core/java/com/android/server/location/provider/LocationProviderManager.java32
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java12
-rw-r--r--services/core/java/com/android/server/pm/ApexManager.java55
-rw-r--r--services/core/java/com/android/server/pm/AppDataHelper.java7
-rw-r--r--services/core/java/com/android/server/pm/ComputerEngine.java2
-rw-r--r--services/core/java/com/android/server/pm/DeletePackageHelper.java29
-rw-r--r--services/core/java/com/android/server/pm/DomainVerificationConnection.java2
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java1393
-rw-r--r--services/core/java/com/android/server/pm/PackageHandler.java90
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java18
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java42
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java58
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceInjector.java10
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java1
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceUtils.java95
-rw-r--r--services/core/java/com/android/server/pm/PackageRemovedInfo.java54
-rw-r--r--services/core/java/com/android/server/pm/ReconcilePackageUtils.java292
-rw-r--r--services/core/java/com/android/server/pm/ScanPackageHelper.java1716
-rw-r--r--services/core/java/com/android/server/pm/ScanPackageUtils.java1006
-rw-r--r--services/core/java/com/android/server/pm/Settings.java13
-rw-r--r--services/core/java/com/android/server/pm/ShortcutPackage.java77
-rw-r--r--services/core/java/com/android/server/pm/ShortcutService.java4
-rw-r--r--services/core/java/com/android/server/pm/VerificationParams.java399
-rw-r--r--services/core/java/com/android/server/pm/VerificationUtils.java31
-rw-r--r--services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java3
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java7
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java1
-rw-r--r--services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java9
-rw-r--r--services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java35
-rw-r--r--services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java34
-rw-r--r--services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java142
-rw-r--r--services/core/java/com/android/server/uri/UriGrantsManagerService.java14
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java140
-rw-r--r--services/core/java/com/android/server/wm/ActivityClientController.java17
-rw-r--r--services/core/java/com/android/server/wm/ActivityInterceptorCallback.java25
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java108
-rw-r--r--services/core/java/com/android/server/wm/ActivityStartInterceptor.java8
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java3
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java8
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java3
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java65
-rw-r--r--services/core/java/com/android/server/wm/DisplayRotation.java8
-rw-r--r--services/core/java/com/android/server/wm/DisplayWindowSettings.java13
-rw-r--r--services/core/java/com/android/server/wm/FadeAnimationController.java25
-rw-r--r--services/core/java/com/android/server/wm/FadeRotationAnimationController.java100
-rw-r--r--services/core/java/com/android/server/wm/InsetsSourceProvider.java8
-rw-r--r--services/core/java/com/android/server/wm/LockTaskController.java48
-rw-r--r--services/core/java/com/android/server/wm/NavBarFadeAnimationController.java8
-rw-r--r--services/core/java/com/android/server/wm/PinnedTaskController.java5
-rw-r--r--services/core/java/com/android/server/wm/RefreshRatePolicy.java14
-rw-r--r--services/core/java/com/android/server/wm/SeamlessRotator.java14
-rw-r--r--services/core/java/com/android/server/wm/Task.java15
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java2
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java31
-rw-r--r--services/core/java/com/android/server/wm/Transition.java10
-rw-r--r--services/core/java/com/android/server/wm/TransitionController.java17
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerInternal.java26
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java6
-rw-r--r--services/core/jni/Android.bp2
-rw-r--r--services/core/jni/com_android_server_UsbAlsaJackDetector.cpp9
-rw-r--r--services/core/jni/com_android_server_location_GnssLocationProvider.cpp2
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java71
-rw-r--r--services/java/com/android/server/SystemServer.java37
-rw-r--r--services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java5
-rw-r--r--services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java6
-rw-r--r--services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java1
-rw-r--r--services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupTask.java2
-rw-r--r--services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java2
-rw-r--r--services/selectiontoolbar/Android.bp22
-rw-r--r--services/selectiontoolbar/OWNERS1
-rw-r--r--services/selectiontoolbar/java/com/android/server/selectiontoolbar/DefaultSelectionToolbarRenderService.java48
-rw-r--r--services/selectiontoolbar/java/com/android/server/selectiontoolbar/RemoteSelectionToolbarRenderService.java68
-rw-r--r--services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerService.java104
-rw-r--r--services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerServiceImpl.java128
-rw-r--r--services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarServiceNameResolver.java43
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt8
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt29
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/injector/FakeAppOpsHelper.java11
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/injector/LocationAttributionHelperTest.java143
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java19
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java33
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java122
-rw-r--r--services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java43
-rw-r--r--services/tests/servicestests/src/com/android/server/backup/internal/BackupHandlerTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java26
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ScanTests.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java42
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java22
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java24
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java187
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java27
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java11
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java9
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java12
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TransitionTests.java40
-rw-r--r--services/usage/java/com/android/server/usage/StorageStatsService.java22
-rw-r--r--telecomm/java/android/telecom/CallAudioState.java4
-rw-r--r--telecomm/java/android/telecom/Connection.java4
-rw-r--r--telecomm/java/android/telecom/ConnectionRequest.java12
-rw-r--r--telecomm/java/android/telecom/DisconnectCause.java2
-rw-r--r--telecomm/java/android/telecom/ParcelableCall.java16
-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/PhoneAccountSuggestion.java2
-rw-r--r--telecomm/java/android/telecom/StatusHints.java4
-rw-r--r--telephony/java/android/service/carrier/CarrierService.java98
-rw-r--r--telephony/java/android/service/carrier/ICarrierService.aidl2
-rw-r--r--telephony/java/android/telephony/AccessNetworkConstants.java18
-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.java69
-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/NetworkRegistrationInfo.java8
-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/SignalStrengthUpdateRequest.java8
-rw-r--r--telephony/java/android/telephony/SubscriptionInfo.java53
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java101
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java322
-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/DataService.java31
-rw-r--r--telephony/java/android/telephony/data/DataServiceCallback.java27
-rw-r--r--telephony/java/android/telephony/data/IDataServiceCallback.aidl2
-rw-r--r--telephony/java/android/telephony/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/euicc/EuiccCardManager.java97
-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
-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/ImsService.java133
-rw-r--r--telephony/java/android/telephony/ims/ImsSsData.java4
-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/feature/MmTelFeature.java271
-rw-r--r--telephony/java/android/telephony/ims/feature/RcsFeature.java21
-rw-r--r--telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java4
-rw-r--r--telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java146
-rw-r--r--telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java377
-rw-r--r--telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java38
-rw-r--r--telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java37
-rw-r--r--telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java114
-rw-r--r--telephony/java/android/telephony/ims/stub/ImsUtImplBase.java109
-rw-r--r--telephony/java/android/telephony/ims/stub/SipTransportImplBase.java25
-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
-rwxr-xr-xtelephony/java/com/android/internal/telephony/ISub.aidl11
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl10
-rw-r--r--telephony/java/com/android/internal/telephony/NetworkScanResult.java2
-rw-r--r--telephony/java/com/android/internal/telephony/OperatorInfo.java2
-rw-r--r--telephony/java/com/android/internal/telephony/RILConstants.java2
-rw-r--r--telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt6
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt6
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt56
-rw-r--r--tests/MultiUser/Android.bp27
-rw-r--r--tests/MultiUser/AndroidManifest.xml18
-rw-r--r--tests/MultiUser/TEST_MAPPING7
-rw-r--r--tests/MultiUser/src/com/android/test/multiuser/MultiUserSettingsTests.java164
-rw-r--r--tools/aapt2/link/ManifestFixer.cpp11
1043 files changed, 29057 insertions, 10886 deletions
diff --git a/Android.bp b/Android.bp
index 54983d615913..acd3b34d4e7b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -25,6 +25,18 @@
//
// READ ME: ########################################################
+// TODO(b/21090328): Remove filter after we are ready to.
+soong_config_module_type {
+ name: "java_library_with_nonpublic_deps",
+ module_type: "java_library",
+ config_namespace: "ANDROID",
+ bool_variables: ["include_nonpublic_framework_api"],
+ properties: [
+ "static_libs",
+ "libs",
+ ],
+}
+
package {
default_applicable_licenses: ["frameworks_base_license"],
}
@@ -142,7 +154,7 @@ filegroup {
],
}
-java_library {
+java_library_with_nonpublic_deps {
name: "framework-updatable-stubs-module_libs_api",
static_libs: [
"android.net.ipsec.ike.stubs.module_lib",
@@ -156,17 +168,23 @@ java_library {
"framework-scheduling.stubs.module_lib",
"framework-sdkextensions.stubs.module_lib",
"framework-statsd.stubs.module_lib",
- "framework-supplementalapi.stubs.module_lib",
"framework-supplementalprocess.stubs.module_lib",
"framework-tethering.stubs.module_lib",
"framework-uwb.stubs.module_lib",
"framework-wifi.stubs.module_lib",
],
+ soong_config_variables: {
+ include_nonpublic_framework_api: {
+ static_libs: [
+ "framework-supplementalapi.stubs.module_lib",
+ ],
+ },
+ },
sdk_version: "module_current",
visibility: ["//visibility:private"],
}
-java_library {
+java_library_with_nonpublic_deps {
name: "framework-all",
installable: false,
static_libs: [
@@ -181,13 +199,19 @@ java_library {
"framework-scheduling.impl",
"framework-sdkextensions.impl",
"framework-statsd.impl",
- "framework-supplementalapi.impl",
"framework-supplementalprocess.impl",
"framework-tethering.impl",
"framework-uwb.impl",
"framework-wifi.impl",
"updatable-media",
],
+ soong_config_variables: {
+ include_nonpublic_framework_api: {
+ static_libs: [
+ "framework-supplementalapi.stubs.module_lib",
+ ],
+ },
+ },
apex_available: ["//apex_available:platform"],
sdk_version: "core_platform",
visibility: [
@@ -364,6 +388,7 @@ java_defaults {
"tv_tuner_resource_manager_aidl_interface-java",
"soundtrigger_middleware-aidl-java",
"modules-utils-preconditions",
+ "modules-utils-synchronous-result-receiver",
"modules-utils-os",
"framework-permission-aidl-java",
"spatializer-aidl-java",
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 92c63c9ab841..9543fbd38a73 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -23,6 +23,14 @@
// and comparing them against the checked in API signature, and also checking compatibility
// with the latest frozen API signature.
+// TODO(b/21090328): Remove filter after we are ready to.
+soong_config_module_type_import {
+ from: "frameworks/base/Android.bp",
+ module_types: [
+ "java_library_with_nonpublic_deps",
+ ],
+}
+
/////////////////////////////////////////////////////////////////////
// Common metalava configs
/////////////////////////////////////////////////////////////////////
@@ -250,7 +258,6 @@ modules_public_stubs = [
"framework-scheduling.stubs",
"framework-sdkextensions.stubs",
"framework-statsd.stubs",
- "framework-supplementalapi.stubs",
"framework-supplementalprocess.stubs",
"framework-tethering.stubs",
"framework-uwb.stubs",
@@ -273,7 +280,6 @@ modules_system_stubs = [
"framework-scheduling.stubs.system",
"framework-sdkextensions.stubs.system",
"framework-statsd.stubs.system",
- "framework-supplementalapi.stubs",
"framework-supplementalprocess.stubs",
"framework-tethering.stubs.system",
"framework-uwb.stubs.system",
@@ -301,21 +307,35 @@ java_defaults {
visibility: ["//visibility:private"],
}
-java_library {
+java_library_with_nonpublic_deps {
name: "android-non-updatable.stubs",
defaults: ["android-non-updatable_defaults_stubs_current"],
srcs: [":api-stubs-docs-non-updatable"],
libs: modules_public_stubs,
+ soong_config_variables: {
+ include_nonpublic_framework_api: {
+ libs: [
+ "framework-supplementalapi.stubs",
+ ],
+ },
+ },
dist: {
dir: "apistubs/android/public",
},
}
-java_library {
+java_library_with_nonpublic_deps {
name: "android-non-updatable.stubs.system",
defaults: ["android-non-updatable_defaults_stubs_current"],
srcs: [":system-api-stubs-docs-non-updatable"],
libs: modules_system_stubs,
+ soong_config_variables: {
+ include_nonpublic_framework_api: {
+ libs: [
+ "framework-supplementalapi.stubs",
+ ],
+ },
+ },
dist: {
dir: "apistubs/android/system",
},
@@ -336,11 +356,18 @@ java_library {
},
}
-java_library {
+java_library_with_nonpublic_deps {
name: "android-non-updatable.stubs.test",
defaults: ["android-non-updatable_defaults_stubs_current"],
srcs: [":test-api-stubs-docs-non-updatable"],
libs: modules_system_stubs,
+ soong_config_variables: {
+ include_nonpublic_framework_api: {
+ libs: [
+ "framework-supplementalapi.stubs",
+ ],
+ },
+ },
dist: {
dir: "apistubs/android/test",
},
@@ -359,21 +386,35 @@ java_defaults {
defaults_visibility: ["//frameworks/base/services"],
}
-java_library {
+java_library_with_nonpublic_deps {
name: "android_stubs_current",
static_libs: modules_public_stubs + [
"android-non-updatable.stubs",
"private-stub-annotations-jar",
],
+ soong_config_variables: {
+ include_nonpublic_framework_api: {
+ static_libs: [
+ "framework-supplementalapi.stubs",
+ ],
+ },
+ },
defaults: ["android.jar_defaults"],
}
-java_library {
+java_library_with_nonpublic_deps {
name: "android_system_stubs_current",
static_libs: modules_system_stubs + [
"android-non-updatable.stubs.system",
"private-stub-annotations-jar",
],
+ soong_config_variables: {
+ include_nonpublic_framework_api: {
+ static_libs: [
+ "framework-supplementalapi.stubs",
+ ],
+ },
+ },
defaults: [
"android.jar_defaults",
"android_stubs_dists_default",
@@ -394,7 +435,7 @@ java_library {
],
}
-java_library {
+java_library_with_nonpublic_deps {
name: "android_test_stubs_current",
// Modules do not have test APIs, but we want to include their SystemApis, like we include
// the SystemApi of framework-non-updatable-sources.
@@ -402,6 +443,13 @@ java_library {
"android-non-updatable.stubs.test",
"private-stub-annotations-jar",
],
+ soong_config_variables: {
+ include_nonpublic_framework_api: {
+ static_libs: [
+ "framework-supplementalapi.stubs",
+ ],
+ },
+ },
defaults: [
"android.jar_defaults",
"android_stubs_dists_default",
diff --git a/apex/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 630d5cef7311..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();
@@ -1542,11 +1543,25 @@ public class JobInfo implements Parcelable {
*
* <p>Note that trigger URIs can not be used in combination with
* {@link #setPeriodic(long)} or {@link #setPersisted(boolean)}. To continually monitor
- * for content changes, you need to schedule a new JobInfo observing the same URIs
- * before you finish execution of the JobService handling the most recent changes.
+ * for content changes, you need to schedule a new JobInfo using the same job ID and
+ * observing the same URIs in place of calling
+ * {@link JobService#jobFinished(JobParameters, boolean)}. Remember that
+ * {@link JobScheduler#schedule(JobInfo)} stops a running job if it uses the same job ID,
+ * so only call it after you've finished processing the most recent changes (in other words,
+ * call {@link JobScheduler#schedule(JobInfo)} where you would have normally called
+ * {@link JobService#jobFinished(JobParameters, boolean)}.
* Following this pattern will ensure you do not lose any content changes: while your
* job is running, the system will continue monitoring for content changes, and propagate
- * any it sees over to the next job you schedule.</p>
+ * any changes it sees over to the next job you schedule, so you do not have to worry
+ * about missing new changes. <b>Scheduling the new job
+ * before or during processing will cause the current job to be stopped (as described in
+ * {@link JobScheduler#schedule(JobInfo)}), meaning the wakelock will be released for the
+ * current job and your app process may be killed since it will no longer be in a valid
+ * component lifecycle.</b>
+ * Since {@link JobScheduler#schedule(JobInfo)} stops the current job, you do not
+ * need to call {@link JobService#jobFinished(JobParameters, boolean)} if you call
+ * {@link JobScheduler#schedule(JobInfo)} using the same job ID as the
+ * currently running job.</p>
*
* <p>Because setting this property is not compatible with periodic or
* persisted jobs, doing so will throw an {@link java.lang.IllegalArgumentException} when
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 3da508d490f1..86d7a5a5a62e 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1559,6 +1559,13 @@ public class JobSchedulerService extends com.android.server.SystemService
}
}
+ /** Return the current bias of the given UID. */
+ public int getUidBias(int uid) {
+ synchronized (mLock) {
+ return mUidBiasOverride.get(uid, JobInfo.BIAS_DEFAULT);
+ }
+ }
+
@Override
public void onDeviceIdleStateChanged(boolean deviceIdle) {
synchronized (mLock) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 9cae864576ce..00d1bfffd9a6 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -366,9 +366,6 @@ public final class JobServiceContext implements ServiceConnection {
} catch (RemoteException e) {
// Whatever.
}
- mEconomyManagerInternal.noteOngoingEventStarted(
- job.getSourceUserId(), job.getSourcePackageName(),
- getRunningActionId(job), String.valueOf(job.getJobId()));
final String jobPackage = job.getSourcePackageName();
final int jobUserId = job.getSourceUserId();
UsageStatsManagerInternal usageStats =
@@ -401,25 +398,6 @@ public final class JobServiceContext implements ServiceConnection {
}
}
- @EconomicPolicy.AppAction
- private static int getRunningActionId(@NonNull JobStatus job) {
- switch (job.getEffectivePriority()) {
- case JobInfo.PRIORITY_MAX:
- return JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING;
- case JobInfo.PRIORITY_HIGH:
- return JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING;
- case JobInfo.PRIORITY_LOW:
- return JobSchedulerEconomicPolicy.ACTION_JOB_LOW_RUNNING;
- case JobInfo.PRIORITY_MIN:
- return JobSchedulerEconomicPolicy.ACTION_JOB_MIN_RUNNING;
- default:
- Slog.wtf(TAG, "Unknown priority: " + getPriorityString(job.getEffectivePriority()));
- // Intentional fallthrough
- case JobInfo.PRIORITY_DEFAULT:
- return JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING;
- }
- }
-
/**
* Used externally to query the running job. Will return null if there is no job running.
*/
@@ -1043,9 +1021,6 @@ public final class JobServiceContext implements ServiceConnection {
} catch (RemoteException e) {
// Whatever.
}
- mEconomyManagerInternal.noteOngoingEventStopped(
- mRunningJob.getSourceUserId(), mRunningJob.getSourcePackageName(),
- getRunningActionId(mRunningJob), String.valueOf(mRunningJob.getJobId()));
if (mParams.getStopReason() == JobParameters.STOP_REASON_TIMEOUT) {
mEconomyManagerInternal.noteInstantaneousEvent(
mRunningJob.getSourceUserId(), mRunningJob.getSourcePackageName(),
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java
index be3a3ee2921b..fd6aa7a1d282 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java
@@ -16,9 +16,12 @@
package com.android.server.job.controllers;
+import static android.app.job.JobInfo.getPriorityString;
+
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
import android.annotation.NonNull;
+import android.app.job.JobInfo;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
@@ -30,6 +33,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.server.JobSchedulerBackgroundThread;
import com.android.server.LocalServices;
import com.android.server.job.JobSchedulerService;
+import com.android.server.tare.EconomicPolicy;
import com.android.server.tare.EconomyManagerInternal;
import com.android.server.tare.EconomyManagerInternal.ActionBill;
import com.android.server.tare.JobSchedulerEconomicPolicy;
@@ -49,6 +53,56 @@ public class TareController extends StateController {
|| Log.isLoggable(TAG, Log.DEBUG);
/**
+ * Bill to use while we're waiting to start a min priority job. If a job isn't running yet,
+ * don't consider it eligible to run unless it can pay for a job start and at least some
+ * period of execution time. We don't want min priority jobs to use up all available credits,
+ * so we make sure to only run them while there are enough credits to run higher priority jobs.
+ */
+ private static final ActionBill BILL_JOB_START_MIN =
+ new ActionBill(List.of(
+ new EconomyManagerInternal.AnticipatedAction(
+ JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_START, 1, 0),
+ new EconomyManagerInternal.AnticipatedAction(
+ JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, 0, 120_000L)
+ ));
+
+ /**
+ * Bill to use when a min priority job is currently running. We don't want min priority jobs
+ * to use up remaining credits, so we make sure to only run them while there are enough
+ * credits to run higher priority jobs. We stop the job when the app's credits get too low.
+ */
+ private static final ActionBill BILL_JOB_RUNNING_MIN =
+ new ActionBill(List.of(
+ new EconomyManagerInternal.AnticipatedAction(
+ JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, 0, 60_000L)
+ ));
+
+ /**
+ * Bill to use while we're waiting to start a low priority job. If a job isn't running yet,
+ * don't consider it eligible to run unless it can pay for a job start and at least some
+ * period of execution time. We don't want low priority jobs to use up all available credits,
+ * so we make sure to only run them while there are enough credits to run higher priority jobs.
+ */
+ private static final ActionBill BILL_JOB_START_LOW =
+ new ActionBill(List.of(
+ new EconomyManagerInternal.AnticipatedAction(
+ JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_START, 1, 0),
+ new EconomyManagerInternal.AnticipatedAction(
+ JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, 0, 60_000L)
+ ));
+
+ /**
+ * Bill to use when a low priority job is currently running. We don't want low priority jobs
+ * to use up all available credits, so we make sure to only run them while there are enough
+ * credits to run higher priority jobs. We stop the job when the app's credits get too low.
+ */
+ private static final ActionBill BILL_JOB_RUNNING_LOW =
+ new ActionBill(List.of(
+ new EconomyManagerInternal.AnticipatedAction(
+ JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, 0, 30_000L)
+ ));
+
+ /**
* Bill to use while we're waiting to start a job. If a job isn't running yet, don't consider it
* eligible to run unless it can pay for a job start and at least some period of execution time.
*/
@@ -61,9 +115,9 @@ public class TareController extends StateController {
));
/**
- * Bill to use when a default is currently running. We want to track and make sure the app can
- * continue to pay for 1 more second of execution time. We stop the job when the app can no
- * longer pay for that time.
+ * Bill to use when a default priority job is currently running. We want to track and make
+ * sure the app can continue to pay for 1 more second of execution time. We stop the job when
+ * the app can no longer pay for that time.
*/
private static final ActionBill BILL_JOB_RUNNING_DEFAULT =
new ActionBill(List.of(
@@ -75,7 +129,57 @@ public class TareController extends StateController {
* Bill to use while we're waiting to start a job. If a job isn't running yet, don't consider it
* eligible to run unless it can pay for a job start and at least some period of execution time.
*/
- private static final ActionBill BILL_JOB_START_EXPEDITED =
+ private static final ActionBill BILL_JOB_START_HIGH =
+ new ActionBill(List.of(
+ new EconomyManagerInternal.AnticipatedAction(
+ JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_START, 1, 0),
+ new EconomyManagerInternal.AnticipatedAction(
+ JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING, 0, 30_000L)
+ ));
+
+ /**
+ * Bill to use when a high priority job is currently running. We want to track and make sure
+ * the app can continue to pay for 1 more second of execution time. We stop the job when the
+ * app can no longer pay for that time.
+ */
+ private static final ActionBill BILL_JOB_RUNNING_HIGH =
+ new ActionBill(List.of(
+ new EconomyManagerInternal.AnticipatedAction(
+ JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING, 0, 1_000L)
+ ));
+
+
+ /**
+ * Bill to use while we're waiting to start a max priority job. This should only be used for
+ * requested-EJs that aren't allowed to run as EJs. If a job isn't running yet, don't consider
+ * it eligible to run unless it can pay for a job start and at least some period of execution
+ * time.
+ */
+ private static final ActionBill BILL_JOB_START_MAX =
+ new ActionBill(List.of(
+ new EconomyManagerInternal.AnticipatedAction(
+ JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 1, 0),
+ new EconomyManagerInternal.AnticipatedAction(
+ JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING, 0, 30_000L)
+ ));
+
+ /**
+ * Bill to use when a max priority job is currently running. This should only be used for
+ * requested-EJs that aren't allowed to run as EJs. We want to track and make sure
+ * the app can continue to pay for 1 more second of execution time. We stop the job when the
+ * app can no longer pay for that time.
+ */
+ private static final ActionBill BILL_JOB_RUNNING_MAX =
+ new ActionBill(List.of(
+ new EconomyManagerInternal.AnticipatedAction(
+ JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING, 0, 1_000L)
+ ));
+
+ /**
+ * Bill to use while we're waiting to start a job. If a job isn't running yet, don't consider it
+ * eligible to run unless it can pay for a job start and at least some period of execution time.
+ */
+ private static final ActionBill BILL_JOB_START_MAX_EXPEDITED =
new ActionBill(List.of(
new EconomyManagerInternal.AnticipatedAction(
JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 1, 0),
@@ -84,16 +188,39 @@ public class TareController extends StateController {
));
/**
- * Bill to use when an EJ is currently running (as an EJ). We want to track and make sure the
- * app can continue to pay for 1 more second of execution time. We stop the job when the app can
- * no longer pay for that time.
+ * Bill to use when a max priority EJ is currently running (as an EJ). We want to track and
+ * make sure the app can continue to pay for 1 more second of execution time. We stop the job
+ * when the app can no longer pay for that time.
*/
- private static final ActionBill BILL_JOB_RUNNING_EXPEDITED =
+ private static final ActionBill BILL_JOB_RUNNING_MAX_EXPEDITED =
new ActionBill(List.of(
new EconomyManagerInternal.AnticipatedAction(
JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING, 0, 1_000L)
));
+ /**
+ * Bill to use while we're waiting to start a job. If a job isn't running yet, don't consider it
+ * eligible to run unless it can pay for a job start and at least some period of execution time.
+ */
+ private static final ActionBill BILL_JOB_START_HIGH_EXPEDITED =
+ new ActionBill(List.of(
+ new EconomyManagerInternal.AnticipatedAction(
+ JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_START, 1, 0),
+ new EconomyManagerInternal.AnticipatedAction(
+ JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING, 0, 30_000L)
+ ));
+
+ /**
+ * Bill to use when a high priority EJ is currently running (as an EJ). We want to track and
+ * make sure the app can continue to pay for 1 more second of execution time. We stop the job
+ * when the app can no longer pay for that time.
+ */
+ private static final ActionBill BILL_JOB_RUNNING_HIGH_EXPEDITED =
+ new ActionBill(List.of(
+ new EconomyManagerInternal.AnticipatedAction(
+ JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING, 0, 1_000L)
+ ));
+
private final EconomyManagerInternal mEconomyManagerInternal;
private final BackgroundJobsController mBackgroundJobsController;
@@ -161,6 +288,14 @@ public class TareController extends StateController {
}
};
+ /**
+ * List of jobs that started while the UID was in the TOP state. There will be no more than
+ * 16 ({@link JobSchedulerService#MAX_JOB_CONTEXTS_COUNT}) running at once, so an ArraySet is
+ * fine.
+ */
+ @GuardedBy("mLock")
+ private final ArraySet<JobStatus> mTopStartedJobs = new ArraySet<>();
+
@GuardedBy("mLock")
private boolean mIsEnabled;
@@ -175,6 +310,7 @@ public class TareController extends StateController {
}
@Override
+ @GuardedBy("mLock")
public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
final long nowElapsed = sElapsedRealtimeClock.millis();
jobStatus.setTareWealthConstraintSatisfied(nowElapsed, hasEnoughWealthLocked(jobStatus));
@@ -188,6 +324,7 @@ public class TareController extends StateController {
}
@Override
+ @GuardedBy("mLock")
public void prepareForExecutionLocked(JobStatus jobStatus) {
final int userId = jobStatus.getSourceUserId();
final String pkgName = jobStatus.getSourcePackageName();
@@ -200,16 +337,30 @@ public class TareController extends StateController {
removeJobFromBillList(jobStatus, billToJobMap.keyAt(i));
}
}
- if (jobStatus.shouldTreatAsExpeditedJob()) {
- addJobToBillList(jobStatus, BILL_JOB_RUNNING_EXPEDITED);
+ addJobToBillList(jobStatus, getRunningBill(jobStatus));
+
+ final int uid = jobStatus.getSourceUid();
+ if (mService.getUidBias(uid) == JobInfo.BIAS_TOP_APP) {
+ if (DEBUG) {
+ Slog.d(TAG, jobStatus.toShortString() + " is top started job");
+ }
+ mTopStartedJobs.add(jobStatus);
+ // Top jobs won't count towards quota so there's no need to involve the EconomyManager.
+ } else {
+ mEconomyManagerInternal.noteOngoingEventStarted(userId, pkgName,
+ getRunningActionId(jobStatus), String.valueOf(jobStatus.getJobId()));
}
- addJobToBillList(jobStatus, BILL_JOB_RUNNING_DEFAULT);
}
@Override
+ @GuardedBy("mLock")
public void unprepareFromExecutionLocked(JobStatus jobStatus) {
final int userId = jobStatus.getSourceUserId();
final String pkgName = jobStatus.getSourcePackageName();
+ mEconomyManagerInternal.noteOngoingEventStopped(userId, pkgName,
+ getRunningActionId(jobStatus), String.valueOf(jobStatus.getJobId()));
+ mTopStartedJobs.remove(jobStatus);
+
final ArraySet<ActionBill> bills = getPossibleStartBills(jobStatus);
ArrayMap<ActionBill, ArraySet<JobStatus>> billToJobMap =
mRegisteredBillsAndJobs.get(userId, pkgName);
@@ -226,10 +377,14 @@ public class TareController extends StateController {
}
@Override
+ @GuardedBy("mLock")
public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
boolean forUpdate) {
final int userId = jobStatus.getSourceUserId();
final String pkgName = jobStatus.getSourcePackageName();
+ mEconomyManagerInternal.noteOngoingEventStopped(userId, pkgName,
+ getRunningActionId(jobStatus), String.valueOf(jobStatus.getJobId()));
+ mTopStartedJobs.remove(jobStatus);
ArrayMap<ActionBill, ArraySet<JobStatus>> billToJobMap =
mRegisteredBillsAndJobs.get(userId, pkgName);
if (billToJobMap != null) {
@@ -270,7 +425,16 @@ public class TareController extends StateController {
if (!mIsEnabled) {
return true;
}
- return canAffordBillLocked(jobStatus, BILL_JOB_START_EXPEDITED);
+ if (jobStatus.getEffectivePriority() == JobInfo.PRIORITY_MAX) {
+ return canAffordBillLocked(jobStatus, BILL_JOB_START_MAX_EXPEDITED);
+ }
+ return canAffordBillLocked(jobStatus, BILL_JOB_START_HIGH_EXPEDITED);
+ }
+
+ /** @return true if the job was started while the app was in the TOP state. */
+ @GuardedBy("mLock")
+ private boolean isTopStartedJobLocked(@NonNull final JobStatus jobStatus) {
+ return mTopStartedJobs.contains(jobStatus);
}
@GuardedBy("mLock")
@@ -278,14 +442,9 @@ public class TareController extends StateController {
if (!mIsEnabled) {
return mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS;
}
- if (jobStatus.shouldTreatAsExpeditedJob()) {
- return mEconomyManagerInternal.getMaxDurationMs(
- jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(),
- BILL_JOB_RUNNING_EXPEDITED);
- }
return mEconomyManagerInternal.getMaxDurationMs(
jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(),
- BILL_JOB_RUNNING_DEFAULT);
+ getRunningBill(jobStatus));
}
@GuardedBy("mLock")
@@ -336,17 +495,93 @@ public class TareController extends StateController {
// TODO: factor in network cost when available
final ArraySet<ActionBill> bills = new ArraySet<>();
if (jobStatus.isRequestedExpeditedJob()) {
- bills.add(BILL_JOB_START_EXPEDITED);
+ if (jobStatus.getEffectivePriority() == JobInfo.PRIORITY_MAX) {
+ bills.add(BILL_JOB_START_MAX_EXPEDITED);
+ } else {
+ bills.add(BILL_JOB_START_HIGH_EXPEDITED);
+ }
+ }
+ switch (jobStatus.getEffectivePriority()) {
+ case JobInfo.PRIORITY_MAX:
+ bills.add(BILL_JOB_START_MAX);
+ break;
+ case JobInfo.PRIORITY_HIGH:
+ bills.add(BILL_JOB_START_HIGH);
+ break;
+ case JobInfo.PRIORITY_DEFAULT:
+ bills.add(BILL_JOB_START_DEFAULT);
+ break;
+ case JobInfo.PRIORITY_LOW:
+ bills.add(BILL_JOB_START_LOW);
+ break;
+ case JobInfo.PRIORITY_MIN:
+ bills.add(BILL_JOB_START_MIN);
+ break;
+ default:
+ Slog.wtf(TAG, "Unexpected priority: "
+ + JobInfo.getPriorityString(jobStatus.getEffectivePriority()));
+ break;
}
- bills.add(BILL_JOB_START_DEFAULT);
return bills;
}
+ @NonNull
+ private ActionBill getRunningBill(JobStatus jobStatus) {
+ // TODO: factor in network cost when available
+ if (jobStatus.shouldTreatAsExpeditedJob()) {
+ if (jobStatus.getEffectivePriority() == JobInfo.PRIORITY_MAX) {
+ return BILL_JOB_RUNNING_MAX_EXPEDITED;
+ } else {
+ return BILL_JOB_RUNNING_HIGH_EXPEDITED;
+ }
+ }
+ switch (jobStatus.getEffectivePriority()) {
+ case JobInfo.PRIORITY_MAX:
+ return BILL_JOB_RUNNING_MAX;
+ case JobInfo.PRIORITY_HIGH:
+ return BILL_JOB_RUNNING_HIGH;
+ case JobInfo.PRIORITY_LOW:
+ return BILL_JOB_RUNNING_LOW;
+ case JobInfo.PRIORITY_MIN:
+ return BILL_JOB_RUNNING_MIN;
+ default:
+ Slog.wtf(TAG, "Got unexpected priority: " + jobStatus.getEffectivePriority());
+ // Intentional fallthrough
+ case JobInfo.PRIORITY_DEFAULT:
+ return BILL_JOB_RUNNING_DEFAULT;
+ }
+ }
+
+ @EconomicPolicy.AppAction
+ private static int getRunningActionId(@NonNull JobStatus job) {
+ switch (job.getEffectivePriority()) {
+ case JobInfo.PRIORITY_MAX:
+ return JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING;
+ case JobInfo.PRIORITY_HIGH:
+ return JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING;
+ case JobInfo.PRIORITY_LOW:
+ return JobSchedulerEconomicPolicy.ACTION_JOB_LOW_RUNNING;
+ case JobInfo.PRIORITY_MIN:
+ return JobSchedulerEconomicPolicy.ACTION_JOB_MIN_RUNNING;
+ default:
+ Slog.wtf(TAG, "Unknown priority: " + getPriorityString(job.getEffectivePriority()));
+ // Intentional fallthrough
+ case JobInfo.PRIORITY_DEFAULT:
+ return JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING;
+ }
+ }
+
@GuardedBy("mLock")
private boolean canAffordBillLocked(@NonNull JobStatus jobStatus, @NonNull ActionBill bill) {
if (!mIsEnabled) {
return true;
}
+ if (mService.getUidBias(jobStatus.getSourceUid()) == JobInfo.BIAS_TOP_APP
+ || isTopStartedJobLocked(jobStatus)) {
+ // Jobs for the top app should always be allowed to run, and any jobs started while
+ // the app is on top shouldn't consume any credits.
+ return true;
+ }
final int userId = jobStatus.getSourceUserId();
final String pkgName = jobStatus.getSourcePackageName();
ArrayMap<ActionBill, Boolean> actionAffordability =
@@ -370,11 +605,23 @@ public class TareController extends StateController {
if (!mIsEnabled) {
return true;
}
+ if (!jobStatus.isRequestedExpeditedJob()) {
+ return false;
+ }
+ if (mService.getUidBias(jobStatus.getSourceUid()) == JobInfo.BIAS_TOP_APP
+ || isTopStartedJobLocked(jobStatus)) {
+ // Jobs for the top app should always be allowed to run, and any jobs started while
+ // the app is on top shouldn't consume any credits.
+ return true;
+ }
if (mService.isCurrentlyRunningLocked(jobStatus)) {
- return canAffordBillLocked(jobStatus, BILL_JOB_RUNNING_EXPEDITED);
+ return canAffordBillLocked(jobStatus, getRunningBill(jobStatus));
}
- return canAffordBillLocked(jobStatus, BILL_JOB_START_EXPEDITED);
+ if (jobStatus.getEffectivePriority() == JobInfo.PRIORITY_MAX) {
+ return canAffordBillLocked(jobStatus, BILL_JOB_START_MAX_EXPEDITED);
+ }
+ return canAffordBillLocked(jobStatus, BILL_JOB_START_HIGH_EXPEDITED);
}
@GuardedBy("mLock")
@@ -382,20 +629,28 @@ public class TareController extends StateController {
if (!mIsEnabled) {
return true;
}
+ if (mService.getUidBias(jobStatus.getSourceUid()) == JobInfo.BIAS_TOP_APP
+ || isTopStartedJobLocked(jobStatus)) {
+ // Jobs for the top app should always be allowed to run, and any jobs started while
+ // the app is on top shouldn't consume any credits.
+ return true;
+ }
if (mService.isCurrentlyRunningLocked(jobStatus)) {
if (jobStatus.isRequestedExpeditedJob()) {
- return canAffordBillLocked(jobStatus, BILL_JOB_RUNNING_EXPEDITED)
+ return canAffordBillLocked(jobStatus, getRunningBill(jobStatus))
|| canAffordBillLocked(jobStatus, BILL_JOB_RUNNING_DEFAULT);
}
- return canAffordBillLocked(jobStatus, BILL_JOB_RUNNING_DEFAULT);
+ return canAffordBillLocked(jobStatus, getRunningBill(jobStatus));
}
- if (jobStatus.isRequestedExpeditedJob()
- && canAffordBillLocked(jobStatus, BILL_JOB_START_EXPEDITED)) {
- return true;
+ final ArraySet<ActionBill> bills = getPossibleStartBills(jobStatus);
+ for (int i = 0; i < bills.size(); ++i) {
+ ActionBill bill = bills.valueAt(i);
+ if (canAffordBillLocked(jobStatus, bill)) {
+ return true;
+ }
}
-
- return canAffordBillLocked(jobStatus, BILL_JOB_START_DEFAULT);
+ return false;
}
/**
@@ -417,11 +672,23 @@ public class TareController extends StateController {
@NonNull
private String getBillName(@NonNull ActionBill bill) {
- if (bill.equals(BILL_JOB_START_EXPEDITED)) {
- return "EJ_START_BILL";
+ if (bill.equals(BILL_JOB_START_MAX_EXPEDITED)) {
+ return "EJ_MAX_START_BILL";
}
- if (bill.equals(BILL_JOB_RUNNING_EXPEDITED)) {
- return "EJ_RUNNING_BILL";
+ if (bill.equals(BILL_JOB_RUNNING_MAX_EXPEDITED)) {
+ return "EJ_MAX_RUNNING_BILL";
+ }
+ if (bill.equals(BILL_JOB_START_HIGH_EXPEDITED)) {
+ return "EJ_HIGH_START_BILL";
+ }
+ if (bill.equals(BILL_JOB_RUNNING_HIGH_EXPEDITED)) {
+ return "EJ_HIGH_RUNNING_BILL";
+ }
+ if (bill.equals(BILL_JOB_START_HIGH)) {
+ return "HIGH_START_BILL";
+ }
+ if (bill.equals(BILL_JOB_RUNNING_HIGH)) {
+ return "HIGH_RUNNING_BILL";
}
if (bill.equals(BILL_JOB_START_DEFAULT)) {
return "DEFAULT_START_BILL";
@@ -429,7 +696,19 @@ public class TareController extends StateController {
if (bill.equals(BILL_JOB_RUNNING_DEFAULT)) {
return "DEFAULT_RUNNING_BILL";
}
- return "UNKNOWN_BILL (" + bill.toString() + ")";
+ if (bill.equals(BILL_JOB_START_LOW)) {
+ return "LOW_START_BILL";
+ }
+ if (bill.equals(BILL_JOB_RUNNING_LOW)) {
+ return "LOW_RUNNING_BILL";
+ }
+ if (bill.equals(BILL_JOB_START_MIN)) {
+ return "MIN_START_BILL";
+ }
+ if (bill.equals(BILL_JOB_RUNNING_MIN)) {
+ return "MIN_RUNNING_BILL";
+ }
+ return "UNKNOWN_BILL (" + bill + ")";
}
@Override
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
index 005c447c7220..a6a007f46b58 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
@@ -46,6 +46,7 @@ import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArrayMap;
+import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -681,8 +682,8 @@ class Agent {
final long minBalance = mIrs.getMinBalanceLocked(userId, pkgName);
final double perc = batteryLevel / 100d;
// TODO: maybe don't give credits to bankrupt apps until battery level >= 50%
- if (ledger.getCurrentBalance() < minBalance) {
- final long shortfall = minBalance - getBalanceLocked(userId, pkgName);
+ final long shortfall = minBalance - ledger.getCurrentBalance();
+ if (shortfall > 0) {
recordTransactionLocked(userId, pkgName, ledger,
new Ledger.Transaction(now, now, REGULATION_BASIC_INCOME,
null, (long) (perc * shortfall)), true);
@@ -1170,5 +1171,57 @@ class Agent {
void dumpLocked(IndentingPrintWriter pw) {
pw.println();
mBalanceThresholdAlarmQueue.dump(pw);
+
+ pw.println();
+ pw.println("Ongoing events:");
+ pw.increaseIndent();
+ boolean printedEvents = false;
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ for (int u = mCurrentOngoingEvents.numMaps() - 1; u >= 0; --u) {
+ final int userId = mCurrentOngoingEvents.keyAt(u);
+ for (int p = mCurrentOngoingEvents.numElementsForKey(userId) - 1; p >= 0; --p) {
+ final String pkgName = mCurrentOngoingEvents.keyAt(u, p);
+ final SparseArrayMap<String, OngoingEvent> ongoingEvents =
+ mCurrentOngoingEvents.get(userId, pkgName);
+
+ boolean printedApp = false;
+
+ for (int e = ongoingEvents.numMaps() - 1; e >= 0; --e) {
+ final int eventId = ongoingEvents.keyAt(e);
+ for (int t = ongoingEvents.numElementsForKey(eventId) - 1; t >= 0; --t) {
+ if (!printedApp) {
+ printedApp = true;
+ pw.println(appToString(userId, pkgName));
+ pw.increaseIndent();
+ }
+ printedEvents = true;
+
+ OngoingEvent ongoingEvent = ongoingEvents.valueAt(e, t);
+
+ pw.print(EconomicPolicy.eventToString(ongoingEvent.eventId));
+ if (ongoingEvent.tag != null) {
+ pw.print("(");
+ pw.print(ongoingEvent.tag);
+ pw.print(")");
+ }
+ pw.print(" runtime=");
+ TimeUtils.formatDuration(nowElapsed - ongoingEvent.startTimeElapsed, pw);
+ pw.print(" delta/sec=");
+ pw.print(ongoingEvent.deltaPerSec);
+ pw.print(" refCount=");
+ pw.print(ongoingEvent.refCount);
+ pw.println();
+ }
+ }
+
+ if (printedApp) {
+ pw.decreaseIndent();
+ }
+ }
+ }
+ if (!printedEvents) {
+ pw.print("N/A");
+ }
+ pw.decreaseIndent();
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
index 20a300abe72a..36895a5c3a25 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -567,20 +567,23 @@ public class InternalResourceService extends SystemService {
final String pkgName = event.getPackageName();
if (DEBUG) {
Slog.d(TAG, "Processing event " + event.getEventType()
+ + " (" + event.mInstanceId + ")"
+ " for " + appToString(userId, pkgName));
}
final long nowElapsed = SystemClock.elapsedRealtime();
switch (event.getEventType()) {
case UsageEvents.Event.ACTIVITY_RESUMED:
mAgent.noteOngoingEventLocked(userId, pkgName,
- EconomicPolicy.REWARD_TOP_ACTIVITY, null, nowElapsed);
+ EconomicPolicy.REWARD_TOP_ACTIVITY, String.valueOf(event.mInstanceId),
+ nowElapsed);
break;
case UsageEvents.Event.ACTIVITY_PAUSED:
case UsageEvents.Event.ACTIVITY_STOPPED:
case UsageEvents.Event.ACTIVITY_DESTROYED:
final long now = getCurrentTimeMillis();
mAgent.stopOngoingActionLocked(userId, pkgName,
- EconomicPolicy.REWARD_TOP_ACTIVITY, null, nowElapsed, now);
+ EconomicPolicy.REWARD_TOP_ACTIVITY, String.valueOf(event.mInstanceId),
+ nowElapsed, now);
break;
case UsageEvents.Event.USER_INTERACTION:
case UsageEvents.Event.CHOOSER_ACTION:
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java b/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
index a234ae6142fc..f4917ad82761 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
@@ -138,6 +138,11 @@ class Ledger {
dumpTime(pw, transaction.endTimeMs);
pw.print(": ");
pw.print(EconomicPolicy.eventToString(transaction.eventId));
+ if (transaction.tag != null) {
+ pw.print("(");
+ pw.print(transaction.tag);
+ pw.print(")");
+ }
pw.print(" --> ");
pw.println(narcToString(transaction.delta));
}
diff --git a/core/api/current.txt b/core/api/current.txt
index 154c42f1565f..f84322eb7a04 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1420,6 +1420,7 @@ package android {
field public static final int supportsMultipleDisplays = 16844182; // 0x1010596
field public static final int supportsPictureInPicture = 16844023; // 0x10104f7
field public static final int supportsRtl = 16843695; // 0x10103af
+ field public static final int supportsStylusHandwriting;
field public static final int supportsSwitchingToNextInputMethod = 16843755; // 0x10103eb
field public static final int supportsUploading = 16843419; // 0x101029b
field public static final int suppressesSpellChecker = 16844355; // 0x1010643
@@ -3148,8 +3149,12 @@ package android.accessibilityservice {
method public void addOnShowModeChangedListener(@NonNull android.accessibilityservice.AccessibilityService.SoftKeyboardController.OnShowModeChangedListener, @Nullable android.os.Handler);
method public int getShowMode();
method public boolean removeOnShowModeChangedListener(@NonNull android.accessibilityservice.AccessibilityService.SoftKeyboardController.OnShowModeChangedListener);
+ method @CheckResult public int setInputMethodEnabled(@NonNull String, boolean) throws java.lang.SecurityException;
method public boolean setShowMode(int);
method public boolean switchToInputMethod(@NonNull String);
+ field public static final int ENABLE_IME_FAIL_BY_ADMIN = 1; // 0x1
+ field public static final int ENABLE_IME_FAIL_UNKNOWN = 2; // 0x2
+ field public static final int ENABLE_IME_SUCCESS = 0; // 0x0
}
public static interface AccessibilityService.SoftKeyboardController.OnShowModeChangedListener {
@@ -5443,12 +5448,30 @@ package android.app {
public final class GameManager {
method public int getGameMode();
+ method public void setGameState(@NonNull android.app.GameState);
field public static final int GAME_MODE_BATTERY = 3; // 0x3
field public static final int GAME_MODE_PERFORMANCE = 2; // 0x2
field public static final int GAME_MODE_STANDARD = 1; // 0x1
field public static final int GAME_MODE_UNSUPPORTED = 0; // 0x0
}
+ public final class GameState implements android.os.Parcelable {
+ ctor public GameState(boolean, int);
+ ctor public GameState(boolean, int, @Nullable String, @NonNull android.os.Bundle);
+ method public int describeContents();
+ method @Nullable public String getDescription();
+ method @NonNull public android.os.Bundle getMetadata();
+ method public int getMode();
+ method public boolean isLoading();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.GameState> CREATOR;
+ field public static final int MODE_CONTENT = 4; // 0x4
+ field public static final int MODE_GAMEPLAY_INTERRUPTIBLE = 2; // 0x2
+ field public static final int MODE_GAMEPLAY_UNINTERRUPTIBLE = 3; // 0x3
+ field public static final int MODE_NONE = 1; // 0x1
+ field public static final int MODE_UNKNOWN = 0; // 0x0
+ }
+
public class Instrumentation {
ctor public Instrumentation();
method public android.os.TestLooperManager acquireLooperManager(android.os.Looper);
@@ -9290,10 +9313,10 @@ package android.bluetooth {
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void disconnect();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean discoverServices();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean executeReliableWrite();
- method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
- method public int getConnectionState(android.bluetooth.BluetoothDevice);
+ method @Deprecated public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+ method @Deprecated public int getConnectionState(android.bluetooth.BluetoothDevice);
method public android.bluetooth.BluetoothDevice getDevice();
- method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
+ method @Deprecated public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
method public android.bluetooth.BluetoothGattService getService(java.util.UUID);
method public java.util.List<android.bluetooth.BluetoothGattService> getServices();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean readCharacteristic(android.bluetooth.BluetoothGattCharacteristic);
@@ -10227,6 +10250,7 @@ package android.content {
method @Nullable public String getPackageName();
method public int getUid();
method public boolean isTrusted(@NonNull android.content.Context);
+ method @NonNull public static android.content.AttributionSource myAttributionSource();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.content.AttributionSource> CREATOR;
}
@@ -11544,6 +11568,7 @@ package android.content {
field public static final String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME";
field public static final String EXTRA_PERMISSION_GROUP_NAME = "android.intent.extra.PERMISSION_GROUP_NAME";
field public static final String EXTRA_PHONE_NUMBER = "android.intent.extra.PHONE_NUMBER";
+ field public static final String EXTRA_PREVIOUS_UID = "android.intent.extra.PREVIOUS_UID";
field public static final String EXTRA_PROCESS_TEXT = "android.intent.extra.PROCESS_TEXT";
field public static final String EXTRA_PROCESS_TEXT_READONLY = "android.intent.extra.PROCESS_TEXT_READONLY";
field public static final String EXTRA_QUICK_VIEW_FEATURES = "android.intent.extra.QUICK_VIEW_FEATURES";
@@ -11575,6 +11600,7 @@ package android.content {
field public static final String EXTRA_TIMEZONE = "time-zone";
field public static final String EXTRA_TITLE = "android.intent.extra.TITLE";
field public static final String EXTRA_UID = "android.intent.extra.UID";
+ field public static final String EXTRA_UID_CHANGING = "android.intent.extra.UID_CHANGING";
field public static final String EXTRA_USER = "android.intent.extra.USER";
field public static final String EXTRA_USER_INITIATED = "android.intent.extra.USER_INITIATED";
field public static final int FILL_IN_ACTION = 1; // 0x1
@@ -13385,6 +13411,7 @@ package android.content.pm {
method public boolean isDynamic();
method public boolean isEnabled();
method public boolean isImmutable();
+ method public boolean isIncludedIn(int);
method public boolean isPinned();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.ShortcutInfo> CREATOR;
@@ -13397,6 +13424,7 @@ package android.content.pm {
field public static final int DISABLED_REASON_UNKNOWN = 3; // 0x3
field public static final int DISABLED_REASON_VERSION_LOWER = 100; // 0x64
field public static final String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation";
+ field public static final int SURFACE_LAUNCHER = 1; // 0x1
}
public static class ShortcutInfo.Builder {
@@ -13405,6 +13433,7 @@ package android.content.pm {
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>);
method @NonNull public android.content.pm.ShortcutInfo.Builder setDisabledMessage(@NonNull CharSequence);
+ method @NonNull public android.content.pm.ShortcutInfo.Builder setExcludedFromSurfaces(int);
method @NonNull public android.content.pm.ShortcutInfo.Builder setExtras(@NonNull android.os.PersistableBundle);
method @NonNull public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon);
method @NonNull public android.content.pm.ShortcutInfo.Builder setIntent(@NonNull android.content.Intent);
@@ -16564,6 +16593,26 @@ package android.graphics {
method public boolean setUseCompositingLayer(boolean, @Nullable android.graphics.Paint);
}
+ public class RuntimeShader extends android.graphics.Shader {
+ ctor public RuntimeShader(@NonNull String);
+ ctor public RuntimeShader(@NonNull String, boolean);
+ method public boolean isForceOpaque();
+ method public void setColorUniform(@NonNull String, @ColorInt int);
+ method public void setColorUniform(@NonNull String, @ColorLong long);
+ method public void setColorUniform(@NonNull String, @NonNull android.graphics.Color);
+ method public void setFloatUniform(@NonNull String, float);
+ method public void setFloatUniform(@NonNull String, float, float);
+ method public void setFloatUniform(@NonNull String, float, float, float);
+ method public void setFloatUniform(@NonNull String, float, float, float, float);
+ method public void setFloatUniform(@NonNull String, @NonNull float[]);
+ method public void setInputShader(@NonNull String, @NonNull android.graphics.Shader);
+ method public void setIntUniform(@NonNull String, int);
+ method public void setIntUniform(@NonNull String, int, int);
+ method public void setIntUniform(@NonNull String, int, int, int);
+ method public void setIntUniform(@NonNull String, int, int, int, int);
+ method public void setIntUniform(@NonNull String, @NonNull int[]);
+ }
+
public class Shader {
ctor @Deprecated public Shader();
method public boolean getLocalMatrix(@NonNull android.graphics.Matrix);
@@ -17888,6 +17937,7 @@ package android.hardware {
field public static final int RGB_565 = 4; // 0x4
field public static final int RGB_888 = 3; // 0x3
field public static final int S_UI8 = 53; // 0x35
+ field public static final long USAGE_COMPOSER_OVERLAY = 2048L; // 0x800L
field public static final long USAGE_CPU_READ_OFTEN = 3L; // 0x3L
field public static final long USAGE_CPU_READ_RARELY = 2L; // 0x2L
field public static final long USAGE_CPU_WRITE_OFTEN = 48L; // 0x30L
@@ -18527,6 +18577,7 @@ package android.hardware.camera2 {
method @NonNull public android.hardware.camera2.CameraExtensionCharacteristics getCameraExtensionCharacteristics(@NonNull String) throws android.hardware.camera2.CameraAccessException;
method @NonNull public String[] getCameraIdList() throws android.hardware.camera2.CameraAccessException;
method @NonNull public java.util.Set<java.util.Set<java.lang.String>> getConcurrentCameraIds() throws android.hardware.camera2.CameraAccessException;
+ method public int getTorchStrengthLevel(@NonNull String) throws android.hardware.camera2.CameraAccessException;
method @RequiresPermission(android.Manifest.permission.CAMERA) public boolean isConcurrentSessionConfigurationSupported(@NonNull java.util.Map<java.lang.String,android.hardware.camera2.params.SessionConfiguration>) throws android.hardware.camera2.CameraAccessException;
method @RequiresPermission(android.Manifest.permission.CAMERA) public void openCamera(@NonNull String, @NonNull android.hardware.camera2.CameraDevice.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method @RequiresPermission(android.Manifest.permission.CAMERA) public void openCamera(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraDevice.StateCallback) throws android.hardware.camera2.CameraAccessException;
@@ -18535,6 +18586,7 @@ package android.hardware.camera2 {
method public void registerTorchCallback(@NonNull android.hardware.camera2.CameraManager.TorchCallback, @Nullable android.os.Handler);
method public void registerTorchCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraManager.TorchCallback);
method public void setTorchMode(@NonNull String, boolean) throws android.hardware.camera2.CameraAccessException;
+ method public void turnOnTorchWithStrengthLevel(@NonNull String, int) throws android.hardware.camera2.CameraAccessException;
method public void unregisterAvailabilityCallback(@NonNull android.hardware.camera2.CameraManager.AvailabilityCallback);
method public void unregisterTorchCallback(@NonNull android.hardware.camera2.CameraManager.TorchCallback);
}
@@ -18552,6 +18604,7 @@ package android.hardware.camera2 {
ctor public CameraManager.TorchCallback();
method public void onTorchModeChanged(@NonNull String, boolean);
method public void onTorchModeUnavailable(@NonNull String);
+ method public void onTorchStrengthLevelChanged(@NonNull String, int);
}
public abstract class CameraMetadata<TKey> {
@@ -26097,11 +26150,14 @@ package android.media.tv {
field public static final String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
field public static final String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
field public static final String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+ field public static final String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
field public static final String COLUMN_LONG_DESCRIPTION = "long_description";
+ field public static final String COLUMN_MULTI_SERIES_ID = "multi_series_id";
field public static final String COLUMN_POSTER_ART_URI = "poster_art_uri";
field public static final String COLUMN_RECORDING_PROHIBITED = "recording_prohibited";
field public static final String COLUMN_REVIEW_RATING = "review_rating";
field public static final String COLUMN_REVIEW_RATING_STYLE = "review_rating_style";
+ field public static final String COLUMN_SCRAMBLED = "scrambled";
field public static final String COLUMN_SEARCHABLE = "searchable";
field public static final String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number";
field @Deprecated public static final String COLUMN_SEASON_NUMBER = "season_number";
@@ -26161,7 +26217,9 @@ package android.media.tv {
field public static final String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
field public static final String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
field public static final String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+ field public static final String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
field public static final String COLUMN_LONG_DESCRIPTION = "long_description";
+ field public static final String COLUMN_MULTI_SERIES_ID = "multi_series_id";
field public static final String COLUMN_POSTER_ART_URI = "poster_art_uri";
field public static final String COLUMN_RECORDING_DATA_BYTES = "recording_data_bytes";
field public static final String COLUMN_RECORDING_DATA_URI = "recording_data_uri";
@@ -31952,6 +32010,7 @@ package android.os {
method @Nullable public <T> java.util.ArrayList<T> readArrayList(@Nullable ClassLoader, @NonNull Class<? extends T>);
method public void readBinderArray(@NonNull android.os.IBinder[]);
method public void readBinderList(@NonNull java.util.List<android.os.IBinder>);
+ method @Nullable public byte[] readBlob();
method public boolean readBoolean();
method public void readBooleanArray(@NonNull boolean[]);
method @Nullable public android.os.Bundle readBundle();
@@ -32011,6 +32070,8 @@ package android.os {
method public void writeArray(@Nullable Object[]);
method public void writeBinderArray(@Nullable android.os.IBinder[]);
method public void writeBinderList(@Nullable java.util.List<android.os.IBinder>);
+ method public void writeBlob(@Nullable byte[]);
+ method public void writeBlob(@Nullable byte[], int, int);
method public void writeBoolean(boolean);
method public void writeBooleanArray(@Nullable boolean[]);
method public void writeBundle(@Nullable android.os.Bundle);
@@ -37862,6 +37923,28 @@ package android.security {
method @Deprecated @NonNull public android.security.KeyPairGeneratorSpec.Builder setSubject(@NonNull javax.security.auth.x500.X500Principal);
}
+ public class KeyStoreException extends java.lang.Exception {
+ method public int getNumericErrorCode();
+ method public boolean isSystemError();
+ method public boolean isTransientFailure();
+ method public boolean requiresUserAuthentication();
+ field public static final int ERROR_ATTESTATION_CHALLENGE_TOO_LARGE = 9; // 0x9
+ field public static final int ERROR_ID_ATTESTATION_FAILURE = 8; // 0x8
+ field public static final int ERROR_INCORRECT_USAGE = 13; // 0xd
+ field public static final int ERROR_INTERNAL_SYSTEM_ERROR = 4; // 0x4
+ field public static final int ERROR_KEYMINT_FAILURE = 10; // 0xa
+ field public static final int ERROR_KEYSTORE_FAILURE = 11; // 0xb
+ field public static final int ERROR_KEYSTORE_UNINITIALIZED = 3; // 0x3
+ field public static final int ERROR_KEY_CORRUPTED = 7; // 0x7
+ field public static final int ERROR_KEY_DOES_NOT_EXIST = 6; // 0x6
+ field public static final int ERROR_KEY_NOT_TEMPORALLY_VALID = 14; // 0xe
+ field public static final int ERROR_KEY_OPERATION_EXPIRED = 15; // 0xf
+ field public static final int ERROR_OTHER = 1; // 0x1
+ field public static final int ERROR_PERMISSION_DENIED = 5; // 0x5
+ field public static final int ERROR_UNIMPLEMENTED = 12; // 0xc
+ field public static final int ERROR_USER_AUTHENTICATION_REQUIRED = 2; // 0x2
+ }
+
@Deprecated public final class KeyStoreParameter implements java.security.KeyStore.ProtectionParameter {
method @Deprecated public boolean isEncryptionRequired();
}
@@ -38696,9 +38779,11 @@ package android.service.carrier {
public abstract class CarrierService extends android.app.Service {
ctor public CarrierService();
- method public final void notifyCarrierNetworkChange(boolean);
+ method @Deprecated public final void notifyCarrierNetworkChange(boolean);
+ method public final void notifyCarrierNetworkChange(int, boolean);
method @CallSuper public android.os.IBinder onBind(android.content.Intent);
- method public abstract android.os.PersistableBundle onLoadConfig(android.service.carrier.CarrierIdentifier);
+ method @Deprecated public abstract android.os.PersistableBundle onLoadConfig(android.service.carrier.CarrierIdentifier);
+ method @Nullable public android.os.PersistableBundle onLoadConfig(int, @Nullable android.service.carrier.CarrierIdentifier);
field public static final String CARRIER_SERVICE_INTERFACE = "android.service.carrier.CarrierService";
}
@@ -41393,6 +41478,7 @@ package android.telephony {
field public static final String KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array";
field public static final String KEY_CDMA_ROAMING_MODE_INT = "cdma_roaming_mode_int";
field public static final String KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array";
+ field public static final String KEY_CELLULAR_USAGE_SETTING_INT = "cellular_usage_setting_int";
field public static final String KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL = "check_pricing_with_carrier_data_roaming_bool";
field public static final String KEY_CI_ACTION_ON_SYS_UPDATE_BOOL = "ci_action_on_sys_update_bool";
field public static final String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING = "ci_action_on_sys_update_extra_string";
@@ -43009,6 +43095,7 @@ package android.telephony {
method public int getSimSlotIndex();
method public int getSubscriptionId();
method public int getSubscriptionType();
+ method public int getUsageSetting();
method public boolean isEmbedded();
method public boolean isOpportunistic();
method public void writeToParcel(android.os.Parcel, int);
@@ -43083,6 +43170,10 @@ package android.telephony {
field public static final int PHONE_NUMBER_SOURCE_UICC = 1; // 0x1
field public static final int SUBSCRIPTION_TYPE_LOCAL_SIM = 0; // 0x0
field public static final int SUBSCRIPTION_TYPE_REMOTE_SIM = 1; // 0x1
+ field public static final int USAGE_SETTING_DATA_CENTRIC = 2; // 0x2
+ field public static final int USAGE_SETTING_DEFAULT = 0; // 0x0
+ field public static final int USAGE_SETTING_UNKNOWN = -1; // 0xffffffff
+ field public static final int USAGE_SETTING_VOICE_CENTRIC = 1; // 0x1
}
public static class SubscriptionManager.OnOpportunisticSubscriptionsChangedListener {
@@ -43412,6 +43503,7 @@ package android.telephony {
field public static final int DATA_ENABLED_REASON_USER = 0; // 0x0
field public static final int DATA_SUSPENDED = 3; // 0x3
field public static final int DATA_UNKNOWN = -1; // 0xffffffff
+ field public static final int DEFAULT_PORT_INDEX = 0; // 0x0
field public static final int ERI_FLASH = 2; // 0x2
field public static final int ERI_OFF = 1; // 0x1
field public static final int ERI_ON = 0; // 0x0
@@ -49046,6 +49138,7 @@ package android.view {
method @NonNull public android.view.SurfaceControl build();
method @NonNull public android.view.SurfaceControl.Builder setBufferSize(@IntRange(from=0) int, @IntRange(from=0) int);
method @NonNull public android.view.SurfaceControl.Builder setFormat(int);
+ method @NonNull public android.view.SurfaceControl.Builder setHidden(boolean);
method @NonNull public android.view.SurfaceControl.Builder setName(@NonNull String);
method @NonNull public android.view.SurfaceControl.Builder setOpaque(boolean);
method @NonNull public android.view.SurfaceControl.Builder setParent(@Nullable android.view.SurfaceControl);
@@ -49060,11 +49153,18 @@ package android.view {
method @NonNull public android.view.SurfaceControl.Transaction merge(@NonNull android.view.SurfaceControl.Transaction);
method @NonNull public android.view.SurfaceControl.Transaction reparent(@NonNull android.view.SurfaceControl, @Nullable android.view.SurfaceControl);
method @NonNull public android.view.SurfaceControl.Transaction setAlpha(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0, to=1.0) float);
+ method @NonNull public android.view.SurfaceControl.Transaction setBuffer(@NonNull android.view.SurfaceControl, @Nullable android.hardware.HardwareBuffer);
method @NonNull public android.view.SurfaceControl.Transaction setBufferSize(@NonNull android.view.SurfaceControl, @IntRange(from=0) int, @IntRange(from=0) int);
+ method @NonNull public android.view.SurfaceControl.Transaction setBufferTransform(@NonNull android.view.SurfaceControl, int);
+ method @NonNull public android.view.SurfaceControl.Transaction setCrop(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Rect);
+ method @NonNull public android.view.SurfaceControl.Transaction setDamageRegion(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Region);
method @NonNull public android.view.SurfaceControl.Transaction setFrameRate(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0) float, int);
method @NonNull public android.view.SurfaceControl.Transaction setFrameRate(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0) float, int, int);
method @NonNull public android.view.SurfaceControl.Transaction setGeometry(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Rect, @Nullable android.graphics.Rect, int);
method @NonNull public android.view.SurfaceControl.Transaction setLayer(@NonNull android.view.SurfaceControl, @IntRange(from=java.lang.Integer.MIN_VALUE, to=java.lang.Integer.MAX_VALUE) int);
+ method @NonNull public android.view.SurfaceControl.Transaction setOpaque(@NonNull android.view.SurfaceControl, boolean);
+ method @NonNull public android.view.SurfaceControl.Transaction setPosition(@NonNull android.view.SurfaceControl, float, float);
+ method @NonNull public android.view.SurfaceControl.Transaction setScale(@NonNull android.view.SurfaceControl, float, float);
method @NonNull public android.view.SurfaceControl.Transaction setVisibility(@NonNull android.view.SurfaceControl, boolean);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.SurfaceControl.Transaction> CREATOR;
@@ -51706,7 +51806,6 @@ package android.view.accessibility {
public static final class AccessibilityNodeInfo.CollectionItemInfo {
ctor public AccessibilityNodeInfo.CollectionItemInfo(int, int, int, int, boolean);
ctor public AccessibilityNodeInfo.CollectionItemInfo(int, int, int, int, boolean, boolean);
- ctor public AccessibilityNodeInfo.CollectionItemInfo(@Nullable String, int, int, @Nullable String, int, int, boolean, boolean);
method public int getColumnIndex();
method public int getColumnSpan();
method @Nullable public String getColumnTitle();
@@ -51720,6 +51819,19 @@ package android.view.accessibility {
method @NonNull public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(@Nullable String, int, int, @Nullable String, int, int, boolean, boolean);
}
+ public static final class AccessibilityNodeInfo.CollectionItemInfo.Builder {
+ ctor public AccessibilityNodeInfo.CollectionItemInfo.Builder();
+ method @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo build();
+ method @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo.Builder setColumnIndex(int);
+ method @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo.Builder setColumnSpan(int);
+ method @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo.Builder setColumnTitle(@Nullable String);
+ method @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo.Builder setHeading(boolean);
+ method @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo.Builder setRowIndex(int);
+ method @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo.Builder setRowSpan(int);
+ method @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo.Builder setRowTitle(@Nullable String);
+ method @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo.Builder setSelected(boolean);
+ }
+
public static final class AccessibilityNodeInfo.ExtraRenderingInfo {
method @Nullable public android.util.Size getLayoutSize();
method public float getTextSizeInPx();
@@ -52755,6 +52867,7 @@ package android.view.inputmethod {
method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager);
method public CharSequence loadLabel(android.content.pm.PackageManager);
method public boolean shouldShowInInputMethodPicker();
+ method public boolean supportsStylusHandwriting();
method public boolean suppressesSpellChecker();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.InputMethodInfo> CREATOR;
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index b8ce02e72571..a51d2b165516 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -215,6 +215,10 @@ package android.media.session {
package android.net {
+ public final class ConnectivityFrameworkInitializerTiramisu {
+ method public static void registerServiceWrappers();
+ }
+
public final class EthernetNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
ctor public EthernetNetworkSpecifier(@NonNull String);
method public int describeContents();
@@ -345,6 +349,7 @@ package android.os {
package android.os.storage {
public class StorageManager {
+ method public long computeStorageCacheBytes(@NonNull java.io.File);
method public void notifyAppIoBlocked(@NonNull java.util.UUID, int, int, int);
method public void notifyAppIoResumed(@NonNull java.util.UUID, int, int, int);
field public static final int APP_IO_BLOCKED_REASON_TRANSCODING = 1; // 0x1
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index c9b8ba2f7fe2..99e47b1c6b90 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -70,6 +70,7 @@ package android {
field public static final String BIND_TRANSLATION_SERVICE = "android.permission.BIND_TRANSLATION_SERVICE";
field public static final String BIND_TRUST_AGENT = "android.permission.BIND_TRUST_AGENT";
field public static final String BIND_TV_REMOTE_SERVICE = "android.permission.BIND_TV_REMOTE_SERVICE";
+ field public static final String BLUETOOTH_MAP = "android.permission.BLUETOOTH_MAP";
field public static final String BRICK = "android.permission.BRICK";
field public static final String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE";
field public static final String BROADCAST_CLOSE_SYSTEM_DIALOGS = "android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS";
@@ -310,6 +311,7 @@ package android {
field public static final String TV_VIRTUAL_REMOTE_CONTROLLER = "android.permission.TV_VIRTUAL_REMOTE_CONTROLLER";
field public static final String UNLIMITED_SHORTCUTS_API_CALLS = "android.permission.UNLIMITED_SHORTCUTS_API_CALLS";
field public static final String UPDATE_APP_OPS_STATS = "android.permission.UPDATE_APP_OPS_STATS";
+ field public static final String UPDATE_DEVICE_MANAGEMENT_RESOURCES = "android.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES";
field public static final String UPDATE_DOMAIN_VERIFICATION_USER_SELECTION = "android.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION";
field public static final String UPDATE_FONTS = "android.permission.UPDATE_FONTS";
field public static final String UPDATE_LOCK = "android.permission.UPDATE_LOCK";
@@ -358,6 +360,7 @@ package android {
field public static final int config_showDefaultAssistant = 17891329; // 0x1110001
field public static final int config_showDefaultEmergency = 17891330; // 0x1110002
field public static final int config_showDefaultHome = 17891331; // 0x1110003
+ field public static final int config_systemCaptionsServiceCallsEnabled;
}
public static final class R.color {
@@ -724,6 +727,8 @@ package android.app {
method @RequiresPermission(android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND) public void setBackgroundActivityStartsAllowed(boolean);
method public void setDontSendToRestrictedApps(boolean);
method public void setPendingIntentBackgroundActivityLaunchAllowed(boolean);
+ method public void setRequireAllOfPermissions(@Nullable String[]);
+ method public void setRequireNoneOfPermissions(@Nullable String[]);
method @RequiresPermission(anyOf={android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) public void setTemporaryAppAllowlist(long, int, int, @Nullable String);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) public void setTemporaryAppWhitelistDuration(long);
method public android.os.Bundle toBundle();
@@ -969,7 +974,8 @@ package android.app.admin {
}
public class DevicePolicyManager {
- method public int checkProvisioningPreCondition(@NonNull String, @NonNull String);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public int checkProvisioningPreCondition(@NonNull String, @NonNull String);
+ method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public android.os.UserHandle createAndProvisionManagedProfile(@NonNull android.app.admin.ManagedProfileProvisioningParams) throws android.app.admin.ProvisioningException;
method @Nullable public android.content.Intent createProvisioningIntentFromNfcIntent(@NonNull android.content.Intent);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean getBluetoothContactSharingDisabled(@NonNull android.os.UserHandle);
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwner();
@@ -991,6 +997,7 @@ package android.app.admin {
method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long);
method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long, boolean);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public boolean packageHasActiveAdmins(String);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
method @Deprecated @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public boolean setActiveProfileOwner(@NonNull android.content.ComponentName, String) throws java.lang.IllegalArgumentException;
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setDeviceProvisioningConfigApplied();
method @Deprecated @RequiresPermission(value=android.Manifest.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS, conditional=true) public void setProfileOwnerCanAccessDeviceIds(@NonNull android.content.ComponentName);
@@ -1058,6 +1065,65 @@ package android.app.admin {
field public static final int STATE_USER_UNMANAGED = 0; // 0x0
}
+ public final class FullyManagedDeviceProvisioningParams implements android.os.Parcelable {
+ method public boolean canDeviceOwnerGrantSensorsPermissions();
+ method public int describeContents();
+ method @NonNull public android.content.ComponentName getDeviceAdminComponentName();
+ method public long getLocalTime();
+ method @Nullable public java.util.Locale getLocale();
+ method @NonNull public String getOwnerName();
+ method @Nullable public String getTimeZone();
+ method public boolean isLeaveAllSystemAppsEnabled();
+ method public void writeToParcel(@NonNull android.os.Parcel, @Nullable int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.FullyManagedDeviceProvisioningParams> CREATOR;
+ }
+
+ public static final class FullyManagedDeviceProvisioningParams.Builder {
+ ctor public FullyManagedDeviceProvisioningParams.Builder(@NonNull android.content.ComponentName, @NonNull String);
+ method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams build();
+ method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setCanDeviceOwnerGrantSensorsPermissions(boolean);
+ method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLeaveAllSystemAppsEnabled(boolean);
+ method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLocalTime(long);
+ method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLocale(@Nullable java.util.Locale);
+ method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setTimeZone(@Nullable String);
+ }
+
+ public final class ManagedProfileProvisioningParams implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.accounts.Account getAccountToMigrate();
+ method @NonNull public String getOwnerName();
+ method @NonNull public android.content.ComponentName getProfileAdminComponentName();
+ method @Nullable public String getProfileName();
+ method public boolean isKeepingAccountOnMigration();
+ method public boolean isLeaveAllSystemAppsEnabled();
+ method public boolean isOrganizationOwnedProvisioning();
+ method public void writeToParcel(@NonNull android.os.Parcel, @Nullable int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.ManagedProfileProvisioningParams> CREATOR;
+ }
+
+ public static final class ManagedProfileProvisioningParams.Builder {
+ ctor public ManagedProfileProvisioningParams.Builder(@NonNull android.content.ComponentName, @NonNull String);
+ method @NonNull public android.app.admin.ManagedProfileProvisioningParams build();
+ method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setAccountToMigrate(@Nullable android.accounts.Account);
+ method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setKeepingAccountOnMigration(boolean);
+ method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setLeaveAllSystemAppsEnabled(boolean);
+ method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setOrganizationOwnedProvisioning(boolean);
+ method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setProfileName(@Nullable String);
+ }
+
+ public class ProvisioningException extends android.util.AndroidException {
+ ctor public ProvisioningException(@NonNull Exception, int);
+ method public int getProvisioningError();
+ field public static final int ERROR_ADMIN_PACKAGE_INSTALLATION_FAILED = 3; // 0x3
+ field public static final int ERROR_PRE_CONDITION_FAILED = 1; // 0x1
+ field public static final int ERROR_PROFILE_CREATION_FAILED = 2; // 0x2
+ field public static final int ERROR_REMOVE_NON_REQUIRED_APPS_FAILED = 6; // 0x6
+ field public static final int ERROR_SETTING_PROFILE_OWNER_FAILED = 4; // 0x4
+ field public static final int ERROR_SET_DEVICE_OWNER_FAILED = 7; // 0x7
+ field public static final int ERROR_STARTING_PROFILE_FAILED = 5; // 0x5
+ field public static final int ERROR_UNKNOWN = 0; // 0x0
+ }
+
public final class SystemUpdatePolicy implements android.os.Parcelable {
method public android.app.admin.SystemUpdatePolicy.InstallationOption getInstallationOptionAt(long);
field public static final int TYPE_PAUSE = 4; // 0x4
@@ -1967,8 +2033,8 @@ package android.app.usage {
method public void reportUsageStop(@NonNull android.app.Activity, @NonNull String);
method @RequiresPermission(android.Manifest.permission.CHANGE_APP_IDLE_STATE) public void setAppStandbyBucket(String, int);
method @RequiresPermission(android.Manifest.permission.CHANGE_APP_IDLE_STATE) public void setAppStandbyBuckets(java.util.Map<java.lang.String,java.lang.Integer>);
- method @RequiresPermission(android.Manifest.permission.CHANGE_APP_LAUNCH_TIME_ESTIMATE) public void setEstimatedLaunchTime(@NonNull String, long);
- method @RequiresPermission(android.Manifest.permission.CHANGE_APP_LAUNCH_TIME_ESTIMATE) public void setEstimatedLaunchTimes(@NonNull java.util.Map<java.lang.String,java.lang.Long>);
+ method @RequiresPermission(android.Manifest.permission.CHANGE_APP_LAUNCH_TIME_ESTIMATE) public void setEstimatedLaunchTimeMillis(@NonNull String, long);
+ method @RequiresPermission(android.Manifest.permission.CHANGE_APP_LAUNCH_TIME_ESTIMATE) public void setEstimatedLaunchTimesMillis(@NonNull java.util.Map<java.lang.String,java.lang.Long>);
method @RequiresPermission(allOf={android.Manifest.permission.SUSPEND_APPS, android.Manifest.permission.OBSERVE_APP_USAGE}) public void unregisterAppUsageLimitObserver(int);
method @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE) public void unregisterAppUsageObserver(int);
method @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE) public void unregisterUsageSessionObserver(int);
@@ -4362,13 +4428,24 @@ package android.hardware.location {
field @NonNull public static final android.os.Parcelable.Creator<android.hardware.location.NanoAppMessage> CREATOR;
}
+ public final class NanoAppRpcService implements android.os.Parcelable {
+ ctor public NanoAppRpcService(long, int);
+ method public int describeContents();
+ method public long getId();
+ method public int getVersion();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.hardware.location.NanoAppRpcService> CREATOR;
+ }
+
public final class NanoAppState implements android.os.Parcelable {
ctor public NanoAppState(long, int, boolean);
ctor public NanoAppState(long, int, boolean, @NonNull java.util.List<java.lang.String>);
+ ctor public NanoAppState(long, int, boolean, @NonNull java.util.List<java.lang.String>, @NonNull java.util.List<android.hardware.location.NanoAppRpcService>);
method public int describeContents();
method public long getNanoAppId();
method @NonNull public java.util.List<java.lang.String> getNanoAppPermissions();
method public long getNanoAppVersion();
+ method @NonNull public java.util.List<android.hardware.location.NanoAppRpcService> getRpcServices();
method public boolean isEnabled();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.hardware.location.NanoAppState> CREATOR;
@@ -5875,9 +5952,11 @@ package android.media.audiopolicy {
method public android.media.AudioTrack createAudioTrackSource(android.media.audiopolicy.AudioMix) throws java.lang.IllegalArgumentException;
method public int detachMixes(@NonNull java.util.List<android.media.audiopolicy.AudioMix>);
method public int getFocusDuckingBehavior();
+ method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioFocusInfo> getFocusStack();
method public int getStatus();
method public boolean removeUidDeviceAffinity(int);
method public boolean removeUserIdDeviceAffinity(int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean sendFocusLoss(@NonNull android.media.AudioFocusInfo) throws java.lang.IllegalStateException;
method public int setFocusDuckingBehavior(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException;
method public void setRegistration(String);
method public boolean setUidDeviceAffinity(int, @NonNull java.util.List<android.media.AudioDeviceInfo>);
@@ -6447,6 +6526,7 @@ package android.media.tv.tuner.dvr {
method public int flush();
method public long read(long);
method public long read(@NonNull byte[], long, long);
+ method public long seek(long);
method public void setFileDescriptor(@NonNull android.os.ParcelFileDescriptor);
method public int start();
method public int stop();
@@ -6582,6 +6662,7 @@ package android.media.tv.tuner.filter {
public class DownloadEvent extends android.media.tv.tuner.filter.FilterEvent {
method public int getDataLength();
+ method public int getDownloadId();
method public int getItemFragmentIndex();
method public int getItemId();
method public int getLastItemFragmentIndex();
@@ -6591,11 +6672,13 @@ package android.media.tv.tuner.filter {
public class DownloadSettings extends android.media.tv.tuner.filter.Settings {
method @NonNull public static android.media.tv.tuner.filter.DownloadSettings.Builder builder(int);
method public int getDownloadId();
+ method public boolean useDownloadId();
}
public static class DownloadSettings.Builder {
method @NonNull public android.media.tv.tuner.filter.DownloadSettings build();
method @NonNull public android.media.tv.tuner.filter.DownloadSettings.Builder setDownloadId(int);
+ method @NonNull public android.media.tv.tuner.filter.DownloadSettings.Builder setUseDownloadId(boolean);
}
public class Filter implements java.lang.AutoCloseable {
@@ -6694,12 +6777,14 @@ package android.media.tv.tuner.filter {
method public long getAudioHandle();
method public long getAvDataId();
method public long getDataLength();
+ method public long getDts();
method @Nullable public android.media.tv.tuner.filter.AudioDescriptor getExtraMetaData();
method @Nullable public android.media.MediaCodec.LinearBlock getLinearBlock();
method @IntRange(from=0) public int getMpuSequenceNumber();
method public long getOffset();
method public long getPts();
method public int getStreamId();
+ method public boolean isDtsPresent();
method public boolean isPrivateData();
method public boolean isPtsPresent();
method public boolean isSecureMemory();
@@ -6809,7 +6894,8 @@ package android.media.tv.tuner.filter {
}
public class SectionEvent extends android.media.tv.tuner.filter.FilterEvent {
- method public int getDataLength();
+ method @Deprecated public int getDataLength();
+ method public long getDataLengthLong();
method public int getSectionNumber();
method public int getTableId();
method public int getVersion();
@@ -7541,6 +7627,7 @@ package android.media.tv.tuner.frontend {
method public int getSignalStrength();
method public int getSnr();
method public int getSpectralInversion();
+ method @NonNull public int[] getStreamIdList();
method public int getSymbolRate();
method @IntRange(from=0, to=65535) public int getSystemId();
method public int getTransmissionMode();
@@ -7587,6 +7674,7 @@ package android.media.tv.tuner.frontend {
field public static final int FRONTEND_STATUS_TYPE_SIGNAL_STRENGTH = 6; // 0x6
field public static final int FRONTEND_STATUS_TYPE_SNR = 1; // 0x1
field public static final int FRONTEND_STATUS_TYPE_SPECTRAL = 10; // 0xa
+ field public static final int FRONTEND_STATUS_TYPE_STREAM_ID_LIST = 39; // 0x27
field public static final int FRONTEND_STATUS_TYPE_SYMBOL_RATE = 7; // 0x7
field public static final int FRONTEND_STATUS_TYPE_T2_SYSTEM_ID = 29; // 0x1d
field public static final int FRONTEND_STATUS_TYPE_TRANSMISSION_MODE = 27; // 0x1b
@@ -9215,6 +9303,7 @@ package android.os {
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setUserIcon(@NonNull android.graphics.Bitmap) throws android.os.UserManager.UserOperationException;
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setUserName(@Nullable String);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean someUserHasAccount(@NonNull String, @NonNull String);
+ field @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public static final String ACTION_CREATE_SUPERVISED_USER = "android.os.action.CREATE_SUPERVISED_USER";
field public static final String ACTION_USER_RESTRICTIONS_CHANGED = "android.os.action.USER_RESTRICTIONS_CHANGED";
field @Deprecated public static final String DISALLOW_OEM_UNLOCK = "no_oem_unlock";
field public static final String DISALLOW_RUN_IN_BACKGROUND = "no_run_in_background";
@@ -12226,6 +12315,14 @@ package android.telephony {
field public static final int ROAMING_TYPE_UNKNOWN = 1; // 0x1
}
+ public final class SignalStrengthUpdateRequest implements android.os.Parcelable {
+ method public boolean isSystemThresholdReportingRequestedWhileIdle();
+ }
+
+ public static final class SignalStrengthUpdateRequest.Builder {
+ method @NonNull @RequiresPermission("android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH") public android.telephony.SignalStrengthUpdateRequest.Builder setSystemThresholdReportingRequestedWhileIdle(boolean);
+ }
+
public final class SmsCbCmasInfo implements android.os.Parcelable {
ctor public SmsCbCmasInfo(int, int, int, int, int, int);
method public int describeContents();
@@ -12521,6 +12618,7 @@ package android.telephony {
}
public class TelephonyManager {
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void addCarrierPrivilegesListener(int, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CarrierPrivilegesListener);
method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) @WorkerThread public void bootstrapAuthenticationRequest(int, @NonNull android.net.Uri, @NonNull android.telephony.gba.UaSecurityProtocolIdentifier, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.BootstrapAuthenticationCallback);
method @Deprecated @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void call(String, String);
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.PinResult changeIccLockPin(@NonNull String, @NonNull String);
@@ -12545,6 +12643,8 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCarrierPrivilegeStatus(int);
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<java.lang.String> getCarrierPrivilegedPackagesForAllActiveSubscriptions();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CarrierRestrictionRules getCarrierRestrictionRules();
+ method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getCarrierServicePackageName();
+ method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getCarrierServicePackageNameForLogicalSlot(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCdmaEnhancedRoamingIndicatorDisplayNumber();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn(int);
@@ -12587,10 +12687,14 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getVoiceActivationState();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handlePinMmi(String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handlePinMmiForSubscriber(int, String);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean iccCloseLogicalChannelBySlot(int, int);
- method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannelBySlot(int, @Nullable String, int);
- method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduBasicChannelBySlot(int, int, int, int, int, int, @Nullable String);
- method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduLogicalChannelBySlot(int, int, int, int, int, int, int, @Nullable String);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void iccCloseLogicalChannelByPort(int, int, int);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean iccCloseLogicalChannelBySlot(int, int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannelByPort(int, int, @Nullable String, int);
+ method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannelBySlot(int, @Nullable String, int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduBasicChannelByPort(int, int, int, int, int, int, int, @Nullable String);
+ method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduBasicChannelBySlot(int, int, int, int, int, int, @Nullable String);
+ method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduLogicalChannelByPort(int, int, int, int, int, int, int, int, @Nullable String);
+ method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduLogicalChannelBySlot(int, int, int, int, int, int, int, @Nullable String);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAnyRadioPoweredOn();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApnMetered(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApplicationOnUicc(int);
@@ -12616,6 +12720,7 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyOtaEmergencyNumberDbInstalled();
method @RequiresPermission(android.Manifest.permission.REBOOT) public int prepareForUnattendedReboot();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean rebootRadio();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void removeCarrierPrivilegesListener(@NonNull android.telephony.TelephonyManager.CarrierPrivilegesListener);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void reportDefaultNetworkStatus(boolean);
method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestModemActivityInfo(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.ModemActivityInfo,android.telephony.TelephonyManager.ModemActivityInfoException>);
@@ -12792,6 +12897,10 @@ package android.telephony {
field public static final int RESULT_SUCCESS = 0; // 0x0
}
+ public static interface TelephonyManager.CarrierPrivilegesListener {
+ method public void onCarrierPrivilegesChanged(@NonNull java.util.List<java.lang.String>, @NonNull int[]);
+ }
+
public static class TelephonyManager.ModemActivityInfoException extends java.lang.Exception {
method public int getErrorCode();
field public static final int ERROR_INVALID_INFO_RECEIVED = 2; // 0x2
@@ -13039,6 +13148,7 @@ package android.telephony.data {
method public final int getSlotIndex();
method public final void notifyApnUnthrottled(@NonNull String);
method public final void notifyDataCallListChanged(java.util.List<android.telephony.data.DataCallResponse>);
+ method public final void notifyDataProfileUnthrottled(@NonNull android.telephony.data.DataProfile);
method public void requestDataCallList(@NonNull android.telephony.data.DataServiceCallback);
method public void setDataProfile(@NonNull java.util.List<android.telephony.data.DataProfile>, boolean, @NonNull android.telephony.data.DataServiceCallback);
method public void setInitialAttachApn(@NonNull android.telephony.data.DataProfile, boolean, @NonNull android.telephony.data.DataServiceCallback);
@@ -13049,6 +13159,7 @@ package android.telephony.data {
public class DataServiceCallback {
method public void onApnUnthrottled(@NonNull String);
method public void onDataCallListChanged(@NonNull java.util.List<android.telephony.data.DataCallResponse>);
+ method public void onDataProfileUnthrottled(@NonNull android.telephony.data.DataProfile);
method public void onDeactivateDataCallComplete(int);
method public void onRequestDataCallListComplete(int, @NonNull java.util.List<android.telephony.data.DataCallResponse>);
method public void onSetDataProfileComplete(int);
@@ -13156,6 +13267,7 @@ package android.telephony.euicc {
method public void removeNotificationFromList(String, int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
method public void requestAllProfiles(String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.service.euicc.EuiccProfileInfo[]>);
method public void requestDefaultSmdpAddress(String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.String>);
+ method public void requestEnabledProfileForPort(@NonNull String, int, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.euicc.EuiccCardManager.ResultCallback<android.service.euicc.EuiccProfileInfo>);
method public void requestEuiccChallenge(String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
method public void requestEuiccInfo1(String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
method public void requestEuiccInfo2(String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
@@ -13179,6 +13291,7 @@ package android.telephony.euicc {
field public static final int RESULT_CALLER_NOT_ALLOWED = -3; // 0xfffffffd
field public static final int RESULT_EUICC_NOT_FOUND = -2; // 0xfffffffe
field public static final int RESULT_OK = 0; // 0x0
+ field public static final int RESULT_PROFILE_NOT_FOUND = -4; // 0xfffffffc
field public static final int RESULT_UNKNOWN_ERROR = -1; // 0xffffffff
}
@@ -13763,6 +13876,7 @@ package android.telephony.ims {
method public void disableIms(int);
method public void enableIms(int);
method public android.telephony.ims.stub.ImsConfigImplBase getConfig(int);
+ method @NonNull public java.util.concurrent.Executor getExecutor();
method public long getImsServiceCapabilities();
method public android.telephony.ims.stub.ImsRegistrationImplBase getRegistration(int);
method @Nullable public android.telephony.ims.stub.SipTransportImplBase getSipTransport(int);
@@ -14381,6 +14495,7 @@ package android.telephony.ims.feature {
public class MmTelFeature extends android.telephony.ims.feature.ImsFeature {
ctor public MmTelFeature();
+ ctor public MmTelFeature(@NonNull java.util.concurrent.Executor);
method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
method public void changeOfferedRtpHeaderExtensionTypes(@NonNull java.util.Set<android.telephony.ims.RtpHeaderExtensionType>);
method @Nullable public android.telephony.ims.ImsCallProfile createCallProfile(int, int);
@@ -14414,7 +14529,7 @@ package android.telephony.ims.feature {
}
public class RcsFeature extends android.telephony.ims.feature.ImsFeature {
- ctor @Deprecated public RcsFeature();
+ ctor public RcsFeature();
ctor public RcsFeature(@NonNull java.util.concurrent.Executor);
method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
method @NonNull public android.telephony.ims.stub.RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(@NonNull android.telephony.ims.stub.CapabilityExchangeEventListener);
@@ -14519,6 +14634,7 @@ package android.telephony.ims.stub {
}
public class ImsConfigImplBase {
+ ctor public ImsConfigImplBase(@NonNull java.util.concurrent.Executor);
ctor public ImsConfigImplBase();
method public int getConfigInt(int);
method public String getConfigString(int);
@@ -14571,6 +14687,7 @@ package android.telephony.ims.stub {
public class ImsRegistrationImplBase {
ctor public ImsRegistrationImplBase();
+ ctor public ImsRegistrationImplBase(@NonNull java.util.concurrent.Executor);
method public final void onDeregistered(android.telephony.ims.ImsReasonInfo);
method public final void onRegistered(int);
method public final void onRegistered(@NonNull android.telephony.ims.ImsRegistrationAttributes);
@@ -14683,6 +14800,7 @@ package android.telephony.ims.stub {
}
public class SipTransportImplBase {
+ ctor public SipTransportImplBase();
ctor public SipTransportImplBase(@NonNull java.util.concurrent.Executor);
method public void createSipDelegate(int, @NonNull android.telephony.ims.DelegateRequest, @NonNull android.telephony.ims.DelegateStateCallback, @NonNull android.telephony.ims.DelegateMessageCallback);
method public void destroySipDelegate(@NonNull android.telephony.ims.stub.SipDelegate, int);
diff --git a/core/api/system-removed.txt b/core/api/system-removed.txt
index 9cb9ddcc6280..2c5acf182d51 100644
--- a/core/api/system-removed.txt
+++ b/core/api/system-removed.txt
@@ -60,14 +60,6 @@ package android.app.search {
}
-package android.bluetooth {
-
- public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile {
- method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean setPriority(android.bluetooth.BluetoothDevice, int);
- }
-
-}
-
package android.content {
public class Intent implements java.lang.Cloneable android.os.Parcelable {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index a92150079ec9..89dc678360f1 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -353,6 +353,10 @@ package android.app {
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void togglePanel();
}
+ public static final class StatusBarManager.DisableInfo {
+ method public boolean isRotationSuggestionDisabled();
+ }
+
public final class SyncNotedAppOp implements android.os.Parcelable {
ctor public SyncNotedAppOp(int, @IntRange(from=0L) int, @Nullable String, @NonNull String);
}
@@ -440,7 +444,6 @@ package android.app.admin {
public class DevicePolicyManager {
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void clearOrganizationId();
method @RequiresPermission(android.Manifest.permission.CLEAR_FREEZE_PERIOD) public void clearSystemUpdatePolicyFreezePeriodRecord();
- method @Nullable public android.os.UserHandle createAndProvisionManagedProfile(@NonNull android.app.admin.ManagedProfileProvisioningParams) throws android.app.admin.ProvisioningException;
method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceNetworkLogs();
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void forceRemoveActiveAdmin(@NonNull android.content.ComponentName, int);
method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceSecurityLogs();
@@ -457,7 +460,6 @@ package android.app.admin {
method @RequiresPermission(anyOf={android.Manifest.permission.MARK_DEVICE_ORGANIZATION_OWNED, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}, conditional=true) public void markProfileOwnerOnOrganizationOwnedDevice(@NonNull android.content.ComponentName);
method @NonNull public static String operationSafetyReasonToString(int);
method @NonNull public static String operationToString(int);
- method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void resetDefaultCrossProfileIntentFilters(int);
method @RequiresPermission(allOf={android.Manifest.permission.MANAGE_DEVICE_ADMINS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public void setActiveAdmin(@NonNull android.content.ComponentName, boolean, int);
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean setDeviceOwner(@NonNull android.content.ComponentName, @Nullable String, int);
@@ -507,66 +509,6 @@ package android.app.admin {
field public static final int OPERATION_SWITCH_USER = 2; // 0x2
field public static final int OPERATION_UNINSTALL_CA_CERT = 40; // 0x28
field public static final int OPERATION_WIPE_DATA = 8; // 0x8
- field public static final int PROVISIONING_RESULT_ADMIN_PACKAGE_INSTALLATION_FAILED = 3; // 0x3
- field public static final int PROVISIONING_RESULT_PRE_CONDITION_FAILED = 1; // 0x1
- field public static final int PROVISIONING_RESULT_PROFILE_CREATION_FAILED = 2; // 0x2
- field public static final int PROVISIONING_RESULT_REMOVE_NON_REQUIRED_APPS_FAILED = 6; // 0x6
- field public static final int PROVISIONING_RESULT_SETTING_PROFILE_OWNER_FAILED = 4; // 0x4
- field public static final int PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED = 7; // 0x7
- field public static final int PROVISIONING_RESULT_STARTING_PROFILE_FAILED = 5; // 0x5
- }
-
- public final class FullyManagedDeviceProvisioningParams implements android.os.Parcelable {
- method public boolean canDeviceOwnerGrantSensorsPermissions();
- method public int describeContents();
- method @NonNull public android.content.ComponentName getDeviceAdminComponentName();
- method public long getLocalTime();
- method @Nullable public java.util.Locale getLocale();
- method @NonNull public String getOwnerName();
- method @Nullable public String getTimeZone();
- method public boolean isLeaveAllSystemAppsEnabled();
- method public void logParams(@NonNull String);
- method public void writeToParcel(@NonNull android.os.Parcel, @Nullable int);
- field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.FullyManagedDeviceProvisioningParams> CREATOR;
- }
-
- public static final class FullyManagedDeviceProvisioningParams.Builder {
- ctor public FullyManagedDeviceProvisioningParams.Builder(@NonNull android.content.ComponentName, @NonNull String);
- method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams build();
- method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setDeviceOwnerCanGrantSensorsPermissions(boolean);
- method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLeaveAllSystemAppsEnabled(boolean);
- method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLocalTime(long);
- method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLocale(@Nullable java.util.Locale);
- method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setTimeZone(@Nullable String);
- }
-
- public final class ManagedProfileProvisioningParams implements android.os.Parcelable {
- method public int describeContents();
- method @Nullable public android.accounts.Account getAccountToMigrate();
- method @NonNull public String getOwnerName();
- method @NonNull public android.content.ComponentName getProfileAdminComponentName();
- method @Nullable public String getProfileName();
- method public boolean isKeepAccountMigrated();
- method public boolean isLeaveAllSystemAppsEnabled();
- method public boolean isOrganizationOwnedProvisioning();
- method public void logParams(@NonNull String);
- method public void writeToParcel(@NonNull android.os.Parcel, @Nullable int);
- field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.ManagedProfileProvisioningParams> CREATOR;
- }
-
- public static final class ManagedProfileProvisioningParams.Builder {
- ctor public ManagedProfileProvisioningParams.Builder(@NonNull android.content.ComponentName, @NonNull String);
- method @NonNull public android.app.admin.ManagedProfileProvisioningParams build();
- method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setAccountToMigrate(@Nullable android.accounts.Account);
- method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setKeepAccountMigrated(boolean);
- method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setLeaveAllSystemAppsEnabled(boolean);
- method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setOrganizationOwnedProvisioning(boolean);
- method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setProfileName(@Nullable String);
- }
-
- public class ProvisioningException extends android.util.AndroidException {
- ctor public ProvisioningException(@NonNull Exception, int);
- method public int getProvisioningResult();
}
public static final class SecurityLog.SecurityEvent implements android.os.Parcelable {
@@ -1972,6 +1914,7 @@ package android.os.storage {
}
public class StorageManager {
+ method public long computeStorageCacheBytes(@NonNull java.io.File);
method @NonNull public static java.util.UUID convert(@NonNull String);
method @NonNull public static String convert(@NonNull java.util.UUID);
method public boolean isAppIoBlocked(@NonNull java.util.UUID, int, int, int);
@@ -2210,8 +2153,8 @@ package android.security {
}
public class KeyStoreException extends java.lang.Exception {
- ctor public KeyStoreException(int, String);
method public int getErrorCode();
+ method public static boolean hasFailureInfoForError(int);
}
}
diff --git a/core/java/Android.bp b/core/java/Android.bp
index ca9a46847dea..343830a468dc 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -399,6 +399,21 @@ filegroup {
],
}
+aidl_interface {
+ name: "android.os.statsbootstrap_aidl",
+ unstable: true,
+ srcs: [
+ "android/os/IStatsBootstrapAtomService.aidl",
+ "android/os/StatsBootstrapAtom.aidl",
+ "android/os/StatsBootstrapAtomValue.aidl",
+ ],
+ backend: {
+ cpp: {
+ enabled: true,
+ },
+ },
+}
+
// utility classes statically linked into wifi-service
filegroup {
name: "framework-wifi-service-shared-srcs",
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/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index a642696b25ec..3d3855164274 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -21,6 +21,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY
import android.accessibilityservice.GestureDescription.MotionEventGenerator;
import android.annotation.CallbackExecutor;
+import android.annotation.CheckResult;
import android.annotation.ColorInt;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -1394,6 +1395,12 @@ public abstract class AccessibilityService extends Service {
* {@link AccessibilityService#onServiceConnected()} has not yet been
* called) or the service has been disconnected, this method will
* return a default value of {@code 1.0f}.
+ * </p>
+ * <p>
+ * <strong>Note:</strong> This legacy API gets the scale of full-screen
+ * magnification. To get the scale of the current controlling magnifier,
+ * use {@link #getMagnificationConfig} instead.
+ * </p>
*
* @return the current magnification scale
*/
@@ -1422,6 +1429,12 @@ public abstract class AccessibilityService extends Service {
* {@link AccessibilityService#onServiceConnected()} has not yet been
* called) or the service has been disconnected, this method will
* return a default value of {@code 0.0f}.
+ * </p>
+ * <p>
+ * <strong>Note:</strong> This legacy API gets the center position of full-screen
+ * magnification. To get the magnification center of the current controlling magnifier,
+ * use {@link #getMagnificationConfig} instead.
+ * </p>
*
* @return the unscaled screen-relative X coordinate of the center of
* the magnified region
@@ -1451,6 +1464,12 @@ public abstract class AccessibilityService extends Service {
* {@link AccessibilityService#onServiceConnected()} has not yet been
* called) or the service has been disconnected, this method will
* return a default value of {@code 0.0f}.
+ * </p>
+ * <p>
+ * <strong>Note:</strong> This legacy API gets the center position of full-screen
+ * magnification. To get the magnification center of the current controlling magnifier,
+ * use {@link #getMagnificationConfig} instead.
+ * </p>
*
* @return the unscaled screen-relative Y coordinate of the center of
* the magnified region
@@ -1571,6 +1590,11 @@ public abstract class AccessibilityService extends Service {
* {@link AccessibilityService#onServiceConnected()} has not yet been
* called) or the service has been disconnected, this method will have
* no effect and return {@code false}.
+ * <p>
+ * <strong>Note:</strong> This legacy API sets the scale of full-screen
+ * magnification. To set the scale of the specified magnifier,
+ * use {@link #setMagnificationConfig} instead.
+ * </p>
*
* @param scale the magnification scale to set, must be >= 1 and <= 8
* @param animate {@code true} to animate from the current scale or
@@ -1602,6 +1626,12 @@ public abstract class AccessibilityService extends Service {
* {@link AccessibilityService#onServiceConnected()} has not yet been
* called) or the service has been disconnected, this method will have
* no effect and return {@code false}.
+ * </p>
+ * <p>
+ * <strong>Note:</strong> This legacy API sets the center of full-screen
+ * magnification. To set the center of the specified magnifier,
+ * use {@link #setMagnificationConfig} instead.
+ * </p>
*
* @param centerX the unscaled screen-relative X coordinate on which to
* center the viewport
@@ -1704,6 +1734,29 @@ public abstract class AccessibilityService extends Service {
private ArrayMap<OnShowModeChangedListener, Handler> mListeners;
private final Object mLock;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ ENABLE_IME_SUCCESS,
+ ENABLE_IME_FAIL_BY_ADMIN,
+ ENABLE_IME_FAIL_UNKNOWN
+ })
+ public @interface EnableImeResult {}
+ /**
+ * Return value for {@link #setInputMethodEnabled(String, boolean)}. The action succeeded.
+ */
+ public static final int ENABLE_IME_SUCCESS = 0;
+ /**
+ * Return value for {@link #setInputMethodEnabled(String, boolean)}. The action failed
+ * because the InputMethod is not permitted by device policy manager.
+ */
+ public static final int ENABLE_IME_FAIL_BY_ADMIN = 1;
+ /**
+ * Return value for {@link #setInputMethodEnabled(String, boolean)}. The action failed
+ * and the reason is unknown.
+ */
+ public static final int ENABLE_IME_FAIL_UNKNOWN = 2;
+
SoftKeyboardController(@NonNull AccessibilityService service, @NonNull Object lock) {
mService = service;
mLock = lock;
@@ -1932,6 +1985,39 @@ public abstract class AccessibilityService extends Service {
}
return false;
}
+
+ /**
+ * Enable or disable the specified IME for the user for whom the service is activated. The
+ * IME needs to be in the same package as the service and needs to be allowed by device
+ * policy, if there is one. The change will persist until the specified IME is next
+ * explicitly enabled or disabled by whatever means, such as user choice, and may persist
+ * beyond the life cycle of the requesting service.
+ *
+ * @param imeId The ID of the input method to enable or disable. This IME must be installed.
+ * @param enabled {@code true} if the input method associated with {@code imeId} should be
+ * enabled.
+ * @return status code for the result of enabling/disabling the input method associated
+ * with {@code imeId}.
+ * @throws SecurityException if the input method is not in the same package as the service.
+ *
+ * @see android.view.inputmethod.InputMethodInfo#getId()
+ */
+ @CheckResult
+ @EnableImeResult
+ public int setInputMethodEnabled(@NonNull String imeId, boolean enabled)
+ throws SecurityException {
+ final IAccessibilityServiceConnection connection =
+ AccessibilityInteractionClient.getInstance(mService).getConnection(
+ mService.mConnectionId);
+ if (connection != null) {
+ try {
+ return connection.setInputMethodEnabled(imeId, enabled);
+ } catch (RemoteException re) {
+ throw new RuntimeException(re);
+ }
+ }
+ return ENABLE_IME_FAIL_UNKNOWN;
+ }
}
/**
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/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 124dd2ab8240..93e69145f90c 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -102,6 +102,8 @@ interface IAccessibilityServiceConnection {
boolean switchToInputMethod(String imeId);
+ int setInputMethodEnabled(String imeId, boolean enabled);
+
boolean isAccessibilityButtonAvailable();
void sendGesture(int sequence, in ParceledListSlice gestureSteps);
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 2c41e8d0925a..3cbae99224c7 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -23,6 +23,7 @@ import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Looper;
+import android.os.SystemProperties;
import android.os.Trace;
import android.util.AndroidRuntimeException;
import android.util.Log;
@@ -74,6 +75,8 @@ import java.util.HashMap;
public class ValueAnimator extends Animator implements AnimationHandler.AnimationFrameCallback {
private static final String TAG = "ValueAnimator";
private static final boolean DEBUG = false;
+ private static final boolean TRACE_ANIMATION_FRACTION = SystemProperties.getBoolean(
+ "persist.debug.animator.trace_fraction", false);
/**
* Internal constants
@@ -1554,6 +1557,10 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
@CallSuper
@UnsupportedAppUsage
void animateValue(float fraction) {
+ if (TRACE_ANIMATION_FRACTION) {
+ Trace.traceCounter(Trace.TRACE_TAG_VIEW, getNameForTrace() + hashCode(),
+ (int) (fraction * 1000));
+ }
fraction = mInterpolator.getInterpolation(fraction);
mCurrentFraction = fraction;
int numValues = mValues.length;
diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java
index db7ab1a6f379..eb4a355c8ae7 100644
--- a/core/java/android/app/ActivityClient.java
+++ b/core/java/android/app/ActivityClient.java
@@ -20,6 +20,7 @@ import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Intent;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.os.Bundle;
import android.os.IBinder;
import android.os.PersistableBundle;
@@ -498,6 +499,28 @@ public class ActivityClient {
}
}
+ /**
+ * Shows or hides a Camera app compat toggle for stretched issues with the requested state.
+ *
+ * @param token The token for the window that needs a control.
+ * @param showControl Whether the control should be shown or hidden.
+ * @param transformationApplied Whether the treatment is already applied.
+ * @param callback The callback executed when the user clicks on a control.
+ */
+ void requestCompatCameraControl(Resources res, IBinder token, boolean showControl,
+ boolean transformationApplied, ICompatCameraControlCallback callback) {
+ if (!res.getBoolean(com.android.internal.R.bool
+ .config_isCameraCompatControlForStretchedIssuesEnabled)) {
+ return;
+ }
+ try {
+ getActivityClientController().requestCompatCameraControl(
+ token, showControl, transformationApplied, callback);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
public static ActivityClient getInstance() {
return sInstance.get();
}
diff --git a/core/java/android/app/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 1c53fbd4de49..a8894dcd0661 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -561,8 +561,8 @@ public final class ActivityThread extends ClientTransactionHandler
private Configuration mPendingOverrideConfig;
// Used for consolidating configs before sending on to Activity.
private Configuration tmpConfig = new Configuration();
- // Callback used for updating activity override config.
- ViewRootImpl.ActivityConfigCallback configCallback;
+ // Callback used for updating activity override config and camera compat control state.
+ ViewRootImpl.ActivityConfigCallback activityConfigCallback;
ActivityClientRecord nextIdle;
// Indicates whether this activity is currently the topmost resumed one in the system.
@@ -660,13 +660,30 @@ public final class ActivityThread extends ClientTransactionHandler
stopped = false;
hideForNow = false;
nextIdle = null;
- configCallback = (Configuration overrideConfig, int newDisplayId) -> {
- if (activity == null) {
- throw new IllegalStateException(
- "Received config update for non-existing activity");
+ activityConfigCallback = new ViewRootImpl.ActivityConfigCallback() {
+ @Override
+ public void onConfigurationChanged(Configuration overrideConfig,
+ int newDisplayId) {
+ if (activity == null) {
+ throw new IllegalStateException(
+ "Received config update for non-existing activity");
+ }
+ activity.mMainThread.handleActivityConfigurationChanged(
+ ActivityClientRecord.this, overrideConfig, newDisplayId);
}
- activity.mMainThread.handleActivityConfigurationChanged(this, overrideConfig,
- newDisplayId);
+
+ @Override
+ public void requestCompatCameraControl(boolean showControl,
+ boolean transformationApplied, ICompatCameraControlCallback callback) {
+ if (activity == null) {
+ throw new IllegalStateException(
+ "Received camera compat control update for non-existing activity");
+ }
+ ActivityClient.getInstance().requestCompatCameraControl(
+ activity.getResources(), token, showControl, transformationApplied,
+ callback);
+ }
+
};
}
@@ -2700,6 +2717,16 @@ public final class ActivityThread extends ClientTransactionHandler
}
}
+ void onSystemUiContextCleanup(ContextImpl context) {
+ synchronized (this) {
+ if (mDisplaySystemUiContexts == null) return;
+ final int index = mDisplaySystemUiContexts.indexOfValue(context);
+ if (index >= 0) {
+ mDisplaySystemUiContexts.removeAt(index);
+ }
+ }
+ }
+
public void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) {
synchronized (this) {
getSystemContext().installSystemApplicationInfo(info, classLoader);
@@ -3662,7 +3689,7 @@ public final class ActivityThread extends ClientTransactionHandler
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
- r.referrer, r.voiceInteractor, window, r.configCallback,
+ r.referrer, r.voiceInteractor, window, r.activityConfigCallback,
r.assistToken, r.shareableActivityToken);
if (customIntent != null) {
@@ -4093,12 +4120,12 @@ public final class ActivityThread extends ClientTransactionHandler
}
private void handleStartBinderTracking() {
- Binder.enableTracing();
+ Binder.enableStackTracking();
}
private void handleStopBinderTrackingAndDump(ParcelFileDescriptor fd) {
try {
- Binder.disableTracing();
+ Binder.disableStackTracking();
Binder.getTransactionTracker().writeTracesToFile(fd);
} finally {
IoUtils.closeQuietly(fd);
@@ -4972,7 +4999,8 @@ public final class ActivityThread extends ClientTransactionHandler
Slog.w(TAG, "Activity top position already set to onTop=" + onTop);
return;
}
- throw new IllegalStateException("Activity top position already set to onTop=" + onTop);
+ // TODO(b/209744518): Remove this short-term workaround while fixing the binder failure.
+ Slog.e(TAG, "Activity top position already set to onTop=" + onTop);
}
r.isTopResumedActivity = onTop;
@@ -5502,8 +5530,8 @@ public final class ActivityThread extends ClientTransactionHandler
} else {
final ViewRootImpl viewRoot = v.getViewRootImpl();
if (viewRoot != null) {
- // Clear the callback to avoid the destroyed activity from receiving
- // configuration changes that are no longer effective.
+ // Clear callbacks to avoid the destroyed activity from receiving
+ // configuration or camera compat changes that are no longer effective.
viewRoot.setActivityConfigCallback(null);
}
wm.removeViewImmediate(v);
@@ -6637,7 +6665,7 @@ public final class ActivityThread extends ClientTransactionHandler
boolean isAppProfileable = isAppDebuggable || data.appInfo.isProfileable();
Trace.setAppTracingAllowed(isAppProfileable);
if ((isAppProfileable || Build.IS_DEBUGGABLE) && data.enableBinderTracking) {
- Binder.enableTracing();
+ Binder.enableStackTracking();
}
// Initialize heap profiling.
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/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/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java
index 5b8cc70cc0a8..3108d9178440 100644
--- a/core/java/android/app/BroadcastOptions.java
+++ b/core/java/android/app/BroadcastOptions.java
@@ -43,6 +43,8 @@ public class BroadcastOptions extends ComponentOptions {
private int mMaxManifestReceiverApiLevel = Build.VERSION_CODES.CUR_DEVELOPMENT;
private boolean mDontSendToRestrictedApps = false;
private boolean mAllowBackgroundActivityStarts;
+ private String[] mRequireAllOfPermissions;
+ private String[] mRequireNoneOfPermissions;
/**
* How long to temporarily put an app on the power allowlist when executing this broadcast
@@ -85,6 +87,20 @@ public class BroadcastOptions extends ComponentOptions {
"android:broadcast.allowBackgroundActivityStarts";
/**
+ * Corresponds to {@link #setRequireAllOfPermissions}
+ * @hide
+ */
+ public static final String KEY_REQUIRE_ALL_OF_PERMISSIONS =
+ "android:broadcast.requireAllOfPermissions";
+
+ /**
+ * Corresponds to {@link #setRequireNoneOfPermissions}
+ * @hide
+ */
+ public static final String KEY_REQUIRE_NONE_OF_PERMISSIONS =
+ "android:broadcast.requireNoneOfPermissions";
+
+ /**
* @hide
* @deprecated Use {@link android.os.PowerExemptionManager#
* TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED} instead.
@@ -132,6 +148,8 @@ public class BroadcastOptions extends ComponentOptions {
mDontSendToRestrictedApps = opts.getBoolean(KEY_DONT_SEND_TO_RESTRICTED_APPS, false);
mAllowBackgroundActivityStarts = opts.getBoolean(KEY_ALLOW_BACKGROUND_ACTIVITY_STARTS,
false);
+ mRequireAllOfPermissions = opts.getStringArray(KEY_REQUIRE_ALL_OF_PERMISSIONS);
+ mRequireNoneOfPermissions = opts.getStringArray(KEY_REQUIRE_NONE_OF_PERMISSIONS);
}
/**
@@ -323,6 +341,44 @@ public class BroadcastOptions extends ComponentOptions {
}
/**
+ * Use this to configure a broadcast to be sent to apps that hold all permissions in
+ * the list. This is only for use with the {@link Context#sendBroadcast(Intent intent,
+ * @Nullable String receiverPermission, @Nullable Bundle options)}.
+ *
+ * <p> If both {@link #setRequireAllOfPermissions(String[])} and
+ * {@link #setRequireNoneOfPermissions(String[])} are used, then receivers must have all of the
+ * permissions set by {@link #setRequireAllOfPermissions(String[])}, and none of the
+ * permissions set by {@link #setRequireNoneOfPermissions(String[])} to get the broadcast.
+ *
+ * @param requiredPermissions a list of Strings of permission the receiver must have, or null
+ * to clear any previously set value.
+ * @hide
+ */
+ @SystemApi
+ public void setRequireAllOfPermissions(@Nullable String[] requiredPermissions) {
+ mRequireAllOfPermissions = requiredPermissions;
+ }
+
+ /**
+ * Use this to configure a broadcast to be sent to apps that don't hold any permissions in
+ * list. This is only for use with the {@link Context#sendBroadcast(Intent intent,
+ * @Nullable String receiverPermission, @Nullable Bundle options)}.
+ *
+ * <p> If both {@link #setRequireAllOfPermissions(String[])} and
+ * {@link #setRequireNoneOfPermissions(String[])} are used, then receivers must have all of the
+ * permissions set by {@link #setRequireAllOfPermissions(String[])}, and none of the
+ * permissions set by {@link #setRequireNoneOfPermissions(String[])} to get the broadcast.
+ *
+ * @param excludedPermissions a list of Strings of permission the receiver must not have,
+ * or null to clear any previously set value.
+ * @hide
+ */
+ @SystemApi
+ public void setRequireNoneOfPermissions(@Nullable String[] excludedPermissions) {
+ mRequireNoneOfPermissions = excludedPermissions;
+ }
+
+ /**
* Returns the created options as a Bundle, which can be passed to
* {@link android.content.Context#sendBroadcast(android.content.Intent)
* Context.sendBroadcast(Intent)} and related methods.
@@ -351,6 +407,12 @@ public class BroadcastOptions extends ComponentOptions {
if (mAllowBackgroundActivityStarts) {
b.putBoolean(KEY_ALLOW_BACKGROUND_ACTIVITY_STARTS, true);
}
+ if (mRequireAllOfPermissions != null) {
+ b.putStringArray(KEY_REQUIRE_ALL_OF_PERMISSIONS, mRequireAllOfPermissions);
+ }
+ if (mRequireNoneOfPermissions != null) {
+ b.putStringArray(KEY_REQUIRE_NONE_OF_PERMISSIONS, mRequireNoneOfPermissions);
+ }
return b.isEmpty() ? null : b;
}
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 4a7361efe4cc..f3e9f105500e 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1272,12 +1272,22 @@ class ContextImpl extends Context {
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
String[] receiverPermissions = receiverPermission == null ? null
: new String[] {receiverPermission};
+ String[] excludedPermissions = null;
+ if (options != null) {
+ String[] receiverPermissionsBundle = options.getStringArray(
+ BroadcastOptions.KEY_REQUIRE_ALL_OF_PERMISSIONS);
+ if (receiverPermissionsBundle != null) {
+ receiverPermissions = receiverPermissionsBundle;
+ }
+ excludedPermissions = options.getStringArray(
+ BroadcastOptions.KEY_REQUIRE_NONE_OF_PERMISSIONS);
+ }
try {
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntentWithFeature(
mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
null, Activity.RESULT_OK, null, null, receiverPermissions,
- null /*excludedPermissions=*/, AppOpsManager.OP_NONE, options, false, false,
+ excludedPermissions, AppOpsManager.OP_NONE, options, false, false,
getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -3212,6 +3222,10 @@ class ContextImpl extends Context {
final void performFinalCleanup(String who, String what) {
//Log.i(TAG, "Cleanup up context: " + this);
mPackageInfo.removeContextRegistrations(getOuterContext(), who, what);
+ if (mContextType == CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI
+ && mToken instanceof WindowTokenClient) {
+ mMainThread.onSystemUiContextCleanup(this);
+ }
}
@UnsupportedAppUsage
diff --git a/core/java/android/app/GameManager.java b/core/java/android/app/GameManager.java
index 78759db28539..29e1b70097f2 100644
--- a/core/java/android/app/GameManager.java
+++ b/core/java/android/app/GameManager.java
@@ -167,4 +167,16 @@ public final class GameManager {
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Called by games to communicate the current state to the platform.
+ * @param gameState An object set to the current state.
+ */
+ public void setGameState(@NonNull GameState gameState) {
+ try {
+ mService.setGameState(mContext.getPackageName(), gameState, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/GameState.aidl b/core/java/android/app/GameState.aidl
new file mode 100644
index 000000000000..b829e866a056
--- /dev/null
+++ b/core/java/android/app/GameState.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;
+
+parcelable GameState; \ No newline at end of file
diff --git a/core/java/android/app/GameState.java b/core/java/android/app/GameState.java
new file mode 100644
index 000000000000..979dd34e8276
--- /dev/null
+++ b/core/java/android/app/GameState.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * State of the game passed to the GameManager.
+ *
+ * This includes a top-level state for the game (indicating if the game can be interrupted without
+ * interfering with content that can't be paused). Since content can be loaded in any state, it
+ * includes an independent boolean flag to indicate loading status.
+ *
+ * Free-form metadata (as a Bundle) and a string description can also be specified by the
+ * application.
+ */
+public final class GameState implements Parcelable {
+ /**
+ * Default Game mode is unknown.
+ */
+ public static final int MODE_UNKNOWN = 0;
+
+ /**
+ * No mode means that the game is not in active play, for example the user is using the game
+ * menu.
+ */
+ public static final int MODE_NONE = 1;
+
+ /**
+ * Indicates if the game is in active, but interruptible, game play.
+ */
+ public static final int MODE_GAMEPLAY_INTERRUPTIBLE = 2;
+
+ /**
+ * Indicates if the game is in active user play mode, which is real time and cannot be
+ * interrupted.
+ */
+ public static final int MODE_GAMEPLAY_UNINTERRUPTIBLE = 3;
+
+ /**
+ * Indicates that the current content shown is not gameplay related. For example it can be an
+ * ad, a web page, a text, or a video.
+ */
+ public static final int MODE_CONTENT = 4;
+
+ /**
+ * Implement the parcelable interface.
+ */
+ public static final @NonNull Creator<GameState> CREATOR = new Creator<GameState>() {
+ @Override
+ public GameState createFromParcel(Parcel in) {
+ return new GameState(in);
+ }
+
+ @Override
+ public GameState[] newArray(int size) {
+ return new GameState[size];
+ }
+ };
+
+ // Indicates if the game is loading assets/resources/compiling/etc. This is independent of game
+ // mode because there could be a loading UI displayed, or there could be loading in the
+ // background.
+ private final boolean mIsLoading;
+
+ // One of the states listed above.
+ private final @GameStateMode int mMode;
+
+ // This is a game specific description. For example can be level or scene name.
+ private final @Nullable String mDescription;
+
+ // This contains any other game specific parameters not covered by the fields above. It can be
+ // quality parameter data, settings, or game modes.
+ private final @NonNull Bundle mMetaData;
+
+ /**
+ * Create a GameState with the specified loading status.
+ * @param isLoading Whether the game is in the loading state.
+ * @param mode The game state mode of type @GameStateMode.
+ */
+ public GameState(boolean isLoading, @GameStateMode int mode) {
+ this(isLoading, mode, null, new Bundle());
+ }
+
+ /**
+ * Create a GameState with the given state variables.
+ * @param isLoading Whether the game is in the loading state.
+ * @param mode The game state mode of type @GameStateMode.
+ * @param description An optional description of the state.
+ * @param metaData Optional metadata.
+ */
+ public GameState(boolean isLoading, @GameStateMode int mode, @Nullable String description,
+ @NonNull Bundle metaData) {
+ mIsLoading = isLoading;
+ mMode = mode;
+ mDescription = description;
+ mMetaData = metaData;
+ }
+
+ private GameState(Parcel in) {
+ mIsLoading = in.readBoolean();
+ mMode = in.readInt();
+ mDescription = in.readString();
+ mMetaData = in.readBundle();
+ }
+
+ /**
+ * @return If the game is loading assets/resources/compiling/etc.
+ */
+ public boolean isLoading() {
+ return mIsLoading;
+ }
+
+ /**
+ * @return The game state mode.
+ */
+ public @GameStateMode int getMode() {
+ return mMode;
+ }
+
+ /**
+ * @return The state description, or null if one is not set.
+ */
+ public @Nullable String getDescription() {
+ return mDescription;
+ }
+
+ /**
+ * @return metadata associated with the state.
+ */
+ public @NonNull Bundle getMetadata() {
+ return mMetaData;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeBoolean(mIsLoading);
+ parcel.writeInt(mMode);
+ parcel.writeString(mDescription);
+ parcel.writeBundle(mMetaData);
+ }
+
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({MODE_UNKNOWN, MODE_NONE, MODE_GAMEPLAY_INTERRUPTIBLE, MODE_GAMEPLAY_UNINTERRUPTIBLE,
+ MODE_CONTENT
+
+ })
+ @interface GameStateMode {}
+}
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/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl
index aba6eb9229f2..83c57c573b82 100644
--- a/core/java/android/app/IActivityClientController.aidl
+++ b/core/java/android/app/IActivityClientController.aidl
@@ -17,6 +17,7 @@
package android.app;
import android.app.ActivityManager;
+import android.app.ICompatCameraControlCallback;
import android.app.IRequestFinishCallback;
import android.app.PictureInPictureParams;
import android.content.ComponentName;
@@ -143,4 +144,15 @@ interface IActivityClientController {
/** Reports that the splash screen view has attached to activity. */
oneway void splashScreenAttached(in IBinder token);
+
+ /**
+ * Shows or hides a Camera app compat toggle for stretched issues with the requested state.
+ *
+ * @param token The token for the window that needs a control.
+ * @param showControl Whether the control should be shown or hidden.
+ * @param transformationApplied Whether the treatment is already applied.
+ * @param callback The callback executed when the user clicks on a control.
+ */
+ oneway void requestCompatCameraControl(in IBinder token, boolean showControl,
+ boolean transformationApplied, in ICompatCameraControlCallback callback);
}
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 8f904b57474a..a2578d6d598a 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -520,6 +520,10 @@ interface IActivityManager {
// descriptor.
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
boolean stopBinderTrackingAndDump(in ParcelFileDescriptor fd);
+
+ /** Enables server-side binder tracing for the calling uid. */
+ void enableBinderTracing();
+
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
void suppressResizeConfigChanges(boolean suppress);
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
diff --git a/core/java/android/app/ICompatCameraControlCallback.aidl b/core/java/android/app/ICompatCameraControlCallback.aidl
new file mode 100644
index 000000000000..1a7f21066630
--- /dev/null
+++ b/core/java/android/app/ICompatCameraControlCallback.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;
+
+/**
+ * This callback allows ActivityRecord to ask the calling View to apply the treatment for stretched
+ * issues affecting camera viewfinders when the user clicks on the camera compat control.
+ *
+ * {@hide}
+ */
+oneway interface ICompatCameraControlCallback {
+
+ void applyCameraCompatTreatment();
+
+ void revertCameraCompatTreatment();
+}
diff --git a/core/java/android/app/IGameManagerService.aidl b/core/java/android/app/IGameManagerService.aidl
index 189f0a2fca23..d9aa586c6bbb 100644
--- a/core/java/android/app/IGameManagerService.aidl
+++ b/core/java/android/app/IGameManagerService.aidl
@@ -16,6 +16,8 @@
package android.app;
+import android.app.GameState;
+
/**
* @hide
*/
@@ -24,4 +26,5 @@ interface IGameManagerService {
void setGameMode(String packageName, int gameMode, int userId);
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
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 370031a14f42..eb4585dd7097 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1259,7 +1259,7 @@ public class Instrumentation {
info, title, parent, id,
(Activity.NonConfigurationInstances)lastNonConfigurationInstance,
new Configuration(), null /* referrer */, null /* voiceInteractor */,
- null /* window */, null /* activityConfigCallback */, null /*assistToken*/,
+ null /* window */, null /* activityCallback */, null /*assistToken*/,
null /*shareableActivityToken*/);
return activity;
}
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 5750484c6169..77af474a04ac 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -1352,7 +1352,9 @@ public final class LoadedApk {
Application app = null;
- String appClass = mApplicationInfo.className;
+ final String myProcessName = Process.myProcessName();
+ String appClass = mApplicationInfo.getCustomApplicationClassNameForProcess(
+ myProcessName);
if (forceDefaultAppClass || (appClass == null)) {
appClass = "android.app.Application";
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index af907af2ce0d..779552f1bbed 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5808,6 +5808,7 @@ public class Notification implements Parcelable
p, result);
buildCustomContentIntoTemplate(mContext, standard, customContent,
p, result);
+ makeHeaderExpanded(standard);
return standard;
}
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/OWNERS b/core/java/android/app/OWNERS
index e099716cd45a..068304d75910 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -31,6 +31,7 @@ per-file SystemServiceRegistry.java = file:/services/core/java/com/android/serve
per-file *UserSwitchObserver* = file:/services/core/java/com/android/server/am/OWNERS
per-file UiAutomation.java = file:/services/accessibility/OWNERS
per-file GameManager* = file:/GAME_MANAGER_OWNERS
+per-file GameState* = file:/GAME_MANAGER_OWNERS
per-file IGameManager* = file:/GAME_MANAGER_OWNERS
# ActivityThread
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/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 2392c9aa185c..ae578f5e60ed 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -165,7 +165,7 @@ public class StatusBarManager {
*
* @hide
*/
- public static final int DEFAULT_SETUP_DISABLE2_FLAGS = DISABLE2_ROTATE_SUGGESTIONS;
+ public static final int DEFAULT_SETUP_DISABLE2_FLAGS = DISABLE2_NONE;
/**
* disable flags to be applied when the device is sim-locked.
@@ -712,6 +712,7 @@ public class StatusBarManager {
private boolean mSystemIcons;
private boolean mClock;
private boolean mNotificationIcons;
+ private boolean mRotationSuggestion;
/** @hide */
public DisableInfo(int flags1, int flags2) {
@@ -723,6 +724,7 @@ public class StatusBarManager {
mSystemIcons = (flags1 & DISABLE_SYSTEM_INFO) != 0;
mClock = (flags1 & DISABLE_CLOCK) != 0;
mNotificationIcons = (flags1 & DISABLE_NOTIFICATION_ICONS) != 0;
+ mRotationSuggestion = (flags2 & DISABLE2_ROTATE_SUGGESTIONS) != 0;
}
/** @hide */
@@ -846,14 +848,24 @@ public class StatusBarManager {
}
/**
- * @return {@code true} if no components are disabled (default state)
+ * Returns whether the rotation suggestion is disabled.
*
* @hide
*/
+ @TestApi
+ public boolean isRotationSuggestionDisabled() {
+ return mRotationSuggestion;
+ }
+
+ /**
+ * @return {@code true} if no components are disabled (default state)
+ * @hide
+ */
@SystemApi
public boolean areAllComponentsEnabled() {
return !mStatusBarExpansion && !mNavigateHome && !mNotificationPeeking && !mRecents
- && !mSearch && !mSystemIcons && !mClock && !mNotificationIcons;
+ && !mSearch && !mSystemIcons && !mClock && !mNotificationIcons
+ && !mRotationSuggestion;
}
/** @hide */
@@ -866,6 +878,7 @@ public class StatusBarManager {
mSystemIcons = false;
mClock = false;
mNotificationIcons = false;
+ mRotationSuggestion = false;
}
/**
@@ -875,7 +888,8 @@ public class StatusBarManager {
*/
public boolean areAllComponentsDisabled() {
return mStatusBarExpansion && mNavigateHome && mNotificationPeeking
- && mRecents && mSearch && mSystemIcons && mClock && mNotificationIcons;
+ && mRecents && mSearch && mSystemIcons && mClock && mNotificationIcons
+ && mRotationSuggestion;
}
/** @hide */
@@ -888,6 +902,7 @@ public class StatusBarManager {
mSystemIcons = true;
mClock = true;
mNotificationIcons = true;
+ mRotationSuggestion = true;
}
@NonNull
@@ -904,6 +919,7 @@ public class StatusBarManager {
sb.append(" mSystemIcons=").append(mSystemIcons ? "disabled" : "enabled");
sb.append(" mClock=").append(mClock ? "disabled" : "enabled");
sb.append(" mNotificationIcons=").append(mNotificationIcons ? "disabled" : "enabled");
+ sb.append(" mRotationSuggestion=").append(mRotationSuggestion ? "disabled" : "enabled");
return sb.toString();
@@ -927,6 +943,7 @@ public class StatusBarManager {
if (mSystemIcons) disable1 |= DISABLE_SYSTEM_INFO;
if (mClock) disable1 |= DISABLE_CLOCK;
if (mNotificationIcons) disable1 |= DISABLE_NOTIFICATION_ICONS;
+ if (mRotationSuggestion) disable2 |= DISABLE2_ROTATE_SUGGESTIONS;
return new Pair<Integer, Integer>(disable1, disable2);
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 81e6ae453277..85ddff9a9311 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -131,10 +131,12 @@ import android.media.tv.interactive.TvIAppManager;
import android.media.tv.tunerresourcemanager.ITunerResourceManager;
import android.media.tv.tunerresourcemanager.TunerResourceManager;
import android.net.ConnectivityFrameworkInitializer;
+import android.net.ConnectivityFrameworkInitializerTiramisu;
import android.net.EthernetManager;
import android.net.IEthernetManager;
import android.net.IIpSecService;
import android.net.INetworkPolicyManager;
+import android.net.INetworkStatsService;
import android.net.IPacProxyManager;
import android.net.IVpnManager;
import android.net.IpSecManager;
@@ -146,8 +148,6 @@ import android.net.TetheringManager;
import android.net.VpnManager;
import android.net.lowpan.ILowpanManager;
import android.net.lowpan.LowpanManager;
-import android.net.nsd.INsdManager;
-import android.net.nsd.NsdManager;
import android.net.vcn.IVcnManagementService;
import android.net.vcn.VcnManager;
import android.net.wifi.WifiFrameworkInitializer;
@@ -228,6 +228,8 @@ import android.view.contentcapture.ContentCaptureManager;
import android.view.contentcapture.IContentCaptureManager;
import android.view.displayhash.DisplayHashManager;
import android.view.inputmethod.InputMethodManager;
+import android.view.selectiontoolbar.ISelectionToolbarManager;
+import android.view.selectiontoolbar.SelectionToolbarManager;
import android.view.textclassifier.TextClassificationManager;
import android.view.textservice.TextServicesManager;
import android.view.translation.ITranslationManager;
@@ -367,6 +369,15 @@ public final class SystemServiceRegistry {
return new TextClassificationManager(ctx);
}});
+ registerService(Context.SELECTION_TOOLBAR_SERVICE, SelectionToolbarManager.class,
+ new CachedServiceFetcher<SelectionToolbarManager>() {
+ @Override
+ public SelectionToolbarManager createService(ContextImpl ctx) {
+ IBinder b = ServiceManager.getService(Context.SELECTION_TOOLBAR_SERVICE);
+ return new SelectionToolbarManager(ctx.getOuterContext(),
+ ISelectionToolbarManager.Stub.asInterface(b));
+ }});
+
registerService(Context.FONT_SERVICE, FontManager.class,
new CachedServiceFetcher<FontManager>() {
@Override
@@ -576,15 +587,6 @@ public final class SystemServiceRegistry {
ctx.mMainThread.getHandler());
}});
- registerService(Context.NSD_SERVICE, NsdManager.class,
- new CachedServiceFetcher<NsdManager>() {
- @Override
- public NsdManager createService(ContextImpl ctx) throws ServiceNotFoundException {
- IBinder b = ServiceManager.getServiceOrThrow(Context.NSD_SERVICE);
- INsdManager service = INsdManager.Stub.asInterface(b);
- return new NsdManager(ctx.getOuterContext(), service);
- }});
-
registerService(Context.PEOPLE_SERVICE, PeopleManager.class,
new CachedServiceFetcher<PeopleManager>() {
@Override
@@ -1012,7 +1014,11 @@ public final class SystemServiceRegistry {
new CachedServiceFetcher<NetworkStatsManager>() {
@Override
public NetworkStatsManager createService(ContextImpl ctx) throws ServiceNotFoundException {
- return new NetworkStatsManager(ctx.getOuterContext());
+ // TODO: Replace with an initializer in the module, see
+ // {@code ConnectivityFrameworkInitializer}.
+ final INetworkStatsService service = INetworkStatsService.Stub.asInterface(
+ ServiceManager.getServiceOrThrow(Context.NETWORK_STATS_SERVICE));
+ return new NetworkStatsManager(ctx.getOuterContext(), service);
}});
registerService(Context.PERSISTENT_DATA_BLOCK_SERVICE, PersistentDataBlockManager.class,
@@ -1547,6 +1553,7 @@ public final class SystemServiceRegistry {
SupplementalProcessFrameworkInitializer.registerServiceWrappers();
UwbFrameworkInitializer.registerServiceWrappers();
SafetyCenterFrameworkInitializer.registerServiceWrappers();
+ ConnectivityFrameworkInitializerTiramisu.registerServiceWrappers();
} finally {
// If any of the above code throws, we're in a pretty bad shape and the process
// will likely crash, but we'll reset it just in case there's an exception handler...
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 95b00c17352d..18f9379c4111 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -19,6 +19,7 @@ package android.app;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
@@ -39,6 +40,8 @@ import android.view.DisplayCutout;
import android.window.TaskSnapshot;
import android.window.WindowContainerToken;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Objects;
@@ -274,6 +277,51 @@ public class TaskInfo {
*/
public boolean isSleeping;
+ /**
+ * Camera compat control isn't shown because it's not requested by heuristics.
+ * @hide
+ */
+ public static final int CAMERA_COMPAT_CONTROL_HIDDEN = 0;
+
+ /**
+ * Camera compat control is shown with the treatment suggested.
+ * @hide
+ */
+ public static final int CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED = 1;
+
+ /**
+ * Camera compat control is shown to allow reverting the applied treatment.
+ * @hide
+ */
+ public static final int CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED = 2;
+
+ /**
+ * Camera compat control is dismissed by user.
+ * @hide
+ */
+ public static final int CAMERA_COMPAT_CONTROL_DISMISSED = 3;
+
+ /**
+ * Enum for the Camera app compat control states.
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "CAMERA_COMPAT_CONTROL_" }, value = {
+ CAMERA_COMPAT_CONTROL_HIDDEN,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED,
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED,
+ CAMERA_COMPAT_CONTROL_DISMISSED,
+ })
+ public @interface CameraCompatControlState {};
+
+ /**
+ * State of the Camera app compat control which is used to correct stretched viewfinder
+ * in apps that don't handle all possible configurations and changes between them correctly.
+ * @hide
+ */
+ @CameraCompatControlState
+ public int cameraCompatControlState = CAMERA_COMPAT_CONTROL_HIDDEN;
+
TaskInfo() {
// Do nothing
}
@@ -342,6 +390,17 @@ public class TaskInfo {
launchCookies.add(cookie);
}
+ /** @hide */
+ public boolean hasCameraCompatControl() {
+ return cameraCompatControlState != CAMERA_COMPAT_CONTROL_HIDDEN
+ && cameraCompatControlState != CAMERA_COMPAT_CONTROL_DISMISSED;
+ }
+
+ /** @hide */
+ public boolean hasCompatUI() {
+ return hasCameraCompatControl() || topActivityInSizeCompat;
+ }
+
/**
* @return {@code true} if this task contains the launch cookie.
* @hide
@@ -394,19 +453,20 @@ public class TaskInfo {
* @return {@code true} if parameters that are important for size compat have changed.
* @hide
*/
- public boolean equalsForSizeCompat(@Nullable TaskInfo that) {
+ public boolean equalsForCompatUi(@Nullable TaskInfo that) {
if (that == null) {
return false;
}
return displayId == that.displayId
&& taskId == that.taskId
&& topActivityInSizeCompat == that.topActivityInSizeCompat
- // Bounds are important if top activity is in size compat
- && (!topActivityInSizeCompat || configuration.windowConfiguration.getBounds()
+ && cameraCompatControlState == that.cameraCompatControlState
+ // Bounds are important if top activity has compat controls.
+ && (!hasCompatUI() || configuration.windowConfiguration.getBounds()
.equals(that.configuration.windowConfiguration.getBounds()))
- && (!topActivityInSizeCompat || configuration.getLayoutDirection()
+ && (!hasCompatUI() || configuration.getLayoutDirection()
== that.configuration.getLayoutDirection())
- && (!topActivityInSizeCompat || isVisible == that.isVisible);
+ && (!hasCompatUI() || isVisible == that.isVisible);
}
/**
@@ -449,6 +509,7 @@ public class TaskInfo {
topActivityInSizeCompat = source.readBoolean();
mTopActivityLocusId = source.readTypedObject(LocusId.CREATOR);
displayAreaFeatureId = source.readInt();
+ cameraCompatControlState = source.readInt();
}
/**
@@ -492,6 +553,7 @@ public class TaskInfo {
dest.writeBoolean(topActivityInSizeCompat);
dest.writeTypedObject(mTopActivityLocusId, flags);
dest.writeInt(displayAreaFeatureId);
+ dest.writeInt(cameraCompatControlState);
}
@Override
@@ -525,6 +587,22 @@ public class TaskInfo {
+ " topActivityInSizeCompat=" + topActivityInSizeCompat
+ " locusId=" + mTopActivityLocusId
+ " displayAreaFeatureId=" + displayAreaFeatureId
+ + " cameraCompatControlState="
+ + cameraCompatControlStateToString(cameraCompatControlState)
+ "}";
}
+
+ /** @hide */
+ public static String cameraCompatControlStateToString(
+ @CameraCompatControlState int cameraCompatControlState) {
+ switch (cameraCompatControlState) {
+ case CAMERA_COMPAT_CONTROL_HIDDEN: return "hidden";
+ case CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED: return "treatment-suggested";
+ case CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED: return "treatment-applied";
+ case CAMERA_COMPAT_CONTROL_DISMISSED: return "dismissed";
+ default:
+ throw new AssertionError(
+ "Unexpected camera compat control state: " + cameraCompatControlState);
+ }
+ }
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index ba2828370534..7969cda7b84d 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2354,88 +2354,6 @@ public class DevicePolicyManager {
public @interface ProvisioningPreCondition {}
/**
- * Service-specific error code for {@link #provisionFullyManagedDevice} and
- * {@link #createAndProvisionManagedProfile}:
- * Indicates the call to {@link #checkProvisioningPreCondition} returned an error code.
- *
- * @hide
- */
- @TestApi
- public static final int PROVISIONING_RESULT_PRE_CONDITION_FAILED = 1;
-
- /**
- * Service-specific error code for {@link #createAndProvisionManagedProfile}:
- * Indicates the call to {@link UserManager#createProfileForUserEvenWhenDisallowed}
- * returned {@code null}.
- *
- * @hide
- */
- @TestApi
- public static final int PROVISIONING_RESULT_PROFILE_CREATION_FAILED = 2;
-
- /**
- * Service-specific error code for {@link #createAndProvisionManagedProfile}:
- * Indicates the call to {@link PackageManager#installExistingPackageAsUser} has failed.
- *
- * @hide
- */
- @TestApi
- public static final int PROVISIONING_RESULT_ADMIN_PACKAGE_INSTALLATION_FAILED = 3;
-
- /**
- * Service-specific error code for {@link #createAndProvisionManagedProfile}:
- * Indicates the call to {@link #setProfileOwner} returned {@code false}.
- *
- * @hide
- */
- @TestApi
- public static final int PROVISIONING_RESULT_SETTING_PROFILE_OWNER_FAILED = 4;
-
- /**
- * Service-specific error code for {@link #createAndProvisionManagedProfile}:
- * Indicates that starting the newly created profile has failed.
- *
- * @hide
- */
- @TestApi
- public static final int PROVISIONING_RESULT_STARTING_PROFILE_FAILED = 5;
-
- /**
- * Service-specific error code for {@link #provisionFullyManagedDevice}:
- * Indicates that removing the non required apps have failed.
- *
- * @hide
- */
- @TestApi
- public static final int PROVISIONING_RESULT_REMOVE_NON_REQUIRED_APPS_FAILED = 6;
-
- /**
- * Service-specific error code for {@link #provisionFullyManagedDevice}:
- * Indicates the call to {@link #setDeviceOwner} returned {@code false}.
- *
- * @hide
- */
- @TestApi
- public static final int PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED = 7;
-
- /**
- * Service-specific error codes for {@link #createAndProvisionManagedProfile} and
- * {@link #provisionFullyManagedDevice} indicating all the errors during provisioning.
- *
- * @hide
- */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = { "PROVISIONING_RESULT_" }, value = {
- PROVISIONING_RESULT_PRE_CONDITION_FAILED, PROVISIONING_RESULT_PROFILE_CREATION_FAILED,
- PROVISIONING_RESULT_ADMIN_PACKAGE_INSTALLATION_FAILED,
- PROVISIONING_RESULT_SETTING_PROFILE_OWNER_FAILED,
- PROVISIONING_RESULT_STARTING_PROFILE_FAILED,
- PROVISIONING_RESULT_REMOVE_NON_REQUIRED_APPS_FAILED,
- PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED
- })
- public @interface ProvisioningResult {}
-
- /**
* Disable all configurable SystemUI features during LockTask mode. This includes,
* <ul>
* <li>system info area in the status bar (connectivity icons, clock, etc.)
@@ -5821,13 +5739,23 @@ public class DevicePolicyManager {
"android.app.action.CHECK_POLICY_COMPLIANCE";
/**
- * Broadcast action: notify managed provisioning that new managed user is created.
+ * Broadcast action: notify managed provisioning that PO/DO provisioning has completed.
*
* @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String ACTION_MANAGED_USER_CREATED =
- "android.app.action.MANAGED_USER_CREATED";
+ public static final String ACTION_PROVISIONING_COMPLETED =
+ "android.app.action.PROVISIONING_COMPLETED";
+
+ /**
+ * Extra for {@link #ACTION_PROVISIONING_COMPLETED} to indicate the provisioning action that has
+ * been completed, this can either be {@link #ACTION_PROVISION_MANAGED_PROFILE},
+ * {@link #ACTION_PROVISION_MANAGED_DEVICE}, or {@link #ACTION_PROVISION_MANAGED_USER}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_PROVISIONING_ACTION =
+ "android.app.extra.PROVISIONING_ACTION";
/**
* Broadcast action: notify system that a new (Android) user was added when the device is
@@ -11424,6 +11352,7 @@ public class DevicePolicyManager {
* @hide
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
public @ProvisioningPreCondition int checkProvisioningPreCondition(
@NonNull String action, @NonNull String packageName) {
try {
@@ -14125,7 +14054,8 @@ public class DevicePolicyManager {
* @hide
*/
@Nullable
- @TestApi
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
public UserHandle createAndProvisionManagedProfile(
@NonNull ManagedProfileProvisioningParams provisioningParams)
throws ProvisioningException {
@@ -14158,7 +14088,7 @@ public class DevicePolicyManager {
*
* @hide
*/
- @TestApi
+ @SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
public void provisionFullyManagedDevice(
@NonNull FullyManagedDeviceProvisioningParams provisioningParams)
diff --git a/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java b/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java
index 80655ddb886f..8c232c012f50 100644
--- a/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java
+++ b/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java
@@ -21,7 +21,7 @@ import static java.util.Objects.requireNonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
-import android.annotation.TestApi;
+import android.annotation.SystemApi;
import android.content.ComponentName;
import android.os.Parcel;
import android.os.Parcelable;
@@ -32,9 +32,10 @@ import java.util.Locale;
/**
* Params required to provision a fully managed device, see
* {@link DevicePolicyManager#provisionFullyManagedDevice}.
+ *
* @hide
*/
-@TestApi
+@SystemApi
public final class FullyManagedDeviceProvisioningParams implements Parcelable {
private static final String LEAVE_ALL_SYSTEM_APPS_ENABLED_PARAM =
"LEAVE_ALL_SYSTEM_APPS_ENABLED";
@@ -92,29 +93,50 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable {
return localeStr == null ? null : Locale.forLanguageTag(localeStr);
}
+ /**
+ * Returns the device owner's {@link ComponentName}.
+ */
@NonNull
public ComponentName getDeviceAdminComponentName() {
return mDeviceAdminComponentName;
}
+ /**
+ * Returns the device owner's name.
+ */
@NonNull
public String getOwnerName() {
return mOwnerName;
}
+ /**
+ * Returns {@code true} if system apps should be left enabled after provisioning.
+ */
public boolean isLeaveAllSystemAppsEnabled() {
return mLeaveAllSystemAppsEnabled;
}
+ /**
+ * If set, it returns the time zone to set for the device after provisioning, otherwise returns
+ * {@code null};
+ */
@Nullable
public String getTimeZone() {
return mTimeZone;
}
+ /**
+ * If set, it returns the local time to set for the device after provisioning, otherwise returns
+ * 0.
+ */
public long getLocalTime() {
return mLocalTime;
}
+ /**
+ * If set, it returns the {@link Locale} to set for the device after provisioning, otherwise
+ * returns {@code null}.
+ */
@Nullable
public @SuppressLint("UseIcu") Locale getLocale() {
return mLocale;
@@ -130,6 +152,8 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable {
/**
* Logs the provisioning params using {@link DevicePolicyEventLogger}.
+ *
+ * @hide
*/
public void logParams(@NonNull String callerPackage) {
requireNonNull(callerPackage);
@@ -232,8 +256,7 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable {
* See {@link DevicePolicyManager#EXTRA_PROVISIONING_SENSORS_PERMISSION_GRANT_OPT_OUT}.
*/
@NonNull
- @SuppressLint("MissingGetterMatchingBuilder")
- public Builder setDeviceOwnerCanGrantSensorsPermissions(boolean mayGrant) {
+ public Builder setCanDeviceOwnerGrantSensorsPermissions(boolean mayGrant) {
mDeviceOwnerCanGrantSensorsPermissions = mayGrant;
return this;
}
@@ -261,6 +284,9 @@ public final class FullyManagedDeviceProvisioningParams implements Parcelable {
return 0;
}
+ /**
+ * @hide
+ */
@Override
public String toString() {
return "FullyManagedDeviceProvisioningParams{"
diff --git a/core/java/android/app/admin/ManagedProfileProvisioningParams.java b/core/java/android/app/admin/ManagedProfileProvisioningParams.java
index 1a6099a7f570..ccbef7321be6 100644
--- a/core/java/android/app/admin/ManagedProfileProvisioningParams.java
+++ b/core/java/android/app/admin/ManagedProfileProvisioningParams.java
@@ -21,7 +21,7 @@ import static java.util.Objects.requireNonNull;
import android.accounts.Account;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.TestApi;
+import android.annotation.SystemApi;
import android.content.ComponentName;
import android.os.Parcel;
import android.os.Parcelable;
@@ -33,7 +33,7 @@ import android.stats.devicepolicy.DevicePolicyEnums;
*
* @hide
*/
-@TestApi
+@SystemApi
public final class ManagedProfileProvisioningParams implements Parcelable {
private static final String LEAVE_ALL_SYSTEM_APPS_ENABLED_PARAM =
"LEAVE_ALL_SYSTEM_APPS_ENABLED";
@@ -48,7 +48,7 @@ public final class ManagedProfileProvisioningParams implements Parcelable {
@Nullable private final Account mAccountToMigrate;
private final boolean mLeaveAllSystemAppsEnabled;
private final boolean mOrganizationOwnedProvisioning;
- private final boolean mKeepAccountMigrated;
+ private final boolean mKeepAccountOnMigration;
private ManagedProfileProvisioningParams(
@@ -58,50 +58,75 @@ public final class ManagedProfileProvisioningParams implements Parcelable {
@Nullable Account accountToMigrate,
boolean leaveAllSystemAppsEnabled,
boolean organizationOwnedProvisioning,
- boolean keepAccountMigrated) {
+ boolean keepAccountOnMigration) {
this.mProfileAdminComponentName = requireNonNull(profileAdminComponentName);
this.mOwnerName = requireNonNull(ownerName);
this.mProfileName = profileName;
this.mAccountToMigrate = accountToMigrate;
this.mLeaveAllSystemAppsEnabled = leaveAllSystemAppsEnabled;
this.mOrganizationOwnedProvisioning = organizationOwnedProvisioning;
- this.mKeepAccountMigrated = keepAccountMigrated;
+ this.mKeepAccountOnMigration = keepAccountOnMigration;
}
+ /**
+ * Returns the profile owner's {@link ComponentName}.
+ */
@NonNull
public ComponentName getProfileAdminComponentName() {
return mProfileAdminComponentName;
}
+ /**
+ * Returns the profile owner's name.
+ */
@NonNull
public String getOwnerName() {
return mOwnerName;
}
+ /**
+ * Returns the profile's name if set, otherwise returns {@code null}.
+ */
@Nullable
public String getProfileName() {
return mProfileName;
}
+ /**
+ * If set, it returns the {@link Account} to migrate from the parent profile to the managed
+ * profile after provisioning, otherwise returns {@code null}.
+ */
@Nullable
public Account getAccountToMigrate() {
return mAccountToMigrate;
}
+ /**
+ * Returns {@code true} if system apps should be left enabled after provisioning.
+ */
public boolean isLeaveAllSystemAppsEnabled() {
return mLeaveAllSystemAppsEnabled;
}
+ /**
+ * Returns {@code true} if this is an org owned device.
+ */
public boolean isOrganizationOwnedProvisioning() {
return mOrganizationOwnedProvisioning;
}
- public boolean isKeepAccountMigrated() {
- return mKeepAccountMigrated;
+ /**
+ * Returns {@code true} if the migrated account from {@link #getAccountToMigrate()} should be
+ * kept in parent profile.
+ */
+ public boolean isKeepingAccountOnMigration() {
+ return mKeepAccountOnMigration;
}
/**
* Logs the provisioning params using {@link DevicePolicyEventLogger}.
+ *
+ * @hide
*/
public void logParams(@NonNull String callerPackage) {
requireNonNull(callerPackage);
@@ -109,7 +134,7 @@ public final class ManagedProfileProvisioningParams implements Parcelable {
logParam(callerPackage, LEAVE_ALL_SYSTEM_APPS_ENABLED_PARAM, mLeaveAllSystemAppsEnabled);
logParam(callerPackage, ORGANIZATION_OWNED_PROVISIONING_PARAM,
mOrganizationOwnedProvisioning);
- logParam(callerPackage, KEEP_MIGRATED_ACCOUNT_PARAM, mKeepAccountMigrated);
+ logParam(callerPackage, KEEP_MIGRATED_ACCOUNT_PARAM, mKeepAccountOnMigration);
logParam(callerPackage, ACCOUNT_TO_MIGRATE_PROVIDED_PARAM,
/* value= */ mAccountToMigrate != null);
}
@@ -134,7 +159,7 @@ public final class ManagedProfileProvisioningParams implements Parcelable {
@Nullable private Account mAccountToMigrate;
private boolean mLeaveAllSystemAppsEnabled;
private boolean mOrganizationOwnedProvisioning;
- private boolean mKeepAccountMigrated;
+ private boolean mKeepingAccountOnMigration;
/**
* Initialize a new {@link Builder) to construct a {@link ManagedProfileProvisioningParams}.
@@ -204,8 +229,8 @@ public final class ManagedProfileProvisioningParams implements Parcelable {
* Defaults to {@code false}.
*/
@NonNull
- public Builder setKeepAccountMigrated(boolean keepAccountMigrated) {
- this.mKeepAccountMigrated = keepAccountMigrated;
+ public Builder setKeepingAccountOnMigration(boolean keepingAccountOnMigration) {
+ this.mKeepingAccountOnMigration = keepingAccountOnMigration;
return this;
}
@@ -223,7 +248,7 @@ public final class ManagedProfileProvisioningParams implements Parcelable {
mAccountToMigrate,
mLeaveAllSystemAppsEnabled,
mOrganizationOwnedProvisioning,
- mKeepAccountMigrated);
+ mKeepingAccountOnMigration);
}
}
@@ -232,6 +257,9 @@ public final class ManagedProfileProvisioningParams implements Parcelable {
return 0;
}
+ /**
+ * @hide
+ */
@Override
public String toString() {
return "ManagedProfileProvisioningParams{"
@@ -241,7 +269,7 @@ public final class ManagedProfileProvisioningParams implements Parcelable {
+ ", mAccountToMigrate=" + (mAccountToMigrate == null ? "null" : mAccountToMigrate)
+ ", mLeaveAllSystemAppsEnabled=" + mLeaveAllSystemAppsEnabled
+ ", mOrganizationOwnedProvisioning=" + mOrganizationOwnedProvisioning
- + ", mKeepAccountMigrated=" + mKeepAccountMigrated
+ + ", mKeepAccountOnMigration=" + mKeepAccountOnMigration
+ '}';
}
@@ -253,7 +281,7 @@ public final class ManagedProfileProvisioningParams implements Parcelable {
dest.writeTypedObject(mAccountToMigrate, flags);
dest.writeBoolean(mLeaveAllSystemAppsEnabled);
dest.writeBoolean(mOrganizationOwnedProvisioning);
- dest.writeBoolean(mKeepAccountMigrated);
+ dest.writeBoolean(mKeepAccountOnMigration);
}
public static final @NonNull Creator<ManagedProfileProvisioningParams> CREATOR =
diff --git a/core/java/android/app/admin/ProvisioningException.java b/core/java/android/app/admin/ProvisioningException.java
index 639859bc3dea..d374c162535a 100644
--- a/core/java/android/app/admin/ProvisioningException.java
+++ b/core/java/android/app/admin/ProvisioningException.java
@@ -15,10 +15,15 @@
*/
package android.app.admin;
+import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.TestApi;
+import android.annotation.SystemApi;
+import android.content.pm.PackageManager;
import android.util.AndroidException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Thrown to indicate a failure during {@link DevicePolicyManager#provisionFullyManagedDevice} and
* {@link DevicePolicyManager#createAndProvisionManagedProfile}.
@@ -26,17 +31,88 @@ import android.util.AndroidException;
* @hide
*
*/
-@TestApi
+@SystemApi
public class ProvisioningException extends AndroidException {
- private final @DevicePolicyManager.ProvisioningResult int mProvisioningResult;
+
+ /**
+ * Service-specific error code for {@link DevicePolicyManager#provisionFullyManagedDevice} and
+ * {@link DevicePolicyManager#createAndProvisionManagedProfile}:
+ * Indicates a generic failure.
+ */
+ public static final int ERROR_UNKNOWN = 0;
+
+ /**
+ * Service-specific error code for {@link DevicePolicyManager#provisionFullyManagedDevice} and
+ * {@link DevicePolicyManager#createAndProvisionManagedProfile}:
+ * Indicates the call to {@link DevicePolicyManager#checkProvisioningPreCondition} returned an
+ * error code.
+ */
+ public static final int ERROR_PRE_CONDITION_FAILED = 1;
+
+ /**
+ * Service-specific error code for {@link DevicePolicyManager#createAndProvisionManagedProfile}:
+ * Indicates that the profile creation failed.
+ */
+ public static final int ERROR_PROFILE_CREATION_FAILED = 2;
+
+ /**
+ * Service-specific error code for {@link DevicePolicyManager#createAndProvisionManagedProfile}:
+ * Indicates the call to {@link PackageManager#installExistingPackageAsUser} has failed.
+ */
+ public static final int ERROR_ADMIN_PACKAGE_INSTALLATION_FAILED = 3;
+
+ /**
+ * Service-specific error code for {@link DevicePolicyManager#createAndProvisionManagedProfile}:
+ * Indicates that setting the profile owner failed.
+ */
+ public static final int ERROR_SETTING_PROFILE_OWNER_FAILED = 4;
+
+ /**
+ * Service-specific error code for {@link DevicePolicyManager#createAndProvisionManagedProfile}:
+ * Indicates that starting the newly created profile has failed.
+ */
+ public static final int ERROR_STARTING_PROFILE_FAILED = 5;
+
+ /**
+ * Service-specific error code for {@link DevicePolicyManager#provisionFullyManagedDevice}:
+ * Indicates that removing the non required apps have failed.
+ */
+ public static final int ERROR_REMOVE_NON_REQUIRED_APPS_FAILED = 6;
+
+ /**
+ * Service-specific error code for {@link DevicePolicyManager#provisionFullyManagedDevice}:
+ * Indicates that setting the device owner failed.
+ */
+ public static final int ERROR_SET_DEVICE_OWNER_FAILED = 7;
+
+ /**
+ * Service-specific error codes for {@link DevicePolicyManager#createAndProvisionManagedProfile}
+ * and {@link DevicePolicyManager#provisionFullyManagedDevice} indicating all the errors
+ * during provisioning.
+ *
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "ERROR_" }, value = {
+ ERROR_UNKNOWN, ERROR_PRE_CONDITION_FAILED,
+ ERROR_PROFILE_CREATION_FAILED,
+ ERROR_ADMIN_PACKAGE_INSTALLATION_FAILED,
+ ERROR_SETTING_PROFILE_OWNER_FAILED,
+ ERROR_STARTING_PROFILE_FAILED,
+ ERROR_REMOVE_NON_REQUIRED_APPS_FAILED,
+ ERROR_SET_DEVICE_OWNER_FAILED
+ })
+ public @interface ProvisioningError {}
+
+ private final @ProvisioningError int mProvisioningError;
public ProvisioningException(@NonNull Exception cause,
- @DevicePolicyManager.ProvisioningResult int provisioningResult) {
+ @ProvisioningError int provisioningError) {
super(cause);
- mProvisioningResult = provisioningResult;
+ mProvisioningError = provisioningError;
}
- public @DevicePolicyManager.ProvisioningResult int getProvisioningResult() {
- return mProvisioningResult;
+ public @ProvisioningError int getProvisioningError() {
+ return mProvisioningError;
}
}
diff --git a/core/java/android/app/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/cloudsearch/OWNERS b/core/java/android/app/cloudsearch/OWNERS
new file mode 100644
index 000000000000..aa4da3b4bee0
--- /dev/null
+++ b/core/java/android/app/cloudsearch/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 758286
+
+huiwu@google.com
+srazdan@google.com
diff --git a/core/java/android/app/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/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 3cbb24b586b7..630b8d26f52e 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -779,24 +779,25 @@ public final class UsageStatsManager {
* thinks the app will be launched sooner, 2) the estimated time has passed. Passing in
* {@link Long#MAX_VALUE} effectively clears the previously set launch time for the app.
*
- * @param packageName The package name of the app to set the bucket for.
- * @param estimatedLaunchTime The next time the app is expected to be launched. Units are in
- * milliseconds since epoch (the same as
- * {@link System#currentTimeMillis()}).
+ * @param packageName The package name of the app to set the bucket for.
+ * @param estimatedLaunchTimeMillis The next time the app is expected to be launched. Units are
+ * in milliseconds since epoch (the same as
+ * {@link System#currentTimeMillis()}).
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.CHANGE_APP_LAUNCH_TIME_ESTIMATE)
- public void setEstimatedLaunchTime(@NonNull String packageName,
- @CurrentTimeMillisLong long estimatedLaunchTime) {
+ public void setEstimatedLaunchTimeMillis(@NonNull String packageName,
+ @CurrentTimeMillisLong long estimatedLaunchTimeMillis) {
if (packageName == null) {
throw new NullPointerException("package name cannot be null");
}
- if (estimatedLaunchTime <= 0) {
+ if (estimatedLaunchTimeMillis <= 0) {
throw new IllegalArgumentException("estimated launch time must be positive");
}
try {
- mService.setEstimatedLaunchTime(packageName, estimatedLaunchTime, mContext.getUserId());
+ mService.setEstimatedLaunchTime(
+ packageName, estimatedLaunchTimeMillis, mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -806,19 +807,20 @@ public final class UsageStatsManager {
* Changes the estimated launch times for multiple apps at once. The map is keyed by the
* package name and the value is the estimated launch time.
*
- * @param estimatedLaunchTimes A map of package name to estimated launch time.
- * @see #setEstimatedLaunchTime(String, long)
+ * @param estimatedLaunchTimesMillis A map of package name to estimated launch time.
+ * @see #setEstimatedLaunchTimeMillis(String, long)
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.CHANGE_APP_LAUNCH_TIME_ESTIMATE)
- public void setEstimatedLaunchTimes(@NonNull Map<String, Long> estimatedLaunchTimes) {
- if (estimatedLaunchTimes == null) {
- throw new NullPointerException("estimatedLaunchTimes cannot be null");
+ public void setEstimatedLaunchTimesMillis(
+ @NonNull Map<String, Long> estimatedLaunchTimesMillis) {
+ if (estimatedLaunchTimesMillis == null) {
+ throw new NullPointerException("estimatedLaunchTimesMillis cannot be null");
}
final List<AppLaunchEstimateInfo> estimateList =
- new ArrayList<>(estimatedLaunchTimes.size());
- for (Map.Entry<String, Long> estimateEntry : estimatedLaunchTimes.entrySet()) {
+ new ArrayList<>(estimatedLaunchTimesMillis.size());
+ for (Map.Entry<String, Long> estimateEntry : estimatedLaunchTimesMillis.entrySet()) {
final String pkgName = estimateEntry.getKey();
if (pkgName == null) {
throw new NullPointerException("package name cannot be null");
diff --git a/core/java/android/bluetooth/Attributable.java b/core/java/android/bluetooth/Attributable.java
new file mode 100644
index 000000000000..d9acbe3eefb9
--- /dev/null
+++ b/core/java/android/bluetooth/Attributable.java
@@ -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.bluetooth;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.AttributionSource;
+
+import java.util.List;
+
+/**
+ * Marker interface for a class which can have an {@link AttributionSource}
+ * assigned to it; these are typically {@link android.os.Parcelable} classes
+ * which need to be updated after crossing Binder transaction boundaries.
+ *
+ * @hide
+ */
+public interface Attributable {
+ void setAttributionSource(@NonNull AttributionSource attributionSource);
+
+ static @Nullable <T extends Attributable> T setAttributionSource(
+ @Nullable T attributable,
+ @NonNull AttributionSource attributionSource) {
+ if (attributable != null) {
+ attributable.setAttributionSource(attributionSource);
+ }
+ return attributable;
+ }
+
+ static @Nullable <T extends Attributable> List<T> setAttributionSource(
+ @Nullable List<T> attributableList,
+ @NonNull AttributionSource attributionSource) {
+ if (attributableList != null) {
+ final int size = attributableList.size();
+ for (int i = 0; i < size; i++) {
+ setAttributionSource(attributableList.get(i), attributionSource);
+ }
+ }
+ return attributableList;
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index 1dd32fec2510..8b9cec17a196 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -16,7 +16,8 @@
package android.bluetooth;
-import android.Manifest;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -29,20 +30,21 @@ import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.compat.annotation.UnsupportedAppUsage;
-import android.content.Attributable;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.ParcelUuid;
import android.os.RemoteException;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
@@ -273,7 +275,7 @@ public final class BluetoothA2dp implements BluetoothProfile {
IBluetoothA2dp.class.getName()) {
@Override
public IBluetoothA2dp getServiceInterface(IBinder service) {
- return IBluetoothA2dp.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothA2dp.Stub.asInterface(service);
}
};
@@ -324,17 +326,21 @@ public final class BluetoothA2dp implements BluetoothProfile {
@UnsupportedAppUsage
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- return service.connect(device);
+ final IBluetoothA2dp service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connectWithAttribution(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -366,17 +372,21 @@ public final class BluetoothA2dp implements BluetoothProfile {
@UnsupportedAppUsage
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- return service.disconnect(device);
+ final IBluetoothA2dp service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnectWithAttribution(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -387,19 +397,24 @@ public final class BluetoothA2dp implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()) {
+ final IBluetoothA2dp service = getService();
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevicesWithAttribution(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevicesWithAttribution(mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
}
+ return defaultValue;
}
/**
@@ -410,20 +425,25 @@ public final class BluetoothA2dp implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()) {
+ final IBluetoothA2dp service = getService();
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStatesWithAttribution(states,
+ mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStatesWithAttribution(states,
- mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
}
+ return defaultValue;
}
/**
@@ -434,18 +454,21 @@ public final class BluetoothA2dp implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @BtProfileState int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()
- && isValidDevice(device)) {
- return service.getConnectionState(device);
+ final IBluetoothA2dp service = getService();
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionStateWithAttribution(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
}
+ return defaultValue;
}
/**
@@ -473,18 +496,21 @@ public final class BluetoothA2dp implements BluetoothProfile {
@UnsupportedAppUsage(trackingBug = 171933273)
public boolean setActiveDevice(@Nullable BluetoothDevice device) {
if (DBG) log("setActiveDevice(" + device + ")");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()
- && ((device == null) || isValidDevice(device))) {
- return service.setActiveDevice(device, mAttributionSource);
+ final IBluetoothA2dp service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && ((device == null) || isValidDevice(device))) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setActiveDevice(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -501,18 +527,24 @@ public final class BluetoothA2dp implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothDevice getActiveDevice() {
if (VDBG) log("getActiveDevice()");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()) {
+ final IBluetoothA2dp service = getService();
+ final BluetoothDevice defaultValue = null;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<BluetoothDevice> recv =
+ new SynchronousResultReceiver();
+ service.getActiveDevice(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getActiveDevice(mAttributionSource), mAttributionSource);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return null;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return null;
}
+ return defaultValue;
}
/**
@@ -557,22 +589,23 @@ public final class BluetoothA2dp implements BluetoothProfile {
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()
- && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
+ final IBluetoothA2dp service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -591,19 +624,7 @@ public final class BluetoothA2dp implements BluetoothProfile {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()
- && isValidDevice(device)) {
- return BluetoothAdapter.connectionPolicyToPriority(
- service.getPriority(device, mAttributionSource));
- }
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.PRIORITY_OFF;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.PRIORITY_OFF;
- }
+ return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
}
/**
@@ -625,18 +646,21 @@ public final class BluetoothA2dp implements BluetoothProfile {
})
public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()
- && isValidDevice(device)) {
- return service.getConnectionPolicy(device, mAttributionSource);
+ final IBluetoothA2dp service = getService();
+ final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
}
+ return defaultValue;
}
/**
@@ -648,17 +672,21 @@ public final class BluetoothA2dp implements BluetoothProfile {
@RequiresNoPermission
public boolean isAvrcpAbsoluteVolumeSupported() {
if (DBG) Log.d(TAG, "isAvrcpAbsoluteVolumeSupported");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()) {
- return service.isAvrcpAbsoluteVolumeSupported();
+ final IBluetoothA2dp service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isAvrcpAbsoluteVolumeSupported(recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Error talking to BT service in isAvrcpAbsoluteVolumeSupported()", e);
- return false;
}
+ return defaultValue;
}
/**
@@ -671,14 +699,16 @@ public final class BluetoothA2dp implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void setAvrcpAbsoluteVolume(int volume) {
if (DBG) Log.d(TAG, "setAvrcpAbsoluteVolume");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()) {
+ final IBluetoothA2dp service = getService();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
service.setAvrcpAbsoluteVolume(volume, mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- } catch (RemoteException e) {
- Log.e(TAG, "Error talking to BT service in setAvrcpAbsoluteVolume()", e);
}
}
@@ -691,18 +721,22 @@ public final class BluetoothA2dp implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean isA2dpPlaying(BluetoothDevice device) {
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()
- && isValidDevice(device)) {
- return service.isA2dpPlaying(device, mAttributionSource);
+ if (DBG) log("isA2dpPlaying(" + device + ")");
+ final IBluetoothA2dp service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isA2dpPlaying(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -731,8 +765,7 @@ public final class BluetoothA2dp implements BluetoothProfile {
/**
* Gets the current codec status (configuration and capability).
*
- * @param device the remote Bluetooth device. If null, use the current
- * active A2DP Bluetooth device.
+ * @param device the remote Bluetooth device.
* @return the current codec status
* @hide
*/
@@ -744,26 +777,28 @@ public final class BluetoothA2dp implements BluetoothProfile {
public BluetoothCodecStatus getCodecStatus(@NonNull BluetoothDevice device) {
if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")");
verifyDeviceNotNull(device, "getCodecStatus");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()) {
- return service.getCodecStatus(device, mAttributionSource);
- }
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
+ final IBluetoothA2dp service = getService();
+ final BluetoothCodecStatus defaultValue = null;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<BluetoothCodecStatus> recv =
+ new SynchronousResultReceiver();
+ service.getCodecStatus(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- return null;
- } catch (RemoteException e) {
- Log.e(TAG, "Error talking to BT service in getCodecStatus()", e);
- return null;
}
+ return defaultValue;
}
/**
* Sets the codec configuration preference.
*
- * @param device the remote Bluetooth device. If null, use the current
- * active A2DP Bluetooth device.
+ * @param device the remote Bluetooth device.
* @param codecConfig the codec configuration preference
* @hide
*/
@@ -779,24 +814,23 @@ public final class BluetoothA2dp implements BluetoothProfile {
Log.e(TAG, "setCodecConfigPreference: Codec config can't be null");
throw new IllegalArgumentException("codecConfig cannot be null");
}
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()) {
+ final IBluetoothA2dp service = getService();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
service.setCodecConfigPreference(device, codecConfig, mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return;
- } catch (RemoteException e) {
- Log.e(TAG, "Error talking to BT service in setCodecConfigPreference()", e);
- return;
}
}
/**
* Enables the optional codecs.
*
- * @param device the remote Bluetooth device. If null, use the currect
- * active A2DP Bluetooth device.
+ * @param device the remote Bluetooth device.
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -812,8 +846,7 @@ public final class BluetoothA2dp implements BluetoothProfile {
/**
* Disables the optional codecs.
*
- * @param device the remote Bluetooth device. If null, use the currect
- * active A2DP Bluetooth device.
+ * @param device the remote Bluetooth device.
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -829,26 +862,25 @@ public final class BluetoothA2dp implements BluetoothProfile {
/**
* Enables or disables the optional codecs.
*
- * @param device the remote Bluetooth device. If null, use the currect
- * active A2DP Bluetooth device.
+ * @param device the remote Bluetooth device.
* @param enable if true, enable the optional codecs, other disable them
*/
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
private void enableDisableOptionalCodecs(BluetoothDevice device, boolean enable) {
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()) {
+ final IBluetoothA2dp service = getService();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
if (enable) {
service.enableOptionalCodecs(device, mAttributionSource);
} else {
service.disableOptionalCodecs(device, mAttributionSource);
}
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return;
- } catch (RemoteException e) {
- Log.e(TAG, "Error talking to BT service in enableDisableOptionalCodecs()", e);
- return;
}
}
@@ -866,18 +898,23 @@ public final class BluetoothA2dp implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@OptionalCodecsSupportStatus
public int isOptionalCodecsSupported(@NonNull BluetoothDevice device) {
+ if (DBG) log("isOptionalCodecsSupported(" + device + ")");
verifyDeviceNotNull(device, "isOptionalCodecsSupported");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- return service.supportsOptionalCodecs(device, mAttributionSource);
+ final IBluetoothA2dp service = getService();
+ final int defaultValue = OPTIONAL_CODECS_SUPPORT_UNKNOWN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.supportsOptionalCodecs(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return OPTIONAL_CODECS_SUPPORT_UNKNOWN;
- } catch (RemoteException e) {
- Log.e(TAG, "Error talking to BT service in supportsOptionalCodecs()", e);
- return OPTIONAL_CODECS_SUPPORT_UNKNOWN;
}
+ return defaultValue;
}
/**
@@ -894,18 +931,23 @@ public final class BluetoothA2dp implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@OptionalCodecsPreferenceStatus
public int isOptionalCodecsEnabled(@NonNull BluetoothDevice device) {
+ if (DBG) log("isOptionalCodecsEnabled(" + device + ")");
verifyDeviceNotNull(device, "isOptionalCodecsEnabled");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- return service.getOptionalCodecsEnabled(device, mAttributionSource);
+ final IBluetoothA2dp service = getService();
+ final int defaultValue = OPTIONAL_CODECS_PREF_UNKNOWN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getOptionalCodecsEnabled(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return OPTIONAL_CODECS_PREF_UNKNOWN;
- } catch (RemoteException e) {
- Log.e(TAG, "Error talking to BT service in getOptionalCodecsEnabled()", e);
- return OPTIONAL_CODECS_PREF_UNKNOWN;
}
+ return defaultValue;
}
/**
@@ -923,24 +965,24 @@ public final class BluetoothA2dp implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void setOptionalCodecsEnabled(@NonNull BluetoothDevice device,
@OptionalCodecsPreferenceStatus int value) {
+ if (DBG) log("setOptionalCodecsEnabled(" + device + ")");
verifyDeviceNotNull(device, "setOptionalCodecsEnabled");
- try {
- if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN
- && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED
- && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) {
- Log.e(TAG, "Invalid value passed to setOptionalCodecsEnabled: " + value);
- return;
- }
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()
- && isValidDevice(device)) {
+ if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN
+ && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED
+ && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) {
+ Log.e(TAG, "Invalid value passed to setOptionalCodecsEnabled: " + value);
+ return;
+ }
+ final IBluetoothA2dp service = getService();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
service.setOptionalCodecsEnabled(device, value, mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return;
}
}
@@ -963,17 +1005,21 @@ public final class BluetoothA2dp implements BluetoothProfile {
})
public @Type int getDynamicBufferSupport() {
if (VDBG) log("getDynamicBufferSupport()");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()) {
- return service.getDynamicBufferSupport(mAttributionSource);
+ final IBluetoothA2dp service = getService();
+ final int defaultValue = DYNAMIC_BUFFER_SUPPORT_NONE;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getDynamicBufferSupport(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return DYNAMIC_BUFFER_SUPPORT_NONE;
- } catch (RemoteException e) {
- Log.e(TAG, "failed to get getDynamicBufferSupport, error: ", e);
- return DYNAMIC_BUFFER_SUPPORT_NONE;
}
+ return defaultValue;
}
/**
@@ -994,17 +1040,22 @@ public final class BluetoothA2dp implements BluetoothProfile {
})
public @Nullable BufferConstraints getBufferConstraints() {
if (VDBG) log("getBufferConstraints()");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()) {
- return service.getBufferConstraints(mAttributionSource);
+ final IBluetoothA2dp service = getService();
+ final BufferConstraints defaultValue = null;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<BufferConstraints> recv =
+ new SynchronousResultReceiver();
+ service.getBufferConstraints(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return null;
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- return null;
}
+ return defaultValue;
}
/**
@@ -1029,17 +1080,21 @@ public final class BluetoothA2dp implements BluetoothProfile {
Log.e(TAG, "Trying to set audio buffer length to a negative value: " + value);
return false;
}
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()) {
- return service.setBufferLengthMillis(codec, value, mAttributionSource);
+ final IBluetoothA2dp service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setBufferLengthMillis(codec, value, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- return false;
}
+ return defaultValue;
}
/**
diff --git a/core/java/android/bluetooth/BluetoothA2dpSink.java b/core/java/android/bluetooth/BluetoothA2dpSink.java
index 2dd63a0263fe..59416818ceb3 100755
--- a/core/java/android/bluetooth/BluetoothA2dpSink.java
+++ b/core/java/android/bluetooth/BluetoothA2dpSink.java
@@ -16,28 +16,31 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
-import android.annotation.SdkConstant.SdkConstantType;
import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.compat.annotation.UnsupportedAppUsage;
-import android.content.Attributable;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* This class provides the public APIs to control the Bluetooth A2DP Sink
@@ -87,7 +90,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
"BluetoothA2dpSink", IBluetoothA2dpSink.class.getName()) {
@Override
public IBluetoothA2dpSink getServiceInterface(IBinder service) {
- return IBluetoothA2dpSink.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothA2dpSink.Stub.asInterface(service);
}
};
@@ -141,16 +144,20 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
final IBluetoothA2dpSink service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.connect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -182,16 +189,20 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
final IBluetoothA2dpSink service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.disconnect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -205,17 +216,23 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
final IBluetoothA2dpSink service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -229,18 +246,23 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
final IBluetoothA2dpSink service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -252,18 +274,22 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getConnectionState(BluetoothDevice device) {
- if (VDBG) log("getState(" + device + ")");
+ if (VDBG) log("getConnectionState(" + device + ")");
final IBluetoothA2dpSink service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -283,16 +309,21 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
public BluetoothAudioConfig getAudioConfig(BluetoothDevice device) {
if (VDBG) log("getAudioConfig(" + device + ")");
final IBluetoothA2dpSink service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final BluetoothAudioConfig defaultValue = null;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getAudioConfig(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return null;
+ final SynchronousResultReceiver<BluetoothAudioConfig> recv =
+ new SynchronousResultReceiver();
+ service.getAudioConfig(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return null;
+ return defaultValue;
}
/**
@@ -338,20 +369,22 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
final IBluetoothA2dpSink service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
try {
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -394,16 +427,20 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothA2dpSink service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionPolicy(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return defaultValue;
}
/**
@@ -421,17 +458,22 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
android.Manifest.permission.BLUETOOTH_PRIVILEGED,
})
public boolean isAudioPlaying(@NonNull BluetoothDevice device) {
+ if (VDBG) log("isAudioPlaying(" + device + ")");
final IBluetoothA2dpSink service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.isA2dpPlaying(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isA2dpPlaying(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 602a1862f2cc..2d1ecfb5e052 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -29,7 +29,6 @@ import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
-import android.app.ActivityThread;
import android.app.PropertyInvalidatedCache;
import android.bluetooth.BluetoothDevice.Transport;
import android.bluetooth.BluetoothProfile.ConnectionPolicy;
@@ -48,7 +47,6 @@ import android.bluetooth.le.ScanRecord;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.compat.annotation.UnsupportedAppUsage;
-import android.content.Attributable;
import android.content.AttributionSource;
import android.content.Context;
import android.os.Binder;
@@ -58,7 +56,7 @@ import android.os.ParcelUuid;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
-import android.os.SystemProperties;
+import android.sysprop.BluetoothProperties;
import android.util.Log;
import android.util.Pair;
@@ -799,7 +797,7 @@ public final class BluetoothAdapter {
@RequiresNoPermission
public static synchronized BluetoothAdapter getDefaultAdapter() {
if (sAdapter == null) {
- sAdapter = createAdapter(BluetoothManager.resolveAttributionSource(null));
+ sAdapter = createAdapter(AttributionSource.myAttributionSource());
}
return sAdapter;
}
@@ -1002,7 +1000,6 @@ public final class BluetoothAdapter {
if (!isBleScanAlwaysAvailable()) {
return false;
}
- String packageName = ActivityThread.currentPackageName();
try {
return mManagerService.disableBle(mAttributionSource, mToken);
} catch (RemoteException e) {
@@ -1049,7 +1046,6 @@ public final class BluetoothAdapter {
if (!isBleScanAlwaysAvailable()) {
return false;
}
- String packageName = ActivityThread.currentPackageName();
try {
return mManagerService.enableBle(mAttributionSource, mToken);
} catch (RemoteException e) {
@@ -1352,7 +1348,7 @@ public final class BluetoothAdapter {
return true;
}
Log.e(TAG, "factoryReset(): Setting persist.bluetooth.factoryreset to retry later");
- SystemProperties.set("persist.bluetooth.factoryreset", "true");
+ BluetoothProperties.factory_reset(true);
} catch (RemoteException e) {
Log.e(TAG, "", e);
} finally {
diff --git a/core/java/android/bluetooth/BluetoothAvrcpController.java b/core/java/android/bluetooth/BluetoothAvrcpController.java
index d27c27648008..81fc3e11e9e1 100644
--- a/core/java/android/bluetooth/BluetoothAvrcpController.java
+++ b/core/java/android/bluetooth/BluetoothAvrcpController.java
@@ -16,22 +16,24 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
-import android.annotation.SuppressLint;
import android.annotation.SdkConstant.SdkConstantType;
import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.content.Attributable;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* This class provides the public APIs to control the Bluetooth AVRCP Controller. It currently
@@ -95,8 +97,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile {
"BluetoothAvrcpController", IBluetoothAvrcpController.class.getName()) {
@Override
public IBluetoothAvrcpController getServiceInterface(IBinder service) {
- return IBluetoothAvrcpController.Stub.asInterface(
- Binder.allowBlocking(service));
+ return IBluetoothAvrcpController.Stub.asInterface(service);
}
};
@@ -132,19 +133,24 @@ public final class BluetoothAvrcpController implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
- final IBluetoothAvrcpController service =
- getService();
- if (service != null && isEnabled()) {
+ final IBluetoothAvrcpController service = getService();
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -155,20 +161,24 @@ public final class BluetoothAvrcpController implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
- final IBluetoothAvrcpController service =
- getService();
- if (service != null && isEnabled()) {
+ final IBluetoothAvrcpController service = getService();
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -179,18 +189,21 @@ public final class BluetoothAvrcpController implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
- final IBluetoothAvrcpController service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothAvrcpController service = getService();
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -203,17 +216,22 @@ public final class BluetoothAvrcpController implements BluetoothProfile {
public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) {
if (DBG) Log.d(TAG, "getPlayerSettings");
BluetoothAvrcpPlayerSettings settings = null;
- final IBluetoothAvrcpController service =
- getService();
- if (service != null && isEnabled()) {
+ final IBluetoothAvrcpController service = getService();
+ final BluetoothAvrcpPlayerSettings defaultValue = null;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- settings = service.getPlayerSettings(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Error talking to BT service in getMetadata() " + e);
- return null;
+ final SynchronousResultReceiver<BluetoothAvrcpPlayerSettings> recv =
+ new SynchronousResultReceiver();
+ service.getPlayerSettings(device, mAttributionSource, recv);
+ settings = recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- return settings;
+ return defaultValue;
}
/**
@@ -224,18 +242,21 @@ public final class BluetoothAvrcpController implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) {
if (DBG) Log.d(TAG, "setPlayerApplicationSetting");
- final IBluetoothAvrcpController service =
- getService();
- if (service != null && isEnabled()) {
+ final IBluetoothAvrcpController service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- return service.setPlayerApplicationSetting(plAppSetting, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Error talking to BT service in setPlayerApplicationSetting() " + e);
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setPlayerApplicationSetting(plAppSetting, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -247,18 +268,20 @@ public final class BluetoothAvrcpController implements BluetoothProfile {
public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) {
Log.d(TAG, "sendGroupNavigationCmd dev = " + device + " key " + keyCode + " State = "
+ keyState);
- final IBluetoothAvrcpController service =
- getService();
- if (service != null && isEnabled()) {
+ final IBluetoothAvrcpController service = getService();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- service.sendGroupNavigationCmd(device, keyCode, keyState, mAttributionSource);
- return;
- } catch (RemoteException e) {
- Log.e(TAG, "Error talking to BT service in sendGroupNavigationCmd()", e);
+ final SynchronousResultReceiver recv = new SynchronousResultReceiver();
+ service.sendGroupNavigationCmd(device, keyCode, keyState, mAttributionSource, recv);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
return;
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
}
private boolean isEnabled() {
diff --git a/core/java/android/bluetooth/BluetoothCsipSetCoordinator.java b/core/java/android/bluetooth/BluetoothCsipSetCoordinator.java
index f0a8df0fa72f..ba57ec472a6e 100644
--- a/core/java/android/bluetooth/BluetoothCsipSetCoordinator.java
+++ b/core/java/android/bluetooth/BluetoothCsipSetCoordinator.java
@@ -17,6 +17,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
@@ -27,13 +29,14 @@ import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.IBinder;
import android.os.ParcelUuid;
import android.os.RemoteException;
import android.util.CloseGuard;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -41,6 +44,7 @@ import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Executor;
+import java.util.concurrent.TimeoutException;
/**
* This class provides the public APIs to control the Bluetooth CSIP set coordinator.
@@ -229,8 +233,7 @@ public final class BluetoothCsipSetCoordinator implements BluetoothProfile, Auto
IBluetoothCsipSetCoordinator.class.getName()) {
@Override
public IBluetoothCsipSetCoordinator getServiceInterface(IBinder service) {
- return IBluetoothCsipSetCoordinator.Stub.asInterface(
- Binder.allowBlocking(service));
+ return IBluetoothCsipSetCoordinator.Stub.asInterface(service);
}
};
@@ -283,26 +286,27 @@ public final class BluetoothCsipSetCoordinator implements BluetoothProfile, Auto
public
@Nullable UUID groupLock(int groupId, @Nullable @CallbackExecutor Executor executor,
@Nullable ClientLockCallback cb) {
- if (VDBG) {
- log("groupLockSet()");
- }
+ if (VDBG) log("groupLockSet()");
final IBluetoothCsipSetCoordinator service = getService();
- try {
- if (service != null && isEnabled()) {
- IBluetoothCsipSetCoordinatorLockCallback delegate = null;
- if ((executor != null) && (cb != null)) {
- delegate = new BluetoothCsipSetCoordinatorLockCallbackDelegate(executor, cb);
- }
- return service.groupLock(groupId, delegate, mAttributionSource).getUuid();
+ final UUID defaultValue = null;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ IBluetoothCsipSetCoordinatorLockCallback delegate = null;
+ if ((executor != null) && (cb != null)) {
+ delegate = new BluetoothCsipSetCoordinatorLockCallbackDelegate(executor, cb);
}
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
+ try {
+ final SynchronousResultReceiver<ParcelUuid> recv = new SynchronousResultReceiver();
+ service.groupLock(groupId, delegate, mAttributionSource, recv);
+ final ParcelUuid ret = recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
+ return ret == null ? defaultValue : ret.getUuid();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- return null;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return null;
}
+ return defaultValue;
}
/**
@@ -315,27 +319,26 @@ public final class BluetoothCsipSetCoordinator implements BluetoothProfile, Auto
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
public boolean groupUnlock(@NonNull UUID lockUuid) {
- if (VDBG) {
- log("groupLockSet()");
- }
+ if (VDBG) log("groupLockSet()");
if (lockUuid == null) {
return false;
}
-
final IBluetoothCsipSetCoordinator service = getService();
- try {
- if (service != null && isEnabled()) {
- service.groupUnlock(new ParcelUuid(lockUuid), mAttributionSource);
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver recv = new SynchronousResultReceiver();
+ service.groupUnlock(new ParcelUuid(lockUuid), mAttributionSource, recv);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
return true;
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- }
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -348,22 +351,22 @@ public final class BluetoothCsipSetCoordinator implements BluetoothProfile, Auto
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
public @NonNull Map getGroupUuidMapByDevice(@Nullable BluetoothDevice device) {
- if (VDBG) {
- log("getGroupUuidMapByDevice()");
- }
+ if (VDBG) log("getGroupUuidMapByDevice()");
final IBluetoothCsipSetCoordinator service = getService();
- try {
- if (service != null && isEnabled()) {
- return service.getGroupUuidMapByDevice(device, mAttributionSource);
- }
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
+ final Map defaultValue = new HashMap<>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Map> recv = new SynchronousResultReceiver();
+ service.getGroupUuidMapByDevice(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- return new HashMap<>();
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new HashMap<>();
}
+ return defaultValue;
}
/**
@@ -376,22 +379,23 @@ public final class BluetoothCsipSetCoordinator implements BluetoothProfile, Auto
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
public @NonNull List<Integer> getAllGroupIds(@Nullable ParcelUuid uuid) {
- if (VDBG) {
- log("getAllGroupIds()");
- }
+ if (VDBG) log("getAllGroupIds()");
final IBluetoothCsipSetCoordinator service = getService();
- try {
- if (service != null && isEnabled()) {
- return service.getAllGroupIds(uuid, mAttributionSource);
- }
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
+ final List<Integer> defaultValue = new ArrayList<>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<Integer>> recv =
+ new SynchronousResultReceiver();
+ service.getAllGroupIds(uuid, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- return new ArrayList<Integer>();
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<Integer>();
}
+ return defaultValue;
}
/**
@@ -399,22 +403,23 @@ public final class BluetoothCsipSetCoordinator implements BluetoothProfile, Auto
*/
@Override
public @NonNull List<BluetoothDevice> getConnectedDevices() {
- if (VDBG) {
- log("getConnectedDevices()");
- }
+ if (VDBG) log("getConnectedDevices()");
final IBluetoothCsipSetCoordinator service = getService();
- if (service != null && isEnabled()) {
- try {
- return service.getConnectedDevices(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
- }
- }
+ final List<BluetoothDevice> defaultValue = new ArrayList<>();
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -422,24 +427,24 @@ public final class BluetoothCsipSetCoordinator implements BluetoothProfile, Auto
*/
@Override
public
- @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(
- @NonNull int[] states) {
- if (VDBG) {
- log("getDevicesMatchingStates(states=" + Arrays.toString(states) + ")");
- }
+ @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[] states) {
+ if (VDBG) log("getDevicesMatchingStates(states=" + Arrays.toString(states) + ")");
final IBluetoothCsipSetCoordinator service = getService();
- if (service != null && isEnabled()) {
- try {
- return service.getDevicesMatchingConnectionStates(states, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
- }
- }
+ final List<BluetoothDevice> defaultValue = new ArrayList<>();
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -447,24 +452,23 @@ public final class BluetoothCsipSetCoordinator implements BluetoothProfile, Auto
*/
@Override
public
- @BluetoothProfile.BtProfileState int getConnectionState(
- @Nullable BluetoothDevice device) {
- if (VDBG) {
- log("getState(" + device + ")");
- }
+ @BluetoothProfile.BtProfileState int getConnectionState(@Nullable BluetoothDevice device) {
+ if (VDBG) log("getState(" + device + ")");
final IBluetoothCsipSetCoordinator service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- try {
- return service.getConnectionState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
- }
- }
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return BluetoothProfile.STATE_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -484,26 +488,24 @@ public final class BluetoothCsipSetCoordinator implements BluetoothProfile, Auto
@RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
public boolean setConnectionPolicy(
@Nullable BluetoothDevice device, @ConnectionPolicy int connectionPolicy) {
- if (DBG) {
- log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
- }
+ if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
final IBluetoothCsipSetCoordinator service = getService();
- try {
- if (service != null && isEnabled() && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
- }
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -521,22 +523,22 @@ public final class BluetoothCsipSetCoordinator implements BluetoothProfile, Auto
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) {
- if (VDBG) {
- log("getConnectionPolicy(" + device + ")");
- }
+ if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothCsipSetCoordinator service = getService();
- try {
- if (service != null && isEnabled() && isValidDevice(device)) {
- return service.getConnectionPolicy(device, mAttributionSource);
- }
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
+ final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
}
+ return defaultValue;
}
private boolean isEnabled() {
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 6e918bd6243d..93f026860856 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -32,7 +32,6 @@ import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.companion.AssociationRequest;
import android.compat.annotation.UnsupportedAppUsage;
-import android.content.Attributable;
import android.content.AttributionSource;
import android.content.Context;
import android.os.Build;
@@ -1178,7 +1177,7 @@ public final class BluetoothDevice implements Parcelable, Attributable {
mAddress = address;
mAddressType = ADDRESS_TYPE_PUBLIC;
- mAttributionSource = BluetoothManager.resolveAttributionSource(null);
+ mAttributionSource = AttributionSource.myAttributionSource();
}
/** {@hide} */
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index 4e7c01ad2db1..fe8d1ba80e33 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -1806,32 +1806,33 @@ public final class BluetoothGatt implements BluetoothProfile {
}
/**
- * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
+ * @deprecated Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
* with {@link BluetoothProfile#GATT} as argument
- *
* @throws UnsupportedOperationException
*/
@Override
@RequiresNoPermission
+ @Deprecated
public int getConnectionState(BluetoothDevice device) {
throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
}
/**
- * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
+ * @deprecated Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
* with {@link BluetoothProfile#GATT} as argument
*
* @throws UnsupportedOperationException
*/
@Override
@RequiresNoPermission
+ @Deprecated
public List<BluetoothDevice> getConnectedDevices() {
throw new UnsupportedOperationException(
"Use BluetoothManager#getConnectedDevices instead.");
}
/**
- * Not supported - please use
+ * @deprecated Not supported - please use
* {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
* with {@link BluetoothProfile#GATT} as first argument
*
@@ -1839,6 +1840,7 @@ public final class BluetoothGatt implements BluetoothProfile {
*/
@Override
@RequiresNoPermission
+ @Deprecated
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
throw new UnsupportedOperationException(
"Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
diff --git a/core/java/android/bluetooth/BluetoothGattCharacteristic.java b/core/java/android/bluetooth/BluetoothGattCharacteristic.java
index c5e986e895b2..053e0db3d8a6 100644
--- a/core/java/android/bluetooth/BluetoothGattCharacteristic.java
+++ b/core/java/android/bluetooth/BluetoothGattCharacteristic.java
@@ -313,7 +313,7 @@ public class BluetoothGattCharacteristic implements Parcelable {
};
private BluetoothGattCharacteristic(Parcel in) {
- mUuid = ((ParcelUuid) in.readParcelable(null)).getUuid();
+ mUuid = ((ParcelUuid) in.readParcelable(null, android.os.ParcelUuid.class)).getUuid();
mInstance = in.readInt();
mProperties = in.readInt();
mPermissions = in.readInt();
diff --git a/core/java/android/bluetooth/BluetoothGattDescriptor.java b/core/java/android/bluetooth/BluetoothGattDescriptor.java
index a35d5b99fd7b..6ed4706b5fba 100644
--- a/core/java/android/bluetooth/BluetoothGattDescriptor.java
+++ b/core/java/android/bluetooth/BluetoothGattDescriptor.java
@@ -187,7 +187,7 @@ public class BluetoothGattDescriptor implements Parcelable {
};
private BluetoothGattDescriptor(Parcel in) {
- mUuid = ((ParcelUuid) in.readParcelable(null)).getUuid();
+ mUuid = ((ParcelUuid) in.readParcelable(null, android.os.ParcelUuid.class)).getUuid();
mInstance = in.readInt();
mPermissions = in.readInt();
}
diff --git a/core/java/android/bluetooth/BluetoothGattIncludedService.java b/core/java/android/bluetooth/BluetoothGattIncludedService.java
index 5580619033a6..1ae2ca0a92e1 100644
--- a/core/java/android/bluetooth/BluetoothGattIncludedService.java
+++ b/core/java/android/bluetooth/BluetoothGattIncludedService.java
@@ -76,7 +76,7 @@ public class BluetoothGattIncludedService implements Parcelable {
};
private BluetoothGattIncludedService(Parcel in) {
- mUuid = ((ParcelUuid) in.readParcelable(null)).getUuid();
+ mUuid = ((ParcelUuid) in.readParcelable(null, android.os.ParcelUuid.class)).getUuid();
mInstanceId = in.readInt();
mServiceType = in.readInt();
}
diff --git a/core/java/android/bluetooth/BluetoothGattService.java b/core/java/android/bluetooth/BluetoothGattService.java
index f64d09fc30d9..36bc4772e016 100644
--- a/core/java/android/bluetooth/BluetoothGattService.java
+++ b/core/java/android/bluetooth/BluetoothGattService.java
@@ -180,7 +180,7 @@ public class BluetoothGattService implements Parcelable {
};
private BluetoothGattService(Parcel in) {
- mUuid = ((ParcelUuid) in.readParcelable(null)).getUuid();
+ mUuid = ((ParcelUuid) in.readParcelable(null, android.os.ParcelUuid.class)).getUuid();
mInstanceId = in.readInt();
mServiceType = in.readInt();
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index c0463243f41d..f2a6276ce8fe 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -27,12 +29,10 @@ import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.compat.annotation.UnsupportedAppUsage;
-import android.content.Attributable;
import android.content.AttributionSource;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
@@ -42,8 +42,11 @@ import android.os.RemoteException;
import android.util.CloseGuard;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* Public API for controlling the Bluetooth Headset Service. This includes both
@@ -480,16 +483,20 @@ public final class BluetoothHeadset implements BluetoothProfile {
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.connect(device);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connectWithAttribution(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -521,16 +528,20 @@ public final class BluetoothHeadset implements BluetoothProfile {
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.disconnect(device);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnectWithAttribution(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -542,18 +553,23 @@ public final class BluetoothHeadset implements BluetoothProfile {
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevicesWithAttribution(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevicesWithAttribution(mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -565,18 +581,23 @@ public final class BluetoothHeadset implements BluetoothProfile {
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -588,59 +609,20 @@ public final class BluetoothHeadset implements BluetoothProfile {
public int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getConnectionState(" + device + ")");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
- try {
- return service.getConnectionState(device);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
- }
- }
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
- }
-
- /**
- * Set priority of the profile
- *
- * <p> The device should already be paired.
- * Priority can be one of {@link BluetoothProfile#PRIORITY_ON} or
- * {@link BluetoothProfile#PRIORITY_OFF}
- *
- * @param device Paired bluetooth device
- * @param priority
- * @return true if priority is set, false on error
- * @hide
- * @deprecated Replaced with {@link #setConnectionPolicy(BluetoothDevice, int)}
- * @removed
- */
- @Deprecated
- @SystemApi
- @RequiresLegacyBluetoothAdminPermission
- @RequiresBluetoothConnectPermission
- @RequiresPermission(allOf = {
- android.Manifest.permission.BLUETOOTH_CONNECT,
- android.Manifest.permission.MODIFY_PHONE_STATE,
- })
- public boolean setPriority(BluetoothDevice device, int priority) {
- if (DBG) log("setPriority(" + device + ", " + priority + ")");
- final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
- if (priority != BluetoothProfile.PRIORITY_OFF
- && priority != BluetoothProfile.PRIORITY_ON) {
- return false;
- }
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.setPriority(
- device, BluetoothAdapter.priorityToConnectionPolicy(priority),
- mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionStateWithAttribution(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -666,20 +648,22 @@ public final class BluetoothHeadset implements BluetoothProfile {
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
try {
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -699,18 +683,7 @@ public final class BluetoothHeadset implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
- final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
- try {
- return BluetoothAdapter.connectionPolicyToPriority(
- service.getPriority(device, mAttributionSource));
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.PRIORITY_OFF;
- }
- }
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.PRIORITY_OFF;
+ return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
}
/**
@@ -733,16 +706,20 @@ public final class BluetoothHeadset implements BluetoothProfile {
public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionPolicy(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return defaultValue;
}
/**
@@ -757,15 +734,20 @@ public final class BluetoothHeadset implements BluetoothProfile {
public boolean isNoiseReductionSupported(@NonNull BluetoothDevice device) {
if (DBG) log("isNoiseReductionSupported()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.isNoiseReductionSupported(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isNoiseReductionSupported(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -780,15 +762,20 @@ public final class BluetoothHeadset implements BluetoothProfile {
public boolean isVoiceRecognitionSupported(@NonNull BluetoothDevice device) {
if (DBG) log("isVoiceRecognitionSupported()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.isVoiceRecognitionSupported(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isVoiceRecognitionSupported(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -819,15 +806,20 @@ public final class BluetoothHeadset implements BluetoothProfile {
public boolean startVoiceRecognition(BluetoothDevice device) {
if (DBG) log("startVoiceRecognition()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.startVoiceRecognition(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.startVoiceRecognition(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -848,15 +840,20 @@ public final class BluetoothHeadset implements BluetoothProfile {
public boolean stopVoiceRecognition(BluetoothDevice device) {
if (DBG) log("stopVoiceRecognition()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.stopVoiceRecognition(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.stopVoiceRecognition(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -871,15 +868,20 @@ public final class BluetoothHeadset implements BluetoothProfile {
public boolean isAudioConnected(BluetoothDevice device) {
if (VDBG) log("isAudioConnected()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.isAudioConnected(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isAudioConnected(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -905,17 +907,20 @@ public final class BluetoothHeadset implements BluetoothProfile {
public int getAudioState(BluetoothDevice device) {
if (VDBG) log("getAudioState");
final IBluetoothHeadset service = mService;
- if (service != null && !isDisabled()) {
+ final int defaultValue = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (!isDisabled()) {
try {
- return service.getAudioState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getAudioState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
- return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -933,15 +938,17 @@ public final class BluetoothHeadset implements BluetoothProfile {
public void setAudioRouteAllowed(boolean allowed) {
if (VDBG) log("setAudioRouteAllowed");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- service.setAudioRouteAllowed(allowed, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver recv = new SynchronousResultReceiver();
+ service.setAudioRouteAllowed(allowed, mAttributionSource, recv);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
}
@@ -956,17 +963,20 @@ public final class BluetoothHeadset implements BluetoothProfile {
public boolean getAudioRouteAllowed() {
if (VDBG) log("getAudioRouteAllowed");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- return service.getAudioRouteAllowed(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.getAudioRouteAllowed(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
- return false;
+ return defaultValue;
}
/**
@@ -981,15 +991,17 @@ public final class BluetoothHeadset implements BluetoothProfile {
public void setForceScoAudio(boolean forced) {
if (VDBG) log("setForceScoAudio " + String.valueOf(forced));
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- service.setForceScoAudio(forced, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver recv = new SynchronousResultReceiver();
+ service.setForceScoAudio(forced, mAttributionSource, recv);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
}
@@ -1006,16 +1018,20 @@ public final class BluetoothHeadset implements BluetoothProfile {
public boolean isAudioOn() {
if (VDBG) log("isAudioOn()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- return service.isAudioOn(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isAudioOn(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
-
+ return defaultValue;
}
/**
@@ -1040,18 +1056,22 @@ public final class BluetoothHeadset implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean connectAudio() {
+ if (VDBG) log("connectAudio()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- return service.connectAudio(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connectAudio(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
- return false;
+ return defaultValue;
}
/**
@@ -1069,18 +1089,22 @@ public final class BluetoothHeadset implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnectAudio() {
+ if (VDBG) log("disconnectAudio()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- return service.disconnectAudio(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnectAudio(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
- return false;
+ return defaultValue;
}
/**
@@ -1114,17 +1138,20 @@ public final class BluetoothHeadset implements BluetoothProfile {
public boolean startScoUsingVirtualVoiceCall() {
if (DBG) log("startScoUsingVirtualVoiceCall()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- return service.startScoUsingVirtualVoiceCall(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.startScoUsingVirtualVoiceCall(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
- return false;
+ return defaultValue;
}
/**
@@ -1149,17 +1176,20 @@ public final class BluetoothHeadset implements BluetoothProfile {
public boolean stopScoUsingVirtualVoiceCall() {
if (DBG) log("stopScoUsingVirtualVoiceCall()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- return service.stopScoUsingVirtualVoiceCall(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.stopScoUsingVirtualVoiceCall(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
- return false;
+ return defaultValue;
}
/**
@@ -1179,16 +1209,16 @@ public final class BluetoothHeadset implements BluetoothProfile {
public void phoneStateChanged(int numActive, int numHeld, int callState, String number,
int type, String name) {
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
service.phoneStateChanged(numActive, numHeld, callState, number, type, name,
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
}
@@ -1205,16 +1235,18 @@ public final class BluetoothHeadset implements BluetoothProfile {
public void clccResponse(int index, int direction, int status, int mode, boolean mpty,
String number, int type) {
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver recv = new SynchronousResultReceiver();
service.clccResponse(index, direction, status, mode, mpty, number, type,
- mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ mAttributionSource, recv);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
}
@@ -1246,18 +1278,21 @@ public final class BluetoothHeadset implements BluetoothProfile {
throw new IllegalArgumentException("command is null");
}
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
- try {
- return service.sendVendorSpecificResultCode(device, command, arg,
- mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- }
- }
+ final boolean defaultValue = false;
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.sendVendorSpecificResultCode(device, command, arg,
+ mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return false;
+ return defaultValue;
}
/**
@@ -1291,17 +1326,20 @@ public final class BluetoothHeadset implements BluetoothProfile {
Log.d(TAG, "setActiveDevice: " + device);
}
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && (device == null || isValidDevice(device))) {
- try {
- return service.setActiveDevice(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- }
- }
+ final boolean defaultValue = false;
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && (device == null || isValidDevice(device))) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setActiveDevice(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return false;
+ return defaultValue;
}
/**
@@ -1317,22 +1355,25 @@ public final class BluetoothHeadset implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothDevice getActiveDevice() {
- if (VDBG) {
- Log.d(TAG, "getActiveDevice");
- }
+ if (VDBG) Log.d(TAG, "getActiveDevice");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
+ final BluetoothDevice defaultValue = null;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<BluetoothDevice> recv =
+ new SynchronousResultReceiver();
+ service.getActiveDevice(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getActiveDevice(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- }
- return null;
+ return defaultValue;
}
/**
@@ -1347,21 +1388,22 @@ public final class BluetoothHeadset implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
public boolean isInbandRingingEnabled() {
- if (DBG) {
- log("isInbandRingingEnabled()");
- }
+ if (DBG) log("isInbandRingingEnabled()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
- try {
- return service.isInbandRingingEnabled(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- }
- }
+ final boolean defaultValue = false;
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isInbandRingingEnabled(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return false;
+ return defaultValue;
}
/**
@@ -1381,7 +1423,7 @@ public final class BluetoothHeadset implements BluetoothProfile {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
if (DBG) Log.d(TAG, "Proxy object connected");
- mService = IBluetoothHeadset.Stub.asInterface(Binder.allowBlocking(service));
+ mService = IBluetoothHeadset.Stub.asInterface(service);
mHandler.sendMessage(mHandler.obtainMessage(
MESSAGE_HEADSET_SERVICE_CONNECTED));
}
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java
index a5a247087209..7d7a7f798bb9 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClient.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
@@ -23,18 +25,19 @@ import android.annotation.SdkConstant.SdkConstantType;
import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.compat.annotation.UnsupportedAppUsage;
-import android.content.Attributable;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* Public API to control Hands Free Profile (HFP role only).
@@ -433,7 +436,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
"BluetoothHeadsetClient", IBluetoothHeadsetClient.class.getName()) {
@Override
public IBluetoothHeadsetClient getServiceInterface(IBinder service) {
- return IBluetoothHeadsetClient.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothHeadsetClient.Stub.asInterface(service);
}
};
@@ -480,18 +483,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.connect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -508,18 +514,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.disconnect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -532,19 +541,24 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled()) {
+ final IBluetoothHeadsetClient service = getService();
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -559,20 +573,24 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled()) {
+ final IBluetoothHeadsetClient service = getService();
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -586,18 +604,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getConnectionState(" + device + ")");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -635,22 +656,23 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
try {
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -687,18 +709,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final @ConnectionPolicy int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionPolicy(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return defaultValue;
}
/**
@@ -716,17 +741,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean startVoiceRecognition(BluetoothDevice device) {
if (DBG) log("startVoiceRecognition()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.startVoiceRecognition(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.startVoiceRecognition(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -740,20 +769,23 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
*/
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
- public boolean sendVendorAtCommand(BluetoothDevice device, int vendorId,
- String atCommand) {
+ public boolean sendVendorAtCommand(BluetoothDevice device, int vendorId, String atCommand) {
if (DBG) log("sendVendorSpecificCommand()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.sendVendorAtCommand(device, vendorId, atCommand, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.sendVendorAtCommand(device, vendorId, atCommand, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -771,17 +803,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean stopVoiceRecognition(BluetoothDevice device) {
if (DBG) log("stopVoiceRecognition()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.stopVoiceRecognition(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.stopVoiceRecognition(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -794,18 +830,24 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) {
if (DBG) log("getCurrentCalls()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final List<BluetoothHeadsetClientCall> defaultValue = null;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
+ final SynchronousResultReceiver<List<BluetoothHeadsetClientCall>> recv =
+ new SynchronousResultReceiver();
+ service.getCurrentCalls(device, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getCurrentCalls(device, mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return null;
+ return defaultValue;
}
/**
@@ -818,17 +860,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public Bundle getCurrentAgEvents(BluetoothDevice device) {
if (DBG) log("getCurrentAgEvents()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final Bundle defaultValue = null;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getCurrentAgEvents(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Bundle> recv = new SynchronousResultReceiver();
+ service.getCurrentAgEvents(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return null;
+ return defaultValue;
}
/**
@@ -845,17 +891,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean acceptCall(BluetoothDevice device, int flag) {
if (DBG) log("acceptCall()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.acceptCall(device, flag, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.acceptCall(device, flag, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -869,17 +919,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean holdCall(BluetoothDevice device) {
if (DBG) log("holdCall()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.holdCall(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.holdCall(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -898,17 +952,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean rejectCall(BluetoothDevice device) {
if (DBG) log("rejectCall()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.rejectCall(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.rejectCall(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -931,17 +989,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call) {
if (DBG) log("terminateCall()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.terminateCall(device, call, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.terminateCall(device, call, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -962,17 +1024,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean enterPrivateMode(BluetoothDevice device, int index) {
if (DBG) log("enterPrivateMode()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.enterPrivateMode(device, index, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.enterPrivateMode(device, index, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -992,17 +1058,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean explicitCallTransfer(BluetoothDevice device) {
if (DBG) log("explicitCallTransfer()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.explicitCallTransfer(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.explicitCallTransfer(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -1018,18 +1088,24 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) {
if (DBG) log("dial()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final BluetoothHeadsetClientCall defaultValue = null;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
+ final SynchronousResultReceiver<BluetoothHeadsetClientCall> recv =
+ new SynchronousResultReceiver();
+ service.dial(device, number, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.dial(device, number, mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return null;
+ return defaultValue;
}
/**
@@ -1046,17 +1122,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean sendDTMF(BluetoothDevice device, byte code) {
if (DBG) log("sendDTMF()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.sendDTMF(device, code, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.sendDTMF(device, code, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -1075,17 +1155,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean getLastVoiceTagNumber(BluetoothDevice device) {
if (DBG) log("getLastVoiceTagNumber()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getLastVoiceTagNumber(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.getLastVoiceTagNumber(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -1098,17 +1182,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getAudioState(BluetoothDevice device) {
if (VDBG) log("getAudioState");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled()) {
+ final IBluetoothHeadsetClient service = getService();
+ final int defaultValue = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- return service.getAudioState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getAudioState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
} else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ return defaultValue;
}
return BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
}
@@ -1124,17 +1212,18 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) {
if (VDBG) log("setAudioRouteAllowed");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled()) {
+ final IBluetoothHeadsetClient service = getService();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- service.setAudioRouteAllowed(device, allowed, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver recv = new SynchronousResultReceiver();
+ service.setAudioRouteAllowed(device, allowed, mAttributionSource, recv);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
}
@@ -1149,19 +1238,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean getAudioRouteAllowed(BluetoothDevice device) {
if (VDBG) log("getAudioRouteAllowed");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled()) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- return service.getAudioRouteAllowed(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.getAudioRouteAllowed(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
- return false;
+ return defaultValue;
}
/**
@@ -1176,19 +1267,22 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean connectAudio(BluetoothDevice device) {
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled()) {
+ if (VDBG) log("connectAudio");
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- return service.connectAudio(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connectAudio(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
- return false;
+ return defaultValue;
}
/**
@@ -1203,19 +1297,22 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnectAudio(BluetoothDevice device) {
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled()) {
+ if (VDBG) log("disconnectAudio");
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- return service.disconnectAudio(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnectAudio(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
- return false;
+ return defaultValue;
}
/**
@@ -1227,19 +1324,22 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public Bundle getCurrentAgFeatures(BluetoothDevice device) {
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled()) {
+ if (VDBG) log("getCurrentAgFeatures");
+ final IBluetoothHeadsetClient service = getService();
+ final Bundle defaultValue = null;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- return service.getCurrentAgFeatures(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Bundle> recv = new SynchronousResultReceiver();
+ service.getCurrentAgFeatures(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
- return null;
+ return defaultValue;
}
private boolean isEnabled() {
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClientCall.java b/core/java/android/bluetooth/BluetoothHeadsetClientCall.java
index 3f1ef8461250..e9dd761efdf6 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClientCall.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClientCall.java
@@ -18,7 +18,6 @@ package android.bluetooth;
import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
-import android.content.Attributable;
import android.content.AttributionSource;
import android.os.Build;
import android.os.Parcel;
@@ -293,7 +292,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable, Attributabl
new Parcelable.Creator<BluetoothHeadsetClientCall>() {
@Override
public BluetoothHeadsetClientCall createFromParcel(Parcel in) {
- return new BluetoothHeadsetClientCall((BluetoothDevice) in.readParcelable(null),
+ return new BluetoothHeadsetClientCall((BluetoothDevice) in.readParcelable(null, android.bluetooth.BluetoothDevice.class),
in.readInt(), UUID.fromString(in.readString()), in.readInt(),
in.readString(), in.readInt() == 1, in.readInt() == 1,
in.readInt() == 1);
diff --git a/core/java/android/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java
index 183f4d55bde5..339a75fe0fbe 100644
--- a/core/java/android/bluetooth/BluetoothHearingAid.java
+++ b/core/java/android/bluetooth/BluetoothHearingAid.java
@@ -16,29 +16,30 @@
package android.bluetooth;
-import android.Manifest;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
-import android.annotation.SuppressLint;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
-import android.content.Attributable;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* This class provides the public APIs to control the Hearing Aid profile.
@@ -139,7 +140,7 @@ public final class BluetoothHearingAid implements BluetoothProfile {
"BluetoothHearingAid", IBluetoothHearingAid.class.getName()) {
@Override
public IBluetoothHearingAid getServiceInterface(IBinder service) {
- return IBluetoothHearingAid.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothHearingAid.Stub.asInterface(service);
}
};
@@ -184,16 +185,20 @@ public final class BluetoothHearingAid implements BluetoothProfile {
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
final IBluetoothHearingAid service = getService();
- try {
- if (service != null && isEnabled() && isValidDevice(device)) {
- return service.connect(device, mAttributionSource);
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -226,16 +231,20 @@ public final class BluetoothHearingAid implements BluetoothProfile {
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
final IBluetoothHearingAid service = getService();
- try {
- if (service != null && isEnabled() && isValidDevice(device)) {
- return service.disconnect(device, mAttributionSource);
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -247,17 +256,23 @@ public final class BluetoothHearingAid implements BluetoothProfile {
public @NonNull List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
final IBluetoothHearingAid service = getService();
- try {
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
}
+ return defaultValue;
}
/**
@@ -270,18 +285,23 @@ public final class BluetoothHearingAid implements BluetoothProfile {
@NonNull int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
final IBluetoothHearingAid service = getService();
- try {
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
}
+ return defaultValue;
}
/**
@@ -294,17 +314,20 @@ public final class BluetoothHearingAid implements BluetoothProfile {
@NonNull BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
final IBluetoothHearingAid service = getService();
- try {
- if (service != null && isEnabled()
- && isValidDevice(device)) {
- return service.getConnectionState(device, mAttributionSource);
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
}
+ return defaultValue;
}
/**
@@ -333,18 +356,20 @@ public final class BluetoothHearingAid implements BluetoothProfile {
public boolean setActiveDevice(@Nullable BluetoothDevice device) {
if (DBG) log("setActiveDevice(" + device + ")");
final IBluetoothHearingAid service = getService();
- try {
- if (service != null && isEnabled()
- && ((device == null) || isValidDevice(device))) {
- service.setActiveDevice(device, mAttributionSource);
- return true;
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && ((device == null) || isValidDevice(device))) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setActiveDevice(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -362,17 +387,23 @@ public final class BluetoothHearingAid implements BluetoothProfile {
public @NonNull List<BluetoothDevice> getActiveDevices() {
if (VDBG) log("getActiveDevices()");
final IBluetoothHearingAid service = getService();
- try {
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getActiveDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getActiveDevices(mAttributionSource), mAttributionSource);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<>();
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<>();
}
+ return defaultValue;
}
/**
@@ -419,21 +450,22 @@ public final class BluetoothHearingAid implements BluetoothProfile {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
verifyDeviceNotNull(device, "setConnectionPolicy");
final IBluetoothHearingAid service = getService();
- try {
- if (service != null && isEnabled()
- && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -477,17 +509,20 @@ public final class BluetoothHearingAid implements BluetoothProfile {
if (VDBG) log("getConnectionPolicy(" + device + ")");
verifyDeviceNotNull(device, "getConnectionPolicy");
final IBluetoothHearingAid service = getService();
- try {
- if (service != null && isEnabled()
- && isValidDevice(device)) {
- return service.getConnectionPolicy(device, mAttributionSource);
+ final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
}
+ return defaultValue;
}
/**
@@ -522,19 +557,18 @@ public final class BluetoothHearingAid implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void setVolume(int volume) {
if (DBG) Log.d(TAG, "setVolume(" + volume + ")");
-
final IBluetoothHearingAid service = getService();
- try {
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- return;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver recv = new SynchronousResultReceiver();
+ service.setVolume(volume, mAttributionSource, recv);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
-
- if (!isEnabled()) return;
-
- service.setVolume(volume, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
}
}
@@ -555,24 +589,23 @@ public final class BluetoothHearingAid implements BluetoothProfile {
android.Manifest.permission.BLUETOOTH_PRIVILEGED,
})
public long getHiSyncId(@NonNull BluetoothDevice device) {
- if (VDBG) {
- log("getHiSyncId(" + device + ")");
- }
+ if (VDBG) log("getHiSyncId(" + device + ")");
verifyDeviceNotNull(device, "getConnectionPolicy");
final IBluetoothHearingAid service = getService();
- try {
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- return HI_SYNC_ID_INVALID;
+ final long defaultValue = HI_SYNC_ID_INVALID;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Long> recv = new SynchronousResultReceiver();
+ service.getHiSyncId(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
-
- if (!isEnabled() || !isValidDevice(device)) return HI_SYNC_ID_INVALID;
-
- return service.getHiSyncId(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return HI_SYNC_ID_INVALID;
}
+ return defaultValue;
}
/**
@@ -586,21 +619,22 @@ public final class BluetoothHearingAid implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getDeviceSide(BluetoothDevice device) {
- if (VDBG) {
- log("getDeviceSide(" + device + ")");
- }
+ if (VDBG) log("getDeviceSide(" + device + ")");
final IBluetoothHearingAid service = getService();
- try {
- if (service != null && isEnabled()
- && isValidDevice(device)) {
- return service.getDeviceSide(device, mAttributionSource);
+ final int defaultValue = SIDE_LEFT;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getDeviceSide(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return SIDE_LEFT;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return SIDE_LEFT;
}
+ return defaultValue;
}
/**
@@ -614,21 +648,22 @@ public final class BluetoothHearingAid implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getDeviceMode(BluetoothDevice device) {
- if (VDBG) {
- log("getDeviceMode(" + device + ")");
- }
+ if (VDBG) log("getDeviceMode(" + device + ")");
final IBluetoothHearingAid service = getService();
- try {
- if (service != null && isEnabled()
- && isValidDevice(device)) {
- return service.getDeviceMode(device, mAttributionSource);
+ final int defaultValue = MODE_MONAURAL;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getDeviceMode(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return MODE_MONAURAL;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return MODE_MONAURAL;
}
+ return defaultValue;
}
private boolean isEnabled() {
diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java
index c2744b89aadc..44a355b5f75c 100644
--- a/core/java/android/bluetooth/BluetoothHidDevice.java
+++ b/core/java/android/bluetooth/BluetoothHidDevice.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
@@ -24,17 +26,18 @@ import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.content.Attributable;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
+import java.util.concurrent.TimeoutException;
/**
* Provides the public APIs to control the Bluetooth HID Device profile.
@@ -432,7 +435,7 @@ public final class BluetoothHidDevice implements BluetoothProfile {
"BluetoothHidDevice", IBluetoothHidDevice.class.getName()) {
@Override
public IBluetoothHidDevice getServiceInterface(IBinder service) {
- return IBluetoothHidDevice.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothHidDevice.Stub.asInterface(service);
}
};
@@ -456,18 +459,23 @@ public final class BluetoothHidDevice implements BluetoothProfile {
@RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getConnectedDevices() {
final IBluetoothHidDevice service = getService();
- if (service != null) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
}
-
- return new ArrayList<>();
+ return defaultValue;
}
/** {@inheritDoc} */
@@ -476,19 +484,23 @@ public final class BluetoothHidDevice implements BluetoothProfile {
@RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
final IBluetoothHidDevice service = getService();
- if (service != null) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
}
-
- return new ArrayList<>();
+ return defaultValue;
}
/** {@inheritDoc} */
@@ -497,17 +509,20 @@ public final class BluetoothHidDevice implements BluetoothProfile {
@RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public int getConnectionState(BluetoothDevice device) {
final IBluetoothHidDevice service = getService();
- if (service != null) {
+ final int defaultValue = STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- return service.getConnectionState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
}
-
- return STATE_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -556,18 +571,21 @@ public final class BluetoothHidDevice implements BluetoothProfile {
}
final IBluetoothHidDevice service = getService();
- if (service != null) {
+ final boolean defaultValue = result;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
CallbackWrapper cbw = new CallbackWrapper(executor, callback, mAttributionSource);
- result = service.registerApp(sdp, inQos, outQos, cbw, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ service.registerApp(sdp, inQos, outQos, cbw, mAttributionSource, recv);
+ result = recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
}
-
- return result;
+ return defaultValue;
}
/**
@@ -583,20 +601,21 @@ public final class BluetoothHidDevice implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean unregisterApp() {
- boolean result = false;
-
final IBluetoothHidDevice service = getService();
- if (service != null) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- result = service.unregisterApp(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.unregisterApp(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
}
-
- return result;
+ return defaultValue;
}
/**
@@ -610,20 +629,21 @@ public final class BluetoothHidDevice implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean sendReport(BluetoothDevice device, int id, byte[] data) {
- boolean result = false;
-
final IBluetoothHidDevice service = getService();
- if (service != null) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- result = service.sendReport(device, id, data, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.sendReport(device, id, data, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
}
-
- return result;
+ return defaultValue;
}
/**
@@ -638,20 +658,21 @@ public final class BluetoothHidDevice implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) {
- boolean result = false;
-
final IBluetoothHidDevice service = getService();
- if (service != null) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- result = service.replyReport(device, type, id, data, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.replyReport(device, type, id, data, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
}
-
- return result;
+ return defaultValue;
}
/**
@@ -664,20 +685,21 @@ public final class BluetoothHidDevice implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean reportError(BluetoothDevice device, byte error) {
- boolean result = false;
-
final IBluetoothHidDevice service = getService();
- if (service != null) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- result = service.reportError(device, error, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.reportError(device, error, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
}
-
- return result;
+ return defaultValue;
}
/**
@@ -690,18 +712,20 @@ public final class BluetoothHidDevice implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public String getUserAppName() {
final IBluetoothHidDevice service = getService();
-
- if (service != null) {
+ final String defaultValue = "";
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- return service.getUserAppName(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<String> recv = new SynchronousResultReceiver();
+ service.getUserAppName(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
}
-
- return "";
+ return defaultValue;
}
/**
@@ -715,20 +739,21 @@ public final class BluetoothHidDevice implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean connect(BluetoothDevice device) {
- boolean result = false;
-
final IBluetoothHidDevice service = getService();
- if (service != null) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- result = service.connect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
}
-
- return result;
+ return defaultValue;
}
/**
@@ -741,20 +766,21 @@ public final class BluetoothHidDevice implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnect(BluetoothDevice device) {
- boolean result = false;
-
final IBluetoothHidDevice service = getService();
- if (service != null) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- result = service.disconnect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
}
-
- return result;
+ return defaultValue;
}
/**
@@ -782,23 +808,24 @@ public final class BluetoothHidDevice implements BluetoothProfile {
})
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
- log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
- try {
- final IBluetoothHidDevice service = getService();
- if (service != null && isEnabled()
- && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
+ if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
+ final IBluetoothHidDevice service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
private boolean isEnabled() {
diff --git a/core/java/android/bluetooth/BluetoothHidHost.java b/core/java/android/bluetooth/BluetoothHidHost.java
index fb4cbb2eb134..ecbeddf2b853 100644
--- a/core/java/android/bluetooth/BluetoothHidHost.java
+++ b/core/java/android/bluetooth/BluetoothHidHost.java
@@ -16,26 +16,29 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.content.Attributable;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
@@ -245,7 +248,7 @@ public final class BluetoothHidHost implements BluetoothProfile {
"BluetoothHidHost", IBluetoothHidHost.class.getName()) {
@Override
public IBluetoothHidHost getServiceInterface(IBinder service) {
- return IBluetoothHidHost.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothHidHost.Stub.asInterface(service);
}
};
@@ -293,16 +296,20 @@ public final class BluetoothHidHost implements BluetoothProfile {
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.connect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -335,16 +342,20 @@ public final class BluetoothHidHost implements BluetoothProfile {
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.disconnect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -359,17 +370,23 @@ public final class BluetoothHidHost implements BluetoothProfile {
public @NonNull List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -383,18 +400,23 @@ public final class BluetoothHidHost implements BluetoothProfile {
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -412,16 +434,20 @@ public final class BluetoothHidHost implements BluetoothProfile {
throw new IllegalArgumentException("device must not be null");
}
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -470,20 +496,22 @@ public final class BluetoothHidHost implements BluetoothProfile {
throw new IllegalArgumentException("device must not be null");
}
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
try {
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -529,16 +557,20 @@ public final class BluetoothHidHost implements BluetoothProfile {
throw new IllegalArgumentException("device must not be null");
}
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionPolicy(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return defaultValue;
}
private boolean isEnabled() {
@@ -562,18 +594,20 @@ public final class BluetoothHidHost implements BluetoothProfile {
public boolean virtualUnplug(BluetoothDevice device) {
if (DBG) log("virtualUnplug(" + device + ")");
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.virtualUnplug(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.virtualUnplug(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
-
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
-
+ return defaultValue;
}
/**
@@ -589,16 +623,20 @@ public final class BluetoothHidHost implements BluetoothProfile {
public boolean getProtocolMode(BluetoothDevice device) {
if (VDBG) log("getProtocolMode(" + device + ")");
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getProtocolMode(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.getProtocolMode(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -614,16 +652,20 @@ public final class BluetoothHidHost implements BluetoothProfile {
public boolean setProtocolMode(BluetoothDevice device, int protocolMode) {
if (DBG) log("setProtocolMode(" + device + ")");
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.setProtocolMode(device, protocolMode, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setProtocolMode(device, protocolMode, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -646,17 +688,21 @@ public final class BluetoothHidHost implements BluetoothProfile {
+ "bufferSize=" + bufferSize);
}
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getReport(device, reportType, reportId, bufferSize,
- mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.getReport(device, reportType, reportId, bufferSize, mAttributionSource,
+ recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -674,16 +720,20 @@ public final class BluetoothHidHost implements BluetoothProfile {
public boolean setReport(BluetoothDevice device, byte reportType, String report) {
if (VDBG) log("setReport(" + device + "), reportType=" + reportType + " report=" + report);
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.setReport(device, reportType, report, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setReport(device, reportType, report, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -700,16 +750,20 @@ public final class BluetoothHidHost implements BluetoothProfile {
public boolean sendData(BluetoothDevice device, String report) {
if (DBG) log("sendData(" + device + "), report=" + report);
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.sendData(device, report, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.sendData(device, report, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -725,16 +779,20 @@ public final class BluetoothHidHost implements BluetoothProfile {
public boolean getIdleTime(BluetoothDevice device) {
if (DBG) log("getIdletime(" + device + ")");
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getIdleTime(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.getIdleTime(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -751,16 +809,20 @@ public final class BluetoothHidHost implements BluetoothProfile {
public boolean setIdleTime(BluetoothDevice device, byte idleTime) {
if (DBG) log("setIdletime(" + device + "), idleTime=" + idleTime);
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.setIdleTime(device, idleTime, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setIdleTime(device, idleTime, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
private static void log(String msg) {
diff --git a/core/java/android/bluetooth/BluetoothLeAudio.java b/core/java/android/bluetooth/BluetoothLeAudio.java
index d7940eb9d3a4..15db686b3be4 100644
--- a/core/java/android/bluetooth/BluetoothLeAudio.java
+++ b/core/java/android/bluetooth/BluetoothLeAudio.java
@@ -17,26 +17,28 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.SuppressLint;
import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.content.Attributable;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.CloseGuard;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* This class provides the public APIs to control the LeAudio profile.
@@ -333,7 +335,7 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
IBluetoothLeAudio.class.getName()) {
@Override
public IBluetoothLeAudio getServiceInterface(IBinder service) {
- return IBluetoothLeAudio.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothLeAudio.Stub.asInterface(service);
}
};
@@ -387,17 +389,21 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean connect(@Nullable BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
- try {
- final IBluetoothLeAudio service = getService();
- if (service != null && mAdapter.isEnabled() && isValidDevice(device)) {
- return service.connect(device, mAttributionSource);
+ final IBluetoothLeAudio service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -427,17 +433,21 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnect(@Nullable BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
- try {
- final IBluetoothLeAudio service = getService();
- if (service != null && mAdapter.isEnabled() && isValidDevice(device)) {
- return service.disconnect(device, mAttributionSource);
+ final IBluetoothLeAudio service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -448,18 +458,24 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @NonNull List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
- try {
- final IBluetoothLeAudio service = getService();
- if (service != null && mAdapter.isEnabled()) {
+ final IBluetoothLeAudio service = getService();
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
}
+ return defaultValue;
}
/**
@@ -471,19 +487,24 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(
@NonNull int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
- try {
- final IBluetoothLeAudio service = getService();
- if (service != null && mAdapter.isEnabled()) {
+ final IBluetoothLeAudio service = getService();
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
}
+ return defaultValue;
}
/**
@@ -495,18 +516,21 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @BtProfileState int getConnectionState(@NonNull BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
- try {
- final IBluetoothLeAudio service = getService();
- if (service != null && mAdapter.isEnabled()
- && isValidDevice(device)) {
- return service.getConnectionState(device, mAttributionSource);
+ final IBluetoothLeAudio service = getService();
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
}
+ return defaultValue;
}
/**
@@ -533,19 +557,21 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean setActiveDevice(@Nullable BluetoothDevice device) {
if (DBG) log("setActiveDevice(" + device + ")");
- try {
- final IBluetoothLeAudio service = getService();
- if (service != null && mAdapter.isEnabled()
- && ((device == null) || isValidDevice(device))) {
- service.setActiveDevice(device, mAttributionSource);
- return true;
+ final IBluetoothLeAudio service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled() && ((device == null) || isValidDevice(device))) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setActiveDevice(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -559,19 +585,25 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getActiveDevices() {
- if (VDBG) log("getActiveDevices()");
- try {
- final IBluetoothLeAudio service = getService();
- if (service != null && mAdapter.isEnabled()) {
+ if (VDBG) log("getActiveDevice()");
+ final IBluetoothLeAudio service = getService();
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getActiveDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getActiveDevices(mAttributionSource), mAttributionSource);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<>();
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<>();
}
+ return defaultValue;
}
/**
@@ -585,17 +617,21 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getGroupId(@NonNull BluetoothDevice device) {
if (VDBG) log("getGroupId()");
- try {
- final IBluetoothLeAudio service = getService();
- if (service != null && mAdapter.isEnabled()) {
- return service.getGroupId(device, mAttributionSource);
+ final IBluetoothLeAudio service = getService();
+ final int defaultValue = GROUP_ID_INVALID;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getGroupId(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return GROUP_ID_INVALID;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return GROUP_ID_INVALID;
}
+ return defaultValue;
}
/**
@@ -608,17 +644,18 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
@RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED})
public void setVolume(int volume) {
if (VDBG) log("setVolume(vol: " + volume + " )");
- try {
- final IBluetoothLeAudio service = getService();
- if (service != null && mAdapter.isEnabled()) {
- service.setVolume(volume, mAttributionSource);
- return;
+ final IBluetoothLeAudio service = getService();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled()) {
+ try {
+ final SynchronousResultReceiver recv = new SynchronousResultReceiver();
+ service.setVolume(volume, mAttributionSource, recv);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return;
}
}
@@ -637,16 +674,20 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
public boolean groupAddNode(int group_id, @NonNull BluetoothDevice device) {
if (VDBG) log("groupAddNode()");
final IBluetoothLeAudio service = getService();
- try {
- if (service != null && mAdapter.isEnabled()) {
- return service.groupAddNode(group_id, device, mAttributionSource);
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.groupAddNode(group_id, device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -665,16 +706,20 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
public boolean groupRemoveNode(int group_id, @NonNull BluetoothDevice device) {
if (VDBG) log("groupRemoveNode()");
final IBluetoothLeAudio service = getService();
- try {
- if (service != null && mAdapter.isEnabled()) {
- return service.groupRemoveNode(group_id, device, mAttributionSource);
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.groupRemoveNode(group_id, device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -697,22 +742,23 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
- try {
- final IBluetoothLeAudio service = getService();
- if (service != null && mAdapter.isEnabled()
- && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
+ final IBluetoothLeAudio service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -730,18 +776,21 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
- try {
- final IBluetoothLeAudio service = getService();
- if (service != null && mAdapter.isEnabled()
- && isValidDevice(device)) {
- return service.getConnectionPolicy(device, mAttributionSource);
+ final IBluetoothLeAudio service = getService();
+ final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
}
+ return defaultValue;
}
diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java
index 20152f3d2471..fef6f225dd3b 100644
--- a/core/java/android/bluetooth/BluetoothManager.java
+++ b/core/java/android/bluetooth/BluetoothManager.java
@@ -16,17 +16,12 @@
package android.bluetooth;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.RequiresFeature;
import android.annotation.RequiresNoPermission;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
-import android.app.ActivityThread;
-import android.app.AppGlobals;
import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
-import android.content.Attributable;
import android.content.AttributionSource;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -69,37 +64,11 @@ public final class BluetoothManager {
* @hide
*/
public BluetoothManager(Context context) {
- mAttributionSource = resolveAttributionSource(context);
+ mAttributionSource = (context != null) ? context.getAttributionSource() :
+ AttributionSource.myAttributionSource();
mAdapter = BluetoothAdapter.createAdapter(mAttributionSource);
}
- /** {@hide} */
- public static @NonNull AttributionSource resolveAttributionSource(@Nullable Context context) {
- AttributionSource res = null;
- if (context != null) {
- res = context.getAttributionSource();
- }
- if (res == null) {
- res = ActivityThread.currentAttributionSource();
- }
- if (res == null) {
- int uid = android.os.Process.myUid();
- if (uid == android.os.Process.ROOT_UID) {
- uid = android.os.Process.SYSTEM_UID;
- }
- try {
- res = new AttributionSource.Builder(uid)
- .setPackageName(AppGlobals.getPackageManager().getPackagesForUid(uid)[0])
- .build();
- } catch (RemoteException ignored) {
- }
- }
- if (res == null) {
- throw new IllegalStateException("Failed to resolve AttributionSource");
- }
- return res;
- }
-
/**
* Get the BLUETOOTH Adapter for this device.
*
diff --git a/core/java/android/bluetooth/BluetoothMap.java b/core/java/android/bluetooth/BluetoothMap.java
index 86796519df4c..56e497262421 100644
--- a/core/java/android/bluetooth/BluetoothMap.java
+++ b/core/java/android/bluetooth/BluetoothMap.java
@@ -16,28 +16,31 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.RequiresNoPermission;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
-import android.annotation.SdkConstant.SdkConstantType;
import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.compat.annotation.UnsupportedAppUsage;
-import android.content.Attributable;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.CloseGuard;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* This class provides the APIs to control the Bluetooth MAP
@@ -88,7 +91,7 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
"BluetoothMap", IBluetoothMap.class.getName()) {
@Override
public IBluetoothMap getServiceInterface(IBinder service) {
- return IBluetoothMap.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothMap.Stub.asInterface(service);
}
};
@@ -143,17 +146,20 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
public int getState() {
if (VDBG) log("getState()");
final IBluetoothMap service = getService();
- if (service != null) {
- try {
- return service.getState(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final int defaultValue = BluetoothMap.STATE_ERROR;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getState(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return BluetoothMap.STATE_ERROR;
+ return defaultValue;
}
/**
@@ -169,18 +175,23 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
public BluetoothDevice getClient() {
if (VDBG) log("getClient()");
final IBluetoothMap service = getService();
- if (service != null) {
+ final BluetoothDevice defaultValue = null;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<BluetoothDevice> recv =
+ new SynchronousResultReceiver();
+ service.getClient(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getClient(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) log(Log.getStackTraceString(new Throwable()));
}
- return null;
+ return defaultValue;
}
/**
@@ -195,17 +206,20 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
public boolean isConnected(BluetoothDevice device) {
if (VDBG) log("isConnected(" + device + ")");
final IBluetoothMap service = getService();
- if (service != null) {
- try {
- return service.isConnected(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final boolean defaultValue = false;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isConnected(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return false;
+ return defaultValue;
}
/**
@@ -234,16 +248,20 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
final IBluetoothMap service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.disconnect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -285,17 +303,23 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
public @NonNull List<BluetoothDevice> getConnectedDevices() {
if (DBG) log("getConnectedDevices()");
final IBluetoothMap service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -310,18 +334,23 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (DBG) log("getDevicesMatchingStates()");
final IBluetoothMap service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -336,16 +365,21 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
public int getConnectionState(BluetoothDevice device) {
if (DBG) log("getConnectionState(" + device + ")");
final IBluetoothMap service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
+ final SynchronousResultReceiver<Integer> recv =
+ new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -391,20 +425,22 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
final IBluetoothMap service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
try {
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -447,16 +483,20 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothMap service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionPolicy(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return defaultValue;
}
private static void log(String msg) {
diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java
index 042b58669a06..03536f9aad39 100644
--- a/core/java/android/bluetooth/BluetoothMapClient.java
+++ b/core/java/android/bluetooth/BluetoothMapClient.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -26,19 +28,20 @@ import android.annotation.SystemApi;
import android.app.PendingIntent;
import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.compat.annotation.UnsupportedAppUsage;
-import android.content.Attributable;
import android.content.AttributionSource;
import android.content.Context;
import android.net.Uri;
-import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* This class provides the APIs to control the Bluetooth MAP MCE Profile.
@@ -181,7 +184,7 @@ public final class BluetoothMapClient implements BluetoothProfile {
"BluetoothMapClient", IBluetoothMapClient.class.getName()) {
@Override
public IBluetoothMapClient getServiceInterface(IBinder service) {
- return IBluetoothMapClient.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothMapClient.Stub.asInterface(service);
}
};
@@ -222,17 +225,20 @@ public final class BluetoothMapClient implements BluetoothProfile {
public boolean isConnected(BluetoothDevice device) {
if (VDBG) Log.d(TAG, "isConnected(" + device + ")");
final IBluetoothMapClient service = getService();
- if (service != null) {
- try {
- return service.isConnected(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final boolean defaultValue = false;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isConnected(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return false;
+ return defaultValue;
}
/**
@@ -249,17 +255,20 @@ public final class BluetoothMapClient implements BluetoothProfile {
public boolean connect(BluetoothDevice device) {
if (DBG) Log.d(TAG, "connect(" + device + ")" + "for MAPS MCE");
final IBluetoothMapClient service = getService();
- if (service != null) {
- try {
- return service.connect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final boolean defaultValue = false;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return false;
+ return defaultValue;
}
/**
@@ -278,15 +287,20 @@ public final class BluetoothMapClient implements BluetoothProfile {
public boolean disconnect(BluetoothDevice device) {
if (DBG) Log.d(TAG, "disconnect(" + device + ")");
final IBluetoothMapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.disconnect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -301,17 +315,23 @@ public final class BluetoothMapClient implements BluetoothProfile {
public List<BluetoothDevice> getConnectedDevices() {
if (DBG) Log.d(TAG, "getConnectedDevices()");
final IBluetoothMapClient service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<>();
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<>();
+ return defaultValue;
}
/**
@@ -326,18 +346,23 @@ public final class BluetoothMapClient implements BluetoothProfile {
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (DBG) Log.d(TAG, "getDevicesMatchingStates()");
final IBluetoothMapClient service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<>();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<>();
+ return defaultValue;
}
/**
@@ -352,16 +377,20 @@ public final class BluetoothMapClient implements BluetoothProfile {
public int getConnectionState(BluetoothDevice device) {
if (DBG) Log.d(TAG, "getConnectionState(" + device + ")");
final IBluetoothMapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver<>();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -406,20 +435,22 @@ public final class BluetoothMapClient implements BluetoothProfile {
@ConnectionPolicy int connectionPolicy) {
if (DBG) Log.d(TAG, "setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
final IBluetoothMapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
try {
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -461,16 +492,20 @@ public final class BluetoothMapClient implements BluetoothProfile {
public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) Log.d(TAG, "getConnectionPolicy(" + device + ")");
final IBluetoothMapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionPolicy(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return defaultValue;
}
/**
@@ -495,18 +530,8 @@ public final class BluetoothMapClient implements BluetoothProfile {
public boolean sendMessage(@NonNull BluetoothDevice device, @NonNull Collection<Uri> contacts,
@NonNull String message, @Nullable PendingIntent sentIntent,
@Nullable PendingIntent deliveredIntent) {
- if (DBG) Log.d(TAG, "sendMessage(" + device + ", " + contacts + ", " + message);
- final IBluetoothMapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- try {
- return service.sendMessage(device, contacts.toArray(new Uri[contacts.size()]),
- message, sentIntent, deliveredIntent, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
- }
- }
- return false;
+ return sendMessage(device, contacts.toArray(new Uri[contacts.size()]), message, sentIntent,
+ deliveredIntent);
}
/**
@@ -532,16 +557,21 @@ public final class BluetoothMapClient implements BluetoothProfile {
PendingIntent sentIntent, PendingIntent deliveredIntent) {
if (DBG) Log.d(TAG, "sendMessage(" + device + ", " + contacts + ", " + message);
final IBluetoothMapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.sendMessage(device, contacts, message, sentIntent, deliveredIntent,
- mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.sendMessage(device, contacts, message, sentIntent, deliveredIntent,
+ mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- return false;
+ return defaultValue;
}
/**
@@ -559,15 +589,20 @@ public final class BluetoothMapClient implements BluetoothProfile {
public boolean getUnreadMessages(BluetoothDevice device) {
if (DBG) Log.d(TAG, "getUnreadMessages(" + device + ")");
final IBluetoothMapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getUnreadMessages(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.getUnreadMessages(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- return false;
+ return defaultValue;
}
/**
@@ -581,13 +616,21 @@ public final class BluetoothMapClient implements BluetoothProfile {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean isUploadingSupported(BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "isUploadingSupported(" + device + ")");
final IBluetoothMapClient service = getService();
- try {
- return (service != null && isEnabled() && isValidDevice(device))
- && ((service.getSupportedFeatures(device, mAttributionSource)
- & UPLOADING_FEATURE_BITMASK) > 0);
- } catch (RemoteException e) {
- Log.e(TAG, e.getMessage());
+ final int defaultValue = 0;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getSupportedFeatures(device, mAttributionSource, recv);
+ return (recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue)
+ & UPLOADING_FEATURE_BITMASK) > 0;
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
return false;
}
@@ -616,16 +659,21 @@ public final class BluetoothMapClient implements BluetoothProfile {
public boolean setMessageStatus(BluetoothDevice device, String handle, int status) {
if (DBG) Log.d(TAG, "setMessageStatus(" + device + ", " + handle + ", " + status + ")");
final IBluetoothMapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device) && handle != null &&
- (status == READ || status == UNREAD || status == UNDELETED || status == DELETED)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device) && handle != null && (status == READ
+ || status == UNREAD || status == UNDELETED || status == DELETED)) {
try {
- return service.setMessageStatus(device, handle, status, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setMessageStatus(device, handle, status, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- return false;
+ return defaultValue;
}
private boolean isEnabled() {
diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java
index 577be3d2aea4..d4ad4ef47acd 100644
--- a/core/java/android/bluetooth/BluetoothPan.java
+++ b/core/java/android/bluetooth/BluetoothPan.java
@@ -16,7 +16,8 @@
package android.bluetooth;
-import android.Manifest;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
@@ -27,19 +28,20 @@ import android.annotation.SystemApi;
import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.compat.annotation.UnsupportedAppUsage;
-import android.content.Attributable;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* This class provides the APIs to control the Bluetooth Pan
@@ -190,7 +192,7 @@ public final class BluetoothPan implements BluetoothProfile {
"BluetoothPan", IBluetoothPan.class.getName()) {
@Override
public IBluetoothPan getServiceInterface(IBinder service) {
- return IBluetoothPan.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothPan.Stub.asInterface(service);
}
};
@@ -251,16 +253,20 @@ public final class BluetoothPan implements BluetoothProfile {
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
final IBluetoothPan service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.connect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -291,16 +297,20 @@ public final class BluetoothPan implements BluetoothProfile {
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
final IBluetoothPan service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.disconnect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -324,22 +334,23 @@ public final class BluetoothPan implements BluetoothProfile {
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
- try {
- final IBluetoothPan service = getService();
- if (service != null && isEnabled()
- && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
+ final IBluetoothPan service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -356,17 +367,23 @@ public final class BluetoothPan implements BluetoothProfile {
public @NonNull List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
final IBluetoothPan service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -383,18 +400,23 @@ public final class BluetoothPan implements BluetoothProfile {
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
final IBluetoothPan service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -411,16 +433,20 @@ public final class BluetoothPan implements BluetoothProfile {
public int getConnectionState(@NonNull BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
final IBluetoothPan service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -440,11 +466,16 @@ public final class BluetoothPan implements BluetoothProfile {
String pkgName = mContext.getOpPackageName();
if (DBG) log("setBluetoothTethering(" + value + "), calling package:" + pkgName);
final IBluetoothPan service = getService();
- if (service != null && isEnabled()) {
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- service.setBluetoothTethering(value, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver recv = new SynchronousResultReceiver();
+ service.setBluetoothTethering(value, mAttributionSource, recv);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
}
@@ -461,14 +492,20 @@ public final class BluetoothPan implements BluetoothProfile {
public boolean isTetheringOn() {
if (VDBG) log("isTetheringOn()");
final IBluetoothPan service = getService();
- if (service != null && isEnabled()) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- return service.isTetheringOn(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isTetheringOn(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- return false;
+ return defaultValue;
}
@UnsupportedAppUsage
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index 8ee38d3e1260..e13792918103 100644
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -24,7 +24,6 @@ import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.compat.annotation.UnsupportedAppUsage;
-import android.content.Attributable;
import android.content.AttributionSource;
import android.content.ComponentName;
import android.content.Context;
diff --git a/core/java/android/bluetooth/BluetoothPbapClient.java b/core/java/android/bluetooth/BluetoothPbapClient.java
index c7dd6bd9af14..e096de8cb829 100644
--- a/core/java/android/bluetooth/BluetoothPbapClient.java
+++ b/core/java/android/bluetooth/BluetoothPbapClient.java
@@ -16,23 +16,25 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
-import android.annotation.SuppressLint;
import android.annotation.SdkConstant.SdkConstantType;
import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.content.Attributable;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* This class provides the APIs to control the Bluetooth PBAP Client Profile.
@@ -66,7 +68,7 @@ public final class BluetoothPbapClient implements BluetoothProfile {
"BluetoothPbapClient", IBluetoothPbapClient.class.getName()) {
@Override
public IBluetoothPbapClient getServiceInterface(IBinder service) {
- return IBluetoothPbapClient.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothPbapClient.Stub.asInterface(service);
}
};
@@ -125,18 +127,20 @@ public final class BluetoothPbapClient implements BluetoothProfile {
log("connect(" + device + ") for PBAP Client.");
}
final IBluetoothPbapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- try {
- return service.connect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
- }
- }
+ final boolean defaultValue = false;
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return false;
+ return defaultValue;
}
/**
@@ -157,19 +161,21 @@ public final class BluetoothPbapClient implements BluetoothProfile {
log("disconnect(" + device + ")" + new Exception());
}
final IBluetoothPbapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = true;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- service.disconnect(device, mAttributionSource);
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnect(device, mAttributionSource, recv);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
return true;
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- }
- return false;
+ return defaultValue;
}
/**
@@ -186,19 +192,23 @@ public final class BluetoothPbapClient implements BluetoothProfile {
log("getConnectedDevices()");
}
final IBluetoothPbapClient service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- }
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -214,20 +224,23 @@ public final class BluetoothPbapClient implements BluetoothProfile {
log("getDevicesMatchingStates()");
}
final IBluetoothPbapClient service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- }
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -243,18 +256,20 @@ public final class BluetoothPbapClient implements BluetoothProfile {
log("getConnectionState(" + device + ")");
}
final IBluetoothPbapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- try {
- return service.getConnectionState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
- }
- }
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return BluetoothProfile.STATE_DISCONNECTED;
+ return defaultValue;
}
private static void log(String msg) {
@@ -313,22 +328,22 @@ public final class BluetoothPbapClient implements BluetoothProfile {
log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
}
final IBluetoothPbapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
- try {
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
- }
- }
+ final boolean defaultValue = false;
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return false;
+ return defaultValue;
}
/**
@@ -372,17 +387,19 @@ public final class BluetoothPbapClient implements BluetoothProfile {
log("getConnectionPolicy(" + device + ")");
}
final IBluetoothPbapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- try {
- return service.getConnectionPolicy(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
- }
- }
+ final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return defaultValue;
}
}
diff --git a/core/java/android/bluetooth/BluetoothSap.java b/core/java/android/bluetooth/BluetoothSap.java
index fda19ed6d0cc..808fa3913316 100644
--- a/core/java/android/bluetooth/BluetoothSap.java
+++ b/core/java/android/bluetooth/BluetoothSap.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.Manifest;
import android.annotation.RequiresNoPermission;
import android.annotation.RequiresPermission;
@@ -24,17 +26,18 @@ import android.annotation.SdkConstant.SdkConstantType;
import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.compat.annotation.UnsupportedAppUsage;
-import android.content.Attributable;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* This class provides the APIs to control the Bluetooth SIM
@@ -105,7 +108,7 @@ public final class BluetoothSap implements BluetoothProfile {
"BluetoothSap", IBluetoothSap.class.getName()) {
@Override
public IBluetoothSap getServiceInterface(IBinder service) {
- return IBluetoothSap.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothSap.Stub.asInterface(service);
}
};
@@ -156,17 +159,20 @@ public final class BluetoothSap implements BluetoothProfile {
public int getState() {
if (VDBG) log("getState()");
final IBluetoothSap service = getService();
- if (service != null) {
- try {
- return service.getState(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final int defaultValue = BluetoothSap.STATE_ERROR;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getState(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return BluetoothSap.STATE_ERROR;
+ return defaultValue;
}
/**
@@ -181,18 +187,23 @@ public final class BluetoothSap implements BluetoothProfile {
public BluetoothDevice getClient() {
if (VDBG) log("getClient()");
final IBluetoothSap service = getService();
- if (service != null) {
+ final BluetoothDevice defaultValue = null;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<BluetoothDevice> recv =
+ new SynchronousResultReceiver();
+ service.getClient(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getClient(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) log(Log.getStackTraceString(new Throwable()));
}
- return null;
+ return defaultValue;
}
/**
@@ -207,17 +218,20 @@ public final class BluetoothSap implements BluetoothProfile {
public boolean isConnected(BluetoothDevice device) {
if (VDBG) log("isConnected(" + device + ")");
final IBluetoothSap service = getService();
- if (service != null) {
- try {
- return service.isConnected(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final boolean defaultValue = false;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isConnected(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return false;
+ return defaultValue;
}
/**
@@ -245,16 +259,20 @@ public final class BluetoothSap implements BluetoothProfile {
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
final IBluetoothSap service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.disconnect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -268,17 +286,23 @@ public final class BluetoothSap implements BluetoothProfile {
public List<BluetoothDevice> getConnectedDevices() {
if (DBG) log("getConnectedDevices()");
final IBluetoothSap service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -292,18 +316,23 @@ public final class BluetoothSap implements BluetoothProfile {
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (DBG) log("getDevicesMatchingStates()");
final IBluetoothSap service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -317,16 +346,20 @@ public final class BluetoothSap implements BluetoothProfile {
public int getConnectionState(BluetoothDevice device) {
if (DBG) log("getConnectionState(" + device + ")");
final IBluetoothSap service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -371,20 +404,22 @@ public final class BluetoothSap implements BluetoothProfile {
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
final IBluetoothSap service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
try {
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -426,16 +461,20 @@ public final class BluetoothSap implements BluetoothProfile {
public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothSap service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionPolicy(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return defaultValue;
}
private static void log(String msg) {
diff --git a/core/java/android/bluetooth/BluetoothUtils.java b/core/java/android/bluetooth/BluetoothUtils.java
new file mode 100644
index 000000000000..867469241f84
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothUtils.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import java.time.Duration;
+
+/**
+ * {@hide}
+ */
+public final class BluetoothUtils {
+ /**
+ * This utility class cannot be instantiated
+ */
+ private BluetoothUtils() {}
+
+ /**
+ * Timeout value for synchronous binder call
+ */
+ private static final Duration SYNC_CALLS_TIMEOUT = Duration.ofSeconds(5);
+
+ /**
+ * @return timeout value for synchronous binder call
+ */
+ static Duration getSyncTimeout() {
+ return SYNC_CALLS_TIMEOUT;
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothVolumeControl.java b/core/java/android/bluetooth/BluetoothVolumeControl.java
index 678c11a59f31..27532aabc3fc 100644
--- a/core/java/android/bluetooth/BluetoothVolumeControl.java
+++ b/core/java/android/bluetooth/BluetoothVolumeControl.java
@@ -17,6 +17,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.Manifest;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -27,18 +29,18 @@ import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.Attributable;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.CloseGuard;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* This class provides the public APIs to control the Bluetooth Volume Control service.
@@ -88,7 +90,7 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose
IBluetoothVolumeControl.class.getName()) {
@Override
public IBluetoothVolumeControl getServiceInterface(IBinder service) {
- return IBluetoothVolumeControl.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothVolumeControl.Stub.asInterface(service);
}
};
@@ -136,17 +138,23 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose
public @NonNull List<BluetoothDevice> getConnectedDevices() {
if (DBG) log("getConnectedDevices()");
final IBluetoothVolumeControl service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -161,18 +169,23 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (DBG) log("getDevicesMatchingStates()");
final IBluetoothVolumeControl service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -187,16 +200,20 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose
public int getConnectionState(BluetoothDevice device) {
if (DBG) log("getConnectionState(" + device + ")");
final IBluetoothVolumeControl service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -214,18 +231,19 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose
})
public void setVolume(@Nullable BluetoothDevice device,
@IntRange(from = 0, to = 255) int volume) {
- if (DBG)
- log("setVolume(" + volume + ")");
+ if (DBG) log("setVolume(" + volume + ")");
final IBluetoothVolumeControl service = getService();
- try {
- if (service != null && isEnabled()) {
- service.setVolume(device, volume, mAttributionSource);
- return;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver recv = new SynchronousResultReceiver();
+ service.setVolume(device, volume, mAttributionSource, recv);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null)
- Log.w(TAG, "Proxy not attached to service");
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
}
}
@@ -251,20 +269,22 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
final IBluetoothVolumeControl service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
try {
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -287,16 +307,20 @@ public final class BluetoothVolumeControl implements BluetoothProfile, AutoClose
public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothVolumeControl service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionPolicy(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return defaultValue;
}
private boolean isEnabled() {
diff --git a/core/java/android/bluetooth/BufferConstraints.java b/core/java/android/bluetooth/BufferConstraints.java
index 97d97232b7a6..06b45ee5c8b5 100644
--- a/core/java/android/bluetooth/BufferConstraints.java
+++ b/core/java/android/bluetooth/BufferConstraints.java
@@ -55,7 +55,7 @@ public final class BufferConstraints implements Parcelable {
BufferConstraints(Parcel in) {
mBufferConstraintList = new ArrayList<BufferConstraint>();
mBufferConstraints = new HashMap<Integer, BufferConstraint>();
- in.readList(mBufferConstraintList, BufferConstraint.class.getClassLoader());
+ in.readList(mBufferConstraintList, BufferConstraint.class.getClassLoader(), android.bluetooth.BufferConstraint.class);
for (int i = 0; i < mBufferConstraintList.size(); i++) {
mBufferConstraints.put(i, mBufferConstraintList.get(i));
}
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
index ee173dbc4ad4..540e5a778c27 100644
--- a/core/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -23,6 +23,7 @@ import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.app.PendingIntent;
+import android.bluetooth.Attributable;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.IBluetoothGatt;
@@ -30,7 +31,6 @@ import android.bluetooth.IBluetoothManager;
import android.bluetooth.annotations.RequiresBluetoothLocationPermission;
import android.bluetooth.annotations.RequiresBluetoothScanPermission;
import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
-import android.content.Attributable;
import android.content.AttributionSource;
import android.os.Handler;
import android.os.Looper;
@@ -514,16 +514,27 @@ public final class BluetoothLeScanner {
@Override
public void onScanResult(final ScanResult scanResult) {
Attributable.setAttributionSource(scanResult, mAttributionSource);
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "onScanResult() - mScannerId=" + mScannerId);
+ }
if (VDBG) Log.d(TAG, "onScanResult() - " + scanResult.toString());
// Check null in case the scan has been stopped
synchronized (this) {
- if (mScannerId <= 0) return;
+ if (mScannerId <= 0) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Ignoring result as scan stopped.");
+ }
+ return;
+ };
}
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "onScanResult() - handler run");
+ }
mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult);
}
});
diff --git a/core/java/android/bluetooth/le/PeriodicAdvertisingManager.java b/core/java/android/bluetooth/le/PeriodicAdvertisingManager.java
index dea686d18ea2..bbd31170bb41 100644
--- a/core/java/android/bluetooth/le/PeriodicAdvertisingManager.java
+++ b/core/java/android/bluetooth/le/PeriodicAdvertisingManager.java
@@ -18,6 +18,7 @@ package android.bluetooth.le;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
+import android.bluetooth.Attributable;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.IBluetoothGatt;
@@ -25,7 +26,6 @@ import android.bluetooth.IBluetoothManager;
import android.bluetooth.annotations.RequiresBluetoothLocationPermission;
import android.bluetooth.annotations.RequiresBluetoothScanPermission;
import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
-import android.content.Attributable;
import android.content.AttributionSource;
import android.os.Handler;
import android.os.Looper;
diff --git a/core/java/android/bluetooth/le/ScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java
index b059193ae03f..675fe05a7dec 100644
--- a/core/java/android/bluetooth/le/ScanFilter.java
+++ b/core/java/android/bluetooth/le/ScanFilter.java
@@ -200,28 +200,28 @@ public final class ScanFilter implements Parcelable {
address = in.readString();
}
if (in.readInt() == 1) {
- ParcelUuid uuid = in.readParcelable(ParcelUuid.class.getClassLoader());
+ ParcelUuid uuid = in.readParcelable(ParcelUuid.class.getClassLoader(), android.os.ParcelUuid.class);
builder.setServiceUuid(uuid);
if (in.readInt() == 1) {
ParcelUuid uuidMask = in.readParcelable(
- ParcelUuid.class.getClassLoader());
+ ParcelUuid.class.getClassLoader(), android.os.ParcelUuid.class);
builder.setServiceUuid(uuid, uuidMask);
}
}
if (in.readInt() == 1) {
ParcelUuid solicitationUuid = in.readParcelable(
- ParcelUuid.class.getClassLoader());
+ ParcelUuid.class.getClassLoader(), android.os.ParcelUuid.class);
builder.setServiceSolicitationUuid(solicitationUuid);
if (in.readInt() == 1) {
ParcelUuid solicitationUuidMask = in.readParcelable(
- ParcelUuid.class.getClassLoader());
+ ParcelUuid.class.getClassLoader(), android.os.ParcelUuid.class);
builder.setServiceSolicitationUuid(solicitationUuid,
solicitationUuidMask);
}
}
if (in.readInt() == 1) {
ParcelUuid servcieDataUuid =
- in.readParcelable(ParcelUuid.class.getClassLoader());
+ in.readParcelable(ParcelUuid.class.getClassLoader(), android.os.ParcelUuid.class);
if (in.readInt() == 1) {
int serviceDataLength = in.readInt();
byte[] serviceData = new byte[serviceDataLength];
diff --git a/core/java/android/bluetooth/le/ScanResult.java b/core/java/android/bluetooth/le/ScanResult.java
index 522845628487..f437d867ea37 100644
--- a/core/java/android/bluetooth/le/ScanResult.java
+++ b/core/java/android/bluetooth/le/ScanResult.java
@@ -18,8 +18,8 @@ package android.bluetooth.le;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.bluetooth.Attributable;
import android.bluetooth.BluetoothDevice;
-import android.content.Attributable;
import android.content.AttributionSource;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java
index 6e1f8b535bff..1d2f06d34c8c 100644
--- a/core/java/android/companion/AssociationRequest.java
+++ b/core/java/android/companion/AssociationRequest.java
@@ -20,12 +20,15 @@ import static android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED;
import static com.android.internal.util.CollectionUtils.emptyIfNull;
+import static java.util.Objects.requireNonNull;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.StringDef;
import android.annotation.SystemApi;
+import android.annotation.UserIdInt;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
@@ -52,12 +55,11 @@ import java.util.Objects;
* device to be shown instead of a list to choose from
*/
@DataClass(
+ genConstructor = false,
genToString = true,
genEqualsHashCode = true,
genHiddenGetters = true,
genParcelable = true,
- genHiddenConstructor = true,
- genBuilder = false,
genConstDefs = false)
public final class AssociationRequest implements Parcelable {
/**
@@ -151,40 +153,76 @@ public final class AssociationRequest implements Parcelable {
private final boolean mForceConfirmation;
/**
- * The app package making the request.
- *
+ * The app package name of the application the association will belong to.
+ * Populated by the system.
+ * @hide
+ */
+ private @Nullable String mPackageName;
+
+ /**
+ * The UserId of the user the association will belong to.
* Populated by the system.
- *
* @hide
*/
- private @Nullable String mCallingPackage;
+ private @UserIdInt int mUserId;
/**
* The user-readable description of the device profile's privileges.
- *
* Populated by the system.
- *
* @hide
*/
private @Nullable String mDeviceProfilePrivilegesDescription;
/**
* The time at which his request was created
- *
* @hide
*/
- private long mCreationTime;
+ private final long mCreationTime;
/**
* Whether the user-prompt may be skipped once the device is found.
- *
* Populated by the system.
- *
* @hide
*/
private boolean mSkipPrompt;
/**
+ * Creates a new AssociationRequest.
+ *
+ * @param singleDevice
+ * Whether only a single device should match the provided filter.
+ *
+ * When scanning for a single device with a specific {@link BluetoothDeviceFilter} mac
+ * address, bonded devices are also searched among. This allows to obtain the necessary app
+ * privileges even if the device is already paired.
+ * @param deviceFilters
+ * If set, only devices matching either of the given filters will be shown to the user
+ * @param deviceProfile
+ * Profile of the device.
+ * @param displayName
+ * The Display name of the device to be shown in the CDM confirmation UI. Must be non-null for
+ * "self-managed" association.
+ * @param selfManaged
+ * Whether the association is to be managed by the companion application.
+ */
+ private AssociationRequest(
+ boolean singleDevice,
+ @NonNull List<DeviceFilter<?>> deviceFilters,
+ @Nullable @DeviceProfile String deviceProfile,
+ @Nullable CharSequence displayName,
+ boolean selfManaged,
+ boolean forceConfirmation) {
+ mSingleDevice = singleDevice;
+ mDeviceFilters = requireNonNull(deviceFilters);
+ mDeviceProfile = deviceProfile;
+ mDisplayName = displayName;
+ mSelfManaged = selfManaged;
+ mForceConfirmation = forceConfirmation;
+
+ mCreationTime = System.currentTimeMillis();
+ }
+
+ /**
* @return profile of the companion device.
*/
public @Nullable @DeviceProfile String getDeviceProfile() {
@@ -237,8 +275,13 @@ public final class AssociationRequest implements Parcelable {
}
/** @hide */
- public void setCallingPackage(@NonNull String pkg) {
- mCallingPackage = pkg;
+ public void setPackageName(@NonNull String packageName) {
+ mPackageName = packageName;
+ }
+
+ /** @hide */
+ public void setUserId(@UserIdInt int userId) {
+ mUserId = userId;
}
/** @hide */
@@ -248,7 +291,7 @@ public final class AssociationRequest implements Parcelable {
/** @hide */
public void setSkipPrompt(boolean value) {
- mSkipPrompt = true;
+ mSkipPrompt = value;
}
/** @hide */
@@ -258,10 +301,6 @@ public final class AssociationRequest implements Parcelable {
return mDeviceFilters;
}
- private void onConstructed() {
- mCreationTime = System.currentTimeMillis();
- }
-
/**
* A builder for {@link AssociationRequest}
*/
@@ -325,7 +364,7 @@ public final class AssociationRequest implements Parcelable {
@NonNull
public Builder setDisplayName(@NonNull CharSequence displayName) {
checkNotUsed();
- mDisplayName = Objects.requireNonNull(displayName);
+ mDisplayName = requireNonNull(displayName);
return this;
}
@@ -372,15 +411,13 @@ public final class AssociationRequest implements Parcelable {
+ "provide the display name of the device");
}
return new AssociationRequest(mSingleDevice, emptyIfNull(mDeviceFilters),
- mDeviceProfile, mDisplayName, mSelfManaged, mForceConfirmation,
- null, null, -1L, false);
+ mDeviceProfile, mDisplayName, mSelfManaged, mForceConfirmation);
}
}
-
// Code below generated by codegen v1.0.23.
//
// DO NOT MODIFY!
@@ -395,88 +432,29 @@ public final class AssociationRequest implements Parcelable {
/**
- * Creates a new AssociationRequest.
- *
- * @param singleDevice
- * Whether only a single device should match the provided filter.
- *
- * When scanning for a single device with a specific {@link BluetoothDeviceFilter} mac
- * address, bonded devices are also searched among. This allows to obtain the necessary app
- * privileges even if the device is already paired.
- * @param deviceFilters
- * If set, only devices matching either of the given filters will be shown to the user
- * @param deviceProfile
- * Profile of the device.
- * @param displayName
- * The Display name of the device to be shown in the CDM confirmation UI. Must be non-null for
- * "self-managed" association.
- * @param selfManaged
- * Whether the association is to be managed by the companion application.
- * @param forceConfirmation
- * Indicates that the application would prefer the CompanionDeviceManager to collect an explicit
- * confirmation from the user before creating an association, even if such confirmation is not
- * required.
- * @param callingPackage
- * The app package making the request.
- *
- * Populated by the system.
- * @param deviceProfilePrivilegesDescription
- * The user-readable description of the device profile's privileges.
- *
- * Populated by the system.
- * @param creationTime
- * The time at which his request was created
- * @param skipPrompt
- * Whether the user-prompt may be skipped once the device is found.
+ * The app package name of the application the association will belong to.
+ * Populated by the system.
*
- * Populated by the system.
* @hide
*/
@DataClass.Generated.Member
- public AssociationRequest(
- boolean singleDevice,
- @NonNull List<DeviceFilter<?>> deviceFilters,
- @Nullable @DeviceProfile String deviceProfile,
- @Nullable CharSequence displayName,
- boolean selfManaged,
- boolean forceConfirmation,
- @Nullable String callingPackage,
- @Nullable String deviceProfilePrivilegesDescription,
- long creationTime,
- boolean skipPrompt) {
- this.mSingleDevice = singleDevice;
- this.mDeviceFilters = deviceFilters;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mDeviceFilters);
- this.mDeviceProfile = deviceProfile;
- com.android.internal.util.AnnotationValidations.validate(
- DeviceProfile.class, null, mDeviceProfile);
- this.mDisplayName = displayName;
- this.mSelfManaged = selfManaged;
- this.mForceConfirmation = forceConfirmation;
- this.mCallingPackage = callingPackage;
- this.mDeviceProfilePrivilegesDescription = deviceProfilePrivilegesDescription;
- this.mCreationTime = creationTime;
- this.mSkipPrompt = skipPrompt;
-
- onConstructed();
+ public @Nullable String getPackageName() {
+ return mPackageName;
}
/**
- * The app package making the request.
- *
+ * The UserId of the user the association will belong to.
* Populated by the system.
*
* @hide
*/
@DataClass.Generated.Member
- public @Nullable String getCallingPackage() {
- return mCallingPackage;
+ public @UserIdInt int getUserId() {
+ return mUserId;
}
/**
* The user-readable description of the device profile's privileges.
- *
* Populated by the system.
*
* @hide
@@ -498,7 +476,6 @@ public final class AssociationRequest implements Parcelable {
/**
* Whether the user-prompt may be skipped once the device is found.
- *
* Populated by the system.
*
* @hide
@@ -521,7 +498,8 @@ public final class AssociationRequest implements Parcelable {
"displayName = " + mDisplayName + ", " +
"selfManaged = " + mSelfManaged + ", " +
"forceConfirmation = " + mForceConfirmation + ", " +
- "callingPackage = " + mCallingPackage + ", " +
+ "packageName = " + mPackageName + ", " +
+ "userId = " + mUserId + ", " +
"deviceProfilePrivilegesDescription = " + mDeviceProfilePrivilegesDescription + ", " +
"creationTime = " + mCreationTime + ", " +
"skipPrompt = " + mSkipPrompt +
@@ -547,7 +525,8 @@ public final class AssociationRequest implements Parcelable {
&& Objects.equals(mDisplayName, that.mDisplayName)
&& mSelfManaged == that.mSelfManaged
&& mForceConfirmation == that.mForceConfirmation
- && Objects.equals(mCallingPackage, that.mCallingPackage)
+ && Objects.equals(mPackageName, that.mPackageName)
+ && mUserId == that.mUserId
&& Objects.equals(mDeviceProfilePrivilegesDescription, that.mDeviceProfilePrivilegesDescription)
&& mCreationTime == that.mCreationTime
&& mSkipPrompt == that.mSkipPrompt;
@@ -566,7 +545,8 @@ public final class AssociationRequest implements Parcelable {
_hash = 31 * _hash + Objects.hashCode(mDisplayName);
_hash = 31 * _hash + Boolean.hashCode(mSelfManaged);
_hash = 31 * _hash + Boolean.hashCode(mForceConfirmation);
- _hash = 31 * _hash + Objects.hashCode(mCallingPackage);
+ _hash = 31 * _hash + Objects.hashCode(mPackageName);
+ _hash = 31 * _hash + mUserId;
_hash = 31 * _hash + Objects.hashCode(mDeviceProfilePrivilegesDescription);
_hash = 31 * _hash + Long.hashCode(mCreationTime);
_hash = 31 * _hash + Boolean.hashCode(mSkipPrompt);
@@ -583,16 +563,17 @@ public final class AssociationRequest implements Parcelable {
if (mSingleDevice) flg |= 0x1;
if (mSelfManaged) flg |= 0x10;
if (mForceConfirmation) flg |= 0x20;
- if (mSkipPrompt) flg |= 0x200;
+ if (mSkipPrompt) flg |= 0x400;
if (mDeviceProfile != null) flg |= 0x4;
if (mDisplayName != null) flg |= 0x8;
- if (mCallingPackage != null) flg |= 0x40;
- if (mDeviceProfilePrivilegesDescription != null) flg |= 0x80;
+ if (mPackageName != null) flg |= 0x40;
+ if (mDeviceProfilePrivilegesDescription != null) flg |= 0x100;
dest.writeInt(flg);
dest.writeParcelableList(mDeviceFilters, flags);
if (mDeviceProfile != null) dest.writeString(mDeviceProfile);
if (mDisplayName != null) dest.writeCharSequence(mDisplayName);
- if (mCallingPackage != null) dest.writeString(mCallingPackage);
+ if (mPackageName != null) dest.writeString(mPackageName);
+ dest.writeInt(mUserId);
if (mDeviceProfilePrivilegesDescription != null) dest.writeString(mDeviceProfilePrivilegesDescription);
dest.writeLong(mCreationTime);
}
@@ -612,13 +593,14 @@ public final class AssociationRequest implements Parcelable {
boolean singleDevice = (flg & 0x1) != 0;
boolean selfManaged = (flg & 0x10) != 0;
boolean forceConfirmation = (flg & 0x20) != 0;
- boolean skipPrompt = (flg & 0x200) != 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 callingPackage = (flg & 0x40) == 0 ? null : in.readString();
- String deviceProfilePrivilegesDescription = (flg & 0x80) == 0 ? null : in.readString();
+ String packageName = (flg & 0x40) == 0 ? null : in.readString();
+ int userId = in.readInt();
+ String deviceProfilePrivilegesDescription = (flg & 0x100) == 0 ? null : in.readString();
long creationTime = in.readLong();
this.mSingleDevice = singleDevice;
@@ -631,12 +613,15 @@ public final class AssociationRequest implements Parcelable {
this.mDisplayName = displayName;
this.mSelfManaged = selfManaged;
this.mForceConfirmation = forceConfirmation;
- this.mCallingPackage = callingPackage;
+ this.mPackageName = packageName;
+ this.mUserId = userId;
+ com.android.internal.util.AnnotationValidations.validate(
+ UserIdInt.class, null, mUserId);
this.mDeviceProfilePrivilegesDescription = deviceProfilePrivilegesDescription;
this.mCreationTime = creationTime;
this.mSkipPrompt = skipPrompt;
- onConstructed();
+ // onConstructed(); // You can define this method to get a callback
}
@DataClass.Generated.Member
@@ -654,10 +639,10 @@ public final class AssociationRequest implements Parcelable {
};
@DataClass.Generated(
- time = 1638368698639L,
+ time = 1638962248060L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/companion/AssociationRequest.java",
- inputSignatures = "public static final java.lang.String DEVICE_PROFILE_WATCH\npublic static final @android.annotation.RequiresPermission @android.annotation.SystemApi java.lang.String DEVICE_PROFILE_APP_STREAMING\npublic static final @android.annotation.RequiresPermission @android.annotation.SystemApi java.lang.String DEVICE_PROFILE_AUTOMOTIVE_PROJECTION\nprivate final boolean mSingleDevice\nprivate final @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate final @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate final @android.annotation.Nullable java.lang.CharSequence mDisplayName\nprivate final boolean mSelfManaged\nprivate final boolean mForceConfirmation\nprivate @android.annotation.Nullable java.lang.String mCallingPackage\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\nprivate long mCreationTime\nprivate boolean mSkipPrompt\npublic @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String getDeviceProfile()\npublic @android.annotation.Nullable java.lang.CharSequence getDisplayName()\npublic @android.annotation.SystemApi @android.annotation.RequiresPermission boolean isSelfManaged()\npublic @android.annotation.SystemApi @android.annotation.RequiresPermission boolean isForceConfirmation()\npublic boolean isSingleDevice()\npublic void setCallingPackage(java.lang.String)\npublic void setDeviceProfilePrivilegesDescription(java.lang.String)\npublic void setSkipPrompt(boolean)\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nprivate void onConstructed()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.CharSequence mDisplayName\nprivate boolean mSelfManaged\nprivate boolean mForceConfirmation\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDisplayName(java.lang.CharSequence)\npublic @android.annotation.SystemApi @android.annotation.RequiresPermission @android.annotation.NonNull android.companion.AssociationRequest.Builder setSelfManaged(boolean)\npublic @android.annotation.SystemApi @android.annotation.RequiresPermission @android.annotation.NonNull android.companion.AssociationRequest.Builder setForceConfirmation(boolean)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genHiddenConstructor=true, genBuilder=false, genConstDefs=false)")
+ inputSignatures = "public static final java.lang.String DEVICE_PROFILE_WATCH\npublic static final @android.annotation.RequiresPermission @android.annotation.SystemApi java.lang.String DEVICE_PROFILE_APP_STREAMING\npublic static final @android.annotation.RequiresPermission @android.annotation.SystemApi java.lang.String DEVICE_PROFILE_AUTOMOTIVE_PROJECTION\nprivate final boolean mSingleDevice\nprivate final @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate final @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate final @android.annotation.Nullable java.lang.CharSequence mDisplayName\nprivate final boolean mSelfManaged\nprivate final boolean mForceConfirmation\nprivate @android.annotation.Nullable java.lang.String mPackageName\nprivate @android.annotation.UserIdInt int mUserId\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\nprivate final long mCreationTime\nprivate boolean mSkipPrompt\npublic @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String getDeviceProfile()\npublic @android.annotation.Nullable java.lang.CharSequence getDisplayName()\npublic @android.annotation.SystemApi @android.annotation.RequiresPermission boolean isSelfManaged()\npublic @android.annotation.SystemApi @android.annotation.RequiresPermission boolean isForceConfirmation()\npublic boolean isSingleDevice()\npublic void setPackageName(java.lang.String)\npublic void setUserId(int)\npublic void setDeviceProfilePrivilegesDescription(java.lang.String)\npublic void setSkipPrompt(boolean)\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.CharSequence mDisplayName\nprivate boolean mSelfManaged\nprivate boolean mForceConfirmation\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDisplayName(java.lang.CharSequence)\npublic @android.annotation.SystemApi @android.annotation.RequiresPermission @android.annotation.NonNull android.companion.AssociationRequest.Builder setSelfManaged(boolean)\npublic @android.annotation.SystemApi @android.annotation.RequiresPermission @android.annotation.NonNull android.companion.AssociationRequest.Builder setForceConfirmation(boolean)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genConstructor=false, genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genConstDefs=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/companion/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/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index 6ae2bb5b642a..157e709a67f0 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -22,6 +22,7 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.ActivityThread;
+import android.app.AppGlobals;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
@@ -191,10 +192,42 @@ public final class AttributionSource implements Parcelable {
return new ScopedParcelState(this);
}
- /** @hide */
- public static AttributionSource myAttributionSource() {
- return new AttributionSource(Process.myUid(), ActivityThread.currentOpPackageName(),
- /*attributionTag*/ null, (String[]) /*renouncedPermissions*/ null, /*next*/ null);
+ /**
+ * Returns a generic {@link AttributionSource} that represents the entire
+ * calling process.
+ *
+ * <p>Callers are <em>strongly</em> encouraged to use a more specific
+ * attribution source whenever possible, such as from
+ * {@link Context#getAttributionSource()}, since that enables developers to
+ * have more detailed and scoped control over attribution within
+ * sub-components of their app.
+ *
+ * @see Context#createAttributionContext(String)
+ * @see Context#getAttributionTag()
+ * @return a generic {@link AttributionSource} representing the entire
+ * calling process
+ * @throws IllegalStateException when no accurate {@link AttributionSource}
+ * can be determined
+ */
+ public static @NonNull AttributionSource myAttributionSource() {
+
+ final AttributionSource globalSource = ActivityThread.currentAttributionSource();
+ if (globalSource != null) {
+ return globalSource;
+ }
+
+ int uid = Process.myUid();
+ if (uid == Process.ROOT_UID) {
+ uid = Process.SYSTEM_UID;
+ }
+ try {
+ return new AttributionSource.Builder(uid)
+ .setPackageName(AppGlobals.getPackageManager().getPackagesForUid(uid)[0])
+ .build();
+ } catch (Exception ignored) {
+ }
+
+ throw new IllegalStateException("Failed to resolve AttributionSource");
}
/**
@@ -247,7 +280,7 @@ public final class AttributionSource implements Parcelable {
* whether the attribution source is one for the calling app to prevent the caller
* to pass you a source from another app without including themselves in the
* attribution chain.
- *f
+ *
* @return if the attribution source cannot be trusted to be from the caller.
*/
public boolean checkCallingUid() {
diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java
index f335ae483bf7..d46a0c67341f 100644
--- a/core/java/android/content/BroadcastReceiver.java
+++ b/core/java/android/content/BroadcastReceiver.java
@@ -392,7 +392,7 @@ public abstract class BroadcastReceiver {
PendingResult res = mPendingResult;
mPendingResult = null;
- if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+ if (res != null && Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
res.mReceiverClassName = getClass().getName();
Trace.traceCounter(Trace.TRACE_TAG_ACTIVITY_MANAGER,
"BroadcastReceiver#goAsync#ClassName:" + res.mReceiverClassName,
diff --git a/core/java/android/content/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 543239bef101..6d13712b68ae 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4790,6 +4790,15 @@ public abstract class Context {
/**
* Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.view.selectiontoolbar.SelectionToolbarManager} for selection toolbar service.
+ *
+ * @see #getSystemService(String)
+ * @hide
+ */
+ public static final String SELECTION_TOOLBAR_SERVICE = "selection_toolbar";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.graphics.fonts.FontManager} for font services.
*
* @see #getSystemService(String)
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 983d0cc4b088..1e6029f951fc 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -5915,6 +5915,14 @@ public class Intent implements Parcelable, Cloneable {
public static final String EXTRA_UID = "android.intent.extra.UID";
/**
+ * Used as an optional int extra field in {@link android.content.Intent#ACTION_PACKAGE_ADDED}
+ * intents to supply the previous uid the package had been assigned.
+ * This would only be set when a package is leaving sharedUserId in an upgrade, or when a
+ * system app upgrade that had left sharedUserId is getting uninstalled.
+ */
+ public static final String EXTRA_PREVIOUS_UID = "android.intent.extra.PREVIOUS_UID";
+
+ /**
* @hide String array of package names.
*/
@SystemApi
@@ -5946,6 +5954,16 @@ public class Intent implements Parcelable, Cloneable {
public static final String EXTRA_REPLACING = "android.intent.extra.REPLACING";
/**
+ * Used as a boolean extra field in {@link android.content.Intent#ACTION_PACKAGE_REMOVED},
+ * {@link android.content.Intent#ACTION_UID_REMOVED}, and
+ * {@link android.content.Intent#ACTION_PACKAGE_ADDED}
+ * intents to indicate that this package is changing its UID.
+ * This would only be set when a package is leaving sharedUserId in an upgrade, or when a
+ * system app upgrade that had left sharedUserId is getting uninstalled.
+ */
+ public static final String EXTRA_UID_CHANGING = "android.intent.extra.UID_CHANGING";
+
+ /**
* Used as an int extra field in {@link android.app.AlarmManager} pending intents
* to tell the application being invoked how many pending alarms are being
* delivered with the intent. For one-shot alarms this will always be 1.
diff --git a/core/java/android/content/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/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 2998f764a2f3..76b4e5c4dba0 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -33,6 +33,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
import android.os.storage.StorageManager;
+import android.util.ArrayMap;
import android.util.Printer;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
@@ -1533,6 +1534,15 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
private int mHiddenApiPolicy = HIDDEN_API_ENFORCEMENT_DEFAULT;
+ /**
+ * A map from a process name to an (custom) application class name in this package, derived
+ * from the <processes> tag in the app's manifest. This map may not contain all the process
+ * names. Processses not in this map will use the default app class name,
+ * which is {@link #className}, or the default class {@link android.app.Application}.
+ */
+ @Nullable
+ private ArrayMap<String, String> mAppClassNamesByProcess;
+
public void dump(Printer pw, String prefix) {
dump(pw, prefix, DUMP_FLAG_ALL);
}
@@ -1540,8 +1550,14 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
/** @hide */
public void dump(Printer pw, String prefix, int dumpFlags) {
super.dumpFront(pw, prefix);
- if ((dumpFlags & DUMP_FLAG_DETAILS) != 0 && className != null) {
- pw.println(prefix + "className=" + className);
+ if ((dumpFlags & DUMP_FLAG_DETAILS) != 0) {
+ if (className != null) {
+ pw.println(prefix + "className=" + className);
+ }
+ for (int i = 0; i < ArrayUtils.size(mAppClassNamesByProcess); i++) {
+ pw.println(prefix + " process=" + mAppClassNamesByProcess.keyAt(i)
+ + " className=" + mAppClassNamesByProcess.valueAt(i));
+ }
}
if (permission != null) {
pw.println(prefix + "permission=" + permission);
@@ -1967,6 +1983,16 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
dest.writeInt(nativeHeapZeroInitialized);
sForBoolean.parcel(requestRawExternalStorageAccess, dest, parcelableFlags);
dest.writeLong(createTimestamp);
+ if (mAppClassNamesByProcess == null) {
+ dest.writeInt(0);
+ } else {
+ final int size = mAppClassNamesByProcess.size();
+ dest.writeInt(size);
+ for (int i = 0; i < size; i++) {
+ dest.writeString(mAppClassNamesByProcess.keyAt(i));
+ dest.writeString(mAppClassNamesByProcess.valueAt(i));
+ }
+ }
}
public static final @android.annotation.NonNull Parcelable.Creator<ApplicationInfo> CREATOR
@@ -2055,6 +2081,13 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
nativeHeapZeroInitialized = source.readInt();
requestRawExternalStorageAccess = sForBoolean.unparcel(source);
createTimestamp = source.readLong();
+ final int allClassesSize = source.readInt();
+ if (allClassesSize > 0) {
+ mAppClassNamesByProcess = new ArrayMap<>(allClassesSize);
+ for (int i = 0; i < allClassesSize; i++) {
+ mAppClassNamesByProcess.put(source.readString(), source.readString());
+ }
+ }
}
/**
@@ -2538,6 +2571,19 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
requestRawExternalStorageAccess = value;
}
+ /**
+ * Replaces {@link #mAppClassNamesByProcess}. This takes over the ownership of the passed map.
+ * Do not modify the argument at the callsite.
+ * {@hide}
+ */
+ public void setAppClassNamesByProcess(@Nullable ArrayMap<String, String> value) {
+ if (ArrayUtils.size(value) == 0) {
+ mAppClassNamesByProcess = null;
+ } else {
+ mAppClassNamesByProcess = value;
+ }
+ }
+
/** {@hide} */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public String getCodePath() { return scanSourceDir; }
@@ -2568,4 +2614,21 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
public int getNativeHeapZeroInitialized() {
return nativeHeapZeroInitialized;
}
+
+ /**
+ * Return the application class name defined in the manifest. The class name set in the
+ * <processes> tag for this process, then return it. Otherwise it'll return the class
+ * name set in the <application> tag. If neither is set, it'll return null.
+ * @hide
+ */
+ @Nullable
+ public String getCustomApplicationClassNameForProcess(String processName) {
+ if (mAppClassNamesByProcess != null) {
+ String byProcess = mAppClassNamesByProcess.get(processName);
+ if (byProcess != null) {
+ return byProcess;
+ }
+ }
+ return className;
+ }
}
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 9310ac5b8583..251d5e8b6134 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);
}
@@ -2533,13 +2533,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/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index e2c91a4b1bea..f31f78fb81f3 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -7297,7 +7297,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();
}
@@ -7305,19 +7305,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);
@@ -7327,10 +7327,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;
}
@@ -7364,7 +7364,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;
}
@@ -7385,7 +7385,7 @@ public class PackageParser {
}
mSharedUserLabel = dest.readInt();
- mSigningDetails = dest.readParcelable(boot);
+ mSigningDetails = dest.readParcelable(boot, android.content.pm.PackageParser.SigningDetails.class);
mPreferredOrder = dest.readInt();
@@ -7397,19 +7397,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;
}
@@ -7806,13 +7806,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>() {
@@ -7867,7 +7867,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>() {
@@ -8160,7 +8160,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();
@@ -8254,7 +8254,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;
@@ -8344,7 +8344,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) {
@@ -8436,7 +8436,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 4ba2ee65f221..3443c757e6bc 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -134,8 +134,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 a264bebb5d88..7d4f7ecef29c 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -352,6 +352,16 @@ public final class ShortcutInfo implements Parcelable {
return disabledReason >= DISABLED_REASON_RESTORE_ISSUE_START;
}
+ /** @hide */
+ @IntDef(flag = true, value = {SURFACE_LAUNCHER})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Surface {}
+
+ /**
+ * Indicates system surfaces managed by a launcher app. e.g. Long-Press Menu.
+ */
+ public static final int SURFACE_LAUNCHER = 1 << 0;
+
/**
* Shortcut category for messaging related actions, such as chat.
*/
@@ -451,6 +461,8 @@ public final class ShortcutInfo implements Parcelable {
@Nullable private String mStartingThemeResName;
+ private int mExcludedSurfaces;
+
private ShortcutInfo(Builder b) {
mUserId = b.mContext.getUserId();
@@ -474,6 +486,7 @@ public final class ShortcutInfo implements Parcelable {
if (b.mIsLongLived) {
setLongLived();
}
+ mExcludedSurfaces = b.mExcludedSurfaces;
mRank = b.mRank;
mExtras = b.mExtras;
mLocusId = b.mLocusId;
@@ -587,6 +600,7 @@ public final class ShortcutInfo implements Parcelable {
mLastChangedTimestamp = source.mLastChangedTimestamp;
mDisabledReason = source.mDisabledReason;
mLocusId = source.mLocusId;
+ mExcludedSurfaces = source.mExcludedSurfaces;
// Just always keep it since it's cheep.
mIconResId = source.mIconResId;
@@ -1025,6 +1039,8 @@ public final class ShortcutInfo implements Parcelable {
private int mStartingThemeResId;
+ private int mExcludedSurfaces;
+
/**
* Old style constructor.
* @hide
@@ -1385,6 +1401,22 @@ public final class ShortcutInfo implements Parcelable {
}
/**
+ * Sets which surfaces a shortcut will be excluded from.
+ *
+ * If the shortcut is set to be excluded from {@link #SURFACE_LAUNCHER}, shortcuts will be
+ * excluded from the search result of {@link android.content.pm.LauncherApps#getShortcuts(
+ * android.content.pm.LauncherApps.ShortcutQuery, UserHandle)} nor
+ * {@link android.content.pm.ShortcutManager#getShortcuts(int)}. This generally means the
+ * shortcut would not be displayed by a launcher app (e.g. in Long-Press menu), while
+ * remain visible in other surfaces such as assistant or on-device-intelligence.
+ */
+ @NonNull
+ public Builder setExcludedFromSurfaces(final int surfaces) {
+ mExcludedSurfaces = surfaces;
+ return this;
+ }
+
+ /**
* Creates a {@link ShortcutInfo} instance.
*/
@NonNull
@@ -2137,13 +2169,20 @@ public final class ShortcutInfo implements Parcelable {
mCategories = cloneCategories(categories);
}
+ /**
+ * Return true if the shortcut is included in specified surface.
+ */
+ public boolean isIncludedIn(@Surface int surface) {
+ return (mExcludedSurfaces & surface) == 0;
+ }
+
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();
@@ -2153,7 +2192,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();
@@ -2163,7 +2202,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();
@@ -2182,9 +2221,10 @@ 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();
}
@Override
@@ -2237,6 +2277,7 @@ public final class ShortcutInfo implements Parcelable {
dest.writeParcelable(mLocusId, flags);
dest.writeString8(mIconUri);
dest.writeString8(mStartingThemeResName);
+ dest.writeInt(mExcludedSurfaces);
}
public static final @NonNull Creator<ShortcutInfo> CREATOR =
@@ -2346,6 +2387,9 @@ public final class ShortcutInfo implements Parcelable {
if (isLongLived()) {
sb.append("Liv");
}
+ if (!isIncludedIn(SURFACE_LAUNCHER)) {
+ sb.append("Hid-L");
+ }
sb.append("]");
addIndentOrComma(sb, indent);
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/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 2fa5df72656b..fc9f1a55d84c 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -118,6 +118,7 @@ public interface ParsingPackage extends ParsingPackageRead {
ParsingPackage addQueriesProvider(String authority);
+ /** Sets a process name -> {@link ParsedProcess} map coming from the <processes> tag. */
ParsingPackage setProcesses(@NonNull Map<String, ParsedProcess> processes);
ParsingPackage asSplit(
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index f03ab6ab94f2..dbd3d5c4a7e7 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -62,6 +62,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.storage.StorageManager;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Pair;
import android.util.SparseArray;
@@ -297,6 +298,9 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden,
// @DataClass.ParcelWith(ParsingUtils.StringPairListParceler.class)
private List<Pair<String, ParsedIntentInfo>> preferredActivityFilters = emptyList();
+ /**
+ * Map from a process name to a {@link ParsedProcess}.
+ */
@NonNull
private Map<String, ParsedProcess> processes = emptyMap();
@@ -1131,10 +1135,46 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden,
appInfo.setSplitCodePaths(splitCodePaths);
appInfo.setSplitResourcePaths(splitCodePaths);
appInfo.setVersionCode(mLongVersionCode);
+ appInfo.setAppClassNamesByProcess(buildAppClassNamesByProcess());
return appInfo;
}
+ /**
+ * Create a map from a process name to the custom application class for this process,
+ * which comes from <processes><process android:name="xxx">.
+ *
+ * The original information is stored in {@link #processes}, but it's stored in
+ * a form of: [process name] -[1:N]-> [package name] -[1:N]-> [class name].
+ * We scan it and collect the process names and their app class names, only for this package.
+ *
+ * The resulting map only contains processes with a custom application class set.
+ */
+ @Nullable
+ private ArrayMap<String, String> buildAppClassNamesByProcess() {
+ if (processes == null) {
+ return null;
+ }
+ final ArrayMap<String, String> ret = new ArrayMap<>(4);
+ for (String processName : processes.keySet()) {
+ final ParsedProcess process = processes.get(processName);
+ final ArrayMap<String, String> appClassesByPackage =
+ process.getAppClassNamesByPackage();
+
+ for (int i = 0; i < appClassesByPackage.size(); i++) {
+ final String packageName = appClassesByPackage.keyAt(i);
+
+ if (this.packageName.equals(packageName)) {
+ final String appClassName = appClassesByPackage.valueAt(i);
+ if (!TextUtils.isEmpty(appClassName)) {
+ ret.put(processName, appClassName);
+ }
+ }
+ }
+ }
+ return ret;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -1368,7 +1408,7 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden,
this.processes = in.readHashMap(boot);
this.metaData = in.readBundle(boot);
this.volumeUuid = sForInternedString.unparcel(in);
- this.signingDetails = in.readParcelable(boot);
+ this.signingDetails = in.readParcelable(boot, android.content.pm.SigningDetails.class);
this.mPath = in.readString();
this.queriesIntents = in.createTypedArrayList(Intent.CREATOR);
this.queriesPackages = sForInternedStringList.unparcel(in);
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index fb24cb2ef3bd..16deaa05afce 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -3511,7 +3511,7 @@ public class ParsingPackageUtils {
ArraySet<PublicKey> keys = new ArraySet<>(M);
for (int j = 0; j < M; ++j) {
- PublicKey pk = (PublicKey) in.readSerializable();
+ PublicKey pk = (PublicKey) in.readSerializable(java.security.PublicKey.class.getClassLoader(), java.security.PublicKey.class);
keys.add(pk);
}
diff --git a/core/java/android/content/pm/parsing/ParsingUtils.java b/core/java/android/content/pm/parsing/ParsingUtils.java
index cce984eb93a7..6dfb268c3d64 100644
--- a/core/java/android/content/pm/parsing/ParsingUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingUtils.java
@@ -138,7 +138,7 @@ public class ParsingUtils {
final List<Pair<String, ParsedIntentInfo>> list = new ArrayList<>(size);
for (int i = 0; i < size; ++i) {
list.add(Pair.create(source.readString(), source.readParcelable(
- ParsedIntentInfoImpl.class.getClassLoader())));
+ ParsedIntentInfoImpl.class.getClassLoader(), android.content.pm.parsing.component.ParsedIntentInfo.class)));
}
return list;
diff --git a/core/java/android/content/pm/parsing/component/ParsedPermissionImpl.java b/core/java/android/content/pm/parsing/component/ParsedPermissionImpl.java
index 2145e441553a..45038cf225fb 100644
--- a/core/java/android/content/pm/parsing/component/ParsedPermissionImpl.java
+++ b/core/java/android/content/pm/parsing/component/ParsedPermissionImpl.java
@@ -116,7 +116,7 @@ public class ParsedPermissionImpl extends ParsedComponentImpl implements ParsedP
this.requestRes = in.readInt();
this.protectionLevel = in.readInt();
this.tree = in.readBoolean();
- this.parsedPermissionGroup = in.readParcelable(boot);
+ this.parsedPermissionGroup = in.readParcelable(boot, android.content.pm.parsing.component.ParsedPermissionGroup.class);
this.knownCerts = sForStringSet.unparcel(in);
}
diff --git a/core/java/android/content/pm/parsing/component/ParsedProcess.java b/core/java/android/content/pm/parsing/component/ParsedProcess.java
index c2d5163cfcc1..27a540d25891 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProcess.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProcess.java
@@ -19,6 +19,7 @@ package android.content.pm.parsing.component;
import android.annotation.NonNull;
import android.content.pm.ApplicationInfo;
import android.os.Parcelable;
+import android.util.ArrayMap;
import java.util.Set;
@@ -37,6 +38,15 @@ public interface ParsedProcess extends Parcelable {
@NonNull
String getName();
+ /**
+ * The app class names in this (potentially shared) process, from a package name to
+ * the application class name.
+ * It's a map, because in shared processes, different packages can have different application
+ * classes.
+ */
+ @NonNull
+ ArrayMap<String, String> getAppClassNamesByPackage();
+
@ApplicationInfo.NativeHeapZeroInitialized
int getNativeHeapZeroInitialized();
}
diff --git a/core/java/android/content/pm/parsing/component/ParsedProcessImpl.java b/core/java/android/content/pm/parsing/component/ParsedProcessImpl.java
index 3fd60eb49576..d404ecfd38c7 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProcessImpl.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProcessImpl.java
@@ -22,6 +22,7 @@ import android.annotation.NonNull;
import android.content.pm.ApplicationInfo;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.ArrayMap;
import android.util.ArraySet;
import com.android.internal.annotations.VisibleForTesting;
@@ -39,6 +40,11 @@ public class ParsedProcessImpl implements ParsedProcess {
@NonNull
private String name;
+
+ /** @see ParsedProcess#getAppClassNamesByPackage() */
+ @NonNull
+ private ArrayMap<String, String> appClassNamesByPackage = ArrayMap.EMPTY;
+
@NonNull
@DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedStringSet.class)
private Set<String> deniedPermissions = emptySet();
@@ -55,6 +61,8 @@ public class ParsedProcessImpl implements ParsedProcess {
public ParsedProcessImpl(@NonNull ParsedProcess other) {
name = other.getName();
+ appClassNamesByPackage = (other.getAppClassNamesByPackage().size() == 0)
+ ? ArrayMap.EMPTY : new ArrayMap<>(other.getAppClassNamesByPackage());
deniedPermissions = new ArraySet<>(other.getDeniedPermissions());
gwpAsanMode = other.getGwpAsanMode();
memtagMode = other.getMemtagMode();
@@ -66,6 +74,21 @@ public class ParsedProcessImpl implements ParsedProcess {
gwpAsanMode = other.getGwpAsanMode();
memtagMode = other.getMemtagMode();
nativeHeapZeroInitialized = other.getNativeHeapZeroInitialized();
+
+ final ArrayMap<String, String> oacn = other.getAppClassNamesByPackage();
+ for (int i = 0; i < oacn.size(); i++) {
+ appClassNamesByPackage.put(oacn.keyAt(i), oacn.valueAt(i));
+ }
+ }
+
+ /**
+ * Sets a custom application name used in this process for a given package.
+ */
+ public void putAppClassNameForPackage(String packageName, String className) {
+ if (appClassNamesByPackage.size() == 0) {
+ appClassNamesByPackage = new ArrayMap<>(4);
+ }
+ appClassNamesByPackage.put(packageName, className);
}
@@ -83,9 +106,14 @@ public class ParsedProcessImpl implements ParsedProcess {
//@formatter:off
+ /**
+ * Creates a new ParsedProcessImpl.
+ *
+ */
@DataClass.Generated.Member
public ParsedProcessImpl(
@NonNull String name,
+ @NonNull ArrayMap<String,String> appClassNamesByPackage,
@NonNull Set<String> deniedPermissions,
@ApplicationInfo.GwpAsanMode int gwpAsanMode,
@ApplicationInfo.MemtagMode int memtagMode,
@@ -93,6 +121,9 @@ public class ParsedProcessImpl implements ParsedProcess {
this.name = name;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, name);
+ this.appClassNamesByPackage = appClassNamesByPackage;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, appClassNamesByPackage);
this.deniedPermissions = deniedPermissions;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, deniedPermissions);
@@ -114,6 +145,14 @@ public class ParsedProcessImpl implements ParsedProcess {
return name;
}
+ /**
+ * @see ParsedProcess#getAppClassNamesByPackage()
+ */
+ @DataClass.Generated.Member
+ public @NonNull ArrayMap<String,String> getAppClassNamesByPackage() {
+ return appClassNamesByPackage;
+ }
+
@DataClass.Generated.Member
public @NonNull Set<String> getDeniedPermissions() {
return deniedPermissions;
@@ -142,6 +181,17 @@ public class ParsedProcessImpl implements ParsedProcess {
return this;
}
+ /**
+ * @see ParsedProcess#getAppClassNamesByPackage()
+ */
+ @DataClass.Generated.Member
+ public @NonNull ParsedProcessImpl setAppClassNamesByPackage(@NonNull ArrayMap<String,String> value) {
+ appClassNamesByPackage = value;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, appClassNamesByPackage);
+ return this;
+ }
+
@DataClass.Generated.Member
public @NonNull ParsedProcessImpl setDeniedPermissions(@NonNull Set<String> value) {
deniedPermissions = value;
@@ -192,6 +242,7 @@ public class ParsedProcessImpl implements ParsedProcess {
// void parcelFieldName(Parcel dest, int flags) { ... }
dest.writeString(name);
+ dest.writeMap(appClassNamesByPackage);
sParcellingForDeniedPermissions.parcel(deniedPermissions, dest, flags);
dest.writeInt(gwpAsanMode);
dest.writeInt(memtagMode);
@@ -210,6 +261,8 @@ public class ParsedProcessImpl implements ParsedProcess {
// static FieldType unparcelFieldName(Parcel in) { ... }
String _name = in.readString();
+ ArrayMap<String,String> _appClassNamesByPackage = new ArrayMap();
+ in.readMap(_appClassNamesByPackage, String.class.getClassLoader());
Set<String> _deniedPermissions = sParcellingForDeniedPermissions.unparcel(in);
int _gwpAsanMode = in.readInt();
int _memtagMode = in.readInt();
@@ -218,6 +271,9 @@ public class ParsedProcessImpl implements ParsedProcess {
this.name = _name;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, name);
+ this.appClassNamesByPackage = _appClassNamesByPackage;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, appClassNamesByPackage);
this.deniedPermissions = _deniedPermissions;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, deniedPermissions);
@@ -249,10 +305,10 @@ public class ParsedProcessImpl implements ParsedProcess {
};
@DataClass.Generated(
- time = 1627605368434L,
+ time = 1639076603310L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedProcessImpl.java",
- inputSignatures = "private @android.annotation.NonNull java.lang.String name\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\nprivate @android.content.pm.ApplicationInfo.GwpAsanMode int gwpAsanMode\nprivate @android.content.pm.ApplicationInfo.MemtagMode int memtagMode\nprivate @android.content.pm.ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized\npublic void addStateFrom(android.content.pm.parsing.component.ParsedProcess)\nclass ParsedProcessImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedProcess]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genParcelable=true, genAidl=false, genBuilder=false)")
+ inputSignatures = "private @android.annotation.NonNull java.lang.String name\nprivate @android.annotation.NonNull android.util.ArrayMap<java.lang.String,java.lang.String> appClassNamesByPackage\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\nprivate @android.content.pm.ApplicationInfo.GwpAsanMode int gwpAsanMode\nprivate @android.content.pm.ApplicationInfo.MemtagMode int memtagMode\nprivate @android.content.pm.ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized\npublic void addStateFrom(android.content.pm.parsing.component.ParsedProcess)\npublic void putAppClassNameForPackage(java.lang.String,java.lang.String)\nclass ParsedProcessImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedProcess]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genParcelable=true, genAidl=false, genBuilder=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java b/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
index cf83586a5dd5..5e4cf661b194 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
@@ -97,7 +97,12 @@ public class ParsedProcessUtils {
return input.error(processNameResult);
}
+ String packageName = pkg.getPackageName();
+ String className = ParsingUtils.buildClassName(packageName,
+ sa.getNonConfigurationString(R.styleable.AndroidManifestProcess_name, 0));
+
proc.setName(processNameResult.getResult());
+ proc.putAppClassNameForPackage(packageName, className);
proc.setGwpAsanMode(sa.getInt(R.styleable.AndroidManifestProcess_gwpAsanMode, -1));
proc.setMemtagMode(sa.getInt(R.styleable.AndroidManifestProcess_memtagMode, -1));
if (sa.hasValue(R.styleable.AndroidManifestProcess_nativeHeapZeroInitialized)) {
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 24c6a5a12178..bfd9fd0a4ef9 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -792,6 +792,21 @@ public final class AssetManager implements AutoCloseable {
}
/**
+ * To get the parent theme resource id according to the parameter theme resource id.
+ * @param resId theme resource id.
+ * @return the parent theme resource id.
+ * @hide
+ */
+ @StyleRes
+ int getParentThemeIdentifier(@StyleRes int resId) {
+ synchronized (this) {
+ ensureValidLocked();
+ // name is checked in JNI.
+ return nativeGetParentThemeIdentifier(mObject, resId);
+ }
+ }
+
+ /**
* Enable resource resolution logging to track the steps taken to resolve the last resource
* entry retrieved. Stores the configuration and package names for each step.
*
@@ -1600,6 +1615,8 @@ public final class AssetManager implements AutoCloseable {
private static native void nativeThemeDump(long ptr, long themePtr, int priority, String tag,
String prefix);
static native @NativeConfig int nativeThemeGetChangingConfigurations(long themePtr);
+ @StyleRes
+ private static native int nativeGetParentThemeIdentifier(long ptr, @StyleRes int styleId);
// AssetInputStream related native methods.
private static native void nativeAssetDestroy(long assetPtr);
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 632d8e5e291c..cb53a2a7a7b8 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -80,6 +80,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
@@ -1507,6 +1508,12 @@ public class Resources {
* retrieve XML attributes with style and theme information applied.
*/
public final class Theme {
+ /**
+ * To trace parent themes needs to prevent a cycle situation.
+ * e.x. A's parent is B, B's parent is C, and C's parent is A.
+ */
+ private static final int MAX_NUMBER_OF_TRACING_PARENT_THEME = 100;
+
private final Object mLock = new Object();
@GuardedBy("mLock")
@@ -1800,6 +1807,13 @@ public class Resources {
}
}
+ @StyleRes
+ /*package*/ int getParentThemeIdentifier(@StyleRes int resId) {
+ synchronized (mLock) {
+ return mThemeImpl.getParentThemeIdentifier(resId);
+ }
+ }
+
/**
* @hide
*/
@@ -1942,6 +1956,35 @@ public class Resources {
final Theme other = (Theme) o;
return getKey().equals(other.getKey());
}
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append('{');
+ int themeResId = getAppliedStyleResId();
+ int i = 0;
+ sb.append("InheritanceMap=[");
+ while (themeResId > 0) {
+ if (i > MAX_NUMBER_OF_TRACING_PARENT_THEME) {
+ sb.append(",...");
+ break;
+ }
+
+ if (i > 0) {
+ sb.append(", ");
+ }
+ sb.append("id=0x").append(Integer.toHexString(themeResId));
+ sb.append(getResourcePackageName(themeResId))
+ .append(":").append(getResourceTypeName(themeResId))
+ .append("/").append(getResourceEntryName(themeResId));
+
+ i++;
+ themeResId = getParentThemeIdentifier(themeResId);
+ }
+ sb.append("], Themes=").append(Arrays.deepToString(getTheme()));
+ sb.append('}');
+ return sb.toString();
+ }
}
static class ThemeKey implements Cloneable {
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index b9f93b85f0bf..4d850b0ccfd5 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -1310,6 +1310,14 @@ public class ResourcesImpl {
return mThemeResId;
}
+ @StyleRes
+ /*package*/ int getParentThemeIdentifier(@StyleRes int resId) {
+ if (resId > 0) {
+ return mAssets.getParentThemeIdentifier(resId);
+ }
+ return 0;
+ }
+
void applyStyle(int resId, boolean force) {
mAssets.applyStyleToTheme(mTheme, resId, force);
mThemeResId = resId;
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index d6f55d65ead5..9eb9cd51284a 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -527,11 +527,12 @@ public class TypedArray implements AutoCloseable {
final TypedValue value = mValue;
getValueAt(index, value);
throw new UnsupportedOperationException(
- "Failed to resolve attribute at index " + attrIndex + ": " + value);
+ "Failed to resolve attribute at index " + attrIndex + ": " + value
+ + ", theme=" + mTheme);
}
throw new UnsupportedOperationException("Can't convert value at index " + attrIndex
- + " to color: type=0x" + Integer.toHexString(type));
+ + " to color: type=0x" + Integer.toHexString(type) + ", theme=" + mTheme);
}
/**
@@ -561,7 +562,8 @@ public class TypedArray implements AutoCloseable {
if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
if (value.type == TypedValue.TYPE_ATTRIBUTE) {
throw new UnsupportedOperationException(
- "Failed to resolve attribute at index " + index + ": " + value);
+ "Failed to resolve attribute at index " + index + ": " + value
+ + ", theme=" + mTheme);
}
return mResources.loadComplexColor(value, value.resourceId, mTheme);
}
@@ -596,7 +598,8 @@ public class TypedArray implements AutoCloseable {
if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
if (value.type == TypedValue.TYPE_ATTRIBUTE) {
throw new UnsupportedOperationException(
- "Failed to resolve attribute at index " + index + ": " + value);
+ "Failed to resolve attribute at index " + index + ": " + value
+ + ", theme=" + mTheme);
}
return mResources.loadColorStateList(value, value.resourceId, mTheme);
}
@@ -637,11 +640,12 @@ public class TypedArray implements AutoCloseable {
final TypedValue value = mValue;
getValueAt(index, value);
throw new UnsupportedOperationException(
- "Failed to resolve attribute at index " + attrIndex + ": " + value);
+ "Failed to resolve attribute at index " + attrIndex + ": " + value
+ + ", theme=" + mTheme);
}
throw new UnsupportedOperationException("Can't convert value at index " + attrIndex
- + " to integer: type=0x" + Integer.toHexString(type));
+ + " to integer: type=0x" + Integer.toHexString(type) + ", theme=" + mTheme);
}
/**
@@ -684,11 +688,12 @@ public class TypedArray implements AutoCloseable {
final TypedValue value = mValue;
getValueAt(index, value);
throw new UnsupportedOperationException(
- "Failed to resolve attribute at index " + attrIndex + ": " + value);
+ "Failed to resolve attribute at index " + attrIndex + ": " + value
+ + ", theme=" + mTheme);
}
throw new UnsupportedOperationException("Can't convert value at index " + attrIndex
- + " to dimension: type=0x" + Integer.toHexString(type));
+ + " to dimension: type=0x" + Integer.toHexString(type) + ", theme=" + mTheme);
}
/**
@@ -732,11 +737,12 @@ public class TypedArray implements AutoCloseable {
final TypedValue value = mValue;
getValueAt(index, value);
throw new UnsupportedOperationException(
- "Failed to resolve attribute at index " + attrIndex + ": " + value);
+ "Failed to resolve attribute at index " + attrIndex + ": " + value
+ + ", theme=" + mTheme);
}
throw new UnsupportedOperationException("Can't convert value at index " + attrIndex
- + " to dimension: type=0x" + Integer.toHexString(type));
+ + " to dimension: type=0x" + Integer.toHexString(type) + ", theme=" + mTheme);
}
/**
@@ -781,11 +787,12 @@ public class TypedArray implements AutoCloseable {
final TypedValue value = mValue;
getValueAt(index, value);
throw new UnsupportedOperationException(
- "Failed to resolve attribute at index " + attrIndex + ": " + value);
+ "Failed to resolve attribute at index " + attrIndex + ": " + value
+ + ", theme=" + mTheme);
}
throw new UnsupportedOperationException("Can't convert value at index " + attrIndex
- + " to dimension: type=0x" + Integer.toHexString(type));
+ + " to dimension: type=0x" + Integer.toHexString(type) + ", theme=" + mTheme);
}
/**
@@ -825,11 +832,12 @@ public class TypedArray implements AutoCloseable {
final TypedValue value = mValue;
getValueAt(index, value);
throw new UnsupportedOperationException(
- "Failed to resolve attribute at index " + attrIndex + ": " + value);
+ "Failed to resolve attribute at index " + attrIndex + ": " + value
+ + ", theme=" + mTheme);
}
throw new UnsupportedOperationException(getPositionDescription()
- + ": You must supply a " + name + " attribute.");
+ + ": You must supply a " + name + " attribute." + ", theme=" + mTheme);
}
/**
@@ -900,11 +908,12 @@ public class TypedArray implements AutoCloseable {
final TypedValue value = mValue;
getValueAt(index, value);
throw new UnsupportedOperationException(
- "Failed to resolve attribute at index " + attrIndex + ": " + value);
+ "Failed to resolve attribute at index " + attrIndex + ": " + value
+ + ", theme=" + mTheme);
}
throw new UnsupportedOperationException("Can't convert value at index " + attrIndex
- + " to fraction: type=0x" + Integer.toHexString(type));
+ + " to fraction: type=0x" + Integer.toHexString(type) + ", theme=" + mTheme);
}
/**
@@ -996,7 +1005,8 @@ public class TypedArray implements AutoCloseable {
if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
if (value.type == TypedValue.TYPE_ATTRIBUTE) {
throw new UnsupportedOperationException(
- "Failed to resolve attribute at index " + index + ": " + value);
+ "Failed to resolve attribute at index " + index + ": " + value
+ + ", theme=" + mTheme);
}
if (density > 0) {
@@ -1032,7 +1042,8 @@ public class TypedArray implements AutoCloseable {
if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
if (value.type == TypedValue.TYPE_ATTRIBUTE) {
throw new UnsupportedOperationException(
- "Failed to resolve attribute at index " + index + ": " + value);
+ "Failed to resolve attribute at index " + index + ": " + value
+ + ", theme=" + mTheme);
}
return mResources.getFont(value, value.resourceId);
}
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/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java
index cad30dda9034..a4a8f313e3ba 100644
--- a/core/java/android/hardware/HardwareBuffer.java
+++ b/core/java/android/hardware/HardwareBuffer.java
@@ -25,6 +25,7 @@ import android.graphics.GraphicBuffer;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
+import android.view.SurfaceControl;
import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
@@ -129,6 +130,15 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable {
public static final long USAGE_GPU_SAMPLED_IMAGE = 1 << 8;
/** Usage: The buffer will be written to by the GPU */
public static final long USAGE_GPU_COLOR_OUTPUT = 1 << 9;
+ /**
+ * The buffer will be used as a composer HAL overlay layer.
+ *
+ * This flag is currently only needed when using
+ * {@link android.view.SurfaceControl.Transaction#setBuffer(SurfaceControl, HardwareBuffer)}
+ * to set a buffer. In all other cases, the framework adds this flag
+ * internally to buffers that could be presented in a composer overlay.
+ */
+ public static final long USAGE_COMPOSER_OVERLAY = 1 << 11;
/** Usage: The buffer must not be used outside of a protected hardware path */
public static final long USAGE_PROTECTED_CONTENT = 1 << 14;
/** Usage: The buffer will be read by a hardware video encoder */
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/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 93f1d615b98b..c12e8195eeb4 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -1036,6 +1036,95 @@ public final class CameraManager {
}
/**
+ * Set the brightness level of the flashlight associated with the given cameraId in torch
+ * mode. If the torch is OFF and torchStrength is >= 1, torch will turn ON with the
+ * strength level specified in torchStrength.
+ *
+ * <p>Use
+ * {@link android.hardware.camera2.CameraCharacteristics#FLASH_INFO_STRENGTH_MAXIMUM_LEVEL}
+ * to check whether the camera device supports flash unit strength control or not. If this value
+ * is greater than 1, applications can call this API to control the flashlight brightness level.
+ * </p>
+ *
+ * <p>If {@link #turnOnTorchWithStrengthLevel} is called to change the brightness level of the
+ * flash unit {@link CameraManager.TorchCallback#onTorchStrengthLevelChanged} will be invoked.
+ * If the new desired strength level is same as previously set level, then this callback will
+ * not be invoked.
+ * If the torch is OFF and {@link #turnOnTorchWithStrengthLevel} is called with level >= 1,
+ * the torch will be turned ON with that brightness level. In this case
+ * {@link CameraManager.TorchCallback#onTorchModeChanged} will also be invoked.
+ * </p>
+ *
+ * <p>When the torch is turned OFF via {@link #setTorchMode}, the flashlight brightness level
+ * will reset to default value
+ * {@link android.hardware.camera2.CameraCharacteristics#FLASH_INFO_STRENGTH_DEFAULT_LEVEL}
+ * In this case the {@link CameraManager.TorchCallback#onTorchStrengthLevelChanged} will not be
+ * invoked.
+ * </p>
+ *
+ * <p>If torch is enabled via {@link #setTorchMode} after calling
+ * {@link #turnOnTorchWithStrengthLevel} with level N then the flash unit will have the
+ * brightness level N.
+ * Since multiple applications are free to call {@link #setTorchMode}, when the latest
+ * application that turned ON the torch mode exits, the torch mode will be turned OFF
+ * and in this case the brightness level will reset to default level.
+ * </p>
+ *
+ * @param cameraId
+ * The unique identifier of the camera device that the flash unit belongs to.
+ * @param torchStrength
+ * The desired brightness level to be set for the flash unit in the range 1 to
+ * {@link android.hardware.camera2.CameraCharacteristics#FLASH_INFO_STRENGTH_MAXIMUM_LEVEL}.
+ *
+ * @throws CameraAccessException if it failed to access the flash unit.
+ * {@link CameraAccessException#CAMERA_IN_USE} will be thrown if the camera device
+ * is in use. {@link CameraAccessException#MAX_CAMERAS_IN_USE} will be thrown if
+ * other camera resources needed to turn on the torch mode are in use.
+ * {@link CameraAccessException#CAMERA_DISCONNECTED} will be thrown if camera
+ * service is not available.
+ * @throws IllegalArgumentException if cameraId was null, cameraId doesn't match any currently
+ * or previously available camera device, the camera device doesn't have a
+ * flash unit or if torchStrength is not within the range i.e. is greater than
+ * the maximum level
+ * {@link android.hardware.camera2.CameraCharacteristics#FLASH_INFO_STRENGTH_MAXIMUM_LEVEL}
+ * or <= 0.
+ *
+ */
+ public void turnOnTorchWithStrengthLevel(@NonNull String cameraId, int torchStrength)
+ throws CameraAccessException {
+ if (CameraManagerGlobal.sCameraServiceDisabled) {
+ throw new IllegalArgumentException("No camera available on device");
+ }
+ CameraManagerGlobal.get().turnOnTorchWithStrengthLevel(cameraId, torchStrength);
+ }
+
+ /**
+ * Returns the brightness level of the flash unit associated with the cameraId.
+ *
+ * @param cameraId
+ * The unique identifier of the camera device that the flash unit belongs to.
+ * @return The brightness level of the flash unit associated with cameraId.
+ * When the torch is turned OFF, the strength level will reset to a default level
+ * {@link android.hardware.camera2.CameraCharacteristics#FLASH_INFO_STRENGTH_DEFAULT_LEVEL}.
+ * In this case the return value will be
+ * {@link android.hardware.camera2.CameraCharacteristics#FLASH_INFO_STRENGTH_DEFAULT_LEVEL}
+ * rather than 0.
+ *
+ * @throws CameraAccessException if it failed to access the flash unit.
+ * @throws IllegalArgumentException if cameraId was null, cameraId doesn't match any currently
+ * or previously available camera device, or the camera device doesn't have a
+ * flash unit.
+ *
+ */
+ public int getTorchStrengthLevel(@NonNull String cameraId)
+ throws CameraAccessException {
+ if (CameraManagerGlobal.sCameraServiceDisabled) {
+ throw new IllegalArgumentException("No camera available on device.");
+ }
+ return CameraManagerGlobal.get().getTorchStrengthLevel(cameraId);
+ }
+
+ /**
* A callback for camera devices becoming available or unavailable to open.
*
* <p>Cameras become available when they are no longer in use, or when a new
@@ -1239,6 +1328,24 @@ public final class CameraManager {
public void onTorchModeChanged(@NonNull String cameraId, boolean enabled) {
// default empty implementation
}
+
+ /**
+ * A camera's flash unit brightness level has been changed in torch mode via
+ * {@link #turnOnTorchWithStrengthLevel}. When the torch is turned OFF, this
+ * callback will not be triggered even though the torch strength level resets to
+ * default value
+ * {@link android.hardware.camera2.CameraCharacteristics#FLASH_INFO_STRENGTH_DEFAULT_LEVEL}
+ *
+ * <p>The default implementation of this method does nothing.</p>
+ *
+ * @param cameraId The unique identifier of the camera whose flash unit brightness level has
+ * been changed.
+ *
+ * @param newStrengthLevel The brightness level of the flash unit that has been changed to.
+ */
+ public void onTorchStrengthLevelChanged(@NonNull String cameraId, int newStrengthLevel) {
+ // default empty implementation
+ }
}
/**
@@ -1642,6 +1749,10 @@ public final class CameraManager {
public void onTorchStatusChanged(int status, String id) throws RemoteException {
}
@Override
+ public void onTorchStrengthLevelChanged(String id, int newStrengthLevel)
+ throws RemoteException {
+ }
+ @Override
public void onCameraAccessPrioritiesChanged() {
}
@Override
@@ -1825,6 +1936,57 @@ public final class CameraManager {
}
}
+ public void turnOnTorchWithStrengthLevel(String cameraId, int torchStrength) throws
+ CameraAccessException {
+ synchronized(mLock) {
+
+ if (cameraId == null) {
+ throw new IllegalArgumentException("cameraId was null");
+ }
+
+ ICameraService cameraService = getCameraService();
+ if (cameraService == null) {
+ throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
+ "Camera service is currently unavailable.");
+ }
+
+ try {
+ cameraService.turnOnTorchWithStrengthLevel(cameraId, torchStrength,
+ mTorchClientBinder);
+ } catch(ServiceSpecificException e) {
+ throwAsPublicException(e);
+ } catch (RemoteException e) {
+ throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
+ "Camera service is currently unavailable.");
+ }
+ }
+ }
+
+ public int getTorchStrengthLevel(String cameraId) throws CameraAccessException {
+ int torchStrength = 0;
+ synchronized(mLock) {
+ if (cameraId == null) {
+ throw new IllegalArgumentException("cameraId was null");
+ }
+
+ ICameraService cameraService = getCameraService();
+ if (cameraService == null) {
+ throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
+ "Camera service is currently unavailable.");
+ }
+
+ try {
+ torchStrength = cameraService.getTorchStrengthLevel(cameraId);
+ } catch(ServiceSpecificException e) {
+ throwAsPublicException(e);
+ } catch (RemoteException e) {
+ throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
+ "Camera service is currently unavailable.");
+ }
+ }
+ return torchStrength;
+ }
+
private void handleRecoverableSetupErrors(ServiceSpecificException e) {
switch (e.errorCode) {
case ICameraService.ERROR_DISCONNECTED:
@@ -1984,6 +2146,18 @@ public final class CameraManager {
}
}
+ private void postSingleTorchStrengthLevelUpdate(final TorchCallback callback,
+ final Executor executor, final String id, final int newStrengthLevel) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> {
+ callback.onTorchStrengthLevelChanged(id, newStrengthLevel);
+ });
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
/**
* Send the state of all known cameras to the provided listener, to initialize
* the listener's knowledge of camera state.
@@ -2167,6 +2341,22 @@ public final class CameraManager {
}
} // onTorchStatusChangedLocked
+ private void onTorchStrengthLevelChangedLocked(String cameraId, int newStrengthLevel) {
+ if (DEBUG) {
+
+ Log.v(TAG,
+ String.format("Camera id %s has torch strength level changed to %d",
+ cameraId, newStrengthLevel));
+ }
+
+ final int callbackCount = mTorchCallbackMap.size();
+ for (int i = 0; i < callbackCount; i++) {
+ final Executor executor = mTorchCallbackMap.valueAt(i);
+ final TorchCallback callback = mTorchCallbackMap.keyAt(i);
+ postSingleTorchStrengthLevelUpdate(callback, executor, cameraId, newStrengthLevel);
+ }
+ } // onTorchStrengthLevelChanged
+
/**
* Register a callback to be notified about camera device availability with the
* global listener singleton.
@@ -2258,6 +2448,14 @@ public final class CameraManager {
}
@Override
+ public void onTorchStrengthLevelChanged(String cameraId, int newStrengthLevel)
+ throws RemoteException {
+ synchronized (mLock) {
+ onTorchStrengthLevelChangedLocked(cameraId, newStrengthLevel);
+ }
+ }
+
+ @Override
public void onCameraAccessPrioritiesChanged() {
synchronized (mLock) {
final int callbackCount = mCallbackMap.size();
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/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/location/NanoAppRpcService.aidl b/core/java/android/hardware/location/NanoAppRpcService.aidl
new file mode 100644
index 000000000000..72ac8af18209
--- /dev/null
+++ b/core/java/android/hardware/location/NanoAppRpcService.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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.hardware.location;
+
+/**
+ * @hide
+ */
+parcelable NanoAppRpcService;
diff --git a/core/java/android/hardware/location/NanoAppRpcService.java b/core/java/android/hardware/location/NanoAppRpcService.java
new file mode 100644
index 000000000000..4b8f0580fe31
--- /dev/null
+++ b/core/java/android/hardware/location/NanoAppRpcService.java
@@ -0,0 +1,159 @@
+/*
+ * 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.hardware.location;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * An RPC service published by a nanoapp.
+ *
+ * This class is meant to be informational and allows a nanoapp client to know if a given
+ * nanoapp publishes a service which supports an RPC interface. The implementation of the RPC
+ * interface is not specified by the Android APIs and is built by the platform implementation
+ * over the message payloads transferred through the {@link ContextHubClient}.
+ *
+ * This class is instantiated as a part of {@link NanoAppState}, which is provided as a result
+ * of {@link ContextHubManager.queryNanoApps(ContextHubInfo)}.
+ *
+ * See the chrePublishRpcServices() API for how this service is published by the nanoapp.
+ *
+ * @hide
+ */
+@SystemApi
+public final class NanoAppRpcService implements Parcelable {
+ private long mServiceId;
+
+ private int mServiceVersion;
+
+ /**
+ * @param serviceId The unique ID of this service, see {#getId()}.
+ * @param serviceVersion The software version of this service, see {#getVersion()}.
+ */
+ public NanoAppRpcService(long serviceId, int serviceVersion) {
+ mServiceId = serviceId;
+ mServiceVersion = serviceVersion;
+ }
+
+ /**
+ * The unique 64-bit ID of an RPC service published by a nanoapp. Note that
+ * the uniqueness is only required within the nanoapp's domain (i.e. the
+ * combination of the nanoapp ID and service id must be unique).
+ *
+ * This ID must remain the same for the given nanoapp RPC service once
+ * published on Android (i.e. must never change).
+ *
+ * @return The service ID.
+ */
+ public long getId() {
+ return mServiceId;
+ }
+
+ /**
+ * The software version of this service, which follows the sematic
+ * versioning scheme (see semver.org). It follows the format
+ * major.minor.patch, where major and minor versions take up one byte
+ * each, and the patch version takes up the final 2 (lower) bytes.
+ * I.e. the version is encoded as 0xMMmmpppp, where MM, mm, pppp are
+ * the major, minor, patch versions, respectively.
+ *
+ * @return The service version.
+ */
+ public int getVersion() {
+ return mServiceVersion;
+ }
+
+ /**
+ * @return The service's major version.
+ */
+ private int getMajorVersion() {
+ return (mServiceVersion & 0xFF000000) >>> 24;
+ }
+
+ /**
+ * @return The service's minor version.
+ */
+ private int getMinorVersion() {
+ return (mServiceVersion & 0x00FF0000) >>> 16;
+ }
+
+ /**
+ * @return The service's patch version.
+ */
+ private int getPatchVersion() {
+ return mServiceVersion & 0x0000FFFF;
+ }
+
+ private NanoAppRpcService(Parcel in) {
+ mServiceId = in.readLong();
+ mServiceVersion = in.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeLong(mServiceId);
+ out.writeInt(mServiceVersion);
+ }
+
+ public static final @NonNull Creator<NanoAppRpcService> CREATOR =
+ new Creator<NanoAppRpcService>() {
+ @Override
+ public NanoAppRpcService createFromParcel(Parcel in) {
+ return new NanoAppRpcService(in);
+ }
+
+ @Override
+ public NanoAppRpcService[] newArray(int size) {
+ return new NanoAppRpcService[size];
+ }
+ };
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "NanoAppRpcService[Id = " + Long.toHexString(mServiceId) + ", version = v"
+ + getMajorVersion() + "." + getMinorVersion() + "." + getPatchVersion() + "]";
+ }
+
+ @Override
+ public boolean equals(@Nullable Object object) {
+ if (object == this) {
+ return true;
+ }
+
+ boolean isEqual = false;
+ if (object instanceof NanoAppRpcService) {
+ NanoAppRpcService other = (NanoAppRpcService) object;
+ isEqual = (other.getId() == mServiceId)
+ && (other.getVersion() == mServiceVersion);
+ }
+
+ return isEqual;
+ }
+
+ @Override
+ public int hashCode() {
+ return (int) getId();
+ }
+}
diff --git a/core/java/android/hardware/location/NanoAppState.java b/core/java/android/hardware/location/NanoAppState.java
index 96b1f19a9cd5..b649054cb4b8 100644
--- a/core/java/android/hardware/location/NanoAppState.java
+++ b/core/java/android/hardware/location/NanoAppState.java
@@ -21,10 +21,16 @@ import android.os.Parcel;
import android.os.Parcelable;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
/**
- * A class describing the nanoapp state information resulting from a query to a Context Hub.
+ * A class describing the nanoapp state information resulting from a query to a Context Hub
+ * through {@link ContextHubManager.queryNanoApps(ContextHubInfo)}. It contains metadata about
+ * the nanoapp running on a Context Hub.
+ *
+ * See "struct chreNanoappInfo" in the CHRE API (system/chre/chre_api) for additional details.
*
* @hide
*/
@@ -33,31 +39,70 @@ public final class NanoAppState implements Parcelable {
private long mNanoAppId;
private int mNanoAppVersion;
private boolean mIsEnabled;
- private List<String> mNanoAppPermissions;
+ private List<String> mNanoAppPermissions = new ArrayList<String>();
+ private List<NanoAppRpcService> mNanoAppRpcServiceList =
+ new ArrayList<NanoAppRpcService>();
+ /**
+ * @param nanoAppId The unique ID of this nanoapp, see {#getNanoAppId()}.
+ * @param appVersion The software version of this nanoapp, see {#getNanoAppVersion()}.
+ * @param enabled True if the nanoapp is enabled and running on the Context Hub.
+ */
public NanoAppState(long nanoAppId, int appVersion, boolean enabled) {
mNanoAppId = nanoAppId;
mNanoAppVersion = appVersion;
mIsEnabled = enabled;
- mNanoAppPermissions = new ArrayList<String>();
}
+ /**
+ * @param nanoAppId The unique ID of this nanoapp, see {#getNanoAppId()}.
+ * @param appVersion The software version of this nanoapp, see {#getNanoAppVersion()}.
+ * @param enabled True if the nanoapp is enabled and running on the Context Hub.
+ * @param nanoAppPermissions The list of permissions required to communicate with this
+ * nanoapp.
+ */
public NanoAppState(long nanoAppId, int appVersion, boolean enabled,
@NonNull List<String> nanoAppPermissions) {
mNanoAppId = nanoAppId;
mNanoAppVersion = appVersion;
mIsEnabled = enabled;
- mNanoAppPermissions = nanoAppPermissions;
+ mNanoAppPermissions = Collections.unmodifiableList(nanoAppPermissions);
+ }
+
+ /**
+ * @param nanoAppId The unique ID of this nanoapp, see {#getNanoAppId()}.
+ * @param appVersion The software version of this nanoapp, see {#getNanoAppVersion()}.
+ * @param enabled True if the nanoapp is enabled and running on the Context Hub.
+ * @param nanoAppPermissions The list of permissions required to communicate with this
+ * nanoapp.
+ * @param nanoAppRpcServiceList The list of RPC services published by this nanoapp, see
+ * {@link NanoAppRpcService} for additional details.
+ */
+ public NanoAppState(long nanoAppId, int appVersion, boolean enabled,
+ @NonNull List<String> nanoAppPermissions,
+ @NonNull List<NanoAppRpcService> nanoAppRpcServiceList) {
+ mNanoAppId = nanoAppId;
+ mNanoAppVersion = appVersion;
+ mIsEnabled = enabled;
+ mNanoAppPermissions = Collections.unmodifiableList(nanoAppPermissions);
+ mNanoAppRpcServiceList = Collections.unmodifiableList(nanoAppRpcServiceList);
}
/**
- * @return the NanoAppInfo for this app
+ * @return the unique ID of this nanoapp, which must never change once released on Android.
*/
public long getNanoAppId() {
return mNanoAppId;
}
/**
+ * The software version of this service, which follows the sematic
+ * versioning scheme (see semver.org). It follows the format
+ * major.minor.patch, where major and minor versions take up one byte
+ * each, and the patch version takes up the final 2 (lower) bytes.
+ * I.e. the version is encoded as 0xMMmmpppp, where MM, mm, pppp are
+ * the major, minor, patch versions, respectively.
+ *
* @return the app version
*/
public long getNanoAppVersion() {
@@ -72,18 +117,29 @@ public final class NanoAppState implements Parcelable {
}
/**
- * @return List of Android permissions that are required to communicate with this app.
+ * @return A read-only list of Android permissions that are all required to communicate with
+ * this nanoapp.
*/
public @NonNull List<String> getNanoAppPermissions() {
return mNanoAppPermissions;
}
+ /**
+ * @return A read-only list of RPC services supported by this nanoapp.
+ */
+ public @NonNull List<NanoAppRpcService> getRpcServices() {
+ return mNanoAppRpcServiceList;
+ }
+
private NanoAppState(Parcel in) {
mNanoAppId = in.readLong();
mNanoAppVersion = in.readInt();
mIsEnabled = (in.readInt() == 1);
mNanoAppPermissions = new ArrayList<String>();
in.readStringList(mNanoAppPermissions);
+ mNanoAppRpcServiceList = Collections.unmodifiableList(
+ Arrays.asList(in.readParcelableArray(
+ NanoAppRpcService.class.getClassLoader(), NanoAppRpcService.class)));
}
@Override
@@ -97,6 +153,7 @@ public final class NanoAppState implements Parcelable {
out.writeInt(mNanoAppVersion);
out.writeInt(mIsEnabled ? 1 : 0);
out.writeStringList(mNanoAppPermissions);
+ out.writeParcelableArray(mNanoAppRpcServiceList.toArray(new NanoAppRpcService[0]), 0);
}
public static final @android.annotation.NonNull Creator<NanoAppState> CREATOR =
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 70fe5d677b4f..f33a035673b8 100644
--- a/core/java/android/net/NetworkPolicy.java
+++ b/core/java/android/net/NetworkPolicy.java
@@ -138,8 +138,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/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/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 ba9332dece26..c6071959f438 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -161,6 +161,7 @@ public abstract class BatteryConsumer {
*/
@IntDef(prefix = {"PROCESS_STATE_"}, value = {
PROCESS_STATE_ANY,
+ PROCESS_STATE_UNSPECIFIED,
PROCESS_STATE_FOREGROUND,
PROCESS_STATE_BACKGROUND,
PROCESS_STATE_FOREGROUND_SERVICE,
@@ -169,7 +170,8 @@ public abstract class BatteryConsumer {
public @interface ProcessState {
}
- public static final int PROCESS_STATE_ANY = 0;
+ public static final int PROCESS_STATE_UNSPECIFIED = 0;
+ public static final int PROCESS_STATE_ANY = PROCESS_STATE_UNSPECIFIED;
public static final int PROCESS_STATE_FOREGROUND = 1;
public static final int PROCESS_STATE_BACKGROUND = 2;
public static final int PROCESS_STATE_FOREGROUND_SERVICE = 3;
@@ -180,7 +182,7 @@ public abstract class BatteryConsumer {
static {
// Assign individually to avoid future mismatch
- sProcessStateNames[PROCESS_STATE_ANY] = "any";
+ sProcessStateNames[PROCESS_STATE_UNSPECIFIED] = "unspecified";
sProcessStateNames[PROCESS_STATE_FOREGROUND] = "fg";
sProcessStateNames[PROCESS_STATE_BACKGROUND] = "bg";
sProcessStateNames[PROCESS_STATE_FOREGROUND_SERVICE] = "fgs";
@@ -188,6 +190,7 @@ public abstract class BatteryConsumer {
private static final int[] SUPPORTED_POWER_COMPONENTS_PER_PROCESS_STATE = {
POWER_COMPONENT_CPU,
+ POWER_COMPONENT_MOBILE_RADIO,
};
static final int COLUMN_INDEX_BATTERY_CONSUMER_TYPE = 0;
@@ -213,7 +216,7 @@ public abstract class BatteryConsumer {
sb.append("powerComponent=").append(sPowerComponentNames[powerComponent]);
dimensionSpecified = true;
}
- if (processState != PROCESS_STATE_ANY) {
+ if (processState != PROCESS_STATE_UNSPECIFIED) {
if (dimensionSpecified) {
sb.append(", ");
}
@@ -283,7 +286,7 @@ public abstract class BatteryConsumer {
if (mShortString == null) {
StringBuilder sb = new StringBuilder();
sb.append(powerComponentIdToString(powerComponent));
- if (processState != PROCESS_STATE_ANY) {
+ if (processState != PROCESS_STATE_UNSPECIFIED) {
sb.append(':');
sb.append(processStateToString(processState));
}
@@ -333,7 +336,7 @@ public abstract class BatteryConsumer {
* for all values of other dimensions such as process state.
*/
public Key getKey(@PowerComponent int componentId) {
- return mData.getKey(componentId, PROCESS_STATE_ANY);
+ return mData.getKey(componentId, PROCESS_STATE_UNSPECIFIED);
}
/**
@@ -352,7 +355,7 @@ public abstract class BatteryConsumer {
*/
public double getConsumedPower(@PowerComponent int componentId) {
return mPowerComponents.getConsumedPower(
- mData.getKeyOrThrow(componentId, PROCESS_STATE_ANY));
+ mData.getKeyOrThrow(componentId, PROCESS_STATE_UNSPECIFIED));
}
/**
@@ -374,7 +377,7 @@ public abstract class BatteryConsumer {
*/
public @PowerModel int getPowerModel(@BatteryConsumer.PowerComponent int componentId) {
return mPowerComponents.getPowerModel(
- mData.getKeyOrThrow(componentId, PROCESS_STATE_ANY));
+ mData.getKeyOrThrow(componentId, PROCESS_STATE_UNSPECIFIED));
}
/**
@@ -706,7 +709,7 @@ public abstract class BatteryConsumer {
if (isSupported) {
for (int processState = 0; processState < PROCESS_STATE_COUNT;
processState++) {
- if (processState == PROCESS_STATE_ANY) {
+ if (processState == PROCESS_STATE_UNSPECIFIED) {
continue;
}
@@ -789,7 +792,7 @@ public abstract class BatteryConsumer {
@NonNull
public T setConsumedPower(@PowerComponent int componentId, double componentPower,
@PowerModel int powerModel) {
- mPowerComponentsBuilder.setConsumedPower(getKey(componentId, PROCESS_STATE_ANY),
+ mPowerComponentsBuilder.setConsumedPower(getKey(componentId, PROCESS_STATE_UNSPECIFIED),
componentPower, powerModel);
return (T) this;
}
@@ -825,8 +828,9 @@ public abstract class BatteryConsumer {
@NonNull
public T setUsageDurationMillis(@UidBatteryConsumer.PowerComponent int componentId,
long componentUsageTimeMillis) {
- mPowerComponentsBuilder.setUsageDurationMillis(getKey(componentId, PROCESS_STATE_ANY),
- componentUsageTimeMillis);
+ mPowerComponentsBuilder
+ .setUsageDurationMillis(getKey(componentId, PROCESS_STATE_UNSPECIFIED),
+ componentUsageTimeMillis);
return (T) this;
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 584f3c43911b..fa209cc02cd1 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -669,7 +669,7 @@ public abstract class BatteryStats implements Parcelable {
case BatteryStats.Uid.PROCESS_STATE_FOREGROUND_SERVICE:
return BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
default:
- return BatteryConsumer.PROCESS_STATE_ANY;
+ return BatteryConsumer.PROCESS_STATE_UNSPECIFIED;
}
}
@@ -963,6 +963,13 @@ public abstract class BatteryStats implements Parcelable {
public abstract long getNetworkActivityPackets(int type, int which);
@UnsupportedAppUsage
public abstract long getMobileRadioActiveTime(int which);
+
+ /**
+ * Returns the amount of time (in microseconds) this UID was in the specified processState.
+ */
+ public abstract long getMobileRadioActiveTimeInProcessState(
+ @BatteryConsumer.ProcessState int processState);
+
public abstract int getMobileRadioActiveCount(int which);
/**
@@ -1061,6 +1068,16 @@ public abstract class BatteryStats implements Parcelable {
public abstract long getMobileRadioMeasuredBatteryConsumptionUC();
/**
+ * Returns the battery consumption (in microcoulombs) of the uid's radio usage when in the
+ * specified process state.
+ * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+ *
+ * {@hide}
+ */
+ public abstract long getMobileRadioMeasuredBatteryConsumptionUC(
+ @BatteryConsumer.ProcessState int processState);
+
+ /**
* Returns the battery consumption (in microcoulombs) of the screen while on and uid active,
* derived from on device power measurement data.
* Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index ed44fb62ee79..a23dae80625d 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -556,7 +556,7 @@ public final class BatteryUsageStats implements Parcelable, Closeable {
}
String label = BatteryConsumer.powerComponentIdToString(componentId);
- if (key.processState != BatteryConsumer.PROCESS_STATE_ANY) {
+ if (key.processState != BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
label = label
+ "(" + BatteryConsumer.processStateToString(key.processState) + ")";
}
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index b069fb336d55..429450ce8e5e 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -22,9 +22,11 @@ import android.annotation.SystemApi;
import android.app.AppOpsManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.util.ExceptionUtils;
+import android.util.IntArray;
import android.util.Log;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BinderCallHeavyHitterWatcher;
import com.android.internal.os.BinderCallHeavyHitterWatcher.BinderCallHeavyHitterListener;
import com.android.internal.os.BinderInternal;
@@ -140,33 +142,55 @@ public class Binder implements IBinder {
/**
* Flag indicating whether we should be tracing transact calls.
*/
- private static volatile boolean sTracingEnabled = false;
+ private static volatile boolean sStackTrackingEnabled = false;
+
+ private static final Object sTracingUidsWriteLock = new Object();
+ private static volatile IntArray sTracingUidsImmutable = new IntArray();
/**
- * Enable Binder IPC tracing.
+ * Enable Binder IPC stack tracking. If enabled, every binder transaction will be logged to
+ * {@link TransactionTracker}.
*
* @hide
*/
- public static void enableTracing() {
- sTracingEnabled = true;
+ public static void enableStackTracking() {
+ sStackTrackingEnabled = true;
}
/**
- * Disable Binder IPC tracing.
+ * Disable Binder IPC stack tracking.
*
* @hide
*/
- public static void disableTracing() {
- sTracingEnabled = false;
+ public static void disableStackTracking() {
+ sStackTrackingEnabled = false;
}
/**
- * Check if binder transaction tracing is enabled.
+ * @hide
+ */
+ public static void enableTracingForUid(int uid) {
+ synchronized (sTracingUidsWriteLock) {
+ final IntArray copy = sTracingUidsImmutable.clone();
+ copy.add(uid);
+ sTracingUidsImmutable = copy;
+ }
+ }
+
+ /**
+ * Check if binder transaction stack tracking is enabled.
*
* @hide
*/
- public static boolean isTracingEnabled() {
- return sTracingEnabled;
+ public static boolean isStackTrackingEnabled() {
+ return sStackTrackingEnabled;
+ }
+
+ /**
+ * @hide
+ */
+ public static boolean isTracingEnabled(int callingUid) {
+ return sTracingUidsImmutable.indexOf(callingUid) != -1;
}
/**
@@ -288,6 +312,9 @@ public class Binder implements IBinder {
private IInterface mOwner;
private String mDescriptor;
+ private volatile String[] mTransactionTraceNames = null;
+ private volatile String mSimpleDescriptor = null;
+ private static final int TRANSACTION_TRACE_NAME_ID_LIMIT = 1024;
/**
* Return the ID of the process that sent you the current transaction
@@ -884,6 +911,53 @@ public class Binder implements IBinder {
}
/**
+ * @hide
+ */
+ @VisibleForTesting
+ public final @NonNull String getTransactionTraceName(int transactionCode) {
+ if (mTransactionTraceNames == null) {
+ final String descriptor = getSimpleDescriptor();
+ final int highestId = Math.min(getMaxTransactionId(), TRANSACTION_TRACE_NAME_ID_LIMIT);
+ final String[] transactionNames = new String[highestId + 1];
+ final StringBuffer buf = new StringBuffer();
+ for (int i = 0; i <= highestId; i++) {
+ String transactionName = getTransactionName(i + FIRST_CALL_TRANSACTION);
+ if (transactionName != null) {
+ buf.append(descriptor).append(':').append(transactionName);
+ } else {
+ buf.append(descriptor).append('#').append(i + FIRST_CALL_TRANSACTION);
+ }
+ transactionNames[i] = buf.toString();
+ buf.setLength(0);
+ }
+ mTransactionTraceNames = transactionNames;
+ mSimpleDescriptor = descriptor;
+ }
+ final int index = transactionCode - FIRST_CALL_TRANSACTION;
+ if (index < 0 || index >= mTransactionTraceNames.length) {
+ return mSimpleDescriptor + "#" + transactionCode;
+ }
+ return mTransactionTraceNames[index];
+ }
+
+ private String getSimpleDescriptor() {
+ final int dot = mDescriptor.lastIndexOf(".");
+ if (dot > 0) {
+ // Strip the package name
+ return mDescriptor.substring(dot + 1);
+ }
+ return mDescriptor;
+ }
+
+ /**
+ * @return The highest user-defined transaction id of all transactions.
+ * @hide
+ */
+ public int getMaxTransactionId() {
+ return 0;
+ }
+
+ /**
* Implemented to call the more convenient version
* {@link #dump(FileDescriptor, PrintWriter, String[])}.
*/
@@ -1181,7 +1255,8 @@ public class Binder implements IBinder {
// Log any exceptions as warnings, don't silently suppress them.
// If the call was {@link IBinder#FLAG_ONEWAY} then these exceptions
// disappear into the ether.
- final boolean tracingEnabled = Binder.isTracingEnabled();
+ final boolean tracingEnabled = Trace.isTagEnabled(Trace.TRACE_TAG_AIDL) &&
+ (Binder.isStackTrackingEnabled() || Binder.isTracingEnabled(callingUid));
try {
final BinderCallHeavyHitterWatcher heavyHitterWatcher = sHeavyHitterWatcher;
if (heavyHitterWatcher != null) {
@@ -1189,9 +1264,7 @@ public class Binder implements IBinder {
heavyHitterWatcher.onTransaction(callingUid, getClass(), code);
}
if (tracingEnabled) {
- final String transactionName = getTransactionName(code);
- Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, getClass().getName() + ":"
- + (transactionName != null ? transactionName : code));
+ Trace.traceBegin(Trace.TRACE_TAG_AIDL, getTransactionTraceName(code));
}
if ((flags & FLAG_COLLECT_NOTED_APP_OPS) != 0) {
@@ -1226,7 +1299,7 @@ public class Binder implements IBinder {
res = true;
} finally {
if (tracingEnabled) {
- Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
+ Trace.traceEnd(Trace.TRACE_TAG_AIDL);
}
if (observer != null) {
// The parcel RPC headers have been called during onTransact so we can now access
@@ -1235,10 +1308,11 @@ public class Binder implements IBinder {
data.readCallingWorkSourceUid());
observer.callEnded(callSession, data.dataSize(), reply.dataSize(), workSourceUid);
}
+
+ checkParcel(this, code, reply, "Unreasonably large binder reply buffer");
+ reply.recycle();
+ data.recycle();
}
- checkParcel(this, code, reply, "Unreasonably large binder reply buffer");
- reply.recycle();
- data.recycle();
// Just in case -- we are done with the IPC, so there should be no more strict
// mode violations that have gathered for this thread. Either they have been
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index 483b549c5637..e9299207ea6b 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -545,7 +545,7 @@ public final class BinderProxy implements IBinder {
}
}
- final boolean tracingEnabled = Binder.isTracingEnabled();
+ final boolean tracingEnabled = Binder.isStackTrackingEnabled();
if (tracingEnabled) {
final Throwable tr = new Throwable();
Binder.getTransactionTracker().addTrace(tr);
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 8292f26a8c6b..aa4b83a5c361 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -192,14 +192,15 @@ public class GraphicsEnvironment {
// We only want to use ANGLE if the developer has explicitly chosen something other than
// default driver.
- final boolean requested = devOptIn.equals(ANGLE_GL_DRIVER_CHOICE_ANGLE);
- if (requested) {
+ final boolean forceAngle = devOptIn.equals(ANGLE_GL_DRIVER_CHOICE_ANGLE);
+ final boolean forceNative = devOptIn.equals(ANGLE_GL_DRIVER_CHOICE_NATIVE);
+ if (forceAngle || forceNative) {
Log.v(TAG, "ANGLE developer option for " + packageName + ": " + devOptIn);
}
final boolean gameModeEnabledAngle = isAngleEnabledByGameMode(context, packageName);
- return requested || gameModeEnabledAngle;
+ return !forceNative && (forceAngle || gameModeEnabledAngle);
}
private int getVulkanVersion(PackageManager pm) {
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 09eac79c991e..3bc3ec83bde5 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -915,11 +915,15 @@ public final class Parcel {
/**
* Write a blob of data into the parcel at the current {@link #dataPosition},
* growing {@link #dataCapacity} if needed.
+ *
+ * <p> If the blob is small, then it is stored in-place, otherwise it is transferred by way of
+ * an anonymous shared memory region. If you prefer send in-place, please use
+ * {@link #writeByteArray(byte[])}.
+ *
* @param b Bytes to place into the parcel.
- * {@hide}
- * {@SystemApi}
+ *
+ * @see #readBlob()
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public final void writeBlob(@Nullable byte[] b) {
writeBlob(b, 0, (b != null) ? b.length : 0);
}
@@ -927,11 +931,16 @@ public final class Parcel {
/**
* Write a blob of data into the parcel at the current {@link #dataPosition},
* growing {@link #dataCapacity} if needed.
+ *
+ * <p> If the blob is small, then it is stored in-place, otherwise it is transferred by way of
+ * an anonymous shared memory region. If you prefer send in-place, please use
+ * {@link #writeByteArray(byte[], int, int)}.
+ *
* @param b Bytes to place into the parcel.
* @param offset Index of first byte to be written.
* @param len Number of bytes to write.
- * {@hide}
- * {@SystemApi}
+ *
+ * @see #readBlob()
*/
public final void writeBlob(@Nullable byte[] b, int offset, int len) {
if (b == null) {
@@ -3210,10 +3219,8 @@ public final class Parcel {
/**
* Read a blob of data from the parcel and return it as a byte array.
- * {@hide}
- * {@SystemApi}
+ * @see #writeBlob(byte[], int, int)
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Nullable
public final byte[] readBlob() {
return nativeReadBlob(mNativePtr);
diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java
index e8631119bbc6..590494cd3273 100644
--- a/core/java/android/os/PowerComponents.java
+++ b/core/java/android/os/PowerComponents.java
@@ -18,6 +18,7 @@ package android.os;
import static android.os.BatteryConsumer.POWER_COMPONENT_ANY;
import static android.os.BatteryConsumer.POWER_COMPONENT_COUNT;
import static android.os.BatteryConsumer.PROCESS_STATE_ANY;
+import static android.os.BatteryConsumer.PROCESS_STATE_UNSPECIFIED;
import static android.os.BatteryConsumer.convertMahToDeciCoulombs;
import android.annotation.NonNull;
@@ -339,7 +340,7 @@ class PowerComponents {
serializer.startTag(null, BatteryUsageStats.XML_TAG_COMPONENT);
serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_ID, componentId);
- if (key.processState != PROCESS_STATE_ANY) {
+ if (key.processState != PROCESS_STATE_UNSPECIFIED) {
serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_PROCESS_STATE,
key.processState);
}
@@ -398,7 +399,7 @@ class PowerComponents {
switch (parser.getName()) {
case BatteryUsageStats.XML_TAG_COMPONENT: {
int componentId = -1;
- int processState = PROCESS_STATE_ANY;
+ int processState = PROCESS_STATE_UNSPECIFIED;
double powerMah = 0;
long durationMs = 0;
int model = BatteryConsumer.POWER_MODEL_UNDEFINED;
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 7455f1fd7996..dbb5a2c5af63 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -2984,7 +2984,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/Trace.java b/core/java/android/os/Trace.java
index ddb6533547bb..d974e0c0713a 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -131,6 +131,10 @@ public final class Trace {
private static native void nativeAsyncTraceBegin(long tag, String name, int cookie);
@FastNative
private static native void nativeAsyncTraceEnd(long tag, String name, int cookie);
+ @FastNative
+ private static native void nativeInstant(long tag, String name);
+ @FastNative
+ private static native void nativeInstantForTrack(long tag, String trackName, String name);
private Trace() {
}
@@ -258,6 +262,42 @@ public final class Trace {
}
/**
+ * Writes a trace message to indicate that a given section of code was invoked.
+ *
+ * @param traceTag The trace tag.
+ * @param methodName The method name to appear in the trace.
+ * @hide
+ */
+ public static void instant(long traceTag, String methodName) {
+ if (methodName == null) {
+ throw new IllegalArgumentException("methodName cannot be null");
+ }
+ if (isTagEnabled(traceTag)) {
+ nativeInstant(traceTag, methodName);
+ }
+ }
+
+ /**
+ * Writes a trace message to indicate that a given section of code was invoked.
+ *
+ * @param traceTag The trace tag.
+ * @param trackName The track where the event should appear in the trace.
+ * @param methodName The method name to appear in the trace.
+ * @hide
+ */
+ public static void instantForTrack(long traceTag, String trackName, String methodName) {
+ if (trackName == null) {
+ throw new IllegalArgumentException("trackName cannot be null");
+ }
+ if (methodName == null) {
+ throw new IllegalArgumentException("methodName cannot be null");
+ }
+ if (isTagEnabled(traceTag)) {
+ nativeInstantForTrack(traceTag, trackName, methodName);
+ }
+ }
+
+ /**
* Checks whether or not tracing is currently enabled. This is useful to avoid intermediate
* string creation for trace sections that require formatting. It is not necessary
* to guard all Trace method calls as they internally already check this. However it is
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 563d6cc3e07f..b3639e419a56 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1546,6 +1546,17 @@ public class UserManager {
private static final String ACTION_CREATE_USER = "android.os.action.CREATE_USER";
/**
+ * Action to start an activity to create a supervised user.
+ * Only devices with non-empty config_supervisedUserCreationPackage support this.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MANAGE_USERS)
+ public static final String ACTION_CREATE_SUPERVISED_USER =
+ "android.os.action.CREATE_SUPERVISED_USER";
+
+ /**
* Extra containing a name for the user being created. Optional parameter passed to
* ACTION_CREATE_USER activity.
* @hide
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index 5758a4edec31..41ab062ee822 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/VibratorInfo.java b/core/java/android/os/VibratorInfo.java
index cf51496ac011..0a0e3c82f4ad 100644
--- a/core/java/android/os/VibratorInfo.java
+++ b/core/java/android/os/VibratorInfo.java
@@ -70,7 +70,7 @@ public class VibratorInfo implements Parcelable {
mPwlePrimitiveDurationMax = in.readInt();
mPwleSizeMax = in.readInt();
mQFactor = in.readFloat();
- mFrequencyMapping = in.readParcelable(VibratorInfo.class.getClassLoader());
+ mFrequencyMapping = in.readParcelable(VibratorInfo.class.getClassLoader(), android.os.VibratorInfo.FrequencyMapping.class);
}
/**
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/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index adf7955cedb1..5e4057bd840d 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1437,11 +1437,36 @@ public class StorageManager {
throw new IllegalStateException("Missing primary storage");
}
- private static final int DEFAULT_THRESHOLD_PERCENTAGE = 5;
- private static final long DEFAULT_THRESHOLD_MAX_BYTES = DataUnit.MEBIBYTES.toBytes(500);
+ /**
+ * Devices having above STORAGE_THRESHOLD_PERCENT_HIGH of total space free are considered to be
+ * in high free space category.
+ *
+ * @hide
+ */
+ public static final int STORAGE_THRESHOLD_PERCENT_HIGH = 20;
+ /**
+ * Devices having below STORAGE_THRESHOLD_PERCENT_LOW of total space free are considered to be
+ * in low free space category.
+ *
+ * @hide
+ */
+ public static final int STORAGE_THRESHOLD_PERCENT_LOW = 5;
+ /**
+ * For devices in high free space category, CACHE_RESERVE_PERCENT_HIGH percent of total space is
+ * allocated for cache.
+ *
+ * @hide
+ */
+ public static final int CACHE_RESERVE_PERCENT_HIGH = 10;
+ /**
+ * For devices in low free space category, CACHE_RESERVE_PERCENT_LOW percent of total space is
+ * allocated for cache.
+ *
+ * @hide
+ */
+ public static final int CACHE_RESERVE_PERCENT_LOW = 2;
- private static final int DEFAULT_CACHE_PERCENTAGE = 10;
- private static final long DEFAULT_CACHE_MAX_BYTES = DataUnit.GIBIBYTES.toBytes(5);
+ private static final long DEFAULT_THRESHOLD_MAX_BYTES = DataUnit.MEBIBYTES.toBytes(500);
private static final long DEFAULT_FULL_THRESHOLD_BYTES = DataUnit.MEBIBYTES.toBytes(1);
@@ -1465,7 +1490,7 @@ public class StorageManager {
@UnsupportedAppUsage
public long getStorageLowBytes(File path) {
final long lowPercent = Settings.Global.getInt(mResolver,
- Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE, DEFAULT_THRESHOLD_PERCENTAGE);
+ Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE, STORAGE_THRESHOLD_PERCENT_LOW);
final long lowBytes = (path.getTotalSpace() * lowPercent) / 100;
final long maxLowBytes = Settings.Global.getLong(mResolver,
@@ -1475,28 +1500,54 @@ public class StorageManager {
}
/**
+ * Compute the minimum number of bytes of storage on the device that could
+ * be reserved for cached data depending on the device state which is then passed on
+ * to getStorageCacheBytes.
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ @SuppressLint("StreamFiles")
+ public long computeStorageCacheBytes(@NonNull File path) {
+ final long totalBytes = path.getTotalSpace();
+ final long usableBytes = path.getUsableSpace();
+ final long storageThresholdHighBytes = totalBytes * STORAGE_THRESHOLD_PERCENT_HIGH / 100;
+ final long storageThresholdLowBytes = getStorageLowBytes(path);
+ long result;
+ if (usableBytes > storageThresholdHighBytes) {
+ // If free space is >STORAGE_THRESHOLD_PERCENT_HIGH of total space,
+ // reserve CACHE_RESERVE_PERCENT_HIGH of total space
+ result = totalBytes * CACHE_RESERVE_PERCENT_HIGH / 100;
+ } else if (usableBytes < storageThresholdLowBytes) {
+ // If free space is <min(STORAGE_THRESHOLD_PERCENT_LOW of total space, 500MB),
+ // reserve CACHE_RESERVE_PERCENT_LOW of total space
+ result = totalBytes * CACHE_RESERVE_PERCENT_LOW / 100;
+ } else {
+ // Else, linearly interpolate the amount of space to reserve
+ result = ((CACHE_RESERVE_PERCENT_HIGH - CACHE_RESERVE_PERCENT_LOW)
+ * (usableBytes - storageThresholdHighBytes) + CACHE_RESERVE_PERCENT_HIGH
+ * (storageThresholdHighBytes - storageThresholdLowBytes)) * totalBytes
+ / (100 * (storageThresholdHighBytes - storageThresholdLowBytes));
+ }
+ return result;
+ }
+
+ /**
* Return the minimum number of bytes of storage on the device that should
* be reserved for cached data.
*
* @hide
*/
- public long getStorageCacheBytes(File path, @AllocateFlags int flags) {
- final long cachePercent = Settings.Global.getInt(mResolver,
- Settings.Global.SYS_STORAGE_CACHE_PERCENTAGE, DEFAULT_CACHE_PERCENTAGE);
- final long cacheBytes = (path.getTotalSpace() * cachePercent) / 100;
-
- final long maxCacheBytes = Settings.Global.getLong(mResolver,
- Settings.Global.SYS_STORAGE_CACHE_MAX_BYTES, DEFAULT_CACHE_MAX_BYTES);
-
- final long result = Math.min(cacheBytes, maxCacheBytes);
+ public long getStorageCacheBytes(@NonNull File path, @AllocateFlags int flags) {
if ((flags & StorageManager.FLAG_ALLOCATE_AGGRESSIVE) != 0) {
return 0;
} else if ((flags & StorageManager.FLAG_ALLOCATE_DEFY_ALL_RESERVED) != 0) {
return 0;
} else if ((flags & StorageManager.FLAG_ALLOCATE_DEFY_HALF_RESERVED) != 0) {
- return result / 2;
+ return computeStorageCacheBytes(path) / 2;
} else {
- return result;
+ return computeStorageCacheBytes(path);
}
}
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index 0c2f8b639092..19e04e69f52e 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 {
mStub = 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/print/PrintJobInfo.java b/core/java/android/print/PrintJobInfo.java
index 63f38f8a7b4c..9bdfd8e69c00 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/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index eef1ff7c7703..b9f5144ef49f 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -8796,6 +8796,8 @@ public final class ContactsContract {
/**
* Get the account that is set as the default account for new contacts, which should be
* initially selected when creating a new contact on contact management apps.
+ * If the setting has not been set by any app, it will return null. Once the setting
+ * is set to non-null Account, it can still be set to null in the future.
*
* @param resolver the ContentResolver to query.
* @return the default account for new contacts, or null if it's not set or set to NULL
@@ -8810,7 +8812,13 @@ public final class ContactsContract {
/**
* Sets the account as the default account that should be initially selected
- * when creating a new contact on contact management apps.
+ * when creating a new contact on contact management apps. Apps can only set one of
+ * the following accounts as the default account:
+ * <ol>
+ * <li>null or custom local account
+ * <li>SIM account
+ * <li>AccountManager accounts
+ * </ol>
*
* @param resolver the ContentResolver to query.
* @param account the account to be set to default.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ad6d85f2712b..7228b5ab7feb 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11558,6 +11558,12 @@ public final class Settings {
@Readable
public static final String PACKAGE_VERIFIER_TIMEOUT = "verifier_timeout";
+ /** Timeout for package verification during streaming installations.
+ * @hide */
+ @Readable
+ public static final String PACKAGE_STREAMING_VERIFIER_TIMEOUT =
+ "streaming_verifier_timeout";
+
/** Timeout for app integrity verification.
* @hide */
@Readable
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 50a44a1bb08d..e3c396976f6c 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -5317,6 +5317,13 @@ public final class Telephony {
public static final String COLUMN_PROFILE_CLASS = "profile_class";
/**
+ * TelephonyProvider column name for the port index of the active UICC port.
+ * <P>Type: INTEGER (int)</P>
+ * @hide
+ */
+ public static final String COLUMN_PORT_INDEX = "port_index";
+
+ /**
* A testing profile can be pre-loaded or downloaded onto
* the eUICC and provides connectivity to test equipment
* for the purpose of testing the device and the eUICC. It
@@ -5446,5 +5453,12 @@ public final class Telephony {
*/
public static final String COLUMN_PHONE_NUMBER_SOURCE_IMS =
"phone_number_source_ims";
+
+ /**
+ * TelephonyProvider column name for the device's preferred usage setting.
+ *
+ * @hide
+ */
+ public static final String COLUMN_USAGE_SETTING = "usage_setting";
}
}
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index 3b4f7e241852..f90055829b89 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -317,16 +317,35 @@ public final class KeymasterDefs {
ErrorCode.MISSING_MIN_MAC_LENGTH; // -58;
public static final int KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH =
ErrorCode.UNSUPPORTED_MIN_MAC_LENGTH; // -59;
+ public static final int KM_ERROR_UNSUPPORTED_KDF = ErrorCode.UNSUPPORTED_KDF; // -60
+ public static final int KM_ERROR_UNSUPPORTED_EC_CURVE = ErrorCode.UNSUPPORTED_EC_CURVE; // -61
+ // -62 is KEY_REQUIRES_UPGRADE and is handled by Keystore.
+ public static final int KM_ERROR_ATTESTATION_CHALLENGE_MISSING =
+ ErrorCode.ATTESTATION_CHALLENGE_MISSING; // -63
+ public static final int KM_ERROR_KEYMINT_NOT_CONFIGURED =
+ ErrorCode.KEYMINT_NOT_CONFIGURED; // -64
+ public static final int KM_ERROR_ATTESTATION_APPLICATION_ID_MISSING =
+ ErrorCode.ATTESTATION_APPLICATION_ID_MISSING; // -65;
public static final int KM_ERROR_CANNOT_ATTEST_IDS =
ErrorCode.CANNOT_ATTEST_IDS; // -66;
+ public static final int KM_ERROR_ROLLBACK_RESISTANCE_UNAVAILABLE =
+ ErrorCode.ROLLBACK_RESISTANCE_UNAVAILABLE; // -67;
public static final int KM_ERROR_HARDWARE_TYPE_UNAVAILABLE =
ErrorCode.HARDWARE_TYPE_UNAVAILABLE; // -68;
public static final int KM_ERROR_DEVICE_LOCKED =
ErrorCode.DEVICE_LOCKED; // -72;
+ public static final int KM_ERROR_STORAGE_KEY_UNSUPPORTED =
+ ErrorCode.STORAGE_KEY_UNSUPPORTED; // -77,
+ public static final int KM_ERROR_INCOMPATIBLE_MGF_DIGEST =
+ ErrorCode.INCOMPATIBLE_MGF_DIGEST; // -78,
+ public static final int KM_ERROR_UNSUPPORTED_MGF_DIGEST =
+ ErrorCode.UNSUPPORTED_MGF_DIGEST; // -79,
public static final int KM_ERROR_MISSING_NOT_BEFORE =
ErrorCode.MISSING_NOT_BEFORE; // -80;
public static final int KM_ERROR_MISSING_NOT_AFTER =
ErrorCode.MISSING_NOT_AFTER; // -80;
+ public static final int KM_ERROR_HARDWARE_NOT_YET_AVAILABLE =
+ ErrorCode.HARDWARE_NOT_YET_AVAILABLE; // -85
public static final int KM_ERROR_UNIMPLEMENTED =
ErrorCode.UNIMPLEMENTED; // -100;
public static final int KM_ERROR_VERSION_MISMATCH =
diff --git a/core/java/android/service/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/cloudsearch/OWNERS b/core/java/android/service/cloudsearch/OWNERS
new file mode 100644
index 000000000000..aa4da3b4bee0
--- /dev/null
+++ b/core/java/android/service/cloudsearch/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 758286
+
+huiwu@google.com
+srazdan@google.com
diff --git a/core/java/android/service/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/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/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/selectiontoolbar/ISelectionToolbarRenderService.aidl b/core/java/android/service/selectiontoolbar/ISelectionToolbarRenderService.aidl
new file mode 100644
index 000000000000..2bd99acbf24a
--- /dev/null
+++ b/core/java/android/service/selectiontoolbar/ISelectionToolbarRenderService.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.selectiontoolbar;
+
+import android.view.selectiontoolbar.ISelectionToolbarCallback;
+import android.view.selectiontoolbar.ShowInfo;
+
+/**
+ * The service to render the selection toolbar menus.
+ *
+ * @hide
+ */
+oneway interface ISelectionToolbarRenderService {
+ void onShow(in ShowInfo showInfo, in ISelectionToolbarCallback callback);
+ void onHide(long widgetToken);
+ void onDismiss(long widgetToken);
+}
diff --git a/core/java/android/service/selectiontoolbar/OWNERS b/core/java/android/service/selectiontoolbar/OWNERS
new file mode 100644
index 000000000000..5500b92868dd
--- /dev/null
+++ b/core/java/android/service/selectiontoolbar/OWNERS
@@ -0,0 +1,10 @@
+# Bug component: 709498
+
+augale@google.com
+joannechung@google.com
+licha@google.com
+lpeter@google.com
+svetoslavganov@google.com
+toki@google.com
+tonymak@google.com
+tymtsai@google.com \ No newline at end of file
diff --git a/core/java/android/service/selectiontoolbar/SelectionToolbarRenderCallback.java b/core/java/android/service/selectiontoolbar/SelectionToolbarRenderCallback.java
new file mode 100644
index 000000000000..6468183880f4
--- /dev/null
+++ b/core/java/android/service/selectiontoolbar/SelectionToolbarRenderCallback.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.selectiontoolbar;
+
+import android.view.selectiontoolbar.ToolbarMenuItem;
+import android.view.selectiontoolbar.WidgetInfo;
+
+/**
+ * The callback that the render service uses to communicate with the host of the selection toolbar
+ * container.
+ *
+ * @hide
+ */
+public interface SelectionToolbarRenderCallback {
+ /**
+ * The selection toolbar is shown.
+ */
+ void onShown(WidgetInfo widgetInfo);
+ /**
+ * The selection toolbar is hidden.
+ */
+ void onHidden(long widgetToken);
+ /**
+ * The selection toolbar is dismissed.
+ */
+ void onDismissed(long widgetToken);
+ /**
+ * The selection toolbar has changed.
+ */
+ void onWidgetUpdated(WidgetInfo info);
+ /**
+ * The menu item on the selection toolbar has been clicked.
+ */
+ void onMenuItemClicked(ToolbarMenuItem item);
+ /**
+ * The error occurred when operating on the selection toolbar.
+ */
+ void onError(int errorCode);
+}
diff --git a/core/java/android/service/selectiontoolbar/SelectionToolbarRenderService.java b/core/java/android/service/selectiontoolbar/SelectionToolbarRenderService.java
new file mode 100644
index 000000000000..6f66c9f1ed3a
--- /dev/null
+++ b/core/java/android/service/selectiontoolbar/SelectionToolbarRenderService.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.selectiontoolbar;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.selectiontoolbar.ISelectionToolbarCallback;
+import android.view.selectiontoolbar.ShowInfo;
+import android.view.selectiontoolbar.ToolbarMenuItem;
+import android.view.selectiontoolbar.WidgetInfo;
+
+/**
+ * Service for rendering selection toolbar.
+ *
+ * @hide
+ */
+public abstract class SelectionToolbarRenderService extends Service {
+
+ private static final String TAG = "SelectionToolbarRenderService";
+
+ /**
+ * The {@link Intent} that must be declared as handled by the service.
+ *
+ * <p>To be supported, the service must also require the
+ * {@link android.Manifest.permission#BIND_SELECTION_TOOLBAR_RENDER_SERVICE} permission so
+ * that other applications can not abuse it.
+ */
+ public static final String SERVICE_INTERFACE =
+ "android.service.selectiontoolbar.SelectionToolbarRenderService";
+
+ private Handler mHandler;
+
+ /**
+ * Binder to receive calls from system server.
+ */
+ private final ISelectionToolbarRenderService mInterface =
+ new ISelectionToolbarRenderService.Stub() {
+
+ @Override
+ public void onShow(ShowInfo showInfo, ISelectionToolbarCallback callback) {
+ mHandler.sendMessage(obtainMessage(SelectionToolbarRenderService::onShow,
+ SelectionToolbarRenderService.this, showInfo,
+ new RemoteCallbackWrapper(callback)));
+ }
+
+ @Override
+ public void onHide(long widgetToken) {
+ mHandler.sendMessage(obtainMessage(SelectionToolbarRenderService::onHide,
+ SelectionToolbarRenderService.this, widgetToken));
+ }
+
+ @Override
+ public void onDismiss(long widgetToken) {
+ mHandler.sendMessage(obtainMessage(SelectionToolbarRenderService::onDismiss,
+ SelectionToolbarRenderService.this, widgetToken));
+ }
+ };
+
+ @CallSuper
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mHandler = new Handler(Looper.getMainLooper(), null, true);
+ }
+
+ @Override
+ @Nullable
+ public final IBinder onBind(@NonNull Intent intent) {
+ if (SERVICE_INTERFACE.equals(intent.getAction())) {
+ return mInterface.asBinder();
+ }
+ Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent);
+ return null;
+ }
+
+
+ /**
+ * Called when showing the selection toolbar.
+ */
+ public abstract void onShow(ShowInfo showInfo, RemoteCallbackWrapper callbackWrapper);
+
+ /**
+ * Called when hiding the selection toolbar.
+ */
+ public abstract void onHide(long widgetToken);
+
+
+ /**
+ * Called when dismissing the selection toolbar.
+ */
+ public abstract void onDismiss(long widgetToken);
+
+ /**
+ * Add avadoc.
+ */
+ public static final class RemoteCallbackWrapper implements SelectionToolbarRenderCallback {
+
+ private final ISelectionToolbarCallback mRemoteCallback;
+
+ RemoteCallbackWrapper(ISelectionToolbarCallback remoteCallback) {
+ mRemoteCallback = remoteCallback;
+ }
+
+ @Override
+ public void onShown(WidgetInfo widgetInfo) {
+ try {
+ mRemoteCallback.onShown(widgetInfo);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void onHidden(long widgetToken) {
+ try {
+ mRemoteCallback.onHidden(widgetToken);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void onDismissed(long widgetToken) {
+ try {
+ mRemoteCallback.onDismissed(widgetToken);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void onWidgetUpdated(WidgetInfo widgetInfo) {
+ try {
+ mRemoteCallback.onWidgetUpdated(widgetInfo);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void onMenuItemClicked(ToolbarMenuItem item) {
+ try {
+ mRemoteCallback.onMenuItemClicked(item);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void onError(int errorCode) {
+ try {
+ mRemoteCallback.onError(errorCode);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+ }
+}
diff --git a/core/java/android/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/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/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 83a6bc0ec99d..73ffd66486d2 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -44,7 +44,6 @@ import android.content.res.TypedArray;
import android.graphics.BLASTBufferQueue;
import android.graphics.Bitmap;
import android.graphics.Canvas;
-import android.graphics.GraphicBuffer;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.graphics.Point;
@@ -1930,9 +1929,7 @@ public abstract class WallpaperService extends Service {
mScreenshotSize.set(mSurfaceSize.x, mSurfaceSize.y);
- GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(hardwareBuffer);
-
- t.setBuffer(mScreenshotSurfaceControl, graphicBuffer);
+ t.setBuffer(mScreenshotSurfaceControl, hardwareBuffer);
t.setColorSpace(mScreenshotSurfaceControl, screenshotBuffer.getColorSpace());
// Place on top everything else.
t.setLayer(mScreenshotSurfaceControl, Integer.MAX_VALUE);
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/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 859fd804dea5..9eaaa91532d0 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -36,18 +36,24 @@ import android.telephony.Annotation.PreciseDisconnectCauses;
import android.telephony.Annotation.RadioPowerState;
import android.telephony.Annotation.SimActivationState;
import android.telephony.Annotation.SrvccState;
+import android.telephony.TelephonyManager.CarrierPrivilegesListener;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.ImsReasonInfo;
import android.util.ArraySet;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.listeners.ListenerExecutor;
+import com.android.internal.telephony.ICarrierPrivilegesListener;
import com.android.internal.telephony.IOnSubscriptionsChangedListener;
import com.android.internal.telephony.ITelephonyRegistry;
+import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.WeakHashMap;
import java.util.concurrent.Executor;
/**
@@ -285,6 +291,8 @@ public class TelephonyRegistryManager {
* UI. There is no timeout associated with showing this UX, so a carrier app must be sure to
* call with active set to false sometime after calling with it set to {@code true}.
* <p>
+ * This will apply to all subscriptions the carrier app has carrier privileges on.
+ * <p>
* Requires Permission: calling app has carrier privileges.
*
* @param active Whether the carrier network change is or shortly will be
@@ -300,6 +308,31 @@ public class TelephonyRegistryManager {
}
/**
+ * Informs the system of an intentional upcoming carrier network change by a carrier app on the
+ * given {@code subscriptionId}. This call only used to allow the system to provide alternative
+ * UI while telephony is performing an action that may result in intentional, temporary network
+ * lack of connectivity.
+ * <p>
+ * Based on the active parameter passed in, this method will either show or hide the
+ * alternative UI. There is no timeout associated with showing this UX, so a carrier app must be
+ * sure to call with active set to false sometime after calling with it set to {@code true}.
+ * <p>
+ * Requires Permission: calling app has carrier privileges.
+ *
+ * @param subscriptionId the subscription of the carrier network.
+ * @param active whether the carrier network change is or shortly will be active. Set this value
+ * to true to begin showing alternative UI and false to stop.
+ * @see TelephonyManager#hasCarrierPrivileges
+ */
+ public void notifyCarrierNetworkChange(int subscriptionId, boolean active) {
+ try {
+ sRegistry.notifyCarrierNetworkChangeWithSubId(subscriptionId, active);
+ } catch (RemoteException ex) {
+ // system server crash
+ }
+ }
+
+ /**
* Notify call state changed on certain subscription.
*
* @param slotIndex for which call state changed. Can be derived from subId except when subId is
@@ -1187,4 +1220,117 @@ public class TelephonyRegistryManager {
listenFromCallback(false, false, subId,
pkgName, attributionTag, callback, new int[0], notifyNow);
}
+
+ private static class CarrierPrivilegesListenerWrapper extends ICarrierPrivilegesListener.Stub
+ implements ListenerExecutor {
+ private final WeakReference<CarrierPrivilegesListener> mListener;
+ private final Executor mExecutor;
+
+ CarrierPrivilegesListenerWrapper(CarrierPrivilegesListener listener, Executor executor) {
+ mListener = new WeakReference<>(listener);
+ mExecutor = executor;
+ }
+
+ @Override
+ public void onCarrierPrivilegesChanged(
+ List<String> privilegedPackageNames, int[] privilegedUids) {
+ Binder.withCleanCallingIdentity(
+ () ->
+ executeSafely(
+ mExecutor,
+ mListener::get,
+ cpl ->
+ cpl.onCarrierPrivilegesChanged(
+ privilegedPackageNames, privilegedUids)));
+ }
+ }
+
+ @GuardedBy("sCarrierPrivilegeListeners")
+ private static final WeakHashMap<
+ CarrierPrivilegesListener, WeakReference<CarrierPrivilegesListenerWrapper>>
+ sCarrierPrivilegeListeners = new WeakHashMap<>();
+
+ /**
+ * Registers a {@link CarrierPrivilegesListener} on the given {@code logicalSlotIndex} to
+ * receive callbacks when the set of packages with carrier privileges changes. The callback will
+ * immediately be called with the latest state.
+ *
+ * @param logicalSlotIndex The SIM slot to listen on
+ * @param executor The executor where {@code listener} will be invoked
+ * @param listener The callback to register
+ */
+ public void addCarrierPrivilegesListener(
+ int logicalSlotIndex,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull CarrierPrivilegesListener listener) {
+ if (listener == null || executor == null) {
+ throw new IllegalArgumentException("listener and executor must be non-null");
+ }
+ synchronized (sCarrierPrivilegeListeners) {
+ WeakReference<CarrierPrivilegesListenerWrapper> existing =
+ sCarrierPrivilegeListeners.get(listener);
+ if (existing != null && existing.get() != null) {
+ Log.d(TAG, "addCarrierPrivilegesListener: listener already registered");
+ return;
+ }
+ CarrierPrivilegesListenerWrapper wrapper =
+ new CarrierPrivilegesListenerWrapper(listener, executor);
+ sCarrierPrivilegeListeners.put(listener, new WeakReference<>(wrapper));
+ try {
+ sRegistry.addCarrierPrivilegesListener(
+ logicalSlotIndex,
+ wrapper,
+ mContext.getOpPackageName(),
+ mContext.getAttributionTag());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Unregisters a {@link CarrierPrivilegesListener}.
+ *
+ * @param listener The callback to unregister
+ */
+ public void removeCarrierPrivilegesListener(@NonNull CarrierPrivilegesListener listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException("listener must be non-null");
+ }
+ synchronized (sCarrierPrivilegeListeners) {
+ WeakReference<CarrierPrivilegesListenerWrapper> ref =
+ sCarrierPrivilegeListeners.remove(listener);
+ if (ref == null) return;
+ CarrierPrivilegesListenerWrapper wrapper = ref.get();
+ if (wrapper == null) return;
+ try {
+ sRegistry.removeCarrierPrivilegesListener(wrapper, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Notify listeners that the set of packages with carrier privileges has changed.
+ *
+ * @param logicalSlotIndex The SIM slot the change occurred on
+ * @param privilegedPackageNames The updated set of packages names with carrier privileges
+ * @param privilegedUids The updated set of UIDs with carrier privileges
+ */
+ public void notifyCarrierPrivilegesChanged(
+ int logicalSlotIndex,
+ @NonNull List<String> privilegedPackageNames,
+ @NonNull int[] privilegedUids) {
+ if (privilegedPackageNames == null || privilegedUids == null) {
+ throw new IllegalArgumentException(
+ "privilegedPackageNames and privilegedUids must be non-null");
+ }
+ try {
+ sRegistry.notifyCarrierPrivilegesChanged(
+ logicalSlotIndex, privilegedPackageNames, privilegedUids);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/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/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 6984e4dfccc4..4789231b0404 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -874,6 +874,18 @@ public class StaticLayout extends Layout {
? Math.max(fmDescent, Math.round(descents[breakIndex]))
: fmDescent;
+ // The fallback ascent/descent may be larger than top/bottom of the default font
+ // metrics. Adjust top/bottom with ascent/descent for avoiding unexpected
+ // clipping.
+ if (fallbackLineSpacing) {
+ if (ascent < fmTop) {
+ fmTop = ascent;
+ }
+ if (descent > fmBottom) {
+ fmBottom = descent;
+ }
+ }
+
v = out(source, here, endPos,
ascent, descent, fmTop, fmBottom,
v, spacingmult, spacingadd, chooseHt, chooseHtv, fm,
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 7e2792c9664f..8124510718ae 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -58,6 +58,10 @@ public class FeatureFlagUtils {
*/
public static final String SETTINGS_APP_LANGUAGE_SELECTION = "settings_app_language_selection";
+ /** @hide */
+ public static final String SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS =
+ "settings_enable_monitor_phantom_procs";
+
private static final Map<String, String> DEFAULT_FLAGS;
static {
@@ -81,6 +85,7 @@ public class FeatureFlagUtils {
DEFAULT_FLAGS.put(SETTINGS_ENABLE_SECURITY_HUB, "true");
DEFAULT_FLAGS.put(SETTINGS_SUPPORT_LARGE_SCREEN, "true");
DEFAULT_FLAGS.put(SETTINGS_APP_LANGUAGE_SELECTION, "false");
+ DEFAULT_FLAGS.put(SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS, "true");
}
private static final Set<String> PERSISTENT_FLAGS;
@@ -89,6 +94,7 @@ public class FeatureFlagUtils {
PERSISTENT_FLAGS.add(SETTINGS_APP_LANGUAGE_SELECTION);
PERSISTENT_FLAGS.add(SETTINGS_PROVIDER_MODEL);
PERSISTENT_FLAGS.add(SETTINGS_SUPPORT_LARGE_SCREEN);
+ PERSISTENT_FLAGS.add(SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS);
}
/**
diff --git a/core/java/android/util/IntArray.java b/core/java/android/util/IntArray.java
index b77265b0ebf6..7b28b8a607de 100644
--- a/core/java/android/util/IntArray.java
+++ b/core/java/android/util/IntArray.java
@@ -34,7 +34,7 @@ public class IntArray implements Cloneable {
private int[] mValues;
private int mSize;
- private IntArray(int[] array, int size) {
+ private IntArray(int[] array, int size) {
mValues = array;
mSize = Preconditions.checkArgumentInRange(size, 0, array.length, "size");
}
@@ -178,10 +178,8 @@ public class IntArray implements Cloneable {
}
@Override
- public IntArray clone() throws CloneNotSupportedException {
- final IntArray clone = (IntArray) super.clone();
- clone.mValues = mValues.clone();
- return clone;
+ public IntArray clone() {
+ return new IntArray(mValues.clone(), mSize);
}
/**
diff --git a/core/java/android/util/MemoryIntArray.java b/core/java/android/util/MemoryIntArray.java
index 9073d3ae91ca..6da38c2c2acb 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/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index b8614ccde6fd..678c80a2094b 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -449,8 +449,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 +475,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();
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/MotionEvent.java b/core/java/android/view/MotionEvent.java
index adb8b86493d5..8db62f6553eb 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -1872,7 +1872,7 @@ public final class MotionEvent extends InputEvent implements Parcelable {
float x, float y, float pressure, float size, int metaState,
float xPrecision, float yPrecision, int deviceId, int edgeFlags) {
return obtain(downTime, eventTime, action, x, y, pressure, size, metaState,
- xPrecision, yPrecision, deviceId, edgeFlags, InputDevice.SOURCE_UNKNOWN,
+ xPrecision, yPrecision, deviceId, edgeFlags, InputDevice.SOURCE_CLASS_POINTER,
DEFAULT_DISPLAY);
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index af7d86cdd1d9..3b5270960c99 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -120,6 +120,8 @@ public final class SurfaceControl implements Parcelable {
long relativeToObject, int zorder);
private static native void nativeSetPosition(long transactionObj, long nativeObject,
float x, float y);
+ private static native void nativeSetScale(long transactionObj, long nativeObject,
+ float x, float y);
private static native void nativeSetSize(long transactionObj, long nativeObject, int w, int h);
private static native void nativeSetTransparentRegionHint(long transactionObj,
long nativeObject, Region region);
@@ -202,9 +204,13 @@ public final class SurfaceControl implements Parcelable {
private static native void nativeReparent(long transactionObj, long nativeObject,
long newParentNativeObject);
private static native void nativeSetBuffer(long transactionObj, long nativeObject,
- GraphicBuffer buffer);
+ HardwareBuffer buffer);
+ private static native void nativeSetBufferTransform(long transactionObj, long nativeObject,
+ int transform);
private static native void nativeSetColorSpace(long transactionObj, long nativeObject,
int colorSpace);
+ private static native void nativeSetDamageRegion(long transactionObj, long nativeObject,
+ Region region);
private static native void nativeOverrideHdrTypes(IBinder displayToken, int[] modes);
@@ -1333,7 +1339,6 @@ public final class SurfaceControl implements Parcelable {
* Set the initial visibility for the SurfaceControl.
*
* @param hidden Whether the Surface is initially HIDDEN.
- * @hide
*/
@NonNull
public Builder setHidden(boolean hidden) {
@@ -2840,16 +2845,38 @@ public final class SurfaceControl implements Parcelable {
}
/**
- * @hide
+ * Sets the SurfaceControl to the specified position relative to the parent
+ * SurfaceControl
+ *
+ * @param sc The SurfaceControl to change position
+ * @param x the X position
+ * @param y the Y position
+ * @return this transaction
*/
- @UnsupportedAppUsage
- public Transaction setPosition(SurfaceControl sc, float x, float y) {
+ @NonNull
+ public Transaction setPosition(@NonNull SurfaceControl sc, float x, float y) {
checkPreconditions(sc);
nativeSetPosition(mNativeObject, sc.mNativeObject, x, y);
return this;
}
/**
+ * Sets the SurfaceControl to the specified scale with (0, 0) as the center point
+ * of the scale.
+ *
+ * @param sc The SurfaceControl to change scale
+ * @param scaleX the X scale
+ * @param scaleY the Y scale
+ * @return this transaction
+ */
+ @NonNull
+ public Transaction setScale(@NonNull SurfaceControl sc, float scaleX, float scaleY) {
+ checkPreconditions(sc);
+ nativeSetScale(mNativeObject, sc.mNativeObject, scaleX, scaleY);
+ return this;
+ }
+
+ /**
* Set the default buffer size for the SurfaceControl, if there is a
* {@link Surface} associated with the control, then
* this will be the default size for buffers dequeued from it.
@@ -3056,7 +3083,9 @@ public final class SurfaceControl implements Parcelable {
* @param sc SurfaceControl to set crop of.
* @param crop Bounds of the crop to apply.
* @hide
+ * @deprecated Use {@link #setCrop(SurfaceControl, Rect)} instead.
*/
+ @Deprecated
@UnsupportedAppUsage
public Transaction setWindowCrop(SurfaceControl sc, Rect crop) {
checkPreconditions(sc);
@@ -3071,6 +3100,28 @@ public final class SurfaceControl implements Parcelable {
}
/**
+ * Bounds the surface and its children to the bounds specified. Size of the surface will be
+ * ignored and only the crop and buffer size will be used to determine the bounds of the
+ * surface. If no crop is specified and the surface has no buffer, the surface bounds is
+ * only constrained by the size of its parent bounds.
+ *
+ * @param sc SurfaceControl to set crop of.
+ * @param crop Bounds of the crop to apply.
+ * @return this This transaction for chaining
+ */
+ public @NonNull Transaction setCrop(@NonNull SurfaceControl sc, @Nullable Rect crop) {
+ checkPreconditions(sc);
+ if (crop != null) {
+ nativeSetWindowCrop(mNativeObject, sc.mNativeObject,
+ crop.left, crop.top, crop.right, crop.bottom);
+ } else {
+ nativeSetWindowCrop(mNativeObject, sc.mNativeObject, 0, 0, 0, 0);
+ }
+
+ return this;
+ }
+
+ /**
* Same as {@link Transaction#setWindowCrop(SurfaceControl, Rect)} but sets the crop rect
* top left at 0, 0.
*
@@ -3215,11 +3266,34 @@ public final class SurfaceControl implements Parcelable {
}
/**
- * Sets the opacity of the surface. Setting the flag is equivalent to creating the
- * Surface with the {@link #OPAQUE} flag.
- * @hide
+ * Indicates whether the surface must be considered opaque, even if its pixel format is
+ * set to translucent. This can be useful if an application needs full RGBA 8888 support
+ * for instance but will still draw every pixel opaque.
+ * <p>
+ * This flag only determines whether opacity will be sampled from the alpha channel.
+ * Plane-alpha from calls to setAlpha() can still result in blended composition
+ * regardless of the opaque setting.
+ *
+ * Combined effects are (assuming a buffer format with an alpha channel):
+ * <ul>
+ * <li>OPAQUE + alpha(1.0) == opaque composition
+ * <li>OPAQUE + alpha(0.x) == blended composition
+ * <li>OPAQUE + alpha(0.0) == no composition
+ * <li>!OPAQUE + alpha(1.0) == blended composition
+ * <li>!OPAQUE + alpha(0.x) == blended composition
+ * <li>!OPAQUE + alpha(0.0) == no composition
+ * </ul>
+ * If the underlying buffer lacks an alpha channel, it is as if setOpaque(true)
+ * were set automatically.
+ *
+ * @see Builder#setOpaque(boolean)
+ *
+ * @param sc The SurfaceControl to update
+ * @param isOpaque true if the buffer's alpha should be ignored, false otherwise
+ * @return this
*/
- public Transaction setOpaque(SurfaceControl sc, boolean isOpaque) {
+ @NonNull
+ public Transaction setOpaque(@NonNull SurfaceControl sc, boolean isOpaque) {
checkPreconditions(sc);
if (isOpaque) {
nativeSetFlags(mNativeObject, sc.mNativeObject, SURFACE_OPAQUE, SURFACE_OPAQUE);
@@ -3498,14 +3572,57 @@ public final class SurfaceControl implements Parcelable {
* created as type {@link #FX_SURFACE_BLAST}
*
* @hide
+ * @deprecated Use {@link #setBuffer(SurfaceControl, HardwareBuffer)} instead
*/
+ @Deprecated
public Transaction setBuffer(SurfaceControl sc, GraphicBuffer buffer) {
+ return setBuffer(sc, HardwareBuffer.createFromGraphicBuffer(buffer));
+ }
+
+ /**
+ * Updates the HardwareBuffer displayed for the SurfaceControl.
+ *
+ * Note that the buffer must be allocated with {@link HardwareBuffer#USAGE_COMPOSER_OVERLAY}
+ * as well as {@link HardwareBuffer#USAGE_GPU_SAMPLED_IMAGE} as the surface control might
+ * be composited using either an overlay or using the GPU.
+ *
+ * @param sc The SurfaceControl to update
+ * @param buffer The buffer to be displayed
+ * @return this
+ */
+ public @NonNull Transaction setBuffer(@NonNull SurfaceControl sc,
+ @Nullable HardwareBuffer buffer) {
checkPreconditions(sc);
nativeSetBuffer(mNativeObject, sc.mNativeObject, buffer);
return this;
}
/**
+ * Sets the buffer transform that should be applied to the current buffer.
+ *
+ * @param sc The SurfaceControl to update
+ * @param transform The transform to apply to the buffer.
+ * @return this
+ */
+ public @NonNull Transaction setBufferTransform(@NonNull SurfaceControl sc,
+ /* TODO: Mark the intdef */ int transform) {
+ checkPreconditions(sc);
+ nativeSetBufferTransform(mNativeObject, sc.mNativeObject, transform);
+ return this;
+ }
+
+ /**
+ * Updates the region for the content on this surface updated in this transaction.
+ *
+ * If unspecified, the complete surface is assumed to be damaged.
+ */
+ public @NonNull Transaction setDamageRegion(@NonNull SurfaceControl sc,
+ @Nullable Region region) {
+ nativeSetDamageRegion(mNativeObject, sc.mNativeObject, region);
+ return this;
+ }
+
+ /**
* Set the color space for the SurfaceControl. The supported color spaces are SRGB
* and Display P3, other color spaces will be treated as SRGB. This can only be used for
* SurfaceControls that were created as type {@link #FX_SURFACE_BLAST}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f5291725b857..8fda48b1a36a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -26977,44 +26977,38 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* Handles drag events sent by the system following a call to
* {@link android.view.View#startDragAndDrop(ClipData,DragShadowBuilder,Object,int)
* startDragAndDrop()}.
- *<p>
- * When the system calls this method, it passes a
- * {@link android.view.DragEvent} object. A call to
- * {@link android.view.DragEvent#getAction()} returns one of the action type constants defined
- * in DragEvent. The method uses these to determine what is happening in the drag and drop
- * operation.
- * </p>
* <p>
- * The default implementation returns false, except if an {@link OnReceiveContentListener}
- * is {@link #setOnReceiveContentListener set} for this view. If an
- * {@link OnReceiveContentListener} is set, the default implementation...
+ * The system calls this method and passes a {@link DragEvent} object in response to drag and
+ * drop events. This method can then call {@link DragEvent#getAction()} to determine the state
+ * of the drag and drop operation.
+ * <p>
+ * The default implementation returns {@code false} unless an {@link OnReceiveContentListener}
+ * has been set for this view (see {@link #setOnReceiveContentListener}), in which case
+ * the default implementation does the following:
* <ul>
- * <li>returns true for an
- * {@link android.view.DragEvent#ACTION_DRAG_STARTED ACTION_DRAG_STARTED} event
- * <li>calls {@link #performReceiveContent} for an
- * {@link android.view.DragEvent#ACTION_DROP ACTION_DROP} event
- * <li>returns true for an {@link android.view.DragEvent#ACTION_DROP ACTION_DROP} event, if
- * the listener consumed some or all of the content
+ * <li>Returns {@code true} for an
+ * {@link DragEvent#ACTION_DRAG_STARTED ACTION_DRAG_STARTED} event
+ * <li>Calls {@link #performReceiveContent} for an
+ * {@link DragEvent#ACTION_DROP ACTION_DROP} event
+ * <li>Returns {@code true} for an {@link DragEvent#ACTION_DROP ACTION_DROP} event if the
+ * {@code OnReceiveContentListener} consumed some or all of the content
* </ul>
- * </p>
*
- * @param event The {@link android.view.DragEvent} sent by the system.
- * The {@link android.view.DragEvent#getAction()} method returns an action type constant defined
- * in DragEvent, indicating the type of drag event represented by this object.
- * @return {@code true} if the method was successful, otherwise {@code false}.
- * <p>
- * The method should return {@code true} in response to an action type of
- * {@link android.view.DragEvent#ACTION_DRAG_STARTED} to receive drag events for the current
- * operation.
- * </p>
- * <p>
- * The method should also return {@code true} in response to an action type of
- * {@link android.view.DragEvent#ACTION_DROP} if it consumed the drop, or
- * {@code false} if it didn't.
- * </p>
- * <p>
- * For all other events, the return value is ignored.
- * </p>
+ * @param event The {@link DragEvent} object sent by the system. The
+ * {@link DragEvent#getAction()} method returns an action type constant that indicates the
+ * type of drag event represented by this object.
+ * @return {@code true} if the method successfully handled the drag event, otherwise
+ * {@code false}.
+ * <p>
+ * The method must return {@code true} in response to an
+ * {@link DragEvent#ACTION_DRAG_STARTED ACTION_DRAG_STARTED} action type to continue to
+ * receive drag events for the current drag and drop operation.
+ * <p>
+ * The method should return {@code true} in response to an
+ * {@link DragEvent#ACTION_DROP ACTION_DROP} action type if the dropped data was consumed
+ * (at least partially); {@code false}, if none of the data was consumed.
+ * <p>
+ * For all other events, the return value is {@code false}.
*/
public boolean onDragEvent(DragEvent event) {
if (mListenerInfo == null || mListenerInfo.mOnReceiveContentListener == null) {
@@ -29003,27 +28997,27 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
- * Interface definition for a callback to be invoked when a drag is being dispatched
- * to this view. The callback will be invoked before the hosting view's own
- * onDrag(event) method. If the listener wants to fall back to the hosting view's
- * onDrag(event) behavior, it should return 'false' from this callback.
+ * Interface definition for a listener that's invoked when a drag event is dispatched to this
+ * view. The listener is invoked before the view's own
+ * {@link #onDragEvent(DragEvent)} method. To fall back to the view's
+ * {@code onDragEvent(DragEvent)} behavior, return {@code false} from the listener method.
*
* <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>For a guide to implementing drag and drop features, read the
- * <a href="{@docRoot}guide/topics/ui/drag-drop.html">Drag and Drop</a> developer guide.</p>
+ * <h3>Developer Guides</h3>
+ * <p>For a guide to implementing drag and drop features, see the
+ * <a href="{@docRoot}guide/topics/ui/drag-drop.html">Drag and drop</a> developer guide.</p>
* </div>
*/
public interface OnDragListener {
/**
- * Called when a drag event is dispatched to a view. This allows listeners
- * to get a chance to override base View behavior.
+ * Called when a drag event is dispatched to a view. Enables listeners to override the
+ * base behavior provided by {@link #onDragEvent(DragEvent)}.
*
- * @param v The View that received the drag event.
- * @param event The {@link android.view.DragEvent} object for the drag event.
- * @return {@code true} if the drag event was handled successfully, or {@code false}
- * if the drag event was not handled. Note that {@code false} will trigger the View
- * to call its {@link #onDragEvent(DragEvent) onDragEvent()} handler.
+ * @param v The {@code View} that received the drag event.
+ * @param event The event object for the drag event.
+ * @return {@code true} if the drag event was handled successfully; {@code false}, if the
+ * drag event was not handled. <b>Note:</b> A {@code false} return value triggers the
+ * view's {@link #onDragEvent(DragEvent)} handler.
*/
boolean onDrag(View v, DragEvent event);
}
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 07d5fc533a3a..25e0eca12bf3 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -1896,7 +1896,7 @@ public class ViewDebug {
private Canvas mCanvas;
private Bitmap mBitmap;
- private boolean mEnabledHwBitmapsInSwMode;
+ private boolean mEnabledHwFeaturesInSwMode;
@Override
public Canvas getCanvas(View view, int width, int height) {
@@ -1913,7 +1913,7 @@ public class ViewDebug {
if (mCanvas == null) {
mCanvas = new Canvas();
}
- mEnabledHwBitmapsInSwMode = mCanvas.isHwBitmapsInSwModeEnabled();
+ mEnabledHwFeaturesInSwMode = mCanvas.isHwFeaturesInSwModeEnabled();
mCanvas.setBitmap(mBitmap);
return mCanvas;
}
@@ -1921,7 +1921,7 @@ public class ViewDebug {
@Override
public Bitmap createBitmap() {
mCanvas.setBitmap(null);
- mCanvas.setHwBitmapsInSwModeEnabled(mEnabledHwBitmapsInSwMode);
+ mCanvas.setHwFeaturesInSwModeEnabled(mEnabledHwFeaturesInSwMode);
return mBitmap;
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 70505fcc09b5..c37120220f1a 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -93,6 +93,7 @@ import android.annotation.Nullable;
import android.annotation.UiContext;
import android.app.ActivityManager;
import android.app.ActivityThread;
+import android.app.ICompatCameraControlCallback;
import android.app.ResourcesManager;
import android.app.WindowConfiguration;
import android.compat.annotation.UnsupportedAppUsage;
@@ -317,7 +318,7 @@ public final class ViewRootImpl implements ViewParent,
private static final ArrayList<ConfigChangedCallback> sConfigCallbacks = new ArrayList<>();
/**
- * Callback for notifying activities about override configuration changes.
+ * Callback for notifying activities.
*/
public interface ActivityConfigCallback {
@@ -327,11 +328,23 @@ public final class ViewRootImpl implements ViewParent,
* @param newDisplayId New display id, {@link Display#INVALID_DISPLAY} if not changed.
*/
void onConfigurationChanged(Configuration overrideConfig, int newDisplayId);
+
+ /**
+ * Notify the corresponding activity about the request to show or hide a camera compat
+ * control for stretched issues in the viewfinder.
+ *
+ * @param showControl Whether the control should be shown or hidden.
+ * @param transformationApplied Whether the treatment is already applied.
+ * @param callback The callback executed when the user clicks on a control.
+ */
+ void requestCompatCameraControl(boolean showControl, boolean transformationApplied,
+ ICompatCameraControlCallback callback);
}
/**
- * Callback used to notify corresponding activity about override configuration change and make
- * sure that all resources are set correctly before updating the ViewRootImpl's internal state.
+ * Callback used to notify corresponding activity about camera compat control changes, override
+ * configuration change and make sure that all resources are set correctly before updating the
+ * ViewRootImpl's internal state.
*/
private ActivityConfigCallback mActivityConfigCallback;
@@ -865,7 +878,10 @@ public final class ViewRootImpl implements ViewParent,
}
}
- /** Add activity config callback to be notified about override config changes. */
+ /**
+ * Add activity config callback to be notified about override config changes and camera
+ * compat control state updates.
+ */
public void setActivityConfigCallback(ActivityConfigCallback callback) {
mActivityConfigCallback = callback;
}
@@ -10498,6 +10514,20 @@ public final class ViewRootImpl implements ViewParent,
}
/**
+ * Shows or hides a Camera app compat toggle for stretched issues with the requested state
+ * for the corresponding activity.
+ *
+ * @param showControl Whether the control should be shown or hidden.
+ * @param transformationApplied Whether the treatment is already applied.
+ * @param callback The callback executed when the user clicks on a control.
+ */
+ public void requestCompatCameraControl(boolean showControl, boolean transformationApplied,
+ ICompatCameraControlCallback callback) {
+ mActivityConfigCallback.requestCompatCameraControl(
+ showControl, transformationApplied, callback);
+ }
+
+ /**
* Redirect the next draw of this ViewRoot (from the UI thread perspective)
* to the passed in consumer. This can be used to create P2P synchronization
* between ViewRoot's however it comes with many caveats.
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 6bfb14b1676d..83712b40c452 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -1332,7 +1332,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/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 370c9600e222..9511958529e5 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -4101,10 +4101,12 @@ public class AccessibilityNodeInfo implements Parcelable {
: new CollectionInfo(ci.mRowCount, ci.mColumnCount,
ci.mHierarchical, ci.mSelectionMode);
CollectionItemInfo cii = other.mCollectionItemInfo;
+ CollectionItemInfo.Builder builder = new CollectionItemInfo.Builder();
mCollectionItemInfo = (cii == null) ? null
- : new CollectionItemInfo(cii.mRowTitle, cii.mRowIndex, cii.mRowSpan,
- cii.mColumnTitle, cii.mColumnIndex, cii.mColumnSpan,
- cii.mHeading, cii.mSelected);
+ : builder.setRowTitle(cii.mRowTitle).setRowIndex(cii.mRowIndex).setRowSpan(
+ cii.mRowSpan).setColumnTitle(cii.mColumnTitle).setColumnIndex(
+ cii.mColumnIndex).setColumnSpan(cii.mColumnSpan).setHeading(
+ cii.mHeading).setSelected(cii.mSelected).build();
ExtraRenderingInfo ti = other.mExtraRenderingInfo;
mExtraRenderingInfo = (ti == null) ? null
: new ExtraRenderingInfo(ti);
@@ -5676,6 +5678,10 @@ public class AccessibilityNodeInfo implements Parcelable {
private String mRowTitle;
private String mColumnTitle;
+ private CollectionItemInfo() {
+ /* do nothing */
+ }
+
/**
* Creates a new instance.
*
@@ -5717,6 +5723,7 @@ public class AccessibilityNodeInfo implements Parcelable {
* @param columnSpan The number of columns the item spans.
* @param heading Whether the item is a heading.
* @param selected Whether the item is selected.
+ * @hide
*/
public CollectionItemInfo(@Nullable String rowTitle, int rowIndex, int rowSpan,
@Nullable String columnTitle, int columnIndex, int columnSpan, boolean heading,
@@ -5827,6 +5834,140 @@ public class AccessibilityNodeInfo implements Parcelable {
mRowTitle = null;
mColumnTitle = null;
}
+
+ /**
+ * Builder for creating {@link CollectionItemInfo} objects.
+ */
+ public static final class Builder {
+ private boolean mHeading;
+ private int mColumnIndex;
+ private int mRowIndex;
+ private int mColumnSpan;
+ private int mRowSpan;
+ private boolean mSelected;
+ private String mRowTitle;
+ private String mColumnTitle;
+
+ /**
+ * Creates a new Builder.
+ */
+ public Builder() {
+ }
+
+ /**
+ * Sets the collection item is a heading.
+ *
+ * @param heading The heading state
+ * @return This builder
+ */
+ @NonNull
+ public CollectionItemInfo.Builder setHeading(boolean heading) {
+ mHeading = heading;
+ return this;
+ }
+
+ /**
+ * Sets the column index at which the item is located.
+ *
+ * @param columnIndex The column index
+ * @return This builder
+ */
+ @NonNull
+ public CollectionItemInfo.Builder setColumnIndex(int columnIndex) {
+ mColumnIndex = columnIndex;
+ return this;
+ }
+
+ /**
+ * Sets the row index at which the item is located.
+ *
+ * @param rowIndex The row index
+ * @return This builder
+ */
+ @NonNull
+ public CollectionItemInfo.Builder setRowIndex(int rowIndex) {
+ mRowIndex = rowIndex;
+ return this;
+ }
+
+ /**
+ * Sets the number of columns the item spans.
+ *
+ * @param columnSpan The number of columns spans
+ * @return This builder
+ */
+ @NonNull
+ public CollectionItemInfo.Builder setColumnSpan(int columnSpan) {
+ mColumnSpan = columnSpan;
+ return this;
+ }
+
+ /**
+ * Sets the number of rows the item spans.
+ *
+ * @param rowSpan The number of rows spans
+ * @return This builder
+ */
+ @NonNull
+ public CollectionItemInfo.Builder setRowSpan(int rowSpan) {
+ mRowSpan = rowSpan;
+ return this;
+ }
+
+ /**
+ * Sets the collection item is selected.
+ *
+ * @param selected The number of rows spans
+ * @return This builder
+ */
+ @NonNull
+ public CollectionItemInfo.Builder setSelected(boolean selected) {
+ mSelected = selected;
+ return this;
+ }
+
+ /**
+ * Sets the row title at which the item is located.
+ *
+ * @param rowTitle The row title
+ * @return This builder
+ */
+ @NonNull
+ public CollectionItemInfo.Builder setRowTitle(@Nullable String rowTitle) {
+ mRowTitle = rowTitle;
+ return this;
+ }
+
+ /**
+ * Sets the column title at which the item is located.
+ *
+ * @param columnTitle The column title
+ * @return This builder
+ */
+ @NonNull
+ public CollectionItemInfo.Builder setColumnTitle(@Nullable String columnTitle) {
+ mColumnTitle = columnTitle;
+ return this;
+ }
+
+ /**
+ * Builds and returns a {@link CollectionItemInfo}.
+ */
+ @NonNull
+ public CollectionItemInfo build() {
+ CollectionItemInfo collectionItemInfo = new CollectionItemInfo();
+ collectionItemInfo.mHeading = mHeading;
+ collectionItemInfo.mColumnIndex = mColumnIndex;
+ collectionItemInfo.mRowIndex = mRowIndex;
+ collectionItemInfo.mColumnSpan = mColumnSpan;
+ collectionItemInfo.mRowSpan = mRowSpan;
+ collectionItemInfo.mSelected = mSelected;
+ collectionItemInfo.mRowTitle = mRowTitle;
+ collectionItemInfo.mColumnTitle = mColumnTitle;
+
+ return collectionItemInfo;
+ }
+ }
}
/**
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
index 76e226163ca1..36e779a44e70 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -908,7 +908,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/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 fbc947071c99..437e54f635ee 100644
--- a/core/java/android/view/inputmethod/CursorAnchorInfo.java
+++ b/core/java/android/view/inputmethod/CursorAnchorInfo.java
@@ -140,7 +140,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);
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/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index 3b15db2ded70..b85fe7c3aae2 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -836,20 +836,25 @@ public interface InputConnection {
boolean beginBatchEdit();
/**
- * Tell the editor that you are done with a batch edit previously
- * initiated with {@link #beginBatchEdit}. This ends the latest
- * batch only.
- *
- * <p><strong>IME authors:</strong> make sure you call this
- * exactly once for each call to {@link #beginBatchEdit}.</p>
- *
- * <p><strong>Editor authors:</strong> please be careful about
- * batch edit nesting. Updates still to be held back until the end
- * of the last batch edit.</p>
+ * Tell the editor that you are done with a batch edit previously initiated with
+ * {@link #beginBatchEdit()}. This ends the latest batch only.
+ *
+ * <p><strong>IME authors:</strong> make sure you call this exactly once for each call to
+ * {@link #beginBatchEdit()}.</p>
+ *
+ * <p><strong>Editor authors:</strong> please be careful about batch edit nesting. Updates still
+ * to be held back until the end of the last batch edit. In case you are delegating this API
+ * call to the one obtained from
+ * {@link android.widget.EditText#onCreateInputConnection(EditorInfo)}, there was an off-by-one
+ * that had returned {@code true} when its nested batch edit count becomes {@code 0} as a result
+ * of invoking this API. This bug is fixed in {@link android.os.Build.VERSION_CODES#TIRAMISU}.
+ * </p>
*
- * @return true if there is still a batch edit in progress after closing
- * the latest one (in other words, if the nesting count is > 0), false
- * otherwise or if the input connection is no longer valid.
+ * @return For editor authors, you must return {@code true} if a batch edit is still in progress
+ * after closing the latest one (in other words, if the nesting count is still a
+ * positive number). Return {@code false} otherwise. For IME authors, you will
+ * always receive {@code true} as long as the request was sent to the editor, and
+ * receive {@code false} only if the input connection is no longer valid.
*/
boolean endBatchEdit();
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 96198c64fa56..7e6e6fd23654 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -141,6 +141,12 @@ public final class InputMethodInfo implements Parcelable {
private final int mHandledConfigChanges;
/**
+ * The flag whether this IME supports Handwriting using stylus input.
+ */
+ private final boolean mSupportsStylusHandwriting;
+
+
+ /**
* @param service the {@link ResolveInfo} corresponds in which the IME is implemented.
* @return a unique ID to be returned by {@link #getId()}. We have used
* {@link ComponentName#flattenToShortString()} for this purpose (and it is already
@@ -234,6 +240,8 @@ public final class InputMethodInfo implements Parcelable {
com.android.internal.R.styleable.InputMethod_showInInputMethodPicker, true);
mHandledConfigChanges = sa.getInt(
com.android.internal.R.styleable.InputMethod_configChanges, 0);
+ mSupportsStylusHandwriting = sa.getBoolean(
+ com.android.internal.R.styleable.InputMethod_supportsStylusHandwriting, false);
sa.recycle();
final int depth = parser.getDepth();
@@ -323,6 +331,7 @@ public final class InputMethodInfo implements Parcelable {
mService = ResolveInfo.CREATOR.createFromParcel(source);
mSubtypes = new InputMethodSubtypeArray(source);
mHandledConfigChanges = source.readInt();
+ mSupportsStylusHandwriting = source.readBoolean();
mForceDefault = false;
}
@@ -335,7 +344,7 @@ public final class InputMethodInfo implements Parcelable {
settingsActivity, null /* subtypes */, 0 /* isDefaultResId */,
false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */,
false /* inlineSuggestionsEnabled */, false /* isVrOnly */,
- 0 /* handledConfigChanges */);
+ 0 /* handledConfigChanges */, false /* supportsStylusHandwriting */);
}
/**
@@ -349,7 +358,8 @@ public final class InputMethodInfo implements Parcelable {
this(buildFakeResolveInfo(packageName, className, label), false /* isAuxIme */,
settingsActivity, null /* subtypes */, 0 /* isDefaultResId */,
false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */,
- false /* inlineSuggestionsEnabled */, false /* isVrOnly */, handledConfigChanges);
+ false /* inlineSuggestionsEnabled */, false /* isVrOnly */, handledConfigChanges,
+ false /* supportsStylusHandwriting */);
}
/**
@@ -361,7 +371,8 @@ public final class InputMethodInfo implements Parcelable {
boolean forceDefault) {
this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault,
true /* supportsSwitchingToNextInputMethod */, false /* inlineSuggestionsEnabled */,
- false /* isVrOnly */, 0 /* handledconfigChanges */);
+ false /* isVrOnly */, 0 /* handledconfigChanges */,
+ false /* supportsStylusHandwriting */);
}
/**
@@ -373,7 +384,7 @@ public final class InputMethodInfo implements Parcelable {
boolean supportsSwitchingToNextInputMethod, boolean isVrOnly) {
this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault,
supportsSwitchingToNextInputMethod, false /* inlineSuggestionsEnabled */, isVrOnly,
- 0 /* handledConfigChanges */);
+ 0 /* handledConfigChanges */, false /* supportsStylusHandwriting */);
}
/**
@@ -383,7 +394,7 @@ public final class InputMethodInfo implements Parcelable {
public InputMethodInfo(ResolveInfo ri, boolean isAuxIme, String settingsActivity,
List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault,
boolean supportsSwitchingToNextInputMethod, boolean inlineSuggestionsEnabled,
- boolean isVrOnly, int handledConfigChanges) {
+ boolean isVrOnly, int handledConfigChanges, boolean supportsStylusHandwriting) {
final ServiceInfo si = ri.serviceInfo;
mService = ri;
mId = new ComponentName(si.packageName, si.name).flattenToShortString();
@@ -398,6 +409,7 @@ public final class InputMethodInfo implements Parcelable {
mShowInInputMethodPicker = true;
mIsVrOnly = isVrOnly;
mHandledConfigChanges = handledConfigChanges;
+ mSupportsStylusHandwriting = supportsStylusHandwriting;
}
private static ResolveInfo buildFakeResolveInfo(String packageName, String className,
@@ -556,6 +568,14 @@ public final class InputMethodInfo implements Parcelable {
return mHandledConfigChanges;
}
+ /**
+ * Returns if IME supports handwriting using stylus input.
+ * @attr ref android.R.styleable#InputMethod_supportsStylusHandwriting
+ */
+ public boolean supportsStylusHandwriting() {
+ return mSupportsStylusHandwriting;
+ }
+
public void dump(Printer pw, String prefix) {
pw.println(prefix + "mId=" + mId
+ " mSettingsActivityName=" + mSettingsActivityName
@@ -563,7 +583,8 @@ public final class InputMethodInfo implements Parcelable {
+ " mSupportsSwitchingToNextInputMethod=" + mSupportsSwitchingToNextInputMethod
+ " mInlineSuggestionsEnabled=" + mInlineSuggestionsEnabled
+ " mSuppressesSpellChecker=" + mSuppressesSpellChecker
- + " mShowInInputMethodPicker=" + mShowInInputMethodPicker);
+ + " mShowInInputMethodPicker=" + mShowInInputMethodPicker
+ + " mSupportsStylusHandwriting=" + mSupportsStylusHandwriting);
pw.println(prefix + "mIsDefaultResId=0x"
+ Integer.toHexString(mIsDefaultResId));
pw.println(prefix + "Service:");
@@ -667,6 +688,7 @@ public final class InputMethodInfo implements Parcelable {
mService.writeToParcel(dest, flags);
mSubtypes.writeToParcel(dest);
dest.writeInt(mHandledConfigChanges);
+ dest.writeBoolean(mSupportsStylusHandwriting);
}
/**
diff --git a/core/java/android/view/selectiontoolbar/ISelectionToolbarCallback.aidl b/core/java/android/view/selectiontoolbar/ISelectionToolbarCallback.aidl
new file mode 100644
index 000000000000..48af7b96b195
--- /dev/null
+++ b/core/java/android/view/selectiontoolbar/ISelectionToolbarCallback.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.selectiontoolbar;
+
+import android.view.selectiontoolbar.ToolbarMenuItem;
+import android.view.selectiontoolbar.WidgetInfo;
+
+/**
+ * Binder interface to notify the selection toolbar events from one process to the other.
+ * @hide
+ */
+oneway interface ISelectionToolbarCallback {
+ void onShown(in WidgetInfo info);
+ void onHidden(long widgetToken);
+ void onDismissed(long widgetToken);
+ void onWidgetUpdated(in WidgetInfo info);
+ void onMenuItemClicked(in ToolbarMenuItem item);
+ void onError(int errorCode);
+}
diff --git a/core/java/android/view/selectiontoolbar/ISelectionToolbarManager.aidl b/core/java/android/view/selectiontoolbar/ISelectionToolbarManager.aidl
new file mode 100644
index 000000000000..4a647ada1d6c
--- /dev/null
+++ b/core/java/android/view/selectiontoolbar/ISelectionToolbarManager.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.selectiontoolbar;
+
+import android.view.selectiontoolbar.ISelectionToolbarCallback;
+import android.view.selectiontoolbar.ShowInfo;
+
+/**
+ * Mediator between apps and selection toolbar service implementation.
+ *
+ * @hide
+ */
+oneway interface ISelectionToolbarManager {
+ void showToolbar(in ShowInfo showInfo, in ISelectionToolbarCallback callback, int userId);
+ void hideToolbar(long widgetToken, int userId);
+ void dismissToolbar(long widgetToken, int userId);
+} \ No newline at end of file
diff --git a/core/java/android/view/selectiontoolbar/OWNERS b/core/java/android/view/selectiontoolbar/OWNERS
new file mode 100644
index 000000000000..5500b92868dd
--- /dev/null
+++ b/core/java/android/view/selectiontoolbar/OWNERS
@@ -0,0 +1,10 @@
+# Bug component: 709498
+
+augale@google.com
+joannechung@google.com
+licha@google.com
+lpeter@google.com
+svetoslavganov@google.com
+toki@google.com
+tonymak@google.com
+tymtsai@google.com \ No newline at end of file
diff --git a/core/java/android/view/selectiontoolbar/SelectionContext.aidl b/core/java/android/view/selectiontoolbar/SelectionContext.aidl
new file mode 100644
index 000000000000..52068312d4a1
--- /dev/null
+++ b/core/java/android/view/selectiontoolbar/SelectionContext.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.selectiontoolbar;
+
+/**
+ * @hide
+ */
+parcelable SelectionContext;
diff --git a/core/java/android/view/selectiontoolbar/SelectionContext.java b/core/java/android/view/selectiontoolbar/SelectionContext.java
new file mode 100644
index 000000000000..21b8d8f11d25
--- /dev/null
+++ b/core/java/android/view/selectiontoolbar/SelectionContext.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.selectiontoolbar;
+
+import android.annotation.NonNull;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * The class holds information for a selection.
+ *
+ * @hide
+ */
+@DataClass(genBuilder = true, genToString = true, genEqualsHashCode = true)
+public final class SelectionContext implements Parcelable {
+
+ /**
+ * The start index of a selection.
+ */
+ private final int mStartIndex;
+
+ /**
+ * The end index of a selection.
+ */
+ private final int mEndIndex;
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/selectiontoolbar/SelectionContext.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @DataClass.Generated.Member
+ /* package-private */ SelectionContext(
+ int startIndex,
+ int endIndex) {
+ this.mStartIndex = startIndex;
+ this.mEndIndex = endIndex;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * The start index of a selection.
+ */
+ @DataClass.Generated.Member
+ public int getStartIndex() {
+ return mStartIndex;
+ }
+
+ /**
+ * The end index of a selection.
+ */
+ @DataClass.Generated.Member
+ public int getEndIndex() {
+ return mEndIndex;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "SelectionContext { " +
+ "startIndex = " + mStartIndex + ", " +
+ "endIndex = " + mEndIndex +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@android.annotation.Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(SelectionContext other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ SelectionContext that = (SelectionContext) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && mStartIndex == that.mStartIndex
+ && mEndIndex == that.mEndIndex;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + mStartIndex;
+ _hash = 31 * _hash + mEndIndex;
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeInt(mStartIndex);
+ dest.writeInt(mEndIndex);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ SelectionContext(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ int startIndex = in.readInt();
+ int endIndex = in.readInt();
+
+ this.mStartIndex = startIndex;
+ this.mEndIndex = endIndex;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<SelectionContext> CREATOR
+ = new Parcelable.Creator<SelectionContext>() {
+ @Override
+ public SelectionContext[] newArray(int size) {
+ return new SelectionContext[size];
+ }
+
+ @Override
+ public SelectionContext createFromParcel(@NonNull android.os.Parcel in) {
+ return new SelectionContext(in);
+ }
+ };
+
+ /**
+ * A builder for {@link SelectionContext}
+ */
+ @SuppressWarnings("WeakerAccess")
+ @DataClass.Generated.Member
+ public static final class Builder {
+
+ private int mStartIndex;
+ private int mEndIndex;
+
+ private long mBuilderFieldsSet = 0L;
+
+ /**
+ * Creates a new Builder.
+ *
+ * @param startIndex
+ * The start index of a selection.
+ * @param endIndex
+ * The end index of a selection.
+ */
+ public Builder(
+ int startIndex,
+ int endIndex) {
+ mStartIndex = startIndex;
+ mEndIndex = endIndex;
+ }
+
+ /**
+ * The start index of a selection.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setStartIndex(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x1;
+ mStartIndex = value;
+ return this;
+ }
+
+ /**
+ * The end index of a selection.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setEndIndex(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2;
+ mEndIndex = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull SelectionContext build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4; // Mark builder used
+
+ SelectionContext o = new SelectionContext(
+ mStartIndex,
+ mEndIndex);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x4) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+
+ @DataClass.Generated(
+ time = 1639488292248L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/view/selectiontoolbar/SelectionContext.java",
+ inputSignatures = "private final int mStartIndex\nprivate final int mEndIndex\nclass SelectionContext extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genToString=true, genEqualsHashCode=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/view/selectiontoolbar/SelectionToolbarManager.java b/core/java/android/view/selectiontoolbar/SelectionToolbarManager.java
new file mode 100644
index 000000000000..60688eaa3600
--- /dev/null
+++ b/core/java/android/view/selectiontoolbar/SelectionToolbarManager.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.selectiontoolbar;
+
+import android.annotation.NonNull;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.RemoteException;
+
+import java.util.Objects;
+
+/**
+ * The {@link SelectionToolbarManager} class provides ways for apps to control the
+ * selection toolbar.
+ *
+ * @hide
+ */
+@SystemService(Context.SELECTION_TOOLBAR_SERVICE)
+public final class SelectionToolbarManager {
+
+ private static final String TAG = "SelectionToolbar";
+
+ /**
+ * The tag which uses for enabling debug log dump. To enable it, we can use command "adb shell
+ * setprop log.tag.UiTranslation DEBUG".
+ */
+ public static final String LOG_TAG = "SelectionToolbar";
+
+
+ @NonNull
+ private final Context mContext;
+ private final ISelectionToolbarManager mService;
+
+ public SelectionToolbarManager(@NonNull Context context,
+ @NonNull ISelectionToolbarManager service) {
+ mContext = Objects.requireNonNull(context);
+ mService = service;
+ }
+
+ /**
+ * Request to show selection toolbar for a given View.
+ */
+ public void showToolbar(@NonNull ShowInfo showInfo,
+ @NonNull ISelectionToolbarCallback callback) {
+ try {
+ Objects.requireNonNull(showInfo);
+ Objects.requireNonNull(callback);
+ mService.showToolbar(showInfo, callback, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Request to hide selection toolbar.
+ */
+ public void hideToolbar(long widgetToken) {
+ try {
+ mService.hideToolbar(widgetToken, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Dismiss to dismiss selection toolbar.
+ */
+ public void dismissToolbar(long widgetToken) {
+ try {
+ mService.dismissToolbar(widgetToken, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/core/java/android/view/selectiontoolbar/ShowInfo.aidl b/core/java/android/view/selectiontoolbar/ShowInfo.aidl
new file mode 100644
index 000000000000..dce9c15dce3e
--- /dev/null
+++ b/core/java/android/view/selectiontoolbar/ShowInfo.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.selectiontoolbar;
+
+/**
+ * @hide
+ */
+parcelable ShowInfo;
diff --git a/core/java/android/view/selectiontoolbar/ShowInfo.java b/core/java/android/view/selectiontoolbar/ShowInfo.java
new file mode 100644
index 000000000000..bbbd5c0df0b4
--- /dev/null
+++ b/core/java/android/view/selectiontoolbar/ShowInfo.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.selectiontoolbar;
+
+import android.annotation.NonNull;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+
+/**
+ * The class holds menu information for render service to render the selection toolbar.
+ *
+ * @hide
+ */
+@DataClass(genToString = true, genEqualsHashCode = true)
+public final class ShowInfo implements Parcelable {
+ /**
+ * The token that is used to identify the selection toolbar. This is initially set to 0
+ * until a selection toolbar has been created for the showToolbar request.
+ */
+ private final long mWidgetToken;
+
+ // TODO: add members when the code really uses it
+
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/selectiontoolbar/ShowInfo.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * Creates a new ShowInfo.
+ *
+ * @param widgetToken
+ * The token that is used to identify the selection toolbar.
+ */
+ @DataClass.Generated.Member
+ public ShowInfo(
+ long widgetToken) {
+ this.mWidgetToken = widgetToken;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * The token that is used to identify the selection toolbar.
+ */
+ @DataClass.Generated.Member
+ public long getWidgetToken() {
+ return mWidgetToken;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "ShowInfo { " +
+ "widgetToken = " + mWidgetToken +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@android.annotation.Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(ShowInfo other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ ShowInfo that = (ShowInfo) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && mWidgetToken == that.mWidgetToken;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + Long.hashCode(mWidgetToken);
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeLong(mWidgetToken);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ ShowInfo(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ long widgetToken = in.readLong();
+
+ this.mWidgetToken = widgetToken;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<ShowInfo> CREATOR
+ = new Parcelable.Creator<ShowInfo>() {
+ @Override
+ public ShowInfo[] newArray(int size) {
+ return new ShowInfo[size];
+ }
+
+ @Override
+ public ShowInfo createFromParcel(@NonNull android.os.Parcel in) {
+ return new ShowInfo(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1639488262761L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/view/selectiontoolbar/ShowInfo.java",
+ inputSignatures = "private final long mWidgetToken\nclass ShowInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/view/selectiontoolbar/ToolbarMenuItem.aidl b/core/java/android/view/selectiontoolbar/ToolbarMenuItem.aidl
new file mode 100644
index 000000000000..711a85a7771e
--- /dev/null
+++ b/core/java/android/view/selectiontoolbar/ToolbarMenuItem.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.selectiontoolbar;
+
+/**
+ * @hide
+ */
+parcelable ToolbarMenuItem;
diff --git a/core/java/android/view/selectiontoolbar/ToolbarMenuItem.java b/core/java/android/view/selectiontoolbar/ToolbarMenuItem.java
new file mode 100644
index 000000000000..5af232c7a34d
--- /dev/null
+++ b/core/java/android/view/selectiontoolbar/ToolbarMenuItem.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.selectiontoolbar;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * The menu item that is used to show the selection toolbar.
+ *
+ * @hide
+ */
+@DataClass(genBuilder = true, genToString = true, genEqualsHashCode = true)
+public final class ToolbarMenuItem implements Parcelable {
+
+ /**
+ * The id of the menu item.
+ */
+ private final int mItemId;
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/selectiontoolbar/ToolbarMenuItem.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @DataClass.Generated.Member
+ /* package-private */ ToolbarMenuItem(
+ int itemId) {
+ this.mItemId = itemId;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * The id of the menu item.
+ */
+ @DataClass.Generated.Member
+ public int getItemId() {
+ return mItemId;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "ToolbarMenuItem { " +
+ "itemId = " + mItemId +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(ToolbarMenuItem other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ ToolbarMenuItem that = (ToolbarMenuItem) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && mItemId == that.mItemId;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + mItemId;
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeInt(mItemId);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ ToolbarMenuItem(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ int itemId = in.readInt();
+
+ this.mItemId = itemId;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<ToolbarMenuItem> CREATOR
+ = new Parcelable.Creator<ToolbarMenuItem>() {
+ @Override
+ public ToolbarMenuItem[] newArray(int size) {
+ return new ToolbarMenuItem[size];
+ }
+
+ @Override
+ public ToolbarMenuItem createFromParcel(@NonNull android.os.Parcel in) {
+ return new ToolbarMenuItem(in);
+ }
+ };
+
+ /**
+ * A builder for {@link ToolbarMenuItem}
+ */
+ @SuppressWarnings("WeakerAccess")
+ @DataClass.Generated.Member
+ public static final class Builder {
+
+ private int mItemId;
+
+ private long mBuilderFieldsSet = 0L;
+
+ /**
+ * Creates a new Builder.
+ *
+ * @param itemId
+ * The id of the menu item.
+ */
+ public Builder(
+ int itemId) {
+ mItemId = itemId;
+ }
+
+ /**
+ * The id of the menu item.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setItemId(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x1;
+ mItemId = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull ToolbarMenuItem build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2; // Mark builder used
+
+ ToolbarMenuItem o = new ToolbarMenuItem(
+ mItemId);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x2) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+
+ @DataClass.Generated(
+ time = 1639488328542L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/view/selectiontoolbar/ToolbarMenuItem.java",
+ inputSignatures = "private final int mItemId\nclass ToolbarMenuItem extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genToString=true, genEqualsHashCode=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/view/selectiontoolbar/WidgetInfo.aidl b/core/java/android/view/selectiontoolbar/WidgetInfo.aidl
new file mode 100644
index 000000000000..1057c516e233
--- /dev/null
+++ b/core/java/android/view/selectiontoolbar/WidgetInfo.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.selectiontoolbar;
+
+/**
+ * @hide
+ */
+parcelable WidgetInfo;
diff --git a/core/java/android/view/selectiontoolbar/WidgetInfo.java b/core/java/android/view/selectiontoolbar/WidgetInfo.java
new file mode 100644
index 000000000000..961d8ac8e5e0
--- /dev/null
+++ b/core/java/android/view/selectiontoolbar/WidgetInfo.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.selectiontoolbar;
+
+import android.annotation.NonNull;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * The class holds the rendered content and the related information from the render service to
+ * be used to show on the selection toolbar.
+ *
+ * @hide
+ */
+@DataClass(genToString = true, genEqualsHashCode = true)
+public final class WidgetInfo implements Parcelable {
+
+ /**
+ * The token that is used to identify the selection toolbar.
+ */
+ private final long mWidgetToken;
+
+ // TODO: add members when the code really uses it
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/selectiontoolbar/WidgetInfo.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * Creates a new WidgetInfo.
+ *
+ * @param widgetToken
+ * The token that is used to identify the selection toolbar.
+ */
+ @DataClass.Generated.Member
+ public WidgetInfo(
+ long widgetToken) {
+ this.mWidgetToken = widgetToken;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * The token that is used to identify the selection toolbar.
+ */
+ @DataClass.Generated.Member
+ public long getWidgetToken() {
+ return mWidgetToken;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "WidgetInfo { " +
+ "widgetToken = " + mWidgetToken +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@android.annotation.Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(WidgetInfo other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ WidgetInfo that = (WidgetInfo) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && mWidgetToken == that.mWidgetToken;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + Long.hashCode(mWidgetToken);
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeLong(mWidgetToken);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ WidgetInfo(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ long widgetToken = in.readLong();
+
+ this.mWidgetToken = widgetToken;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<WidgetInfo> CREATOR
+ = new Parcelable.Creator<WidgetInfo>() {
+ @Override
+ public WidgetInfo[] newArray(int size) {
+ return new WidgetInfo[size];
+ }
+
+ @Override
+ public WidgetInfo createFromParcel(@NonNull android.os.Parcel in) {
+ return new WidgetInfo(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1639488254020L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/view/selectiontoolbar/WidgetInfo.java",
+ inputSignatures = "private final long mWidgetToken\nclass WidgetInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/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/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 dfd853acaf0d..a5f3febafa9a 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -1482,7 +1482,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/window/ITaskOrganizerController.aidl b/core/java/android/window/ITaskOrganizerController.aidl
index a833600e1fbc..022d05da8825 100644
--- a/core/java/android/window/ITaskOrganizerController.aidl
+++ b/core/java/android/window/ITaskOrganizerController.aidl
@@ -66,4 +66,7 @@ interface ITaskOrganizerController {
* Restarts the top activity in the given task by killing its process if it is visible.
*/
void restartTaskTopActivityProcessIfVisible(in WindowContainerToken task);
+
+ /** Updates a state of camera compat control for stretched issues in the viewfinder. */
+ void updateCameraCompatControlState(in WindowContainerToken task, int state);
}
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index 27c7d3158f95..3ec18dbe0ebc 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -24,6 +24,7 @@ import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.app.ActivityManager;
+import android.app.TaskInfo.CameraCompatControlState;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.SurfaceControl;
@@ -238,6 +239,20 @@ public class TaskOrganizer extends WindowOrganizer {
}
/**
+ * Updates a state of camera compat control for stretched issues in the viewfinder.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
+ public void updateCameraCompatControlState(@NonNull WindowContainerToken task,
+ @CameraCompatControlState int state) {
+ try {
+ mTaskOrganizerController.updateCameraCompatControlState(task, state);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Gets the executor to run callbacks on.
* @hide
*/
diff --git a/core/java/android/window/TransitionRequestInfo.aidl b/core/java/android/window/TransitionRequestInfo.aidl
index d2b9ccfd657e..6f980d4d43df 100644
--- a/core/java/android/window/TransitionRequestInfo.aidl
+++ b/core/java/android/window/TransitionRequestInfo.aidl
@@ -17,3 +17,4 @@
package android.window;
parcelable TransitionRequestInfo;
+parcelable TransitionRequestInfo.DisplayChange;
diff --git a/core/java/android/window/TransitionRequestInfo.java b/core/java/android/window/TransitionRequestInfo.java
index f7707317efd7..e0cdb133c4ce 100644
--- a/core/java/android/window/TransitionRequestInfo.java
+++ b/core/java/android/window/TransitionRequestInfo.java
@@ -18,6 +18,8 @@ package android.window;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.WindowConfiguration;
+import android.graphics.Rect;
import android.os.Parcelable;
import android.view.WindowManager;
@@ -42,6 +44,194 @@ public final class TransitionRequestInfo implements Parcelable {
/** If non-null, a remote-transition associated with the source of this transition. */
private @Nullable RemoteTransition mRemoteTransition;
+ /**
+ * If non-null, this request was triggered by this display change. This will not be complete:
+ * The reliable parts should be flags, rotation start/end (if rotating), and start/end bounds
+ * (if size is changing).
+ */
+ private @Nullable DisplayChange mDisplayChange;
+
+ /** constructor override */
+ public TransitionRequestInfo(
+ @WindowManager.TransitionType int type,
+ @Nullable ActivityManager.RunningTaskInfo triggerTask,
+ @Nullable RemoteTransition remoteTransition) {
+ this(type, triggerTask, remoteTransition, null /* displayChange */);
+ }
+
+ /** Requested change to a display. */
+ @DataClass(genToString = true, genSetters = true, genBuilder = false, genConstructor = false)
+ public static class DisplayChange implements Parcelable {
+ private final int mDisplayId;
+ @Nullable private Rect mStartAbsBounds = null;
+ @Nullable private Rect mEndAbsBounds = null;
+ private int mStartRotation = WindowConfiguration.ROTATION_UNDEFINED;
+ private int mEndRotation = WindowConfiguration.ROTATION_UNDEFINED;
+
+ /** Create empty display-change. */
+ public DisplayChange(int displayId) {
+ mDisplayId = displayId;
+ }
+
+ /** Create a display-change representing a rotation. */
+ public DisplayChange(int displayId, int startRotation, int endRotation) {
+ mDisplayId = displayId;
+ mStartRotation = startRotation;
+ mEndRotation = endRotation;
+ }
+
+
+
+ // 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/window/TransitionRequestInfo.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @DataClass.Generated.Member
+ public int getDisplayId() {
+ return mDisplayId;
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable Rect getStartAbsBounds() {
+ return mStartAbsBounds;
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable Rect getEndAbsBounds() {
+ return mEndAbsBounds;
+ }
+
+ @DataClass.Generated.Member
+ public int getStartRotation() {
+ return mStartRotation;
+ }
+
+ @DataClass.Generated.Member
+ public int getEndRotation() {
+ return mEndRotation;
+ }
+
+ @DataClass.Generated.Member
+ public @android.annotation.NonNull DisplayChange setStartAbsBounds(@android.annotation.NonNull Rect value) {
+ mStartAbsBounds = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @android.annotation.NonNull DisplayChange setEndAbsBounds(@android.annotation.NonNull Rect value) {
+ mEndAbsBounds = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @android.annotation.NonNull DisplayChange setStartRotation( int value) {
+ mStartRotation = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @android.annotation.NonNull DisplayChange setEndRotation( int value) {
+ mEndRotation = value;
+ return this;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "DisplayChange { " +
+ "displayId = " + mDisplayId + ", " +
+ "startAbsBounds = " + mStartAbsBounds + ", " +
+ "endAbsBounds = " + mEndAbsBounds + ", " +
+ "startRotation = " + mStartRotation + ", " +
+ "endRotation = " + mEndRotation +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@android.annotation.NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ byte flg = 0;
+ if (mStartAbsBounds != null) flg |= 0x2;
+ if (mEndAbsBounds != null) flg |= 0x4;
+ dest.writeByte(flg);
+ dest.writeInt(mDisplayId);
+ if (mStartAbsBounds != null) dest.writeTypedObject(mStartAbsBounds, flags);
+ if (mEndAbsBounds != null) dest.writeTypedObject(mEndAbsBounds, flags);
+ dest.writeInt(mStartRotation);
+ dest.writeInt(mEndRotation);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ protected DisplayChange(@android.annotation.NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ byte flg = in.readByte();
+ int displayId = in.readInt();
+ Rect startAbsBounds = (flg & 0x2) == 0 ? null : (Rect) in.readTypedObject(Rect.CREATOR);
+ Rect endAbsBounds = (flg & 0x4) == 0 ? null : (Rect) in.readTypedObject(Rect.CREATOR);
+ int startRotation = in.readInt();
+ int endRotation = in.readInt();
+
+ this.mDisplayId = displayId;
+ this.mStartAbsBounds = startAbsBounds;
+ this.mEndAbsBounds = endAbsBounds;
+ this.mStartRotation = startRotation;
+ this.mEndRotation = endRotation;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @android.annotation.NonNull Parcelable.Creator<DisplayChange> CREATOR
+ = new Parcelable.Creator<DisplayChange>() {
+ @Override
+ public DisplayChange[] newArray(int size) {
+ return new DisplayChange[size];
+ }
+
+ @Override
+ public DisplayChange createFromParcel(@android.annotation.NonNull android.os.Parcel in) {
+ return new DisplayChange(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1639445520915L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/window/TransitionRequestInfo.java",
+ inputSignatures = "private final int mDisplayId\nprivate @android.annotation.Nullable android.graphics.Rect mStartAbsBounds\nprivate @android.annotation.Nullable android.graphics.Rect mEndAbsBounds\nprivate int mStartRotation\nprivate int mEndRotation\nclass DisplayChange extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genBuilder=false, genConstructor=false)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+ }
+
+
// Code below generated by codegen v1.0.23.
@@ -67,17 +257,23 @@ public final class TransitionRequestInfo implements Parcelable {
* finish) has caused this transition to occur.
* @param remoteTransition
* If non-null, a remote-transition associated with the source of this transition.
+ * @param displayChange
+ * If non-null, this request was triggered by this display change. This will not be complete:
+ * The reliable parts should be flags, rotation start/end (if rotating), and start/end bounds
+ * (if size is changing).
*/
@DataClass.Generated.Member
public TransitionRequestInfo(
@WindowManager.TransitionType int type,
@Nullable ActivityManager.RunningTaskInfo triggerTask,
- @Nullable RemoteTransition remoteTransition) {
+ @Nullable RemoteTransition remoteTransition,
+ @Nullable DisplayChange displayChange) {
this.mType = type;
com.android.internal.util.AnnotationValidations.validate(
WindowManager.TransitionType.class, null, mType);
this.mTriggerTask = triggerTask;
this.mRemoteTransition = remoteTransition;
+ this.mDisplayChange = displayChange;
// onConstructed(); // You can define this method to get a callback
}
@@ -108,6 +304,16 @@ public final class TransitionRequestInfo implements Parcelable {
}
/**
+ * If non-null, this request was triggered by this display change. This will not be complete:
+ * The reliable parts should be flags, rotation start/end (if rotating), and start/end bounds
+ * (if size is changing).
+ */
+ @DataClass.Generated.Member
+ public @Nullable DisplayChange getDisplayChange() {
+ return mDisplayChange;
+ }
+
+ /**
* If non-null, If non-null, the task containing the activity whose lifecycle change (start or
* finish) has caused this transition to occur.
*/
@@ -126,6 +332,17 @@ public final class TransitionRequestInfo implements Parcelable {
return this;
}
+ /**
+ * If non-null, this request was triggered by this display change. This will not be complete:
+ * The reliable parts should be flags, rotation start/end (if rotating), and start/end bounds
+ * (if size is changing).
+ */
+ @DataClass.Generated.Member
+ public @android.annotation.NonNull TransitionRequestInfo setDisplayChange(@android.annotation.NonNull DisplayChange value) {
+ mDisplayChange = value;
+ return this;
+ }
+
@Override
@DataClass.Generated.Member
public String toString() {
@@ -135,7 +352,8 @@ public final class TransitionRequestInfo implements Parcelable {
return "TransitionRequestInfo { " +
"type = " + mType + ", " +
"triggerTask = " + mTriggerTask + ", " +
- "remoteTransition = " + mRemoteTransition +
+ "remoteTransition = " + mRemoteTransition + ", " +
+ "displayChange = " + mDisplayChange +
" }";
}
@@ -148,10 +366,12 @@ public final class TransitionRequestInfo implements Parcelable {
byte flg = 0;
if (mTriggerTask != null) flg |= 0x2;
if (mRemoteTransition != null) flg |= 0x4;
+ if (mDisplayChange != null) flg |= 0x8;
dest.writeByte(flg);
dest.writeInt(mType);
if (mTriggerTask != null) dest.writeTypedObject(mTriggerTask, flags);
if (mRemoteTransition != null) dest.writeTypedObject(mRemoteTransition, flags);
+ if (mDisplayChange != null) dest.writeTypedObject(mDisplayChange, flags);
}
@Override
@@ -169,12 +389,14 @@ public final class TransitionRequestInfo implements Parcelable {
int type = in.readInt();
ActivityManager.RunningTaskInfo triggerTask = (flg & 0x2) == 0 ? null : (ActivityManager.RunningTaskInfo) in.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR);
RemoteTransition remoteTransition = (flg & 0x4) == 0 ? null : (RemoteTransition) in.readTypedObject(RemoteTransition.CREATOR);
+ DisplayChange displayChange = (flg & 0x8) == 0 ? null : (DisplayChange) in.readTypedObject(DisplayChange.CREATOR);
this.mType = type;
com.android.internal.util.AnnotationValidations.validate(
WindowManager.TransitionType.class, null, mType);
this.mTriggerTask = triggerTask;
this.mRemoteTransition = remoteTransition;
+ this.mDisplayChange = displayChange;
// onConstructed(); // You can define this method to get a callback
}
@@ -194,10 +416,10 @@ public final class TransitionRequestInfo implements Parcelable {
};
@DataClass.Generated(
- time = 1629321632222L,
+ time = 1639445520938L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/window/TransitionRequestInfo.java",
- inputSignatures = "private final @android.view.WindowManager.TransitionType int mType\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mTriggerTask\nprivate @android.annotation.Nullable android.window.RemoteTransition mRemoteTransition\nclass TransitionRequestInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)")
+ inputSignatures = "private final @android.view.WindowManager.TransitionType int mType\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mTriggerTask\nprivate @android.annotation.Nullable android.window.RemoteTransition mRemoteTransition\nprivate @android.annotation.Nullable android.window.TransitionRequestInfo.DisplayChange mDisplayChange\nclass TransitionRequestInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/window/WindowInfosListener.java b/core/java/android/window/WindowInfosListener.java
index 4376e3eb572e..9d4545c6e25d 100644
--- a/core/java/android/window/WindowInfosListener.java
+++ b/core/java/android/window/WindowInfosListener.java
@@ -16,6 +16,8 @@
package android.window;
+import android.graphics.Matrix;
+import android.util.Size;
import android.view.InputWindowHandle;
import libcore.util.NativeAllocationRegistry;
@@ -40,7 +42,8 @@ public abstract class WindowInfosListener {
* @param windowHandles Reverse Z ordered array of window information that was on screen,
* where the first value is the topmost window.
*/
- public abstract void onWindowInfosChanged(InputWindowHandle[] windowHandles);
+ public abstract void onWindowInfosChanged(InputWindowHandle[] windowHandles,
+ DisplayInfo[] displayInfos);
/**
* Register the WindowInfosListener.
@@ -60,4 +63,34 @@ public abstract class WindowInfosListener {
private static native void nativeRegister(long ptr);
private static native void nativeUnregister(long ptr);
private static native long nativeGetFinalizer();
+
+ /**
+ * Describes information about a display that can have windows in it.
+ */
+ public static final class DisplayInfo {
+ public final int mDisplayId;
+
+ /**
+ * Logical display dimensions.
+ */
+ public final Size mLogicalSize;
+
+ /**
+ * The display transform. This takes display coordinates to logical display coordinates.
+ */
+ public final Matrix mTransform;
+
+ private DisplayInfo(int displayId, int logicalWidth, int logicalHeight, Matrix transform) {
+ mDisplayId = displayId;
+ mLogicalSize = new Size(logicalWidth, logicalHeight);
+ mTransform = transform;
+ }
+
+ @Override
+ public String toString() {
+ return "displayId=" + mDisplayId
+ + ", mLogicalSize=" + mLogicalSize
+ + ", mTransform=" + mTransform;
+ }
+ }
}
diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java
index b331a9e81e27..4ba7ef26e9cb 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -15,7 +15,6 @@
*/
package android.window;
-import static android.window.ConfigurationHelper.diffPublicWithSizeBuckets;
import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded;
import static android.window.ConfigurationHelper.isDifferentDisplay;
import static android.window.ConfigurationHelper.shouldUpdateResources;
@@ -222,14 +221,7 @@ public class WindowTokenClient extends IWindowToken.Stub {
() -> windowContext.dispatchConfigurationChanged(newConfig));
}
- // Dispatch onConfigurationChanged only if there's a significant public change to
- // make it compatible with the original behavior.
- final Configuration[] sizeConfigurations = context.getResources()
- .getSizeConfigurations();
- final SizeConfigurationBuckets buckets = sizeConfigurations != null
- ? new SizeConfigurationBuckets(sizeConfigurations) : null;
- final int diff = diffPublicWithSizeBuckets(mConfiguration, newConfig, buckets);
-
+ final int diff = mConfiguration.diffPublicOnly(newConfig);
if (shouldReportConfigChange && diff != 0
&& context instanceof WindowProviderService) {
final WindowProviderService windowProviderService = (WindowProviderService) context;
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/app/chooser/DisplayResolveInfo.java b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
index 4f2e7dbebb04..289daee26f52 100644
--- a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
+++ b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
@@ -237,11 +237,11 @@ 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 */)));
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/inputmethod/EditableInputConnection.java b/core/java/com/android/internal/inputmethod/EditableInputConnection.java
index 410e68bf3581..29bb3111d83e 100644
--- a/core/java/com/android/internal/inputmethod/EditableInputConnection.java
+++ b/core/java/com/android/internal/inputmethod/EditableInputConnection.java
@@ -89,7 +89,7 @@ public final class EditableInputConnection extends BaseInputConnection
// contribution to mTextView's nested batch edit count is zero.
mTextView.endBatchEdit();
mBatchEditNesting--;
- return true;
+ return mBatchEditNesting > 0;
}
}
return false;
diff --git a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
index 6c2b330611f3..662ed6b9306b 100644
--- a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
@@ -25,6 +25,7 @@ import static com.android.internal.inputmethod.InputConnectionProtoDumper.buildG
import static java.lang.annotation.RetentionPolicy.SOURCE;
+import android.annotation.AnyThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
@@ -69,6 +70,82 @@ public final class RemoteInputConnectionImpl extends IInputContext.Stub {
private static final String TAG = "RemoteInputConnectionImpl";
private static final boolean DEBUG = false;
+ /**
+ * An upper limit of calling {@link InputConnection#endBatchEdit()}.
+ *
+ * <p>This is a safeguard against broken {@link InputConnection#endBatchEdit()} implementations,
+ * which are real as we've seen in Bug 208941904. If the retry count reaches to the number
+ * defined here, we fall back into {@link InputMethodManager#restartInput(View)} as a
+ * workaround.</p>
+ */
+ private static final int MAX_END_BATCH_EDIT_RETRY = 16;
+
+ /**
+ * A lightweight per-process type cache to remember classes that never returns {@code false}
+ * from {@link InputConnection#endBatchEdit()}. The implementation is optimized for simplicity
+ * and speed with accepting false-negatives in {@link #contains(Class)}.
+ */
+ private static final class KnownAlwaysTrueEndBatchEditCache {
+ @Nullable
+ private static volatile Class<?> sElement;
+ @Nullable
+ private static volatile Class<?>[] sArray;
+
+ /**
+ * Query if the specified {@link InputConnection} implementation is known to be broken, with
+ * allowing false-negative results.
+ *
+ * @param klass An implementation class of {@link InputConnection} to be tested.
+ * @return {@code true} if the specified type was passed to {@link #add(Class)}.
+ * Note that there is a chance that you still receive {@code false} even if you
+ * called {@link #add(Class)} (false-negative).
+ */
+ @AnyThread
+ static boolean contains(@NonNull Class<? extends InputConnection> klass) {
+ if (klass == sElement) {
+ return true;
+ }
+ final Class<?>[] array = sArray;
+ if (array == null) {
+ return false;
+ }
+ for (Class<?> item : array) {
+ if (item == klass) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Try to remember the specified {@link InputConnection} implementation as a known bad.
+ *
+ * <p>There is a chance that calling this method can accidentally overwrite existing
+ * cache entries. See the document of {@link #contains(Class)} for details.</p>
+ *
+ * @param klass The implementation class of {@link InputConnection} to be remembered.
+ */
+ @AnyThread
+ static void add(@NonNull Class<? extends InputConnection> klass) {
+ if (sElement == null) {
+ // OK to accidentally overwrite an existing element that was set by another thread.
+ sElement = klass;
+ return;
+ }
+
+ final Class<?>[] array = sArray;
+ final int arraySize = array != null ? array.length : 0;
+ final Class<?>[] newArray = new Class<?>[arraySize + 1];
+ for (int i = 0; i < arraySize; ++i) {
+ newArray[i] = array[i];
+ }
+ newArray[arraySize] = klass;
+
+ // OK to accidentally overwrite an existing array that was set by another thread.
+ sArray = newArray;
+ }
+ }
+
@Retention(SOURCE)
private @interface Dispatching {
boolean cancellable();
@@ -155,32 +232,63 @@ public final class RemoteInputConnectionImpl extends IInputContext.Stub {
mH.post(() -> {
try {
if (isFinished()) {
+ // This is a stale request, which can happen. No need to show a warning
+ // because this situation itself is not an error.
return;
}
final InputConnection ic = getInputConnection();
if (ic == null) {
+ // This is a stale request, which can happen. No need to show a warning
+ // because this situation itself is not an error.
+ return;
+ }
+ final View view = getServedView();
+ if (view == null) {
+ // This is a stale request, which can happen. No need to show a warning
+ // because this situation itself is not an error.
return;
}
- // Clean up composing text and batch edit.
- ic.finishComposingText();
- // Also clean up batch edit.
- while (true) {
- if (!ic.endBatchEdit()) {
- break;
+ final Class<? extends InputConnection> icClass = ic.getClass();
+
+ boolean alwaysTrueEndBatchEditDetected =
+ KnownAlwaysTrueEndBatchEditCache.contains(icClass);
+
+ if (!alwaysTrueEndBatchEditDetected) {
+ // Clean up composing text and batch edit.
+ final boolean supportsBatchEdit = ic.beginBatchEdit();
+ ic.finishComposingText();
+ if (supportsBatchEdit) {
+ // Also clean up batch edit.
+ int retryCount = 0;
+ while (true) {
+ if (!ic.endBatchEdit()) {
+ break;
+ }
+ ++retryCount;
+ if (retryCount > MAX_END_BATCH_EDIT_RETRY) {
+ Log.e(TAG, icClass.getTypeName() + "#endBatchEdit() still"
+ + " returns true even after retrying "
+ + MAX_END_BATCH_EDIT_RETRY + " times. Falling back to"
+ + " InputMethodManager#restartInput(View)");
+ alwaysTrueEndBatchEditDetected = true;
+ KnownAlwaysTrueEndBatchEditCache.add(icClass);
+ break;
+ }
+ }
}
}
- final TextSnapshot textSnapshot = ic.takeSnapshot();
- if (textSnapshot == null) {
- final View view = getServedView();
- if (view == null) {
+ if (!alwaysTrueEndBatchEditDetected) {
+ final TextSnapshot textSnapshot = ic.takeSnapshot();
+ if (textSnapshot != null) {
+ mParentInputMethodManager.doInvalidateInput(this, textSnapshot,
+ nextSessionId);
return;
}
- mParentInputMethodManager.restartInput(view);
- return;
}
- mParentInputMethodManager.doInvalidateInput(this, textSnapshot, nextSessionId);
+
+ mParentInputMethodManager.restartInput(view);
} finally {
mHasPendingInvalidation.set(false);
}
diff --git a/core/java/com/android/internal/net/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 2e7629a76dee..2a203acebc72 100644
--- a/core/java/com/android/internal/net/VpnConfig.java
+++ b/core/java/com/android/internal/net/VpnConfig.java
@@ -192,7 +192,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;
@@ -201,7 +201,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 5f84b5a92305..d0a8c32505d4 100644
--- a/core/java/com/android/internal/net/VpnProfile.java
+++ b/core/java/com/android/internal/net/VpnProfile.java
@@ -175,9 +175,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/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 c2224b4ab9d1..209c64a02324 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -161,7 +161,7 @@ public class BatteryStatsImpl extends BatteryStats {
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- static final int VERSION = 204;
+ static final int VERSION = 205;
// The maximum number of names wakelocks we will keep track of
// per uid; once the limit is reached, we batch the remaining wakelocks
@@ -240,6 +240,7 @@ public class BatteryStatsImpl extends BatteryStats {
private static final int[] SUPPORTED_PER_PROCESS_STATE_STANDARD_ENERGY_BUCKETS = {
MeasuredEnergyStats.POWER_BUCKET_CPU,
+ MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO,
};
// TimeInState counters need NUM_PROCESS_STATE states in order to accommodate
@@ -582,6 +583,8 @@ public class BatteryStatsImpl extends BatteryStats {
int UPDATE_ALL =
UPDATE_CPU | UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT | UPDATE_RPM | UPDATE_DISPLAY;
+ int UPDATE_ON_PROC_STATE_CHANGE = UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT;
+
@IntDef(flag = true, prefix = "UPDATE_", value = {
UPDATE_CPU,
UPDATE_WIFI,
@@ -608,6 +611,8 @@ public class BatteryStatsImpl extends BatteryStats {
Future<?> scheduleSyncDueToBatteryLevelChange(long delayMillis);
/** Schedule removal of UIDs corresponding to a removed user */
Future<?> scheduleCleanupDueToRemovedUser(int userId);
+ /** Schedule a sync because of a process state change */
+ Future<?> scheduleSyncDueToProcessStateChange(long delayMillis);
}
public Handler mHandler;
@@ -1703,7 +1708,6 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
-
private static class TimeMultiStateCounter implements TimeBaseObs {
private final TimeBase mTimeBase;
private final LongMultiStateCounter mCounter;
@@ -1736,10 +1740,6 @@ public class BatteryStatsImpl extends BatteryStats {
mCounter.setEnabled(false, elapsedRealtimeUs / 1000);
}
- public LongMultiStateCounter getCounter() {
- return mCounter;
- }
-
public int getStateCount() {
return mCounter.getStateCount();
}
@@ -1753,8 +1753,8 @@ public class BatteryStatsImpl extends BatteryStats {
return mCounter.updateValue(value, timestampMs);
}
- public void addCount(long delta) {
- mCounter.addCount(delta);
+ private void increment(long increment, long timestampMs) {
+ mCounter.incrementValue(increment, timestampMs);
}
/**
@@ -1764,6 +1764,10 @@ public class BatteryStatsImpl extends BatteryStats {
return mCounter.getCount(procState);
}
+ public long getTotalCountLocked() {
+ return mCounter.getTotalCount();
+ }
+
public void logState(Printer pw, String prefix) {
pw.println(prefix + "mCounter=" + mCounter);
}
@@ -7985,7 +7989,7 @@ public class BatteryStatsImpl extends BatteryStats {
LongSamplingCounter[] mNetworkByteActivityCounters;
LongSamplingCounter[] mNetworkPacketActivityCounters;
- LongSamplingCounter mMobileRadioActiveTime;
+ TimeMultiStateCounter mMobileRadioActiveTime;
LongSamplingCounter mMobileRadioActiveCount;
/**
@@ -8195,6 +8199,7 @@ public class BatteryStatsImpl extends BatteryStats {
final int batteryConsumerProcessState =
mapUidProcessStateToBatteryConsumerProcessState(procState);
getCpuActiveTimeCounter().setState(batteryConsumerProcessState, elapsedTimeMs);
+ getMobileRadioActiveTimeCounter().setState(batteryConsumerProcessState, elapsedTimeMs);
final MeasuredEnergyStats energyStats =
getOrCreateMeasuredEnergyStatsIfSupportedLocked();
if (energyStats != null) {
@@ -8594,11 +8599,10 @@ public class BatteryStatsImpl extends BatteryStats {
/** Adds the given charge to the given standard power bucket for this uid. */
@GuardedBy("mBsi")
private void addChargeToStandardBucketLocked(long chargeDeltaUC,
- @StandardPowerBucket int powerBucket) {
+ @StandardPowerBucket int powerBucket, long timestampMs) {
final MeasuredEnergyStats measuredEnergyStats =
getOrCreateMeasuredEnergyStatsLocked();
- measuredEnergyStats.updateStandardBucket(powerBucket, chargeDeltaUC,
- mBsi.mClock.elapsedRealtime());
+ measuredEnergyStats.updateStandardBucket(powerBucket, chargeDeltaUC, timestampMs);
}
/** Adds the given charge to the given custom power bucket for this uid. */
@@ -8690,6 +8694,13 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("mBsi")
@Override
+ public long getMobileRadioMeasuredBatteryConsumptionUC(int processState) {
+ return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO,
+ processState);
+ }
+
+ @GuardedBy("mBsi")
+ @Override
public long getScreenOnMeasuredBatteryConsumptionUC() {
return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON);
}
@@ -9228,9 +9239,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
void noteNetworkActivityLocked(int type, long deltaBytes, long deltaPackets) {
- if (mNetworkByteActivityCounters == null) {
- initNetworkActivityLocked();
- }
+ ensureNetworkActivityLocked();
if (type >= 0 && type < NUM_NETWORK_ACTIVITY_TYPES) {
mNetworkByteActivityCounters[type].addCountLocked(deltaBytes);
mNetworkPacketActivityCounters[type].addCountLocked(deltaPackets);
@@ -9240,14 +9249,25 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
- void noteMobileRadioActiveTimeLocked(long batteryUptime) {
- if (mNetworkByteActivityCounters == null) {
- initNetworkActivityLocked();
- }
- mMobileRadioActiveTime.addCountLocked(batteryUptime);
+ void noteMobileRadioActiveTimeLocked(long batteryUptimeDeltaUs, long elapsedTimeMs) {
+ ensureNetworkActivityLocked();
+ getMobileRadioActiveTimeCounter().increment(batteryUptimeDeltaUs, elapsedTimeMs);
mMobileRadioActiveCount.addCountLocked(1);
}
+ private TimeMultiStateCounter getMobileRadioActiveTimeCounter() {
+ if (mMobileRadioActiveTime == null) {
+ final long timestampMs = mBsi.mClock.elapsedRealtime();
+ mMobileRadioActiveTime = new TimeMultiStateCounter(
+ mBsi.mOnBatteryTimeBase, BatteryConsumer.PROCESS_STATE_COUNT, timestampMs);
+ mMobileRadioActiveTime.setState(
+ mapUidProcessStateToBatteryConsumerProcessState(mProcessState),
+ timestampMs);
+ mMobileRadioActiveTime.update(0, timestampMs);
+ }
+ return mMobileRadioActiveTime;
+ }
+
@Override
public boolean hasNetworkActivity() {
return mNetworkByteActivityCounters != null;
@@ -9275,8 +9295,20 @@ public class BatteryStatsImpl extends BatteryStats {
@Override
public long getMobileRadioActiveTime(int which) {
- return mMobileRadioActiveTime != null
- ? mMobileRadioActiveTime.getCountLocked(which) : 0;
+ return getMobileRadioActiveTimeInProcessState(BatteryConsumer.PROCESS_STATE_ANY);
+ }
+
+ @Override
+ public long getMobileRadioActiveTimeInProcessState(
+ @BatteryConsumer.ProcessState int processState) {
+ if (mMobileRadioActiveTime == null) {
+ return 0;
+ }
+ if (processState == BatteryConsumer.PROCESS_STATE_ANY) {
+ return mMobileRadioActiveTime.getTotalCountLocked();
+ } else {
+ return mMobileRadioActiveTime.getCountLocked(processState);
+ }
}
@Override
@@ -9388,18 +9420,17 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
- void initNetworkActivityLocked() {
- detachIfNotNull(mNetworkByteActivityCounters);
+ void ensureNetworkActivityLocked() {
+ if (mNetworkByteActivityCounters != null) {
+ return;
+ }
+
mNetworkByteActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
- detachIfNotNull(mNetworkPacketActivityCounters);
mNetworkPacketActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
mNetworkByteActivityCounters[i] = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
mNetworkPacketActivityCounters[i] = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
}
- detachIfNotNull(mMobileRadioActiveTime);
- mMobileRadioActiveTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
- detachIfNotNull(mMobileRadioActiveCount);
mMobileRadioActiveCount = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
}
@@ -9896,7 +9927,12 @@ public class BatteryStatsImpl extends BatteryStats {
mNetworkByteActivityCounters[i].writeToParcel(out);
mNetworkPacketActivityCounters[i].writeToParcel(out);
}
- mMobileRadioActiveTime.writeToParcel(out);
+ if (mMobileRadioActiveTime != null) {
+ out.writeBoolean(true);
+ mMobileRadioActiveTime.writeToParcel(out);
+ } else {
+ out.writeBoolean(false);
+ }
mMobileRadioActiveCount.writeToParcel(out);
} else {
out.writeInt(0);
@@ -9996,6 +10032,7 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("mBsi")
void readFromParcelLocked(TimeBase timeBase, TimeBase screenOffTimeBase, Parcel in) {
+ final long timestampMs = mBsi.mClock.elapsedRealtime();
mOnBatteryBackgroundTimeBase.readFromParcel(in);
mOnBatteryScreenOffBackgroundTimeBase.readFromParcel(in);
@@ -10205,7 +10242,14 @@ public class BatteryStatsImpl extends BatteryStats {
mNetworkPacketActivityCounters[i]
= new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in);
}
- mMobileRadioActiveTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in);
+ if (in.readBoolean()) {
+ final TimeMultiStateCounter counter =
+ new TimeMultiStateCounter(mBsi.mOnBatteryTimeBase, in, timestampMs);
+ if (counter.getStateCount() == BatteryConsumer.PROCESS_STATE_COUNT) {
+ mMobileRadioActiveTime = counter;
+ }
+ }
+
mMobileRadioActiveCount = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in);
} else {
mNetworkByteActivityCounters = null;
@@ -10247,7 +10291,6 @@ public class BatteryStatsImpl extends BatteryStats {
mScreenOffCpuFreqTimeMs = LongSamplingCounterArray.readFromParcel(
in, mBsi.mOnBatteryScreenOffTimeBase);
- final long timestampMs = mBsi.mClock.elapsedRealtime();
int stateCount = in.readInt();
if (stateCount != 0) {
final TimeMultiStateCounter counter = new TimeMultiStateCounter(
@@ -11182,6 +11225,9 @@ public class BatteryStatsImpl extends BatteryStats {
onBatteryScreenOffCounter.setState(uidRunningState, elapsedRealtimeMs);
}
+ final int prevBatteryConsumerProcessState =
+ mapUidProcessStateToBatteryConsumerProcessState(mProcessState);
+
mProcessState = uidRunningState;
updateOnBatteryBgTimeBase(uptimeMs * 1000, elapsedRealtimeMs * 1000);
@@ -11191,11 +11237,15 @@ public class BatteryStatsImpl extends BatteryStats {
mapUidProcessStateToBatteryConsumerProcessState(uidRunningState);
getCpuActiveTimeCounter().setState(batteryConsumerProcessState, elapsedRealtimeMs);
+ getMobileRadioActiveTimeCounter()
+ .setState(batteryConsumerProcessState, elapsedRealtimeMs);
final MeasuredEnergyStats energyStats =
getOrCreateMeasuredEnergyStatsIfSupportedLocked();
if (energyStats != null) {
energyStats.setState(batteryConsumerProcessState, elapsedRealtimeMs);
}
+ maybeScheduleExternalStatsSync(prevBatteryConsumerProcessState,
+ batteryConsumerProcessState);
}
if (userAwareService != mInForegroundService) {
@@ -11208,6 +11258,27 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
+ @GuardedBy("mBsi")
+ private void maybeScheduleExternalStatsSync(
+ @BatteryConsumer.ProcessState int oldProcessState,
+ @BatteryConsumer.ProcessState int newProcessState) {
+ if (oldProcessState == newProcessState) {
+ return;
+ }
+ // Transitions between BACKGROUND and such non-foreground states like cached
+ // or nonexistent do not warrant doing a sync. If some of the stats for those
+ // proc states bleed into the PROCESS_STATE_BACKGROUND, that's ok.
+ if ((oldProcessState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED
+ && newProcessState == BatteryConsumer.PROCESS_STATE_BACKGROUND)
+ || (oldProcessState == BatteryConsumer.PROCESS_STATE_BACKGROUND
+ && newProcessState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED)) {
+ return;
+ }
+
+ mBsi.mExternalSync.scheduleSyncDueToProcessStateChange(
+ mBsi.mConstants.PROC_STATE_CHANGE_COLLECTION_DELAY_MS);
+ }
+
/** Whether to consider Uid to be in the background for background timebase purposes. */
public boolean isInBackground() {
// Note that PROCESS_STATE_CACHED and Uid.PROCESS_STATE_NONEXISTENT is
@@ -12805,7 +12876,8 @@ public class BatteryStatsImpl extends BatteryStats {
.calcGlobalPowerWithoutControllerDataMah(globalTimeMs);
}
distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_WIFI,
- consumedChargeUC, uidEstimatedConsumptionMah, totalEstimatedConsumptionMah);
+ consumedChargeUC, uidEstimatedConsumptionMah, totalEstimatedConsumptionMah,
+ elapsedRealtimeMs);
}
}
}
@@ -12963,7 +13035,7 @@ public class BatteryStatsImpl extends BatteryStats {
final long appPackets = entry.rxPackets + entry.txPackets;
final long appRadioTimeUs =
(totalAppRadioTimeUs * appPackets) / totalPackets;
- u.noteMobileRadioActiveTimeLocked(appRadioTimeUs);
+ u.noteMobileRadioActiveTimeLocked(appRadioTimeUs, elapsedRealtimeMs);
// Distribute measured mobile radio charge consumption based on app radio
// active time
@@ -13042,7 +13114,7 @@ public class BatteryStatsImpl extends BatteryStats {
distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO,
consumedChargeUC, uidEstimatedConsumptionMah,
- totalEstimatedConsumptionMah);
+ totalEstimatedConsumptionMah, elapsedRealtimeMs);
}
mNetworkStatsPool.release(delta);
@@ -13330,7 +13402,8 @@ public class BatteryStatsImpl extends BatteryStats {
= mBluetoothPowerCalculator.calculatePowerMah(rxTimeMs, txTimeMs, idleTimeMs);
totalEstimatedMah = Math.max(totalEstimatedMah, controllerMaMs / MILLISECONDS_IN_HOUR);
distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH,
- consumedChargeUC, uidEstimatedConsumptionMah, totalEstimatedMah);
+ consumedChargeUC, uidEstimatedConsumptionMah, totalEstimatedMah,
+ elapsedRealtimeMs);
}
mLastBluetoothActivityInfo.set(info);
@@ -13433,8 +13506,10 @@ public class BatteryStatsImpl extends BatteryStats {
}
if (totalCpuChargeUC <= 0) return;
+ final long timestampMs = mClock.elapsedRealtime();
+
mGlobalMeasuredEnergyStats.updateStandardBucket(MeasuredEnergyStats.POWER_BUCKET_CPU,
- totalCpuChargeUC, mClock.elapsedRealtime());
+ totalCpuChargeUC, timestampMs);
// Calculate the measured microcoulombs/calculated milliamp-hour charge ratio for each
// cluster to normalize each uid's estimated power usage against actual power usage for
@@ -13483,7 +13558,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
uid.addChargeToStandardBucketLocked(uidCpuChargeUC,
- MeasuredEnergyStats.POWER_BUCKET_CPU);
+ MeasuredEnergyStats.POWER_BUCKET_CPU, timestampMs);
}
}
@@ -13583,7 +13658,7 @@ public class BatteryStatsImpl extends BatteryStats {
fgTimeUsArray.put(uid.getUid(), (double) fgTimeUs);
}
distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON,
- totalScreenOnChargeUC, fgTimeUsArray, 0);
+ totalScreenOnChargeUC, fgTimeUsArray, 0, elapsedRealtimeMs);
}
/**
@@ -13627,7 +13702,7 @@ public class BatteryStatsImpl extends BatteryStats {
gnssTimeUsArray.put(uid.getUid(), (double) gnssTimeUs);
}
distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_GNSS, chargeUC,
- gnssTimeUsArray, 0);
+ gnssTimeUsArray, 0, elapsedRealtimeMs);
}
/**
@@ -13699,7 +13774,7 @@ public class BatteryStatsImpl extends BatteryStats {
@SuppressWarnings("GuardedBy") // errorprone false positive on u.addChargeToStandardBucketLocked
private void distributeEnergyToUidsLocked(@StandardPowerBucket int bucket,
long totalConsumedChargeUC, SparseDoubleArray ratioNumerators,
- double minRatioDenominator) {
+ double minRatioDenominator, long timestampMs) {
// If the sum of all app usage was greater than the total, use that instead:
double sumRatioNumerators = 0;
@@ -13714,7 +13789,7 @@ public class BatteryStatsImpl extends BatteryStats {
final double ratioNumerator = ratioNumerators.valueAt(i);
final long uidActualUC
= (long) (totalConsumedChargeUC * ratioNumerator / ratioDenominator + 0.5);
- uid.addChargeToStandardBucketLocked(uidActualUC, bucket);
+ uid.addChargeToStandardBucketLocked(uidActualUC, bucket, timestampMs);
}
}
@@ -14433,7 +14508,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (childUid != null) {
final long delta =
childUid.cpuActiveCounter.update(cpuActiveTimesMs, elapsedRealtimeMs);
- u.getCpuActiveTimeCounter().addCount(delta);
+ u.getCpuActiveTimeCounter().increment(delta, elapsedRealtimeMs);
}
}
});
@@ -15627,7 +15702,6 @@ public class BatteryStatsImpl extends BatteryStats {
for (int procState = 0; procState < BatteryConsumer.PROCESS_STATE_COUNT; procState++) {
procStateNames[procState] = BatteryConsumer.processStateToString(procState);
}
- procStateNames[BatteryConsumer.PROCESS_STATE_ANY] = "untracked";
return procStateNames;
}
@@ -15651,6 +15725,8 @@ public class BatteryStatsImpl extends BatteryStats {
= "external_stats_collection_rate_limit_ms";
public static final String KEY_BATTERY_LEVEL_COLLECTION_DELAY_MS
= "battery_level_collection_delay_ms";
+ public static final String KEY_PROC_STATE_CHANGE_COLLECTION_DELAY_MS =
+ "procstate_change_collection_delay_ms";
public static final String KEY_MAX_HISTORY_FILES = "max_history_files";
public static final String KEY_MAX_HISTORY_BUFFER_KB = "max_history_buffer_kb";
public static final String KEY_BATTERY_CHARGED_DELAY_MS =
@@ -15661,6 +15737,7 @@ public class BatteryStatsImpl extends BatteryStats {
private static final long DEFAULT_UID_REMOVE_DELAY_MS = 5L * 60L * 1000L;
private static final long DEFAULT_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS = 600_000;
private static final long DEFAULT_BATTERY_LEVEL_COLLECTION_DELAY_MS = 300_000;
+ private static final long DEFAULT_PROC_STATE_CHANGE_COLLECTION_DELAY_MS = 60_000;
private static final int DEFAULT_MAX_HISTORY_FILES = 32;
private static final int DEFAULT_MAX_HISTORY_BUFFER_KB = 128; /*Kilo Bytes*/
private static final int DEFAULT_MAX_HISTORY_FILES_LOW_RAM_DEVICE = 64;
@@ -15676,6 +15753,8 @@ public class BatteryStatsImpl extends BatteryStats {
= DEFAULT_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS;
public long BATTERY_LEVEL_COLLECTION_DELAY_MS
= DEFAULT_BATTERY_LEVEL_COLLECTION_DELAY_MS;
+ public long PROC_STATE_CHANGE_COLLECTION_DELAY_MS =
+ DEFAULT_PROC_STATE_CHANGE_COLLECTION_DELAY_MS;
public int MAX_HISTORY_FILES;
public int MAX_HISTORY_BUFFER; /*Bytes*/
public int BATTERY_CHARGED_DELAY_MS = DEFAULT_BATTERY_CHARGED_DELAY_MS;
@@ -15742,6 +15821,9 @@ public class BatteryStatsImpl extends BatteryStats {
BATTERY_LEVEL_COLLECTION_DELAY_MS = mParser.getLong(
KEY_BATTERY_LEVEL_COLLECTION_DELAY_MS,
DEFAULT_BATTERY_LEVEL_COLLECTION_DELAY_MS);
+ PROC_STATE_CHANGE_COLLECTION_DELAY_MS = mParser.getLong(
+ KEY_PROC_STATE_CHANGE_COLLECTION_DELAY_MS,
+ DEFAULT_PROC_STATE_CHANGE_COLLECTION_DELAY_MS);
MAX_HISTORY_FILES = mParser.getInt(KEY_MAX_HISTORY_FILES,
ActivityManager.isLowRamDeviceStatic() ?
@@ -15793,6 +15875,8 @@ public class BatteryStatsImpl extends BatteryStats {
pw.println(EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS);
pw.print(KEY_BATTERY_LEVEL_COLLECTION_DELAY_MS); pw.print("=");
pw.println(BATTERY_LEVEL_COLLECTION_DELAY_MS);
+ pw.print(KEY_PROC_STATE_CHANGE_COLLECTION_DELAY_MS); pw.print("=");
+ pw.println(PROC_STATE_CHANGE_COLLECTION_DELAY_MS);
pw.print(KEY_MAX_HISTORY_FILES); pw.print("=");
pw.println(MAX_HISTORY_FILES);
pw.print(KEY_MAX_HISTORY_BUFFER_KB); pw.print("=");
@@ -16476,14 +16560,18 @@ public class BatteryStatsImpl extends BatteryStats {
}
if (in.readInt() != 0) {
- if (u.mNetworkByteActivityCounters == null) {
- u.initNetworkActivityLocked();
- }
+ u.ensureNetworkActivityLocked();
for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
u.mNetworkByteActivityCounters[i].readSummaryFromParcelLocked(in);
u.mNetworkPacketActivityCounters[i].readSummaryFromParcelLocked(in);
}
- u.mMobileRadioActiveTime.readSummaryFromParcelLocked(in);
+ if (in.readBoolean()) {
+ TimeMultiStateCounter counter = new TimeMultiStateCounter(
+ mOnBatteryTimeBase, in, elapsedRealtimeMs);
+ if (counter.getStateCount() == BatteryConsumer.PROCESS_STATE_COUNT) {
+ u.mMobileRadioActiveTime = counter;
+ }
+ }
u.mMobileRadioActiveCount.readSummaryFromParcelLocked(in);
}
@@ -17033,7 +17121,12 @@ public class BatteryStatsImpl extends BatteryStats {
u.mNetworkByteActivityCounters[i].writeSummaryFromParcelLocked(out);
u.mNetworkPacketActivityCounters[i].writeSummaryFromParcelLocked(out);
}
- u.mMobileRadioActiveTime.writeSummaryFromParcelLocked(out);
+ if (u.mMobileRadioActiveTime != null) {
+ out.writeBoolean(true);
+ u.mMobileRadioActiveTime.writeToParcel(out);
+ } else {
+ out.writeBoolean(false);
+ }
u.mMobileRadioActiveCount.writeSummaryFromParcelLocked(out);
}
diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java
index 175f28ffcda9..ee614cdbdb95 100644
--- a/core/java/com/android/internal/os/CpuPowerCalculator.java
+++ b/core/java/com/android/internal/os/CpuPowerCalculator.java
@@ -156,11 +156,11 @@ public class CpuPowerCalculator extends PowerCalculator {
private void calculateMeasuredPowerPerProcessState(UidBatteryConsumer.Builder app,
BatteryStats.Uid u, BatteryConsumer.Key[] keys) {
for (BatteryConsumer.Key key : keys) {
- // The key for "PROCESS_STATE_ANY" has already been populated with the
- // full energy across all states. We don't want to override it with
+ // The key for PROCESS_STATE_UNSPECIFIED aka PROCESS_STATE_ANY has already been
+ // populated with the full energy across all states. We don't want to override it with
// the energy for "other" states, which excludes the tracked states like
// foreground, background etc.
- if (key.processState == BatteryConsumer.PROCESS_STATE_ANY) {
+ if (key.processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
continue;
}
@@ -184,7 +184,7 @@ public class CpuPowerCalculator extends PowerCalculator {
uidProcState++) {
@BatteryConsumer.ProcessState int procState =
BatteryStats.mapUidProcessStateToBatteryConsumerProcessState(uidProcState);
- if (procState == BatteryConsumer.PROCESS_STATE_ANY) {
+ if (procState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
continue;
}
@@ -199,7 +199,7 @@ public class CpuPowerCalculator extends PowerCalculator {
}
for (BatteryConsumer.Key key : keys) {
- if (key.processState == BatteryConsumer.PROCESS_STATE_ANY) {
+ if (key.processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
continue;
}
diff --git a/core/java/com/android/internal/os/LongMultiStateCounter.java b/core/java/com/android/internal/os/LongMultiStateCounter.java
index ad8e0ed4c2f5..33a9d547be78 100644
--- a/core/java/com/android/internal/os/LongMultiStateCounter.java
+++ b/core/java/com/android/internal/os/LongMultiStateCounter.java
@@ -122,6 +122,13 @@ public final class LongMultiStateCounter implements Parcelable {
/**
* Adds the supplied values to the current accumulated values in the counter.
*/
+ public void incrementValue(long count, long timestampMs) {
+ native_incrementValue(mNativeObject, count, timestampMs);
+ }
+
+ /**
+ * Adds the supplied values to the current accumulated values in the counter.
+ */
public void addCount(long count) {
native_addCount(mNativeObject, count);
}
@@ -144,6 +151,17 @@ public final class LongMultiStateCounter implements Parcelable {
return native_getCount(mNativeObject, state);
}
+ /**
+ * Returns the total accumulated count across all states.
+ */
+ public long getTotalCount() {
+ long total = 0;
+ for (int state = 0; state < mStateCount; state++) {
+ total += native_getCount(mNativeObject, state);
+ }
+ return total;
+ }
+
@Override
public String toString() {
return native_toString(mNativeObject);
@@ -190,6 +208,10 @@ public final class LongMultiStateCounter implements Parcelable {
private static native long native_updateValue(long nativeObject, long value, long timestampMs);
@CriticalNative
+ private static native void native_incrementValue(long nativeObject, long increment,
+ long timestampMs);
+
+ @CriticalNative
private static native void native_addCount(long nativeObject, long count);
@CriticalNative
diff --git a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
index eb5993dc2d61..28cc836396b4 100644
--- a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
+++ b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
@@ -34,6 +34,8 @@ public class MobileRadioPowerCalculator extends PowerCalculator {
private static final int NUM_SIGNAL_STRENGTH_LEVELS =
CellSignalStrength.getNumSignalStrengthLevels();
+ private static final BatteryConsumer.Key[] UNINITIALIZED_KEYS = new BatteryConsumer.Key[0];
+
private final UsageBasedPowerEstimator mActivePowerEstimator;
private final UsageBasedPowerEstimator[] mIdlePowerEstimators =
new UsageBasedPowerEstimator[NUM_SIGNAL_STRENGTH_LEVELS];
@@ -89,14 +91,22 @@ public class MobileRadioPowerCalculator extends PowerCalculator {
PowerAndDuration total = new PowerAndDuration();
- final double powerPerPacketMah = getMobilePowerPerPacket(batteryStats, rawRealtimeUs,
- BatteryStats.STATS_SINCE_CHARGED);
final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
builder.getUidBatteryConsumerBuilders();
+ BatteryConsumer.Key[] keys = UNINITIALIZED_KEYS;
+
for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
final BatteryStats.Uid uid = app.getBatteryStatsUid();
- calculateApp(app, uid, powerPerPacketMah, total, query);
+ if (keys == UNINITIALIZED_KEYS) {
+ if (query.isProcessStateDataNeeded()) {
+ keys = app.getKeys(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO);
+ } else {
+ keys = null;
+ }
+ }
+
+ calculateApp(app, uid, total, query, keys);
}
final long consumptionUC = batteryStats.getMobileRadioMeasuredBatteryConsumptionUC();
@@ -121,34 +131,49 @@ public class MobileRadioPowerCalculator extends PowerCalculator {
}
private void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
- double powerPerPacketMah, PowerAndDuration total,
- BatteryUsageStatsQuery query) {
+ PowerAndDuration total,
+ BatteryUsageStatsQuery query, BatteryConsumer.Key[] keys) {
final long radioActiveDurationMs = calculateDuration(u, BatteryStats.STATS_SINCE_CHARGED);
total.totalAppDurationMs += radioActiveDurationMs;
final long consumptionUC = u.getMobileRadioMeasuredBatteryConsumptionUC();
final int powerModel = getPowerModel(consumptionUC, query);
- final double powerMah = calculatePower(u, powerModel, powerPerPacketMah,
- radioActiveDurationMs, consumptionUC);
+ final double powerMah = calculatePower(u, powerModel, radioActiveDurationMs, consumptionUC);
total.totalAppPowerMah += powerMah;
app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
radioActiveDurationMs)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, powerMah,
powerModel);
+
+ if (query.isProcessStateDataNeeded() && keys != null) {
+ for (BatteryConsumer.Key key: keys) {
+ final int processState = key.processState;
+ if (processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
+ // Already populated with the total across all process states
+ continue;
+ }
+
+ final long durationInStateMs =
+ u.getMobileRadioActiveTimeInProcessState(processState) / 1000;
+ final long consumptionInStateUc =
+ u.getMobileRadioMeasuredBatteryConsumptionUC(processState);
+ final double powerInStateMah = calculatePower(u, powerModel, durationInStateMs,
+ consumptionInStateUc);
+ app.setConsumedPower(key, powerInStateMah, powerModel);
+ }
+ }
}
@Override
public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
- final double mobilePowerPerPacket = getMobilePowerPerPacket(batteryStats, rawRealtimeUs,
- statsType);
PowerAndDuration total = new PowerAndDuration();
for (int i = sippers.size() - 1; i >= 0; i--) {
final BatterySipper app = sippers.get(i);
if (app.drainType == BatterySipper.DrainType.APP) {
final BatteryStats.Uid u = app.uidObj;
- calculateApp(app, u, statsType, mobilePowerPerPacket, total);
+ calculateApp(app, u, statsType, total);
}
}
@@ -172,13 +197,12 @@ public class MobileRadioPowerCalculator extends PowerCalculator {
}
private void calculateApp(BatterySipper app, BatteryStats.Uid u, int statsType,
- double powerPerPacketMah, PowerAndDuration total) {
+ PowerAndDuration total) {
app.mobileActive = calculateDuration(u, statsType);
final long consumptionUC = u.getMobileRadioMeasuredBatteryConsumptionUC();
final int powerModel = getPowerModel(consumptionUC);
- app.mobileRadioPowerMah = calculatePower(u, powerModel, powerPerPacketMah, app.mobileActive,
- consumptionUC);
+ app.mobileRadioPowerMah = calculatePower(u, powerModel, app.mobileActive, consumptionUC);
total.totalAppDurationMs += app.mobileActive;
// Add cost of mobile traffic.
@@ -205,26 +229,15 @@ public class MobileRadioPowerCalculator extends PowerCalculator {
}
private double calculatePower(BatteryStats.Uid u, @BatteryConsumer.PowerModel int powerModel,
- double powerPerPacketMah, long radioActiveDurationMs, long measuredChargeUC) {
+ long radioActiveDurationMs, long measuredChargeUC) {
if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) {
return uCtoMah(measuredChargeUC);
}
if (radioActiveDurationMs > 0) {
- // We are tracking when the radio is up, so can use the active time to
- // determine power use.
return calcPowerFromRadioActiveDurationMah(radioActiveDurationMs);
- } else {
- // We are not tracking when the radio is up, so must approximate power use
- // based on the number of packets.
- final long mobileRxPackets = u.getNetworkActivityPackets(
- BatteryStats.NETWORK_MOBILE_RX_DATA,
- BatteryStats.STATS_SINCE_CHARGED);
- final long mobileTxPackets = u.getNetworkActivityPackets(
- BatteryStats.NETWORK_MOBILE_TX_DATA,
- BatteryStats.STATS_SINCE_CHARGED);
- return (mobileRxPackets + mobileTxPackets) * powerPerPacketMah;
}
+ return 0;
}
private void calculateRemaining(PowerAndDuration total,
@@ -299,21 +312,4 @@ public class MobileRadioPowerCalculator extends PowerCalculator {
public double calcScanTimePowerMah(long scanningTimeMs) {
return mScanPowerEstimator.calculatePower(scanningTimeMs);
}
-
- /**
- * Return estimated power (in mAh) of sending or receiving a packet with the mobile radio.
- */
- private double getMobilePowerPerPacket(BatteryStats stats, long rawRealtimeUs, int statsType) {
- final long radioDataUptimeMs =
- stats.getMobileRadioActiveTime(rawRealtimeUs, statsType) / 1000;
- final double mobilePower = calcPowerFromRadioActiveDurationMah(radioDataUptimeMs);
-
- final long mobileRx = stats.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_RX_DATA,
- statsType);
- final long mobileTx = stats.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_TX_DATA,
- statsType);
- final long mobilePackets = mobileRx + mobileTx;
-
- return mobilePackets != 0 ? mobilePower / mobilePackets : 0;
- }
}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 6b9d95ff9137..611f644ffc7d 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -713,7 +713,7 @@ public class ZygoteInit {
"--setuid=1000",
"--setgid=1000",
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,"
- + "1024,1032,1065,3001,3002,3003,3006,3007,3009,3010,3011",
+ + "1024,1032,1065,3001,3002,3003,3006,3007,3009,3010,3011,3012",
"--capabilities=" + capabilities + "," + capabilities,
"--nice-name=system_server",
"--runtime-args",
diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java
index 4dc9aa5228d6..a52ae107d983 100644
--- a/core/java/com/android/internal/power/MeasuredEnergyStats.java
+++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java
@@ -589,6 +589,10 @@ public class MeasuredEnergyStats {
final int numIndices = mConfig.getNumberOfBuckets();
for (int index = 0; index < numIndices; index++) {
setValueIfSupported(index, 0L);
+ if (mAccumulatedMultiStateChargeMicroCoulomb != null
+ && mAccumulatedMultiStateChargeMicroCoulomb[index] != null) {
+ mAccumulatedMultiStateChargeMicroCoulomb[index].reset();
+ }
}
}
diff --git a/core/java/com/android/internal/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/telephony/ICarrierPrivilegesListener.aidl b/core/java/com/android/internal/telephony/ICarrierPrivilegesListener.aidl
new file mode 100644
index 000000000000..6ca8cecba3c8
--- /dev/null
+++ b/core/java/com/android/internal/telephony/ICarrierPrivilegesListener.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+oneway interface ICarrierPrivilegesListener {
+ void onCarrierPrivilegesChanged(
+ in List<String> privilegedPackageNames, in int[] privilegedUids);
+}
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 6ba0279313b1..9712d7e38a4b 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -32,6 +32,7 @@ import android.telephony.PreciseDataConnectionState;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.emergency.EmergencyNumber;
+import com.android.internal.telephony.ICarrierPrivilegesListener;
import com.android.internal.telephony.IPhoneStateListener;
import com.android.internal.telephony.IOnSubscriptionsChangedListener;
@@ -77,6 +78,7 @@ interface ITelephonyRegistry {
void notifySubscriptionInfoChanged();
void notifyOpportunisticSubscriptionInfoChanged();
void notifyCarrierNetworkChange(in boolean active);
+ void notifyCarrierNetworkChangeWithSubId(in int subId, in boolean active);
void notifyUserMobileDataStateChangedForPhoneId(in int phoneId, in int subId, in boolean state);
void notifyDisplayInfoChanged(int slotIndex, int subId, in TelephonyDisplayInfo telephonyDisplayInfo);
void notifyPhoneCapabilityChanged(in PhoneCapability capability);
@@ -99,4 +101,10 @@ interface ITelephonyRegistry {
void notifyAllowedNetworkTypesChanged(in int phoneId, in int subId, in int reason, in long allowedNetworkType);
void notifyLinkCapacityEstimateChanged(in int phoneId, in int subId,
in List<LinkCapacityEstimate> linkCapacityEstimateList);
+
+ void addCarrierPrivilegesListener(
+ int phoneId, ICarrierPrivilegesListener callback, String pkg, String featureId);
+ void removeCarrierPrivilegesListener(ICarrierPrivilegesListener callback, String pkg);
+ void notifyCarrierPrivilegesChanged(
+ int phoneId, in List<String> privilegedPackageNames, in int[] privilegedUids);
}
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index 4c519f4c779f..e95ba884fed4 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -122,6 +122,11 @@ public class LatencyTracker {
*/
public static final int ACTION_USER_SWITCH = 12;
+ /**
+ * Time it takes to turn on the inner screen for a foldable device.
+ */
+ public static final int ACTION_SWITCH_DISPLAY_UNFOLD = 13;
+
private static final int[] ACTIONS_ALL = {
ACTION_EXPAND_PANEL,
ACTION_TOGGLE_RECENTS,
@@ -135,7 +140,8 @@ public class LatencyTracker {
ACTION_ROTATE_SCREEN_SENSOR,
ACTION_ROTATE_SCREEN_CAMERA_CHECK,
ACTION_LOCKSCREEN_UNLOCK,
- ACTION_USER_SWITCH
+ ACTION_USER_SWITCH,
+ ACTION_SWITCH_DISPLAY_UNFOLD
};
/** @hide */
@@ -152,7 +158,8 @@ public class LatencyTracker {
ACTION_ROTATE_SCREEN_SENSOR,
ACTION_ROTATE_SCREEN_CAMERA_CHECK,
ACTION_LOCKSCREEN_UNLOCK,
- ACTION_USER_SWITCH
+ ACTION_USER_SWITCH,
+ ACTION_SWITCH_DISPLAY_UNFOLD
})
@Retention(RetentionPolicy.SOURCE)
public @interface Action {
@@ -171,7 +178,8 @@ public class LatencyTracker {
FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_SENSOR,
FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_CAMERA_CHECK,
FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOCKSCREEN_UNLOCK,
- FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_USER_SWITCH
+ FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_USER_SWITCH,
+ FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SWITCH_DISPLAY_UNFOLD
};
private static LatencyTracker sLatencyTracker;
@@ -257,6 +265,8 @@ public class LatencyTracker {
return "ACTION_LOCKSCREEN_UNLOCK";
case 13:
return "ACTION_USER_SWITCH";
+ case 14:
+ return "ACTION_SWITCH_DISPLAY_UNFOLD";
default:
throw new IllegalArgumentException("Invalid action");
}
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
index 4ad779551060..04e4819f6ead 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/jni/android_os_Trace.cpp b/core/jni/android_os_Trace.cpp
index f67007cda209..85fd5d99e473 100644
--- a/core/jni/android_os_Trace.cpp
+++ b/core/jni/android_os_Trace.cpp
@@ -90,6 +90,22 @@ static void android_os_Trace_nativeSetTracingEnabled(JNIEnv*, jclass, jboolean e
atrace_set_tracing_enabled(enabled);
}
+static void android_os_Trace_nativeInstant(JNIEnv* env, jclass,
+ jlong tag, jstring nameStr) {
+ withString(env, nameStr, [tag](char* str) {
+ atrace_instant(tag, str);
+ });
+}
+
+static void android_os_Trace_nativeInstantForTrack(JNIEnv* env, jclass,
+ jlong tag, jstring trackStr, jstring nameStr) {
+ withString(env, trackStr, [env, tag, nameStr](char* track) {
+ withString(env, nameStr, [tag, track](char* name) {
+ atrace_instant_for_track(tag, track, name);
+ });
+ });
+}
+
static const JNINativeMethod gTraceMethods[] = {
/* name, signature, funcPtr */
{ "nativeSetAppTracingAllowed",
@@ -116,6 +132,12 @@ static const JNINativeMethod gTraceMethods[] = {
{ "nativeAsyncTraceEnd",
"(JLjava/lang/String;I)V",
(void*)android_os_Trace_nativeAsyncTraceEnd },
+ { "nativeInstant",
+ "(JLjava/lang/String;)V",
+ (void*)android_os_Trace_nativeInstant },
+ { "nativeInstantForTrack",
+ "(JLjava/lang/String;Ljava/lang/String;)V",
+ (void*)android_os_Trace_nativeInstantForTrack },
// ----------- @CriticalNative ----------------
{ "nativeGetEnabledTags",
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 86d781033e5e..8c23b214b8fe 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -881,6 +881,12 @@ static jint NativeGetResourceArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jin
return static_cast<jint>(bag->entry_count);
}
+static jint NativeGetParentThemeIdentifier(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ const auto parentThemeResId = assetmanager->GetParentThemeResourceId(resid);
+ return parentThemeResId.value_or(0);
+}
+
static jint NativeGetResourceIdentifier(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring name,
jstring def_type, jstring def_package) {
ScopedUtfChars name_utf8(env, name);
@@ -1464,6 +1470,8 @@ static const JNINativeMethod gAssetManagerMethods[] = {
{"nativeGetResourceIntArray", "(JI)[I", (void*)NativeGetResourceIntArray},
{"nativeGetResourceArraySize", "(JI)I", (void*)NativeGetResourceArraySize},
{"nativeGetResourceArray", "(JI[I)I", (void*)NativeGetResourceArray},
+ {"nativeGetParentThemeIdentifier", "(JI)I",
+ (void*)NativeGetParentThemeIdentifier},
// AssetManager resource name/ID methods.
{"nativeGetResourceIdentifier", "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
diff --git a/core/jni/android_view_InputQueue.cpp b/core/jni/android_view_InputQueue.cpp
index b910d1664ad1..74bbd7b2cb32 100644
--- a/core/jni/android_view_InputQueue.cpp
+++ b/core/jni/android_view_InputQueue.cpp
@@ -269,8 +269,7 @@ int register_android_view_InputQueue(JNIEnv* env)
return RegisterMethodsOrDie(env, kInputQueuePathName, g_methods, NELEM(g_methods));
}
-AInputQueue* android_view_InputQueue_getNativePtr(jobject inputQueue) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+AInputQueue* android_view_InputQueue_getNativePtr(JNIEnv* env, jobject inputQueue) {
jlong ptr = env->CallLongMethod(inputQueue, gInputQueueClassInfo.getNativePtr);
return reinterpret_cast<AInputQueue*>(ptr);
}
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index fb5fded923f2..67d0c52960e0 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -597,6 +597,14 @@ static void nativeSetPosition(JNIEnv* env, jclass clazz, jlong transactionObj,
transaction->setPosition(ctrl, x, y);
}
+static void nativeSetScale(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject,
+ jfloat xScale, jfloat yScale) {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+
+ SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
+ transaction->setMatrix(ctrl, xScale, 0, 0, yScale);
+}
+
static void nativeSetGeometry(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject,
jobject sourceObj, jobject dstObj, jlong orientation) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
@@ -620,9 +628,19 @@ static void nativeSetBuffer(JNIEnv* env, jclass clazz, jlong transactionObj, jlo
jobject bufferObject) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
- sp<GraphicBuffer> buffer(
- android_graphics_GraphicBuffer_getNativeGraphicsBuffer(env, bufferObject));
- transaction->setBuffer(ctrl, buffer);
+ sp<GraphicBuffer> graphicBuffer(GraphicBuffer::fromAHardwareBuffer(
+ android_hardware_HardwareBuffer_getNativeHardwareBuffer(env, bufferObject)));
+ transaction->setBuffer(ctrl, graphicBuffer);
+}
+
+static void nativeSetBufferTransform(JNIEnv* env, jclass clazz, jlong transactionObj,
+ jlong nativeObject, jint transform) {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+ SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
+ transaction->setTransform(ctrl, transform);
+ bool transformToInverseDisplay = (NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY & transform) ==
+ NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
+ transaction->setTransformToDisplayInverse(ctrl, transformToInverseDisplay);
}
static void nativeSetColorSpace(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject,
@@ -740,6 +758,37 @@ static void nativeSetTransparentRegionHint(JNIEnv* env, jclass clazz, jlong tran
}
}
+static void nativeSetDamageRegion(JNIEnv* env, jclass clazz, jlong transactionObj,
+ jlong nativeObject, jobject regionObj) {
+ SurfaceControl* const surfaceControl = reinterpret_cast<SurfaceControl*>(nativeObject);
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+
+ if (regionObj == nullptr) {
+ transaction->setSurfaceDamageRegion(surfaceControl, Region::INVALID_REGION);
+ return;
+ }
+
+ graphics::RegionIterator iterator(env, regionObj);
+ if (!iterator.isValid()) {
+ transaction->setSurfaceDamageRegion(surfaceControl, Region::INVALID_REGION);
+ return;
+ }
+
+ Region region;
+ while (!iterator.isDone()) {
+ ARect rect = iterator.getRect();
+ region.orSelf(static_cast<const Rect&>(rect));
+ iterator.next();
+ }
+
+ if (region.getBounds().isEmpty()) {
+ transaction->setSurfaceDamageRegion(surfaceControl, Region::INVALID_REGION);
+ return;
+ }
+
+ transaction->setSurfaceDamageRegion(surfaceControl, region);
+}
+
static void nativeSetAlpha(JNIEnv* env, jclass clazz, jlong transactionObj,
jlong nativeObject, jfloat alpha) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
@@ -1905,10 +1954,14 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeSetRelativeLayer },
{"nativeSetPosition", "(JJFF)V",
(void*)nativeSetPosition },
+ {"nativeSetScale", "(JJFF)V",
+ (void*)nativeSetScale },
{"nativeSetSize", "(JJII)V",
(void*)nativeSetSize },
{"nativeSetTransparentRegionHint", "(JJLandroid/graphics/Region;)V",
(void*)nativeSetTransparentRegionHint },
+ { "nativeSetDamageRegion", "(JJLandroid/graphics/Region;)V",
+ (void*)nativeSetDamageRegion },
{"nativeSetAlpha", "(JJF)V",
(void*)nativeSetAlpha },
{"nativeSetColor", "(JJ[F)V",
@@ -2018,8 +2071,9 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeGetDisplayedContentSample },
{"nativeSetGeometry", "(JJLandroid/graphics/Rect;Landroid/graphics/Rect;J)V",
(void*)nativeSetGeometry },
- {"nativeSetBuffer", "(JJLandroid/graphics/GraphicBuffer;)V",
+ {"nativeSetBuffer", "(JJLandroid/hardware/HardwareBuffer;)V",
(void*)nativeSetBuffer },
+ {"nativeSetBufferTransform", "(JJI)V", (void*) nativeSetBufferTransform},
{"nativeSetColorSpace", "(JJI)V",
(void*)nativeSetColorSpace },
{"nativeSyncInputWindows", "(J)V",
diff --git a/core/jni/android_window_WindowInfosListener.cpp b/core/jni/android_window_WindowInfosListener.cpp
index b20addb4611d..1381de567b55 100644
--- a/core/jni/android_window_WindowInfosListener.cpp
+++ b/core/jni/android_window_WindowInfosListener.cpp
@@ -16,8 +16,10 @@
#define LOG_TAG "WindowInfosListener"
+#include <android/graphics/matrix.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/Log.h>
+#include <gui/DisplayInfo.h>
#include <gui/SurfaceComposerClient.h>
#include <nativehelper/JNIHelp.h>
#include <utils/Log.h>
@@ -37,14 +39,30 @@ static struct {
jmethodID onWindowInfosChanged;
} gListenerClassInfo;
+static struct {
+ jclass clazz;
+ jmethodID ctor;
+} gDisplayInfoClassInfo;
+
static jclass gInputWindowHandleClass;
+jobject fromDisplayInfo(JNIEnv* env, gui::DisplayInfo displayInfo) {
+ float transformValues[9];
+ for (int i = 0; i < 9; i++) {
+ transformValues[i] = displayInfo.transform[i % 3][i / 3];
+ }
+ ScopedLocalRef<jobject> matrixObj(env, AMatrix_newInstance(env, transformValues));
+ return env->NewObject(gDisplayInfoClassInfo.clazz, gDisplayInfoClassInfo.ctor,
+ displayInfo.displayId, displayInfo.logicalWidth,
+ displayInfo.logicalHeight, matrixObj.get());
+}
+
struct WindowInfosListener : public gui::WindowInfosListener {
WindowInfosListener(JNIEnv* env, jobject listener)
: mListener(env->NewWeakGlobalRef(listener)) {}
void onWindowInfosChanged(const std::vector<WindowInfo>& windowInfos,
- const std::vector<DisplayInfo>& /*displayInfos*/) override {
+ const std::vector<DisplayInfo>& displayInfos) override {
JNIEnv* env = AndroidRuntime::getJNIEnv();
LOG_ALWAYS_FATAL_IF(env == nullptr, "Unable to retrieve JNIEnv in onWindowInfoChanged.");
@@ -64,7 +82,15 @@ struct WindowInfosListener : public gui::WindowInfosListener {
env->SetObjectArrayElement(jWindowHandlesArray, i, jWindowHandle.get());
}
- env->CallVoidMethod(listener, gListenerClassInfo.onWindowInfosChanged, jWindowHandlesArray);
+ jobjectArray jDisplayInfoArray =
+ 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->CallVoidMethod(listener, gListenerClassInfo.onWindowInfosChanged, jWindowHandlesArray,
+ jDisplayInfoArray);
env->DeleteGlobalRef(listener);
if (env->ExceptionCheck()) {
@@ -126,10 +152,16 @@ int register_android_window_WindowInfosListener(JNIEnv* env) {
gListenerClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
gListenerClassInfo.onWindowInfosChanged =
env->GetMethodID(gListenerClassInfo.clazz, "onWindowInfosChanged",
- "([Landroid/view/InputWindowHandle;)V");
+ "([Landroid/view/InputWindowHandle;[Landroid/window/"
+ "WindowInfosListener$DisplayInfo;)V");
clazz = env->FindClass("android/view/InputWindowHandle");
gInputWindowHandleClass = MakeGlobalRefOrDie(env, clazz);
+
+ clazz = env->FindClass("android/window/WindowInfosListener$DisplayInfo");
+ gDisplayInfoClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
+ gDisplayInfoClassInfo.ctor = env->GetMethodID(gDisplayInfoClassInfo.clazz, "<init>",
+ "(IIILandroid/graphics/Matrix;)V");
return 0;
}
diff --git a/core/jni/com_android_internal_os_LongMultiStateCounter.cpp b/core/jni/com_android_internal_os_LongMultiStateCounter.cpp
index 45652a79a5c6..69c4f3ddd9c5 100644
--- a/core/jni/com_android_internal_os_LongMultiStateCounter.cpp
+++ b/core/jni/com_android_internal_os_LongMultiStateCounter.cpp
@@ -84,6 +84,10 @@ static jlong native_updateValue(jlong nativePtr, jlong value, jlong timestamp) {
return (jlong)asLongMultiStateCounter(nativePtr)->updateValue((int64_t)value, timestamp);
}
+static void native_incrementValue(jlong nativePtr, jlong count, jlong timestamp) {
+ asLongMultiStateCounter(nativePtr)->incrementValue(count, timestamp);
+}
+
static void native_addCount(jlong nativePtr, jlong count) {
asLongMultiStateCounter(nativePtr)->addValue(count);
}
@@ -172,6 +176,8 @@ static const JNINativeMethod g_methods[] = {
// @CriticalNative
{"native_updateValue", "(JJJ)J", (void *)native_updateValue},
// @CriticalNative
+ {"native_incrementValue", "(JJJ)V", (void *)native_incrementValue},
+ // @CriticalNative
{"native_addCount", "(JJ)V", (void *)native_addCount},
// @CriticalNative
{"native_reset", "(J)V", (void *)native_reset},
diff --git a/core/jni/include/android_runtime/android_view_InputQueue.h b/core/jni/include/android_runtime/android_view_InputQueue.h
index c1b611cf303e..115e2f8605b0 100644
--- a/core/jni/include/android_runtime/android_view_InputQueue.h
+++ b/core/jni/include/android_runtime/android_view_InputQueue.h
@@ -80,7 +80,7 @@ private:
Vector<key_value_pair_t<InputEvent*, bool> > mFinishedEvents;
};
-extern AInputQueue* android_view_InputQueue_getNativePtr(jobject inputQueue);
+extern AInputQueue* android_view_InputQueue_getNativePtr(JNIEnv* env, jobject inputQueue);
} // namespace android
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4c367326b670..e2a2ac64e780 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2066,7 +2066,7 @@
<permission android:name="android.permission.BLUETOOTH_PRIVILEGED"
android:protectionLevel="signature|privileged" />
- <!-- Control access to email providers exclusively for Bluetooth
+ <!-- @SystemApi Control access to email providers exclusively for Bluetooth
@hide
-->
<permission android:name="android.permission.BLUETOOTH_MAP"
@@ -3997,6 +3997,14 @@
<permission android:name="android.permission.BIND_TEXTCLASSIFIER_SERVICE"
android:protectionLevel="signature" />
+ <!-- Must be required by a android.service.selectiontoolbar.SelectionToolbarRenderService,
+ to ensure that only the system can bind to it.
+ @hide This is not a third-party API (intended for OEMs and system apps).
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.BIND_SELECTION_TOOLBAR_RENDER_SERVICE"
+ android:protectionLevel="signature" />
+
<!-- Must be required by a android.service.contentcapture.ContentCaptureService,
to ensure that only the system can bind to it.
@SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
@@ -6050,6 +6058,19 @@
<permission android:name="android.permission.SEND_SAFETY_CENTER_UPDATE"
android:protectionLevel="signature|privileged" />
+ <!-- Allows an application to launch device manager setup screens.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.LAUNCH_DEVICE_MANAGER_SETUP"
+ android:protectionLevel="signature|role" />
+
+ <!-- @SystemApi Allows an application to update certain device management related system
+ resources.
+ @hide -->
+ <permission android:name="android.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES"
+ android:protectionLevel="signature|role" />
+
<!-- Attribution for Geofencing service. -->
<attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
<!-- Attribution for Country Detector. -->
@@ -6551,6 +6572,16 @@
</intent-filter>
</service>
+ <!-- TODO: Move to ExtServices or relevant component. -->
+ <service android:name="com.android.server.selectiontoolbar.DefaultSelectionToolbarRenderService"
+ android:permission="android.permission.BIND_SELECTION_TOOLBAR_RENDER_SERVICE"
+ android:process=":ui"
+ android:exported="false">
+ <intent-filter>
+ <action android:name="android.service.selectiontoolbar.SelectionToolbarRenderService"/>
+ </intent-filter>
+ </service>
+
<provider
android:name="com.android.server.textclassifier.IconsContentProvider"
android:authorities="com.android.textclassifier.icons"
diff --git a/core/res/res/drawable/ic_add_supervised_user.xml b/core/res/res/drawable/ic_add_supervised_user.xml
new file mode 100644
index 000000000000..a49377594a22
--- /dev/null
+++ b/core/res/res/drawable/ic_add_supervised_user.xml
@@ -0,0 +1,34 @@
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="40dp"
+ android:height="40dp"
+ android:viewportWidth="20"
+ android:viewportHeight="20"
+ android:tint="?android:attr/colorControlNormal">
+
+ <group
+ android:scaleX="0.5"
+ android:scaleY="0.5">
+
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M15.625,22.5q-2.375,0 -4.063,-1.688 -1.687,-1.687 -1.687,-4.104 0,-2.375 1.688,-4.062 1.687,-1.688 4.062,-1.688 2.417,0 4.105,1.688 1.687,1.687 1.687,4.062 0,2.417 -1.688,4.105 -1.687,1.687 -4.104,1.687zM15.625,19.708q1.292,0 2.146,-0.875 0.854,-0.875 0.854,-2.125t-0.854,-2.125q-0.854,-0.875 -2.146,-0.875 -1.208,0 -2.104,0.875 -0.896,0.875 -0.896,2.125t0.896,2.125q0.896,0.875 2.104,0.875zM27.875,24.333q-1.792,0 -3.063,-1.27 -1.27,-1.271 -1.27,-3.063 0,-1.792 1.27,-3.063 1.271,-1.27 3.063,-1.27 1.833,0 3.083,1.27 1.25,1.271 1.25,3.063 0,1.792 -1.25,3.063 -1.25,1.27 -3.083,1.27zM17.458,33.667q1.959,-3.75 5.063,-5.063 3.104,-1.312 5.354,-1.312 0.958,0 1.813,0.145 0.854,0.146 1.729,0.396 1.041,-1.541 1.75,-3.583 0.708,-2.042 0.708,-4.25 0,-5.792 -4.041,-9.834Q25.791,6.125 20,6.125t-9.834,4.041Q6.125,14.208 6.125,20q0,2.042 0.563,3.938 0.562,1.895 1.604,3.437 1.625,-0.833 3.52,-1.333 1.896,-0.5 3.813,-0.5 1.208,0 2.333,0.188 1.125,0.187 2.125,0.52 -0.833,0.458 -1.562,1 -0.729,0.542 -1.354,1.125 -0.459,-0.042 -0.813,-0.042h-0.729q-1.375,0 -2.916,0.355 -1.542,0.354 -2.751,0.937 1.5,1.583 3.438,2.645 1.937,1.063 4.062,1.397zM20,36.667q-3.417,0 -6.459,-1.313 -3.041,-1.312 -5.312,-3.583 -2.271,-2.271 -3.583,-5.313Q3.333,23.418 3.333,20q0,-3.458 1.313,-6.479Q5.958,10.5 8.229,8.229t5.313,-3.583Q16.582,3.333 20,3.333q3.458,0 6.479,1.313 3.021,1.312 5.292,3.583t3.584,5.292q1.312,3.021 1.312,6.479 0,3.417 -1.313,6.459 -1.312,3.041 -3.583,5.312 -2.271,2.271 -5.292,3.584 -3.021,1.312 -6.479,1.312z"/>
+
+ </group>
+
+</vector>
+
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index c02981e643db..585c372da5fe 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Laat die program toe om die kitsboodskapdiens te gebruik om oproepe sonder jou ingryping te maak."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"lees foonstatus en identiteit"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Laat die program toe om toegang tot die foonfunksies van die toestel te verkry. Hierdie toestemming laat die program toe om te bepaal wat die foonnommer en toestel-IDs is, of die oproep aan die gang is, en die afgeleë nommer wat deur \'n oproep verbind word."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"lees basiese telefoniestatus en -identiteit"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Gee die program toegang tot die toestel se basiese telefoniekenmerke."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"roeteer oproepe deur die stelsel"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Laat die program toe om sy oproepe deur die stelsel te stuur om die oproepervaring te verbeter."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"sien en beheer oproepe deur die stelsel."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tik om jou gesigmodel uit te vee en voeg jou gesig dan weer by"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Stel Gesigslot op"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Ontsluit jou foon deur daarna te kyk"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Skakel "<b>"kameratoegang"</b>" in Instellings &gt; Privaatheid aan om Gesigslot te gebruik"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Stel meer maniere op om te ontsluit"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tik om \'n vingerafdruk by te voeg"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Vingerafdrukslot"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Berei tans <xliff:g id="APPNAME">%1$s</xliff:g> voor."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Begin programme."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Voltooi herlaai."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Skakel skerm af?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Jy het die aan/af-skakelaar gedruk terwyl jy jou vingerafdruk gestel het.\n\nDit skakel gewoonlik jou skerm af."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Skakel af"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Kanselleer"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Gaan voort met opstelling?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Jy het die aan/af-skakelaar gedruk – dit skakel gewoonlik die skerm af.\n\nProbeer liggies tik terwyl jy jou vingerafdruk opstel."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Skakel skerm af"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Doen opstelling"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Gaan voort met vingerafdrukverifiëring?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Jy het die aan/af-skakelaar gedruk – dit skakel gewoonlik die skerm af.\n\nProbeer liggies tik om jou vingerafdruk te verifieer."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Skakel skerm af"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Gaan voort"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> loop"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Tik om na die speletjie terug te keer"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Kies speletjie"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index be4eeb8c693c..f338166ce87f 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"መተግበሪያው ያለእርስዎ ጣልቃ ገብነት ጥሪዎችን ለማድረግ የአይኤምኤስ አገልግሎቱን እንዲጠቀም ያስችለዋል።"</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"የስልክ ሁኔታና ማንነት አንብብ"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"መተግበሪያው የመሳሪያውን የስልክ ባህሪያት ላይ እንዲደርስ ይፈቅድለታል። ይህ ፈቃድ መተግበሪያው የስልክ ቁጥሩን እና የመሳሪያውን መታወቂያዎች፣ ጥሪ የነቃ እንደሆነ፣ እና በጥሪ የተገናኘውን የሩቅ ቁጥር እንዲወስን ይፈቅድለታል።"</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"መሠረታዊ የቴሌፎኒ ሁኔታ እና ማንነት ያንብቡ"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"መተግበሪያው የመሣሪያውን መሠረታዊ የቴሌፎኒ ባህሪያት እንዲደርስ ይፈቅድለታል።"</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"ጥሪዎችን በስርዓቱ በኩል አዙር"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"መተግበሪያው የጥሪ ተሞክሮን እንዲያሻሽል ጥሪዎቹን በስርዓቱ በኩል እንዲያዞር ያስችለዋል።"</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"በሥርዓቱ በኩል ጥሪዎችን ይመልከቱ እና ይቆጣጠሩ።"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"የእርስዎ የመልክ ሞዴል ለመሰረዝ መታ ያድርጉ፣ ከዚያ መልክዎን እንደገና ያክሉ"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"በመልክ መክፈትን ያዋቅሩ"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ስልክዎን በመመልከት ያስከፍቱት"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"በመልክ መክፈትን ለመጠቀም "<b>"የካሜራ መዳረሻ"</b>"ን በቅንብሮች እና ግላዊነት ውስጥ ያብሩ"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"የሚከፍቱባቸው ተጨማሪ መንገዶችን ያቀናብሩ"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"የጣት አሻራን ለማከል መታ ያድርጉ"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"በጣት አሻራ መክፈቻ"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g>ን ማዘጋጀት።"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"መተግበሪያዎችን በማስጀመር ላይ፡፡"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"አጨራረስ ማስነሻ፡፡"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"ማያ ገጽ ይጥፋ?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"የጣት አሻራዎን ሲያዋቅሩ የኃይል አዝራሩን ተጫንተውታል። \n\n ይህ አብዛኛው ጊዜ ማያ ገጽዎን ያጠፈዋል።"</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"አጥፋ"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"ይቅር"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"ማዋቀር ይቀጥሉ?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"የማብሪያ/ማጥፊያ ቁልፉን ተጭነዋል — ይህ ብዙውን ጊዜ ማያ ገጹን ያጠፋል።\n\nየጣት አሻራዎን በሚያዋቅሩበት ጊዜ በትንሹ መታ ለማድረግ ይሞክሩ።"</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"ማያ ገጽን አጥፋ"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"ማዋቀር ቀጥል"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"የጣት አሻራዎን ማረጋገጥ ይቀጥሉ?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"የማብሪያ/ማጥፊያ ቁልፉን ተጭነዋል - ይህ ብዙውን ጊዜ ማያ ገጹን ያጠፋል። \n\n የጣት አሻራዎን ለማረጋገጥ በትንሹ መታ ለማድረግ ይሞክሩ።"</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"ማያ ገጽን አጥፋ"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"ቀጥል"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> አሂድ"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"ወደ ጨዋታ ለመመለስ መታ ያድርጉ"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"ጨዋታ ይምረጡ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 0748af27ed9a..c15f298740c4 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -483,6 +483,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"للسماح للتطبيق باستخدام خدمة الرسائل الفورية لإجراء المكالمات بدون تدخل منك."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"قراءة حالة الهاتف والهوية"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"للسماح للتطبيق بالدخول إلى ميزات الهاتف في الجهاز. ويتيح هذا الإذن للتطبيق تحديد رقم الهاتف ومعرّفات الجهاز، وما إذا كانت هناك مكالمة نشطة والرقم البعيد الذي تم الاتصال به في المكالمة."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"قراءة حالة وهوية الاتصال الهاتفي الأساسيتين"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"يسمح هذا الإذن للتطبيق بالوصول إلى ميزات الاتصال الهاتفي الأساسية للجهاز."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"توجيه المكالمات من خلال النظام"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"يسمح للتطبيق بتوجيه المكالمات من خلال النظام لتحسين تجربة الاتصال."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"رؤية المكالمات والتحكّم فيها من خلال النظام"</string>
@@ -634,6 +636,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"انقر لحذف نموذج الوجه ثم أضِف نموذجًا لوجهك مرة أخرى."</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"إعداد ميزة \"فتح الجهاز بالتعرف على الوجه\""</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"يمكنك فتح قفل هاتفك بمجرّد النظر إلى الشاشة."</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"‏لاستخدام ميزة \"فتح الجهاز بالتعرف على الوجه\"، عليك منح إذن "<b>"الوصول إلى الكاميرا"</b>" في الإعدادات &gt; الخصوصية."</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"إعداد المزيد من الطرق لفتح قفل الجهاز"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"انقر لإضافة بصمة إصبع."</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"فتح الجهاز ببصمة الإصبع"</string>
@@ -1356,10 +1359,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"جارٍ تحضير <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"بدء التطبيقات."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"جارٍ إعادة التشغيل."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"هل تريد إيقاف الشاشة؟"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"أثناء إعداد بصمة الإصبع، ضغطت على زر التشغيل.\n\nيؤدي هذا الإجراء عادةً إلى إيقاف الشاشة."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"إيقاف"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"إلغاء"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"هل تريد مواصلة عملية الإعداد؟"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"ضغطت على زر التشغيل، يؤدي هذا عادةً إلى إيقاف الشاشة.\n\nجرِّب النقر بخفة أثناء إعداد بصمتك."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"إيقاف الشاشة"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"مواصلة عملية الإعداد"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"هل تريد مواصلة تأكيد بصمة إصبعك؟"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"ضغطت على زر التشغيل، يؤدي هذا عادةً إلى إيقاف الشاشة.\n\nجرِّب النقر بخفة لتأكيد بصمة إصبعك."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"إيقاف الشاشة"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"متابعة"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> يعمل"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"انقر للعودة إلى اللعبة"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"اختيار اللعبة"</string>
@@ -1727,7 +1734,7 @@
<string name="default_audio_route_category_name" msgid="5241740395748134483">"النظام"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="4214648773120426288">"صوت بلوتوث"</string>
<string name="wireless_display_route_description" msgid="8297563323032966831">"عرض شاشة لاسلكي"</string>
- <string name="media_route_button_content_description" msgid="2299223698196869956">"إرسال"</string>
+ <string name="media_route_button_content_description" msgid="2299223698196869956">"البث"</string>
<string name="media_route_chooser_title" msgid="6646594924991269208">"الاتصال بجهاز"</string>
<string name="media_route_chooser_title_for_remote_display" msgid="3105906508794326446">"بث الشاشة على الجهاز"</string>
<string name="media_route_chooser_searching" msgid="6119673534251329535">"جارٍ البحث عن الأجهزة…"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 4cfb88ccf00e..9dbfb2e81e86 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"আপোনাৰ হস্তক্ষেপৰ অবিহনে আইএমএছ সেৱা ব্যৱহাৰ কৰি কল কৰিবলৈ এপক অনুমতি দিয়ে।"</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"ফ\'নৰ স্থিতি আৰু পৰিচয় পঢ়ক"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"ডিভাইচত থকা ফ\'নৰ সুবিধাসমূহ ব্য়ৱহাৰ কৰিবলৈ এপটোক অনুমতি দিয়ে৷ এই অনুমতিয়ে কোনো কল সক্ৰিয় হৈ থাককেই বা নাথাকক আৰু দূৰবৰ্তী নম্বৰটো কলৰ দ্বাৰা সংযোজিত হওকেই বা নহওক এপটোক ফ\'ন নম্বৰ আৰু ডিভাইচৰ পৰিচয় নিৰ্ধাৰণ কৰিবলৈ অনুমতি দিয়ে৷"</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"প্ৰাথমিক টেলিফ\'নী স্থিতি আৰু পৰিচয় পঢ়ক"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"এপ্‌টোক ডিভাইচটোৰ প্ৰাথমিক টেলিফ’নী সুবিধাসমূহ এক্সেছ কৰাৰ অনুমতি দিয়ে।"</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"ছিষ্টেমৰ জৰিয়তে কল কৰিব পাৰে"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"কল কৰাৰ অভিজ্ঞতাক উন্নত কৰিবলৈ এপটোক ছিষ্টেমৰ জৰিয়তে কলসমূহ কৰিবলৈ দিয়ে।"</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"ছিষ্টেমৰ জৰিয়তে কলবোৰ চোৱা আৰু নিয়ন্ত্ৰণ কৰা।"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"আপোনাৰ মুখাৱয়বৰ মডেলটো মচিবলৈ টিপক, তাৰ পাছত পুনৰ আপোনাৰ মুখাৱয়ব যোগ দিয়ক"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"ফেচ আনলক সুবিধাটো ছেট আপ কৰক"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"আপোনাৰ ফ’নটোলৈ চাই সেইটো আনলক কৰক"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"ফেচ আনলক সুবিধাটো ব্যৱহাৰ কৰিবলৈ ছেটিং &gt; গোপনীয়তাত "<b>"কেমেৰাৰ এক্সেছ"</b>" অন কৰক"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"আনলক কৰাৰ অধিক উপায় ছেট আপ কৰক"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"এটা ফিংগাৰপ্ৰিণ্ট যোগ দিবলৈ টিপক"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ফিংগাৰপ্ৰিন্ট আনলক"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g>সাজু কৰি থকা হৈছে।"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"আৰম্ভ হৈ থকা এপসমূহ।"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"বুট কাৰ্য সমাপ্ত কৰিছে।"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"স্ক্ৰীন অফ কৰিবনে?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"আপোনাৰ ফিংগাৰপ্ৰিণ্ট ছেট আপ কৰাৰ সময়ত, আপুনি পাৱাৰ বুটামটো টিপিছে।\n\nএইটোৱে সচৰাচৰ আপোনাৰ স্ক্ৰীনখন অফ কৰে।"</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"অফ কৰক"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"বাতিল কৰক"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"ছেট আপ অব্যাহত ৰাখিবনে?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"আপুনি পাৱাৰ বুটামটো টিপিছে — এইটোৱে সাধাৰণতে স্ক্ৰীনখন অফ কৰে।\n\nআপোনাৰ ফিংগাৰপ্ৰিণ্টটো ছেট আপ কৰাৰ সময়ত লাহেকৈ টিপি চাওক।"</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"স্ক্ৰীন অফ কৰক"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"ছেট আপ অব্যাহত ৰাখক"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"ফিংগাৰপ্ৰিণ্ট সত্যাপন কৰা জাৰি ৰাখিবনে?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"আপুনি পাৱাৰ বুটামটো টিপিছে — এইটোৱে সাধাৰণতে স্ক্ৰীনখন অফ কৰে।\n\nআপোনাৰ ফিংগাৰপ্ৰিণ্টটো সত্যাপন কৰিবলৈ লাহেকৈ টিপি চাওক।"</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"স্ক্ৰীন অফ কৰক"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"অব্যাহত ৰাখক"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> চলি আছে"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"গেইমলৈ উভতি যাওক"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"গেইম বাছনি কৰক"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 136563a3859c..1c9355264d8c 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Tətbiqə müdaxilə olmadan zəng etmək üçün IMS xidmətindən istifadə etməyə imkan verir."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"telefon statusunu və identifikasiyanı oxuyur"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Tətbiqə cihazın telefon funksiyalarına giriş icazəsi verir. Belə icazəli tətbiq bu telefonun nömrəsini və cihaz İD\'ni, zəngin aktiv olub-olmadığını və zəng edilən nömrəni müəyyən edə bilər."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"əsas telefoniya statusu və identifikasiyasını oxuyun"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Tətbiqə cihazın əsas telefoniya funksiyalarına giriş etmək imkanı verir."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"zəngləri sistem üzərindən yönləndirin"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Tətbiqə, zəng təcrübəsini yaxşılaşdırmaq üçün, zəngləri sistem üzərindən yönləndirməyə icazə verilir."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"zənglərə sistemdə baxın və nəzarət edin."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Üz modelinizi silmək üçün toxunun, sonra yenidən üzünüzü əlavə edin"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Üz ilə kiliddən çıxarmanı ayarlayın"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Telefona baxaraq onu kiliddən çıxarın"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Üz ilə Kiliddən Açma funksiyasını istifadə etmək üçün Ayarlar &gt; Məxfilik bölməsində "<b>"Kameraya girişi"</b>" aktiv edin"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Kiliddən çıxarmağın daha çox yolunu ayarlayın"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Barmaq izi əlavə etmək üçün toxunun"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Barmaq izi ilə kiliddən çıxarma"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> proqramının hazırlanması."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Tətbiqlər başladılır."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Yükləmə başa çatır."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Ekran deaktiv edilsin?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Barmaq izinizi ayarlayarkən Qidalanma düyməsinə basdınız.\n\nBu, adətən ekranınızı deaktiv edir."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Deaktiv edin"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Ləğv edin"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Ayarlamağa davam edilsin?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Qidalanma düyməsini basdınız — adətən bu, ekranı söndürür.\n\nBarmaq izini ayarlayarkən yüngülcə toxunmağa çalışın."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Ekranı söndürün"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Ayarlamağa davam edin"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Barmaq izini doğrulamağa davam edilsin?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Qidalanma düyməsini basdınız — adətən bu, ekranı söndürür.\n\nBarmaq izini doğrulamaq üçün yüngülcə toxunmağa çalışın."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Ekranı söndürün"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Davam edin"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> çalışır"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Oyuna qayıtmaq üçün klikləyin"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Oyun seçin"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 00fc2b5dac5b..92416316cb85 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -474,6 +474,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Dozvoljava aplikaciji da koristi uslugu razmene trenutnih poruka da bi upućivala pozive bez vaše intervencije."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"čitanje statusa i identiteta telefona"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Dozvoljava aplikaciji da pristupa funkcijama telefona na uređaju. Ova dozvola omogućava aplikaciji da utvrdi broj telefona i ID-ove uređaja, zatim da li je poziv aktivan, kao i broj daljinskog uređaja sa kojim je uspostavljen poziv."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"očitavanje osnovnog telefonskog statusa i identiteta"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Omogućava aplikaciji da pristupa osnovnim telefonskim funkcijama uređaja."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"preusmeravanje poziva preko sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Dozvoljava aplikaciji da preusmerava pozive preko sistema da bi poboljšala doživljaj pozivanja."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"pregled i kontrola poziva preko sistema."</string>
@@ -625,6 +627,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Dodirnite da biste izbrisali model lica, pa ponovo dodajte svoje lice"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Podesite otključavanje licem"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Otključajte telefon tako što ćete ga pogledati"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Da biste koristili otključavanje licem, uključite "<b>"pristup kameri"</b>" u odeljku Podešavanja &gt; Privatnost"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Podesite još načina za otključavanje"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Dodirnite da biste dodali otisak prsta"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Otključavanje otiskom prsta"</string>
@@ -1296,10 +1299,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Priprema se <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Pokretanje aplikacija."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Završavanje pokretanja."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Želite da isključite ekran?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Pritisli ste dugme za uključivanje tokom podešavanja otiska prsta.\n\nTako se najčešće isključuje ekran."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Isključi"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Otkaži"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Želite li da nastavite sa podešavanjem?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Pritisnuli ste dugme za uključivanje – time obično isključujete ekran.\n\nProbajte lagano da dodirnete dok podešavate otisak prsta."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Isključi ekran"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Nastavi podešavanje"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Nastavljate verifikaciju otiska prsta?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Pritisnuli ste dugme za uključivanje – time obično isključujete ekran.\n\nProbajte lagano da dodirnete da biste verifikovali otisak prsta."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Isključi ekran"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Nastavi"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"Aplikacija <xliff:g id="APP">%1$s</xliff:g> je pokrenuta"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Dodirnite da biste se vratili u igru"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Odaberite igru"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 0157e66b5118..849d53b0ad04 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -404,7 +404,7 @@
<string name="permdesc_persistentActivity" product="tablet" msgid="6055271149187369916">"Дазваляе прыкладанню захоўваць некаторыя пастаянныя часткi ў памяцi. Гэта можа абмежаваць аб\'ём памяці, даступнай для іншых прыкладанняў, i запаволiць працу планшэта."</string>
<string name="permdesc_persistentActivity" product="tv" msgid="6800526387664131321">"Дазваляе праграме пастаянна захоўваць некаторыя свае часткі ў памяці прылады. Гэта можа абмежаваць аб\'ём памяці, даступнай для іншых праграм, і запаволіць працу прылады Android TV."</string>
<string name="permdesc_persistentActivity" product="default" msgid="1914841924366562051">"Дазваляе прыкладанню захоўваць некаторыя пастаянныя часткi ў памяцi. Гэта можа абмежаваць аб\'ём памяці, даступнай для іншых прыкладанняў, i запаволiць працу тэлефона."</string>
- <string name="permlab_foregroundService" msgid="1768855976818467491">"запусціць асноўныя сэрвісы"</string>
+ <string name="permlab_foregroundService" msgid="1768855976818467491">"запусціць актыўныя сэрвісы"</string>
<string name="permdesc_foregroundService" msgid="8720071450020922795">"Дазваляе праграме выкарыстоўваць асноўныя сэрвісы."</string>
<string name="permlab_getPackageSize" msgid="375391550792886641">"вымерыць прастору для захоўвання прыкладання"</string>
<string name="permdesc_getPackageSize" msgid="742743530909966782">"Дазваляе прыкладанням атрымліваць яго код, дадзеныя і аб\'ём кэш-памяці"</string>
@@ -477,6 +477,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Дазваляе праграмам выкарыстоўваць службу IMS, каб рабіць выклікі без вашага ўмяшання."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"чытанне статусу тэлефона і ідэнтыфікацыя"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Дазваляе прыкладанням атрымлiваць доступ да функцый тэлефона на прыладзе. Дзякуючы гэтаму дазволу прыкладанне можа вызначаць iдэнтыфiкатары нумару тэлефона i прылады, незалежна ад таго, цi актыўны выклiк, i аддалены нумар, на якi робiцца выклiк."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"счытваць даныя пра асноўны стан тэлефаніі і ідэнтыфікацыю"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Дазваляе праграме мець доступ да асноўных функцый тэлефаніі на прыладзе."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"перанакіраванне выклікаў праз сістэму"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Дазваляе праграме перанакіроўваць выклікі праз сістэму ў мэтах паляпшэння выклікаў."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"праглядаць выклікі і кіраваць імі праз сістэму."</string>
@@ -628,6 +630,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Націсніце, каб выдаліць мадэль твару, пасля дадайце твар яшчэ раз"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Наладзьце распазнаванне твару"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Разблакіруйце свой тэлефон, паглядзеўшы на яго"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Каб выкарыстоўваць распазнаванне твару, уключыце "<b>"доступ да камеры"</b>" праз раздзел \"Налады &gt; Прыватнасць\""</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Наладзьце дадатковыя спосабы разблакіроўкі"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Націсніце, каб дадаць адбітак пальца"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Разблакіраванне адбіткам пальца"</string>
@@ -726,8 +729,8 @@
<string name="permdesc_handoverStatus" msgid="3842269451732571070">"Дазваляе праграме атрымліваць інфармацыю аб бягучых перадачах Android Beam"</string>
<string name="permlab_removeDrmCertificates" msgid="710576248717404416">"выдаленне сертыфікатаў DRM"</string>
<string name="permdesc_removeDrmCertificates" msgid="4068445390318355716">"Дазваляе праграме выдаляць сертыфікаты DRM. Ніколі не павінна патрабавацца для звычайных праграм."</string>
- <string name="permlab_bindCarrierMessagingService" msgid="3363450860593096967">"прывязка да службы паведамленняў аператара"</string>
- <string name="permdesc_bindCarrierMessagingService" msgid="6316457028173478345">"Дазваляе ўладальніку выконваць прывязку да інтэрфейсу верхняга ўзроўню службы паведамленняў аператара. Ніколі не павінна патрабавацца для звычайных праграм."</string>
+ <string name="permlab_bindCarrierMessagingService" msgid="3363450860593096967">"падключэнне да сэрвісу абмену паведамленнямі аператара"</string>
+ <string name="permdesc_bindCarrierMessagingService" msgid="6316457028173478345">"Дазваляе ўладальніку выконваць падключэнне да базавага інтэрфейсу сэрвісу абмену паведамленнямі аператара. Звычайныя праграмы ніколі не выкарыстоўваюць гэты дазвол."</string>
<string name="permlab_bindCarrierServices" msgid="2395596978626237474">"прывязвацца з сэрвісаў аператара"</string>
<string name="permdesc_bindCarrierServices" msgid="9185614481967262900">"Дазваляе ўладальніку ажыццяўляць прывязку да сэрвісаў аператара. Ніколі не павінна патрабавацца для звычайных праграм."</string>
<string name="permlab_access_notification_policy" msgid="5524112842876975537">"атрымліваць доступ да рэжыму «Не турбаваць»"</string>
@@ -1316,10 +1319,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Падрыхтоўка <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Запуск прыкладанняў."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Завяршэнне загрузкі."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Выключыць экран?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Падчас наладкі адбітка пальца вы націскалі кнопку сілкавання.\n\nЗвычайна гэта дзеянне выключае экран."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Выключыць"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Скасаваць"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Працягнуць наладжванне?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Вы націснулі кнопку сілкавання. Звычайна ў выніку гэтага дзеяння выключаецца экран.\n\nПадчас наладжвання адбітка пальца злёгку дакраніцеся да кнопкі."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Выключыць экран"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Працягнуць наладку"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Працягнуць спраўджанне адбітка пальца?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Вы націснулі кнопку сілкавання. Звычайна ў выніку гэтага дзеяння выключаецца экран.\n\nКаб спраўдзіць адбітак пальца, злёгку дакраніцеся да кнопкі."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Выключыць экран"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Працягнуць"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"Прыкладанне \"<xliff:g id="APP">%1$s</xliff:g>\" запушчанае"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Націсніце, каб вярнуцца да гульні"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Выберыце гульню"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index d75d2e7543d6..dad2f03cbd68 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Разрешава на приложението да използва услугата за незабавни съобщения за извършване на обаждания без намеса от ваша страна."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"четене на състоянието и идентификационните данни на телефона"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Разрешава на приложението достъп до телефонните функции на устройството. Това разрешение позволява на приложението да определя телефонния номер и идентификационния номер на устройството, дали се води разговор и отдалечения номер, до който е установена връзка с обаждането."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"четене на основното телефонно състояние и идентичност"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Разрешава на приложението да осъществява достъп до основните телефонни функции на устройството."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"маршрутизиране на обажданията чрез системата"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Разрешава на приложението да маршрутизира обажданията си чрез системата с цел подобряване на свързаната с тях практическа работа."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"вижда и управлява обажданията чрез системата."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Докоснете, за да изтриете модела на лицето си, след което добавете лицето си отново"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Настройване на отключването с лице"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Отключвайте телефона си, като го погледнете"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"За да използвате функцията „Отключване с лице“, включете "<b>"достъпа до камерата"</b>" от „Настройки &gt; Поверителност“"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Настройване на още начини за отключване на телефона"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Докоснете, за да добавите отпечатък"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Отключване с отпечатък"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> се подготвя."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Приложенията се стартират."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Зареждането завършва."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Да се изключи ли екранът?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"При настройването на отпечатъка си натиснахте бутона за захранване.\n\nТова действие обикновено изключва екрана."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Изключване"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Отказ"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Искате ли да продължите с настройването?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Натиснахте бутона за включване/изключване – това обикновено изключва екрана.\n\nОпитайте да докоснете леко, докато настройвате отпечатъка си."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Изключване на екрана"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Напред с настройв."</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Напред с потвърждаването на отпечатъка?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Натиснахте бутона за включване/изключване – това обикновено изключва екрана.\n\nОпитайте да докоснете леко, за да потвърдите отпечатъка си."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Изключване на екрана"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Напред"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> се изпълнява"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Докоснете, за да се върнете към играта"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Избиране на игра"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 30809483882d..d560d5c1e107 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"আপনার হস্তক্ষেপ ছাড়াই কল করতে অ্যাপ্লিকেশানটিকে IMS পরিষেবা ব্যবহারের অনুমতি দিন৷"</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"ফোনের স্থিতি এবং পরিচয় পড়ুন"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"অ্যাপ্লিকেশানটিকে ডিভাইসের ফোন বৈশিষ্ট্যগুলিকে অ্যাক্সেস করার অনুমতি দেয়৷ এই অনুমতিটি অ্যাপ্লিকেশানটিকে একটি কল সক্রিয় থাকা অবস্থায় এবং দূরবর্তী নম্বর একটি কল দ্বারা সংযুক্ত থাকাকালীনও ফোন নম্বর এবং ডিভাইসের IDগুলি নির্ধারণ করার অনুমতি দেয়৷"</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"টেলিফোন সংক্রান্ত স্ট্যাটাস ও পরিচয় সংক্রান্ত প্রাথমিক বিষয় পড়ুন"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"অ্যাপটিকে ডিভাইসের টেলিফোন সংক্রান্ত প্রাথমিক ফিচারগুলি অ্যাক্সেস করার অনুমতি দিন।"</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"সিস্টেমের মাধ্যমে কলগুলি রুট করতে দিন"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"কল করার অভিজ্ঞতা উন্নত করার জন্য অ্যাপকে সিস্টেমের মাধ্যমে তার কলগুলি রুট করতে দেয়।"</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"সিস্টেমের মাধ্যমে কল দেখা এবং নিয়ন্ত্রণ করা।"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"আপনার ফেস মডেল মুছে দেওয়ার জন্য ট্যাপ করুন এবং তারপরে আবার ফেস যোগ করুন"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"\'ফেস আনলক\' সেট আপ করুন"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"আপনার ফোনের দিকে তাকিয়ে এটিকে আনলক করুন"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"\'ফেস আনলক\' ফিচার ব্যবহার করতে \'সেটিংস ও গোপনীয়তা\' বিকল্পে গিয়ে "<b>"ক্যামেরায় অ্যাক্সেস দিন"</b></string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"আনলক করার জন্য বিভিন্ন উপায়ে সেট আপ করুন"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"একটি আঙ্গুলের ছাপ যোগ করতে ট্যাপ করুন"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ফিঙ্গারপ্রিন্ট আনলক"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> প্রস্তুত করা হচ্ছে৷"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"অ্যাপ্লিকেশানগুলি শুরু করা হচ্ছে৷"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"চালু করা সম্পূর্ণ হচ্ছে৷"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"স্ক্রিন বন্ধ করবেন?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"আপনার আঙ্গুলের ছাপ সেট আপ করার সময়, পাওয়ার বোতাম প্রেস করেছিলেন।\n\nএর ফলে সাধারণত আপনার স্ক্রিন বন্ধ হয়ে যায়।"</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"বন্ধ করুন"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"বাতিল করুন"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"সেট-আপ করা চালিয়ে যাবেন?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"আপনি \'পাওয়ার\' বোতাম প্রেস করেছেন — এর ফলে সাধারণত স্ক্রিন বন্ধ হয়ে যায়।\n\nআঙ্গুলের ছাপ সেট-আপ করার সময় হালকাভাবে ট্যাপ করে দেখুন।"</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"স্ক্রিন বন্ধ করুন"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"সেট-আপ চালিয়ে যান"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"আঙ্গুলের ছাপ যাচাই করা চালিয়ে যাবেন?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"আপনি \'পাওয়ার\' বোতাম প্রেস করেছেন — এর ফলে সাধারণত স্ক্রিন বন্ধ হয়ে যায়।\n\nআঙ্গুলের ছাপ যাচাই করতে হালকাভাবে ট্যাপ করে দেখুন।"</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"স্ক্রিন বন্ধ করুন"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"এগিয়ে যান"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> চলছে"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"গেমে ফিরে আসতে ট্যাপ করুন"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"গেম বেছে নিন"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index ea374900ac35..68ccf1546bfc 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -474,6 +474,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Omogućava aplikaciji da koristi IMS uslugu za pozivanje bez vaše intervencije."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"čitanje statusa i identiteta telefona"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Omogućava aplikaciji pristup telefonskim funkcijama uređaja. Ovo odobrenje omogućava aplikaciji određivanje telefonskog i identifikacionog broja uređaja, bez obzira da li je poziv aktivan i da li je uspostavljena veza sa pozivanim brojem."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"očitavanje osnovnog telefonskog statusa i identiteta"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Dozvoljava aplikaciji pristup osnovnim telefonskim funkcijama uređaja."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"usmjeravanje poziva preko sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Dopušta aplikaciji da pozive usmjeri preko sistema radi poboljšanja iskustva pozivanja."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"vidjeti i kontrolirati pozive preko sistema."</string>
@@ -625,6 +627,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Dodirnite da izbrišete model lica, a zatim ponovo dodajte lice"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Postavite otključavanje licem"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Otključajte telefon gledajući u njega"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Da koristite otključavanje licem, uključite "<b>"Pristup kameri"</b>" u meniju Postavke &gt; Privatnost"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Postavite više načina otključavanja"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Dodirnite da dodate otisak prsta"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Otključavanje otiskom prsta"</string>
@@ -1296,10 +1299,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Pripremanje aplikacije <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Pokretanje aplikacija."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Pokretanje pri kraju."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Isključiti ekran?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Prilikom postavljanja otiska prsta, pritisnuli ste dugme za uključivanje.\n\nTime se obično isključuje ekran."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Isključi"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Otkaži"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Nastaviti postavljanje?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Pritisnuli ste dugme za uključivanje. Tako se obično isključuje ekran.\n\nPokušajte ga lagano dodirnuti dok postavljate otisak prsta."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Isključi ekran"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Nastavi postavljanje"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Nastaviti s potvrđivanjem otiska prsta?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Pritisnuli ste dugme za uključivanje. Tako se obično isključuje ekran.\n\nPokušajte ga lagano dodirnuti da potvrdite otisak prsta."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Isključi ekran"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Nastavi"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"Pokrenuta je aplikacija <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Dodirnite za povratak u igru"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Odaberite igru"</string>
@@ -2049,7 +2056,7 @@
<string name="demo_restarting_message" msgid="1160053183701746766">"Vraćanje uređaja na početne postavke…"</string>
<string name="suspended_widget_accessibility" msgid="6331451091851326101">"Onemogućen <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="conference_call" msgid="5731633152336490471">"Konferencijski poziv"</string>
- <string name="tooltip_popup_title" msgid="7863719020269945722">"Savjet za alat"</string>
+ <string name="tooltip_popup_title" msgid="7863719020269945722">"Skočni opis"</string>
<string name="app_category_game" msgid="4534216074910244790">"Igre"</string>
<string name="app_category_audio" msgid="8296029904794676222">"Muzika i zvuk"</string>
<string name="app_category_video" msgid="2590183854839565814">"Filmovi i videozapisi"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 91ba2f0095e7..726bb3f59a1a 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Permet que l\'aplicació utilitzi el servei IMS per fer trucades sense la teva intervenció."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"veure l\'estat i la identitat del telèfon"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Permet que l\'aplicació accedeixi a les funcions de telèfon del dispositiu. Aquest permís permet que l\'aplicació determini el número de telèfon i els identificadors del dispositiu, si hi ha una trucada activa i el número remot connectat amb una trucada."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"llegir la identitat i l\'estat bàsics de telefonia"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Permet que l\'aplicació accedeixi a les funcions de telefonia bàsiques del dispositiu."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"encaminar trucades a través del sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Permet que l\'aplicació encamini les trucades a través del sistema per millorar-ne l\'experiència."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"consulta i controla les trucades a través del sistema."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Toca per suprimir el teu model facial i, a continuació, torna a afegir la teva cara"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configura Desbloqueig facial"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Mira el telèfon per desbloquejar-lo"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Per utilitzar Desbloqueig facial, activa "<b>"Accés a la càmera"</b>" a Configuració &gt; Privadesa"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configura més maneres de desbloquejar el dispositiu"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Toca per afegir una empremta digital"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Desbloqueig amb empremta digital"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"S\'està preparant <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"S\'estan iniciant les aplicacions."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"S\'està finalitzant l\'actualització."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Vols apagar la pantalla?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Mentre configuraves la teva empremta digital has premut el botó d\'engegada.\n\nAixò normalment apaga la pantalla."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Desactiva"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Cancel·la"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Vols continuar amb la configuració?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Has premut el botó d\'engegada, fet que sol apagar la pantalla.\n\nProva de tocar-lo lleugerament en configurar l\'empremta digital."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Apaga la pantalla"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Continua configurant"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Vols continuar verificant l\'empremta?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Has premut el botó d\'engegada, fet que sol apagar la pantalla.\n\nProva de tocar-lo lleugerament per verificar l\'empremta digital."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Apaga la pantalla"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Continua"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> s\'està executant"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Toca per tornar al joc"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Tria el joc"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index c928655ee2b7..abbcc2c45fe8 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -477,6 +477,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Umožňuje aplikaci používat službu zasílání rychlých zpráv k uskutečňování hovorů bez vašeho zásahu."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"čtení stavu a identity telefonu"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Umožňuje aplikaci získat přístup k telefonním funkcím zařízení. Toto oprávnění umožňuje aplikaci zjistit telefonní číslo telefonu, identifikační čísla zařízení, zda zrovna probíhá hovor, a vzdálené číslo, ke kterému je hovor připojen."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"čtení základního stavu a identity související s telefonováním"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Umožňuje aplikaci přístup k základním telefonickým funkcím zařízení."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"směrování volání prostřednictvím systému"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Umožňuje aplikaci směrovat volání prostřednictvím systému za účelem vylepšení funkcí volání."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"zobrazení a ovládání hovorů v systému."</string>
@@ -628,6 +630,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Klepnutím svůj model obličeje smažte a potom ho přidejte znovu"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Nastavte odemknutí obličejem"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Telefon odemknete pohledem"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Pokud chcete používat odemknutí obličejem, v Nastavení &gt; Soukromí zapnetě "<b>"přístup k fotoaparátu"</b></string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Nastavte si více způsobů odemykání"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Klepnutím přidáte otisk prstu"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Odemknutí otiskem prstu"</string>
@@ -1316,10 +1319,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Příprava aplikace <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Spouštění aplikací."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Dokončování inicializace."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Vypnout obrazovku?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Při nastavování otisku prstu jste stiskli vypínač.\n\nTen obvykle vypne obrazovku."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Vypnout"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Zrušit"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Pokračovat v nastavování?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Stiskli jste vypínač – tím se obvykle vypíná obrazovka.\n\nPři nastavování otisku prstu je třeba klepat lehce."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Vypnout obrazovku"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Pokračovat v nastavení"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Pokračovat v ověřování otisku prstu?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Stiskli jste vypínač – tím se obvykle vypíná obrazovka.\n\nZkuste lehkým klepnutím ověřit svůj otisk prstu."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Vypnout obrazovku"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Pokračovat"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"Běží aplikace <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Klepnutím se vrátíte do hry"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Vyberte hru"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 4c60718557db..57673f44cdcc 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Tillader, at appen kan bruge chat-tjenesten til at foretage opkald, uden du gør noget."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"læse telefonens status og identitet"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Tillader, at appen kan få adgang til telefonfunktionerne på enheden. Med denne tilladelse kan appen fastslå telefonnummeret og enheds-id\'erne, hvorvidt et opkald er aktivt samt det eksterne nummer, der oprettes forbindelse til via et opkald."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"tilgå grundlæggende oplysninger om telefonistatus og -identitet"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Giver appen adgang til enhedens grundlæggende telefonifunktioner."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"dirigere opkald gennem systemet"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Tillader appen at dirigere sine opkald gennem systemet for at forbedre opkaldsoplevelsen."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"se og styre opkald via systemet."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tryk for at slette din ansigtsmodel, og tilføj derefter dit ansigt igen"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Konfigurer ansigtslås"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Lås din telefon op ved at kigge på den"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Hvis du vil bruge ansigtslåsen, skal du aktivere "<b>"Kameraadgang"</b>" under Indstillinger &gt; Privatliv"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Konfigurer flere måder at låse op på"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tryk for at tilføje et fingeraftryk"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Oplåsning med fingeraftryk"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Forbereder <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Åbner dine apps."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Gennemfører start."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Vil du slukke skærmen?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Ved konfigurationen af dit fingeraftryk trykkede du på afbryderknappen.\n\nDet medfører normalt, at din skærm slukkes."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Sluk"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Annuller"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Vil du fortsætte konfigurationen?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Du har trykket på afbryderknappen, hvilket som regel slukker skærmen.\n\nPrøv at trykke let på knappen, mens du konfigurerer dit fingeraftryk."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Sluk skærm"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Fortsæt"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Vil du bekræfte dit fingeraftryk?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Du har trykket på afbryderknappen, hvilket som regel slukker skærmen.\n\nPrøv at trykke let på knappen for at bekræfte dit fingeraftryk."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Sluk skærm"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Fortsæt"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> er i gang"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Tryk for at vende tilbage til spillet"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Vælg et spil"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 25ba98b60b43..fde9b0c63539 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Ermöglicht der App die Verwendung des IMS-Dienstes zum Tätigen von Anrufen ohne Nutzereingriffe"</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"Telefonstatus und Identität abrufen"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Ermöglicht der App, auf die Telefonfunktionen des Geräts zuzugreifen. Die Berechtigung erlaubt der App, die Telefonnummer und Geräte-IDs zu erfassen, festzustellen, ob gerade ein Gespräch geführt wird, und die Rufnummer verbundener Anrufer zu lesen."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"Grundlegenden Telefoniestatus und Identität lesen"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Ermöglicht der App, auf die grundlegenden Telefoniefunktionen des Geräts zuzugreifen."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"Anrufe über das System durchführen"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Ermöglicht der App, Anrufe über das System durchzuführen, um die Anrufqualität zu verbessern."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"Anrufe durch das System einsehen und verwalten."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tippe, um dein Gesichtsmodell zu löschen, und füge es dann noch einmal hinzu"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Entsperrung per Gesichtserkennung einrichten"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Entsperre dein Smartphone, indem du es ansiehst"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Wenn du die Entsperrung per Gesichtserkennung verwenden möchtest, aktiviere in den Einstellungen unter „Datenschutz“ die Option "<b>"Kamerazugriff"</b></string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Weitere Möglichkeiten zum Entsperren einrichten"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tippe, um einen Fingerabdruck hinzuzufügen"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Entsperrung per Fingerabdruck"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> wird vorbereitet"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Apps werden gestartet..."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Start wird abgeschlossen..."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Display ausschalten?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Beim Einrichten deines Fingerabdrucks hast du die Ein-/Aus-Taste gedrückt.\n\nDamit wird üblicherweise das Display ausgeschaltet."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Ausschalten"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Abbrechen"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Einrichtung fortsetzen?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Du hast die Ein-/Aus-Taste gedrückt — damit wird der Bildschirm ausgeschaltet.\n\nTippe die Taste leicht an, um deinen Fingerabdruck einzurichten."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Ausschalten"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Weiter einrichten"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Mit der Fingerabdruckprüfung fortfahren?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Du hast die Ein-/Aus-Taste gedrückt — damit wird der Bildschirm ausgeschaltet.\n\nTippe die Taste leicht an, um mit deinem Fingerabdruck deine Identität zu bestätigen."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Ausschalten"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Fortfahren"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> läuft"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Tippe, um zum Spiel zurückzukehren"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Spiel wählen"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 4764e6a4d63f..b0ad4da8246c 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Επιτρέπει στην εφαρμογή τη χρήση της υπηρεσίας IMS για την πραγματοποίηση κλήσεων χωρίς τη δική σας παρέμβαση."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"διαβάζει την κατάσταση και ταυτότητα τηλεφώνου"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Επιτρέπει στην εφαρμογή την πρόσβαση στις λειτουργίες τηλεφώνου της συσκευής. Αυτή η άδεια δίνει τη δυνατότητα στην εφαρμογή να καθορίζει τον αριθμό τηλεφώνου και τα αναγνωριστικά συσκευών, εάν μια κλήση είναι ενεργή, καθώς και τον απομακρυσμένο αριθμό που συνδέεται από μια κλήση."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"ανάγνωση βασικών πληροφοριών κατάστασης και ταυτότητας τηλεφωνίας"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Επιτρέπει στην εφαρμογή να έχει πρόσβαση στις βασικές λειτουργίες τηλεφωνίας της συσκευής."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"δρομολόγηση κλήσεων μέσω του συστήματος"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Επιτρέπει στην εφαρμογή να δρομολογεί τις κλήσεις της μέσω του συστήματος για να βελτιώσει την εμπειρία κλήσης."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"προβολή και έλεγχος κλήσεων μέσω του συστήματος."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Πατήστε για να διαγράψετε το μοντέλο προσώπου και, στη συνέχεια, προσθέστε το πρόσωπό σας ξανά."</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Ρύθμιση της λειτουργίας Ξεκλείδωμα με το πρόσωπο"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Ξεκλειδώστε το τηλέφωνό σας απλώς κοιτώντας το"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Για να χρησιμοποιήσετε τη λειτουργία Ξεκλείδωμα με το πρόσωπο, ενεργοποιήστε την επιλογή "<b>"Πρόσβαση στην κάμερα"</b>" από το μενού Ρυθμίσεις &gt; Απόρρητο"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Ρυθμίστε περισσότερους τρόπους ξεκλειδώματος"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Πατήστε για να προσθέσετε δακτυλικό αποτύπωμα"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Ξεκλείδωμα με δακτυλικό αποτύπωμα"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Προετοιμασία <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Έναρξη εφαρμογών."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Ολοκλήρωση εκκίνησης."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Απενεργοποίηση οθόνης;"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Κατά τη ρύθμιση του δακτυλικού σας αποτυπώματος, πατήσατε το κουμπί λειτουργίας.\n\nΑυτό απενεργοποιεί συνήθως την οθόνη."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Απενεργοποίηση"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Ακύρωση"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Συνέχιση ρύθμισης;"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Πατήσατε το κουμπί λειτουργίας. Αυτό συνήθως απενεργοποιεί την οθόνη.\n\nΔοκιμάστε να πατήσετε απαλά κατά τη ρύθμιση του δακτυλικού σας αποτυπώματος."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Απενεργοπ. οθόνης"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Συνέχιση ρύθμισης"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Συνέχιση επαλήθευσης δακτ. αποτυπώματος;"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Πατήσατε το κουμπί λειτουργίας. Αυτό συνήθως απενεργοποιεί την οθόνη.\n\nΔοκιμάστε να πατήστε απαλά για να επαληθεύσετε το δακτυλικό σας αποτύπωμα."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Απενεργοπ. οθόνης"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Συνέχεια"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"Η εφαρμογή <xliff:g id="APP">%1$s</xliff:g> εκτελείται"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Πατήστε για να επιστρέψετε στο παιχνίδι"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Επιλέξτε παιχνίδι"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index a7647b6019e7..fd68ddd1549a 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Allows the app to use the IMS service to make calls without your intervention."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"read phone status and identity"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Allows the app to access the phone features of the device. This permission allows the app to determine the phone number and device IDs, whether a call is active and the remote number connected by a call."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"read basic telephony status and identity"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Allows the app to access the basic telephony features of the device."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"route calls through the system"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Allows the app to route its calls through the system in order to improve the calling experience."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"see and control calls through the system."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tap to delete your face model, then add your face again"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Set up Face Unlock"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Unlock your phone by looking at it"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"To use Face Unlock, turn on "<b>"Camera access"</b>" in Settings &gt; Privacy"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Set up more ways to unlock"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tap to add a fingerprint"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Fingerprint Unlock"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Preparing <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Starting apps."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Finishing boot."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Turn off screen?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"While setting up your fingerprint, you pressed the power button.\n\nThis usually turns off your screen."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Turn off"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Cancel"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Continue setup?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"You pressed the power button – this usually turns off the screen.\n\nTry tapping lightly while setting up your fingerprint."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Turn off screen"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Continue setup"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Continue verifying your fingerprint?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"You pressed the power button – this usually turns off the screen.\n\nTry tapping lightly to verify your fingerprint."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Turn off screen"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Continue"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> running"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Tap to return to game"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Choose game"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 8c28433e9d9d..265d2e673049 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Allows the app to use the IMS service to make calls without your intervention."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"read phone status and identity"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Allows the app to access the phone features of the device. This permission allows the app to determine the phone number and device IDs, whether a call is active and the remote number connected by a call."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"read basic telephony status and identity"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Allows the app to access the basic telephony features of the device."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"route calls through the system"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Allows the app to route its calls through the system in order to improve the calling experience."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"see and control calls through the system."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tap to delete your face model, then add your face again"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Set up Face Unlock"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Unlock your phone by looking at it"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"To use Face Unlock, turn on "<b>"Camera access"</b>" in Settings &gt; Privacy"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Set up more ways to unlock"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tap to add a fingerprint"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Fingerprint Unlock"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Preparing <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Starting apps."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Finishing boot."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Turn off screen?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"While setting up your fingerprint, you pressed the power button.\n\nThis usually turns off your screen."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Turn off"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Cancel"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Continue setup?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"You pressed the power button – this usually turns off the screen.\n\nTry tapping lightly while setting up your fingerprint."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Turn off screen"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Continue setup"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Continue verifying your fingerprint?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"You pressed the power button – this usually turns off the screen.\n\nTry tapping lightly to verify your fingerprint."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Turn off screen"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Continue"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> running"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Tap to return to game"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Choose game"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 6e2595462ecb..8ee21a3abd43 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Allows the app to use the IMS service to make calls without your intervention."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"read phone status and identity"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Allows the app to access the phone features of the device. This permission allows the app to determine the phone number and device IDs, whether a call is active and the remote number connected by a call."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"read basic telephony status and identity"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Allows the app to access the basic telephony features of the device."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"route calls through the system"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Allows the app to route its calls through the system in order to improve the calling experience."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"see and control calls through the system."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tap to delete your face model, then add your face again"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Set up Face Unlock"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Unlock your phone by looking at it"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"To use Face Unlock, turn on "<b>"Camera access"</b>" in Settings &gt; Privacy"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Set up more ways to unlock"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tap to add a fingerprint"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Fingerprint Unlock"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Preparing <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Starting apps."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Finishing boot."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Turn off screen?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"While setting up your fingerprint, you pressed the power button.\n\nThis usually turns off your screen."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Turn off"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Cancel"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Continue setup?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"You pressed the power button – this usually turns off the screen.\n\nTry tapping lightly while setting up your fingerprint."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Turn off screen"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Continue setup"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Continue verifying your fingerprint?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"You pressed the power button – this usually turns off the screen.\n\nTry tapping lightly to verify your fingerprint."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Turn off screen"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Continue"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> running"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Tap to return to game"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Choose game"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 53031c69ca57..7fada24faaae 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Allows the app to use the IMS service to make calls without your intervention."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"read phone status and identity"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Allows the app to access the phone features of the device. This permission allows the app to determine the phone number and device IDs, whether a call is active and the remote number connected by a call."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"read basic telephony status and identity"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Allows the app to access the basic telephony features of the device."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"route calls through the system"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Allows the app to route its calls through the system in order to improve the calling experience."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"see and control calls through the system."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tap to delete your face model, then add your face again"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Set up Face Unlock"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Unlock your phone by looking at it"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"To use Face Unlock, turn on "<b>"Camera access"</b>" in Settings &gt; Privacy"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Set up more ways to unlock"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tap to add a fingerprint"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Fingerprint Unlock"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Preparing <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Starting apps."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Finishing boot."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Turn off screen?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"While setting up your fingerprint, you pressed the power button.\n\nThis usually turns off your screen."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Turn off"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Cancel"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Continue setup?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"You pressed the power button – this usually turns off the screen.\n\nTry tapping lightly while setting up your fingerprint."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Turn off screen"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Continue setup"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Continue verifying your fingerprint?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"You pressed the power button – this usually turns off the screen.\n\nTry tapping lightly to verify your fingerprint."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Turn off screen"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Continue"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> running"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Tap to return to game"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Choose game"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 0f0e35a1b550..0eaf8ef7c138 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‏‎‏‎‎‏‏‏‎‎‎‎‏‏‏‏‏‎‏‎‎‏‎‏‏‏‏‏‏‎‏‎‏‏‎‎‎‏‏‎‎‎‎‏‎‏‎‎‏‏‏‏‎‏‎‎Allows the app to use the IMS service to make calls without your intervention.‎‏‎‎‏‎"</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‏‎‎‎‏‏‏‎‏‏‎‏‏‎‎‎‏‏‎‎‏‎‏‎‎‎‏‏‏‏‎‏‏‏‎‏‎‏‏‏‏‏‎‎‎‏‎‏‏‎‎‎‏‎read phone status and identity‎‏‎‎‏‎"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‎‏‎‎‏‎‏‏‎‎‏‎‏‏‎‎‎‎‎‎‎‎‎‎‏‎‏‏‎‏‎‏‎‎‎‎‎‏‏‎‏‏‏‎‎‏‏‏‎‏‏‎‏‎‎Allows the app to access the phone features of the device. This permission allows the app to determine the phone number and device IDs, whether a call is active, and the remote number connected by a call.‎‏‎‎‏‎"</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‎‏‎‎‏‏‏‎‏‎‏‏‏‎‎‏‏‏‏‏‏‏‎‏‏‏‎‎‎‏‏‎‏‏‎‏‏‎‏‎‏‏‎‎‎‎‏‎‏‎‏‏‏‎‎‏‏‎read basic telephony status and identity‎‏‎‎‏‎"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‏‏‏‏‏‏‎‎‏‎‎‏‏‏‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‎‏‎‎‎‎Allows the app to access the basic telephony features of the device.‎‏‎‎‏‎"</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‎‏‏‏‎‎‏‏‏‎‎‎‏‏‏‎‎‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‏‎‏‎‏‎‏‏‏‎‎‏‏‎‏‎‎route calls through the system‎‏‎‎‏‎"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‏‏‏‏‏‎‏‎‏‏‎‏‎‏‎‎‏‏‏‏‏‎‎‎‏‏‎‏‏‏‎‏‏‏‎‏‎‎‏‎‏‏‎‏‏‏‎‏‏‎‏‏‏‎‎Allows the app to route its calls through the system in order to improve the calling experience.‎‏‎‎‏‎"</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‏‎‏‏‎‏‏‏‏‎‎‎‏‎‏‏‏‏‎‎‏‏‏‎‎‏‎‎‎‎‏‏‏‎‏‎‎‎‎‏‎‎‎‎‏‎‎‏‎‎‏‎‎‎see and control calls through the system.‎‏‎‎‏‎"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‏‎‎‎‎‏‏‏‎‏‎‏‎‏‏‏‎‎‎‎‏‎‎‏‏‎‎‏‏‏‎‏‎‎‏‎‎‎‎‎‎‎‎‎‎‏‎‏‏‏‎‎‏‎‏‎‎Tap to delete your face model, then add your face again‎‏‎‎‏‎"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‏‎‎‏‎‎‏‎‎‏‎‏‏‎‏‏‏‏‏‎‎‏‏‎‏‎‎‎‎‏‎‏‏‎‎‏‎‏‎‎‏‎‎‎‎‏‏‏‏‎‎Set up Face Unlock‎‏‎‎‏‎"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‏‎‏‎‏‎‎‎‎‎‎‎‏‏‎‏‏‏‎‎‏‎‏‏‎‎‏‎‏‏‎‏‏‏‏‏‏‎‏‏‏‎‏‏‏‏‎‎‏‏‏‏‏‎‎‎Unlock your phone by looking at it‎‏‎‎‏‎"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‎‎‏‎‏‏‎‏‏‎‎‏‏‎‏‎‏‏‏‎‏‎‏‎‎‎‏‎‏‎‎‏‎‎‎‏‎‎‎‏‎‏‏‎‏‏‎‏‏‎‏‏‎‎‎To use Face Unlock, turn on ‎‏‎‎‏‏‎"<b>"‎‏‎‎‏‏‏‎Camera access‎‏‎‎‏‏‎"</b>"‎‏‎‎‏‏‏‎ in Settings &gt; Privacy‎‏‎‎‏‎"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‏‏‎‎‏‎‏‎‏‏‎‎‎‏‎‏‏‏‏‎‏‏‏‎‎‏‎‎‎‎‎‎‏‏‎‎‎‎‎‎‎‏‏‏‎‎‏‏‏‏‎‏‎‏‏‏‎Set up more ways to unlock‎‏‎‎‏‎"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‏‏‎‏‏‎‏‎‎‏‎‏‏‏‎‎‎‎‏‏‎‏‏‎‎‏‏‎‏‏‏‎‎‏‎‏‏‏‎‎‏‎‎‎‎‎‏‎‏‏‏‏‎‏‎‎‎Tap to add a fingerprint‎‏‎‎‏‎"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‏‎‏‎‎‎‎‏‏‎‎‏‎‏‏‏‏‎‎‏‎‏‏‎‏‏‎‎‏‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‎‎‏‏‎‎‏‏‎‏‎‎Fingerprint Unlock‎‏‎‎‏‎"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‎‏‎‏‏‏‏‎‎‏‎‏‎‏‎‏‎‎‏‏‎‏‎‎‎‏‎‎‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‏‏‏‏‏‎‎‏‏‏‎‎‎Preparing ‎‏‎‎‏‏‎<xliff:g id="APPNAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎.‎‏‎‎‏‎"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‎‏‎‎‎‎‎‏‎‏‏‎‏‏‎‏‎‏‏‏‎‎‏‏‎‏‎‏‎‏‏‎‏‎‏‎‎‏‎‎‏‏‏‎‏‏‏‏‏‏‏‎‎‏‏‎Starting apps.‎‏‎‎‏‎"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‎‏‎‏‏‏‏‏‏‏‎‎‏‏‎‏‏‏‏‏‎‏‏‎‏‎‎‎‏‏‏‎‎‎‏‏‏‏‎‏‎‏‎‎‏‎‎‎‏‎‏‏‎‏‎‎Finishing boot.‎‏‎‎‏‎"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‏‏‏‏‏‏‎‎‎‎‏‏‎‎‎‎‎‏‎‏‎‏‏‏‏‏‎‎‎‎‏‎‏‏‏‎‏‎‎‏‎‎‏‎‏‏‎‏‏‏‎‏‏‏‏‏‎Turn off screen?‎‏‎‎‏‎"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‏‎‏‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‎‎‎‏‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‎‏‎‎‎‏‎‏‏‎‏‎‎While setting up your fingerprint, you pressed the Power button.‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎This usually turns off your screen.‎‏‎‎‏‎"</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‎‎‎‎‏‎‏‎‏‎‏‏‏‎‏‎‏‏‏‏‎‏‎‎‏‎‎‎‎‎‏‎‎‏‎‎‎‏‏‏‎‎‏‏‏‏‏‎‏‏‎‏‏‏‎Turn off‎‏‎‎‏‎"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‏‏‏‎‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‎‏‎‎‎‏‏‎‏‏‏‎‎‎‏‏‏‏‏‎‎‎‏‏‏‎‎‎‏‏‏‎‎‎Cancel‎‏‎‎‏‎"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‎‏‏‎‏‎‏‎‏‏‎‎‎‎‎‏‏‎‏‎‏‏‎‎‎‏‏‏‏‏‏‏‎‎‏‎‏‎‎‏‏‎‏‏‏‏‎‎‎‏‏‏‎‎‎Continue setup?‎‏‎‎‏‎"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‏‎‎‎‏‎‎‏‎‏‎‏‏‏‏‏‏‎‎‎‏‏‏‎‎‏‎‏‏‎‎‎‎‏‎‏‏‎‎‎‎‏‎‏‏‏‎‎‏‏‏‏‎‎‎You pressed the power button — this usually turns off the screen.‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎Try tapping lightly while setting up your fingerprint.‎‏‎‎‏‎"</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‎‎‎‏‎‏‎‎‎‏‏‎‏‎‎‏‏‎‎‏‏‎‏‎‎‏‏‎‎‏‎‎‎‎‎‏‏‎‎‎‏‏‏‎‎‏‏‎‏‎‏‎‎‎‎‏‎Turn off screen‎‏‎‎‏‎"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‎‎‎‎‏‎‎‎‎‏‏‏‏‏‏‎‎‏‎‏‎‎‏‏‏‎‏‏‎‏‏‎‎‏‏‏‎‎‏‏‎‏‏‎‏‏‏‏‏‎‏‏‎‏‏‎Continue setup‎‏‎‎‏‎"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‎‎‎‎‎‏‏‏‎‏‏‎‏‎‎‎‎‏‏‎‏‎‏‎‏‏‏‎‎‏‏‎‎‎‏‏‎‎‎‏‏‎‏‎‎‏‎‏‏‏‎‎‎‏‏‎Continue verifying your fingerprint?‎‏‎‎‏‎"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‎‏‏‎‎‏‏‎‎‏‎‏‎‎‏‎‏‏‏‏‏‎‏‎‎‏‎‎‏‏‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‏‎‎‎‎‏‎You pressed the power button — this usually turns off the screen.‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎Try tapping lightly to verify your fingerprint.‎‏‎‎‏‎"</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‎‎‎‏‏‏‎‏‏‎‏‏‏‏‎‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‏‏‏‎‎‎‏‎‏‎‏‎‎‎‎‎‏‏‎‎‏‏‏‎‏‏‎Turn off screen‎‏‎‎‏‎"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‏‎‎‎‏‏‏‎‏‎‎‎‏‏‏‏‎‏‎‏‏‏‏‎‏‏‎‏‏‎‎‏‎‎‎‏‎‎‎‎‎‏‎‎‎‏‏‎‎‎‎‎‎‏‎‏‎‎Continue‎‏‎‎‏‎"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‏‎‏‎‏‏‎‏‎‎‎‎‏‏‏‏‎‎‏‎‎‎‏‎‏‏‏‎‎‏‎‏‏‏‎‎‏‏‎‎‏‏‎‏‏‎‏‏‎‏‎‎‎‎‎‏‎‎‏‏‎<xliff:g id="APP">%1$s</xliff:g>‎‏‎‎‏‏‏‎ running‎‏‎‎‏‎"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‏‎‎‏‏‎‎‏‏‎‏‏‏‏‏‏‎‏‎‎‏‏‎‎‎‎‏‏‏‎‏‏‏‎‎‏‎‏‎‏‎‎‏‏‎‏‎‏‏‎‏‏‏‎‎Tap to return to game‎‏‎‎‏‎"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‎‏‏‎‎‎‏‎‎‎‎‏‏‎‎‎‏‎‏‏‎‏‎‏‏‎‎‏‏‏‏‎‎‏‏‎‏‏‏‎‎‏‏‎‎‎‎‎‎‏‎‏‏‎‎Choose game‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index e7110c9b7e58..93a316ffae83 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -329,17 +329,17 @@
<string name="permgrouplab_notifications" msgid="5472972361980668884">"Notificaciones"</string>
<string name="permgroupdesc_notifications" msgid="4608679556801506580">"mostrar notificaciones"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Recuperar el contenido de las ventanas"</string>
- <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Inspecciona el contenido de la ventana con la que estés interactuando."</string>
+ <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Inspeccionará el contenido de la ventana con la que estés interactuando."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Activar la Exploración táctil"</string>
<string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Los elementos que presiones se dirán en voz alta y podrás explorar la pantalla mediante gestos."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Observar el texto que escribes"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Incluye datos personales, como números de tarjeta de crédito y contraseñas."</string>
<string name="capability_title_canControlMagnification" msgid="7701572187333415795">"Controlar la ampliación de pantalla"</string>
- <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Controla el posicionamiento y el nivel de zoom de la pantalla."</string>
+ <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Controlará el posicionamiento y el nivel de zoom de la pantalla."</string>
<string name="capability_title_canPerformGestures" msgid="9106545062106728987">"Usar gestos"</string>
<string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"Permite presionar, deslizar, pellizcar y usar otros gestos."</string>
<string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Gestos del sensor de huellas dactilares"</string>
- <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Captura los gestos que se hacen en el sensor de huellas dactilares del dispositivo."</string>
+ <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Capturará los gestos que se hacen en el sensor de huellas dactilares del dispositivo."</string>
<string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Tomar captura de pantalla"</string>
<string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Puede tomar una captura de la pantalla."</string>
<string name="permlab_statusBar" msgid="8798267849526214017">"desactivar o modificar la barra de estado"</string>
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Permite que la aplicación utilice el servicio IMS para hacer llamadas sin tu intervención."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"leer la identidad y el estado del dispositivo"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Permite que la aplicación acceda a las funciones de teléfono del dispositivo. La aplicación puede utilizar este permiso para descubrir identificadores de dispositivos y números de teléfono, para saber si una llamada está activa y para conocer el número remoto con el que se ha establecido conexión mediante una llamada."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"leer identidad y estado de telefonía básica"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Permite que la app acceda a las funciones de telefonía básica del dispositivo."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"Transmite llamadas a través del sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Permite que la app transmita las llamadas a través del sistema para mejorar la experiencia de llamadas."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"Mirar y controlar las llamadas con el sistema"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Presiona para borrar el modelo de rostro y, luego, vuelve a agregar tu rostro"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configura Desbloqueo facial"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Desbloquea el teléfono con solo mirarlo"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Para usar Desbloqueo facial, activa el "<b>"Acceso a la cámara"</b>" en Configuración y privacidad"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configura más formas de desbloquear el dispositivo"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Presiona para agregar una huella dactilar"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Desbloqueo con huellas dactilares"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Preparando <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Iniciando aplicaciones"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Finalizando el inicio"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"¿Quieres apagar la pantalla?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Al configurar tu huella dactilar, presionaste el botón de encendido.\n\nPor lo general, esta acción apaga la pantalla."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Apagar"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Cancelar"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"¿Continuar con la configuración?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Presionaste el botón de encendido. Por lo general, esta acción apaga la pantalla.\n\nPresiona suavemente mientras configuras tu huella dactilar."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Apagar pantalla"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Continuar config."</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"¿Verificar huella dactilar?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Presionaste el botón de encendido. Por lo general, esta acción apaga la pantalla.\n\nPresiona suavemente para verificar tu huella dactilar."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Apagar pantalla"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Continuar"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> en ejecución"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Presiona para volver al juego"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Elige un juego"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index a582a0991e9a..22de5d10b7f0 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Permite que la aplicación utilice el servicio IMS para realizar llamadas sin tu intervención."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"consultar la identidad y el estado del teléfono"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Permite que la aplicación acceda a las funciones de teléfono del dispositivo. La aplicación puede utilizar este permiso para descubrir identificadores de dispositivos y números de teléfono, para saber si una llamada está activa y para conocer el número remoto con el que se ha establecido conexión mediante una llamada."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"leer el estado y la identidad básicos de telefonía"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Permite que la aplicación acceda a las funciones de telefonía básicas del dispositivo."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"direccionar llamadas a través del sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Permite a la aplicación direccionar sus llamadas hacia el sistema para mejorar la calidad de estas."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"ver y controlar llamadas a través del sistema."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Toca para eliminar tu modelo facial y luego añade de nuevo tu cara"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configura Desbloqueo facial"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Desbloquea el teléfono con solo mirarlo"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Para usar Desbloqueo Facial, habilita el "<b>"acceso a la cámara"</b>" en Ajustes y privacidad"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configura más formas de desbloqueo"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Toca para añadir una huella digital"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Desbloqueo con huella digital"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Preparando <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Iniciando aplicaciones"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Finalizando inicio..."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"¿Apagar pantalla?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Has pulsado el botón de encendido mientras configurabas tu huella digital.\n\nAl hacer esto, se suele apagar la pantalla."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Desactivar"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Cancelar"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"¿Quieres seguir con la configuración?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Has pulsado el botón de encendido, lo que suele apagar la pantalla.\n\nPrueba a apoyar el dedo ligeramente para verificar tu huella digital."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Apagar pantalla"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Seguir configurando"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"¿Seguir verificando tu huella digital?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Has pulsado el botón de encendido, lo que suele apagar la pantalla.\n\nPrueba a apoyar el dedo ligeramente para verificar tu huella digital."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Apagar pantalla"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Continuar"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> en ejecución"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Toca para volver al juego"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Elegir juego"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 84912d898acd..334a666ffc10 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Võimaldab rakendusel kasutada IMS-teenust kõnede tegemiseks ilma, et peaksite sekkuma."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"Telefoni oleku ja identiteedi lugemine"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Annab rakendusele juurdepääsu seadme telefonifunktsioonidele. See luba võimaldab rakendusel määrata telefoninumbri ja seadme ID-d ning kontrollida, kas kõne on aktiivne ja kaugnumber on kõne kaudu telefoniga ühendatud."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"Lugeda peamiste telefonifunktsioonide olekut ja kasutajate identiteeti"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Võimaldab rakendusel pääseda juurde seadme peamistele telefonifunktsioonidele."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"kõnede marsruutimine süsteemi kaudu"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Võimaldab rakendusel kõnesid süsteemi kaudu marsruutida, et helistamiskogemust täiustada."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"süsteemi kaudu kõnede vaatamine ja juhtimine."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Puudutage näomudeli kustutamiseks, seejärel lisage oma nägu uuesti"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Näoga avamise seadistamine"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Avage telefon seda vaadates"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Näoga avamise kasutamiseks lülitage menüüs Seaded &gt; Privaatsus sisse "<b>"juurdepääs kaamerale"</b>"."</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Seadistage rohkem viise avamiseks"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Puudutage sõrmejälje lisamiseks"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Sõrmejäljega avamine"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Rakenduse <xliff:g id="APPNAME">%1$s</xliff:g> ettevalmistamine."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Rakenduste käivitamine."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Käivitamise lõpuleviimine."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Kas lülitada ekraan välja?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Sõrmejälje seadistamisel vajutasite toitenuppu.\n\nSee lülitab ekraanikuva tavaliselt välja."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Lülita välja"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Tühista"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Kas jätkata seadistamist?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Vajutasite toitenuppu – tavaliselt lülitab see ekraani välja.\n\nPuudutage õrnalt ja seadistage oma sõrmejälg."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Lülita ekraan välja"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Jätka seadistamist"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Kas jätkata sõrmejälje kinnitamist?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Vajutasite toitenuppu – tavaliselt lülitab see ekraani välja.\n\nPuudutage õrnalt, et oma sõrmejälg kinnitada."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Lülita ekraan välja"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Jätka"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> töötab"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Puudutage mängu naasmiseks"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Valige mäng"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index d84bdddd907a..df16b7362996 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Zuk ezer egin beharrik gabe deiak egiteko IMS zerbitzua erabiltzeko baimena ematen die aplikazioei."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"irakurri telefonoaren egoera eta identitatea"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Gailuaren telefono-eginbideak atzitzeko baimena ematen die aplikazioei. Baimen horrek aplikazioari telefono-zenbakia eta gailu IDak zein diren, deirik aktibo dagoen eta deia zer zenbakirekin konektatuta dagoen zehazteko baimena ematen die aplikazioei."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"irakurri oinarrizko egoera telefonikoa eta identitatea"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Gailuaren oinarrizko eginbide telefonikoak atzitzeko baimena ematen dio aplikazioari."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"bideratu deiak sistemaren bidez"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Deiak sistemaren bidez bideratzea baimentzen die aplikazioei, deien zerbitzua ahal bezain ona izan dadin."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"ikusi eta kontrolatu deiak sistemaren bidez."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Sakatu hau aurpegi-eredua ezabatzeko eta, gero, gehitu aurpegia berriro"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Konfiguratu aurpegi bidez desblokeatzeko eginbidea"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Telefonoa desblokeatzeko, begira iezaiozu"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Aurpegi bidez desblokeatzeko aukera erabiltzeko, aktibatu "<b>"kamera atzitzeko baimena"</b>" Ezarpenak &gt; Pribatutasuna atalean"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Konfiguratu telefonoa desblokeatzeko modu gehiago"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Sakatu hau hatz-marka bat gehitzeko"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Hatz-marka bidez desblokeatzea"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> prestatzen."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Aplikazioak abiarazten."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Bertsio-berritzea amaitzen."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Pantaila itzali nahi duzu?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Hatz-marka konfiguratzean, etengailua sakatu duzu.\n\nNormalean, horren ondorioz pantaila itzali egiten da."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Desaktibatu"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Utzi"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Konfiguratzen jarraitu nahi duzu?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Etengailua sakatu duzu; pantaila itzaltzeko balio du horrek.\n\nUki ezazu arin, hatz-marka konfiguratu bitartean."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Itzali pantaila"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Jarraitu konfiguratzen"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Hatz-marka egiaztatzen jarraitu nahi duzu?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Etengailua sakatu duzu; pantaila itzaltzeko balio du horrek.\n\nUki ezazu arin, hatz-marka egiaztatzeko."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Itzali pantaila"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Egin aurrera"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> abian da"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Sakatu jokora itzultzeko"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Aukeratu joko bat"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 8f1daa217325..e66c7837b408 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"‏به برنامه اجازه می‌دهد از سرویس IMS برای برقراری تماس‌ها بدون دخالت شما استفاده کند."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"خواندن وضعیت تلفن و شناسه"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"به برنامه اجازه می‌دهد به ویژگی‌های تلفن دستگاه شما دسترسی پیدا کند. این مجوز به برنامه اجازه می‌دهد شماره تلفن و شناسه‌های دستگاه، فعال بودن یک تماس و شماره راه دوری که با یک تماس متصل شده است را مشخص کند."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"خواندن شناسه و وضعیت اصلی ارتباط دوربرد"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"به برنامه اجازه می‌دهد به ویژگی‌های اصلی ارتباط دوربرد دستگاه دسترسی داشته باشد."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"برقرار کردن تماس‌ها ازطریق سیستم"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"به برنامه امکان می‌دهد برای بهبود تجربه تماس، تماس‌هایش را ازطریق سیستم برقرار کند."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"دیدن و کنترل تماس‌ها ازطریق سیستم."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"برای حذف مدل چهره‌تان ضربه بزنید، سپس چهره‌تان را دوباره اضافه کنید"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"راه‌اندازی «قفل‌گشایی با چهره»"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"برای باز کردن قفل تلفن خود به آن نگاه کنید"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"‏برای استفاده از «قفل‌گشایی با چهره»، "<b>"دسترسی به دوربین"</b>" را در «تنظیمات &gt; حریم‌خصوصی» روشن کنید"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"راه‌اندازی روش‌های بیشتر برای باز کردن قفل"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"برای افزودن اثر انگشت، ضربه بزنید"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"قفل‌گشایی با اثر انگشت"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"آماده‌سازی <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"درحال آغاز کردن برنامه‌ها."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"درحال اتمام راه‌اندازی."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"صفحه‌نمایش خاموش شود؟"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"هنگام راه‌اندازی اثر انگشتتان، دکمه روشن/ خاموش را فشار دادید.\n\nاین کار معمولاً صفحه‌نمایش را خاموش می‌کند."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"خاموش شود"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"لغو شود"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"راه‌اندازی ادامه یابد؟"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"دکمه روشن/ خاموش را فشار دادید — این کار معمولاً صفحه‌نمایش را خاموش می‌کند.\n\nهنگام راه‌اندازی اثر انگشت، آرام ضربه بزنید."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"خاموش کردن صفحه"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"ادامه راه‌اندازی"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"تأیید اثر انگشت را ادامه می‌دهید؟"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"دکمه روشن/ خاموش را فشار دادید — این کار معمولاً صفحه‌نمایش را خاموش می‌کند.\n\nبرای تأیید اثر انگشتتان، آرام ضربه بزنید."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"خاموش کردن صفحه"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"ادامه"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> در حال اجرا"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"برای برگشت به بازی، ضربه بزنید"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"انتخاب بازی"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index b63e72910280..ff17fc1ceb96 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Antaa sovelluksen soittaa puheluita pikaviestipalvelun avulla ilman käyttäjän toimia."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"lue puhelimen tila ja identiteetti"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Antaa sovelluksen käyttää laitteen puhelinominaisuuksia. Sovellus voi määrittää puhelinnumeron ja laitteen tunnuksen, puhelun tilan sekä soitetun numeron."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"lukea tavallisten puheluominaisuuksien tila- ja identiteettitiedot"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Sallii sovellukselle pääsyn laitteen tavallisiin puheluominaisuuksiin."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"ohjata puhelut järjestelmän kautta"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Tämä sallii sovelluksen ohjata puhelut järjestelmän kautta, mikä auttaa parantamaan puhelujen laatua."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"nähdä puhelut ja päättää niistä järjestelmässä"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Poista kasvomalli napauttamalla ja lisää sitten kasvosi uudelleen"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Ota kasvojentunnistusavaus käyttöön"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Avaa puhelimesi lukitus katsomalla laitetta"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Jos haluat käyttää kasvojentunnistusavausta, valitse Asetukset &gt; Yksityisyys ja laita "<b>"Pääsy kameraan"</b>" päälle"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Ota käyttöön lisää tapoja avata lukitus"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Napauta lisätäksesi sormenjälki"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Sormenjälkiavaus"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Valmistellaan: <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Käynnistetään sovelluksia."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Viimeistellään päivitystä."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Sammutetaanko näyttö?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Painoit virtapainiketta ottaessasi sormenjäljen käyttöön.\n\nTämä saa yleensä näytön sammumaan."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Laita pois päältä"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Peru"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Haluatko jatkaa?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Painoit virtapainiketta, mikä yleensä sammuttaa näytön.\n\nKosketa painiketta kevyesti tallentaessasi sormenjälkeä."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Sammuta näyttö"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Jatka käyttöönottoa"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Jatketaanko sormenjäljen vahvistamista?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Painoit virtapainiketta, mikä yleensä sammuttaa näytön.\n\nVahvista sormenjälki koskettamalla painiketta kevyesti."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Sammuta näyttö"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Jatka"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> käynnissä"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Palaa peliin napauttamalla"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Valitse peli"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index cae01af7759d..e367f320ba2b 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Permet à l\'application d\'utiliser le service IMS pour faire des appels sans votre intervention."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"voir l\'état et l\'identité du téléphone"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Permet à l\'application d\'accéder aux fonctionnalités téléphoniques de l\'appareil. Cette autorisation permet à l\'application de déterminer le numéro de téléphone et les identifiants de l\'appareil, si un appel est actif et le numéro distant connecté par un appel."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"Lire l\'état et l\'identité de la téléphonie de base"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Permet à l\'application d\'accéder aux fonctionnalités de téléphonie de base de l\'appareil."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"acheminer les appels dans le système"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Permet à l\'application d\'acheminer ses appels dans le système afin d\'améliorer l\'expérience d\'appel."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"afficher et gérer les appels à l\'aide du système."</string>
@@ -622,6 +624,8 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Touchez pour supprimer votre modèle facial, puis ajoutez votre visage de nouveau"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configurer le déverrouillage par reconnaissance faciale"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Déverrouillez votre téléphone en le regardant"</string>
+ <!-- no translation found for face_sensor_privacy_enabled (7407126963510598508) -->
+ <skip />
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configurer d\'autres méthodes de déverrouillage"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Touchez pour ajouter une empreinte digitale"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Déverrouillage par empreinte digitale"</string>
@@ -1276,10 +1280,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Préparation de <xliff:g id="APPNAME">%1$s</xliff:g> en cours…"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Lancement des applications…"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Finalisation de la mise à jour."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Éteindre l\'écran?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Pendant que vous configuriez votre empreinte digitale, vous avez appuyé sur l\'interrupteur.\n\nAppuyer sur ce bouton ferme habituellement l\'écran."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Éteindre"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Annuler"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Poursuivre la configuration?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Vous avez appuyé sur le l\'interrupteur – cette action éteint habituellement l\'écran.\n\nEssayez de toucher légèrement pendant la configuration de votre empreinte digitale."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Éteindre l\'écran"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Poursuivre configu."</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Poursuivre vérifica. empreinte digitale?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Vous avez appuyé sur le l\'interrupteur – cette action éteint habituellement l\'écran.\n\nEssayez de toucher légèrement pour vérifier votre empreinte digitale."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Éteindre l\'écran"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Continuer"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> en cours d\'exécution"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Touchez pour revenir au jeu"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Choisissez un jeu"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 3955c9f65cdf..e00304ba3d64 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Permet à l\'application d\'utiliser le service IMS pour passer des appels sans votre intervention."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"Voir l\'état et l\'identité du téléphone"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Permet à l\'application d\'accéder aux fonctionnalités téléphoniques de l\'appareil. Cette autorisation permet à l\'application de déterminer le numéro de téléphone et les identifiants de l\'appareil, si un appel est actif et le numéro distant connecté par un appel."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"lire l\'état et l\'identité de téléphonie de base"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Autorise l\'appli à accéder aux fonctionnalités de téléphonie de base de l\'appareil."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"acheminer les appels via le système"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Autorise l\'application à acheminer les appels via le système afin d\'optimiser le confort d\'utilisation."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"voir et contrôler les appels via le système."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Appuyez pour supprimer votre empreinte faciale, puis ajoutez de nouveau votre visage"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configurer le déverrouillage par reconnaissance faciale"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Déverrouillez votre téléphone en le regardant"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Pour utiliser Face Unlock, activez "<b>"Accès à l\'appareil photo"</b>" dans Paramètres &gt; Confidentialité"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configurer d\'autres méthodes de déverrouillage"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Appuyez pour ajouter une empreinte digitale"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Déverrouillage par empreinte digitale"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Préparation de <xliff:g id="APPNAME">%1$s</xliff:g> en cours…"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Lancement des applications…"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Finalisation de la mise à jour."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Éteindre l\'écran ?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Vous avez appuyé sur le bouton Marche/Arrêt pendant la configuration de votre empreinte digitale.\n\nCela éteint généralement l\'écran."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Éteindre"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Annuler"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Poursuivre la configuration ?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Vous avez appuyé sur le bouton Marche/Arrêt, ce qui éteint généralement l\'écran.\n\nEssayez d\'appuyer doucement pendant la configuration de votre empreinte digitale."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Éteindre l\'écran"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Poursuivre"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Continuer de valider votre empreinte ?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Vous avez appuyé sur le bouton Marche/Arrêt, ce qui éteint généralement l\'écran.\n\nPour valider votre empreinte digitale, appuyez plus doucement."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Éteindre l\'écran"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Continuer"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> en cours d\'exécution"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Appuyez pour revenir au jeu"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Choisir un jeu"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index c07287ae9825..6f9ee8fb76a8 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Permite que a aplicación use o servizo de IMS para facer chamadas sen a túa intervención."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"ler o estado e a identidade do teléfono"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Permite á aplicación acceder ás funcións de teléfono do dispositivo. Con este permiso a aplicación pode determinar o número de teléfono e os ID do dispositivo, se unha chamada está activa e o número remoto conectado mediante unha chamada."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"ler a identidade e o estado das funcións básicas de telefonía"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Permítelle á aplicación acceder ás funcións básicas de telefonía do dispositivo."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"dirixir as chamadas a través do sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Permite á aplicación dirixir as súas chamadas a través do sistema para mellorar a túa experiencia durante as chamadas."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"consultar e controlar as chamadas a través do sistema."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Toca para eliminar o teu modelo facial e despois engade de novo a cara"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configurar o desbloqueo facial"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Mira o teléfono para desbloquealo"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Para usar o desbloqueo facial, activa "<b>"Acceso á cámara"</b>" en Configuración &gt; Privacidade"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configura máis maneiras de desbloquear o dispositivo"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Toca para engadir unha impresión dixital"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Desbloqueo mediante impresión dixital"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Preparando <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Iniciando aplicacións."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Está finalizando o arranque"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Queres apagar a pantalla?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Premiches o botón de acendido mentres configurabas a impresión dixital.\n\nAo realizar esta acción, normalmente apágase a pantalla."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Apagar"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Cancelar"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Queres continuar coa configuración?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Premiches o botón de acendido, o que adoita facer que se apague a pantalla.\n\nProba a dar un toque suave namentres configuras a impresión dixital."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Desactivar pantalla"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Seguir configurando"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Queres seguir verificando a impresión?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Premiches o botón de acendido, o que adoita facer que se apague a pantalla.\n\nProba a dar un toque suave para verificar a impresión dixital."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Desactivar pantalla"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Continuar"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> está en execución"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Toca para volver ao xogo"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Escolle un xogo"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 55289549aab9..533d3fbacbbb 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"તમારા હસ્તક્ષેપ વગર કૉલ્સ કરવા માટે IMS સેવાનો ઉપયોગ કરવાની એપ્લિકેશનને મંજૂરી આપે છે."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"ફોન સ્થિતિ અને ઓળખ વાંચો"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"એપ્લિકેશનને ફોન સુવિધાઓને ઍક્સેસ કરવાની મંજૂરી આપે છે. આ પરવાનગી એપ્લિકેશનને ફોન નંબર અને ઉપકરણ ID, કૉલ સક્રિય છે અને કોઈ કૉલ દ્વારા કનેક્ટ થયેલ રિમોટ નંબર નિર્ધારિત કરવાની મંજૂરી આપે છે."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"ટેલિફોન માટેનું મૂળભૂત સ્ટેટસ અને ઓળખ વાંચો"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"ડિવાઇસની ટેલિફોન માટેની મૂળભૂત સુવિધાઓ ઍક્સેસ કરવાની મંજૂરી ઍપને આપે છે."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"સિસ્ટમ મારફતે કૉલ બીજે વાળો"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"કૉલિંગ અનુભવ સુધારવા માટે ઍપ્લિકેશનને સિસ્ટમ મારફતે કૉલ બીજે વાળવાની મંજૂરી આપે છે."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"સિસ્ટમ મારફતે કૉલ જુઓ અને નિયંત્રિત કરો."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"તમારા ચહેરાનું મૉડલ ડિલીટ કરવા માટે ટૅપ કરો, પછી તમારો ચહેરો ફરીથી ઉમેરો"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"ફેસ અનલૉક સુવિધાનું સેટઅપ કરો"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"તમારા ફોનની તરફ જોઈને તેને અનલૉક કરો"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"ફેસ અનલૉક સુવિધાનો ઉપયોગ કરવા માટે, સેટિંગ &gt; પ્રાઇવસીમાં જઈને "<b>"કૅમેરા ઍક્સેસ"</b>" ચાલુ કરો"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"અનલૉક કરવાની બીજી રીતોનું સેટઅપ કરો"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"ફિંગરપ્રિન્ટ ઉમેરવા માટે ટૅપ કરો"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ફિંગરપ્રિન્ટ અનલૉક"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> તૈયાર કરી રહ્યું છે."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"ઍપ્લિકેશનો શરૂ કરી રહ્યાં છે."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"બૂટ સમાપ્ત કરી રહ્યાં છે."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"સ્ક્રીન બંધ કરીએ?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"તમારી ફિંગરપ્રિન્ટની સેટિંગનું સેટઅપ કરતી વખતે, તમે પાવર બટન દબાવ્યું.\n\nઆનાથી સામાન્ય રીતે તમારી સ્ક્રીન બંધ થઈ જાય છે."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"બંધ કરો"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"રદ કરો"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"શું સેટઅપ કરવાનું ચાલુ રાખીએ?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"તમે પાવર બટન દબાવ્યું છે — જેનાથી સામાન્ય રીતે સ્ક્રીન બંધ થઈ જાય છે.\n\nતમારી ફિંગરપ્રિન્ટનું સેટઅપ કરતી વખતે હળવેથી ટૅપ કરવાનો પ્રયાસ કરો."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"સ્ક્રીન બંધ કરો"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"સેટઅપ આગળ વધારો"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"શું તમારી ફિંગરપ્રિન્ટની ચકાસણી કરીએ?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"તમે પાવર બટન દબાવ્યું છે — જેનાથી સામાન્ય રીતે સ્ક્રીન બંધ થઈ જાય છે.\n\nતમારી ફિંગરપ્રિન્ટની ચકાસણી કરવા માટે, તેને હળવેથી ટૅપ કરવાનો પ્રયાસ કરો."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"સ્ક્રીન બંધ કરો"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"આગળ વધો"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> ચાલુ છે"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"ગેમ પર પાછા આવવા માટે ટૅપ કરો"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"ગેમ પસંદ કરો"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index cb6de5ccf4e5..84594373bc76 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"आपके हस्‍तक्षेप के बिना कॉल करने के लिए, ऐप को IMS सेवा का उपयोग करने देती है."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"फ़ोन की स्‍थिति और पहचान पढ़ें"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"ऐप्स को डिवाइस की फ़ोन सुविधाओं तक पहुंचने देता है. यह अनुमति ऐप्स को फ़ोन नंबर और डिवाइस आईडी, कॉल सक्रिय है या नहीं, और कॉल द्वारा कनेक्ट किया गया दूरस्‍थ नंबर तय करने देती है."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"टेलिफ़ोनी सुविधाओं की सामान्य स्थिति और उनकी पहचान से जुड़ी जानकारी पढ़ें"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"ऐसा करने पर, इस ऐप्लिकेशन को डिवाइस की सामान्य टेलिफ़ोनी सुविधाएं ऐक्सेस करने की अनुमति मिलती है."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"सिस्टम के माध्यम से कॉल रूट करें"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"कॉल करने के अनुभव को बेहतर बनाने के लिए ऐप्लिकेशन को सिस्टम के माध्यम से उसके कॉल रूट करने देती है."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"सिस्टम के ज़रिए कॉल देखना और नियंत्रित करना."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"अपने चेहरे का मॉडल मिटाने के लिए टैप करें. इसके बाद, अपना चेहरा फिर से रजिस्टर करें"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"फे़स अनलॉक की सुविधा सेट अप करें"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"अपने फ़ोन की तरफ़ देखकर उसे अनलॉक करें"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"फ़ेस अनलॉक की सुविधा का इस्तेमाल करने के लिए, सेटिंग और निजता में जाकर, "<b>"कैमरे का ऐक्सेस"</b>" चालू करें"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"फ़ोन को अनलॉक करने के दूसरे तरीके सेट अप करें"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"फ़िंगरप्रिंट जोड़ने के लिए टैप करें"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"फ़िंगरप्रिंट अनलॉक"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> तैयार हो रहा है."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"ऐप्स प्रारंभ होने वाले हैं"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"बूट खत्म हो रहा है."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"स्क्रीन बंद करें?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"अपना फ़िंगरप्रिंट सेट अप करते समय, आपने पावर बटन दबाया.\n\nआम तौर पर, इससे स्क्रीन बंद हो जाती है."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"बंद करें"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"अभी नहीं"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"सेट अप जारी रखना है?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"आपने पावर बटन दबाया - आम तौर पर, इससे स्क्रीन बंद हो जाती है.\n\nअपना फ़िंगरप्रिंट सेट अप करते समय, बटन को हल्के से टैप करके देखें."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"स्क्रीन बंद करें"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"सेट अप जारी रखें"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"फ़िंगरप्रिंट की पुष्टि करना जारी रखना है?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"आपने पावर बटन दबाया - आम तौर पर, इससे स्क्रीन बंद हो जाती है.\n\nअपने फ़िंगरप्रिंट की पुष्टि करने के लिए, बटन पर हल्के से टैप करके देखें."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"स्क्रीन बंद करें"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"जारी रखें"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> चल रही है"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"गेम पर वापस जाने के लिए टैप करें"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"गेम चुनें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 1dc25176d5ed..69905ecd8554 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -474,6 +474,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Omogućuje aplikaciji upotrebu usluge izravnih poruka za uspostavljanje poziva bez vaše intervencije."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"čitanje statusa i identiteta telefona"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Aplikaciji omogućuje pristup telefonskim značajkama uređaja. Ta dozvola aplikaciji omogućuje utvrđivanje telefonskog broja i ID-ova uređaja, je li poziv aktivan te udaljeni broj koji je povezan pozivom."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"čitanje osnovnih telefonskih statusa i identiteta"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Dopušta aplikacijama pristup osnovnim telefonskim značajkama uređaja."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"preusmjeravati pozive putem sustava"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Omogućuje aplikaciji da preusmjerava pozive putem sustava radi poboljšanja doživljaja."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"vidjeti i kontrolirati pozive putem sustava."</string>
@@ -625,6 +627,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Dodirnite da biste izbrisali model lica, a zatim ponovo dodajte svoje lice"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Postavite otključavanje licem"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Otključajte telefon gledajući u njega"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Da biste koristili otključavanje licem, uključite opciju "<b>"Pristup kameri"</b>" u odjeljku Postavke &gt; Privatnost"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Postavite više načina otključavanja"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Dodirnite da biste dodali otisak prsta"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Otključavanje otiskom prsta"</string>
@@ -1296,10 +1299,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Pripremanje aplikacije <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Pokretanje aplikacija."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Završetak inicijalizacije."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Isključiti zaslon?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Prilikom postavljanja otiska prsta pritisnuli ste tipku za uključivanje/isključivanje.\n\nTom se radnjom obično isključuje zaslon."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Isključi"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Odustani"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Želite li nastaviti s postavljanjem?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Pritisnuli ste tipku za uključivanje/isključivanje, čime se obično isključuje zaslon.\n\nPokušajte lagano dodirnuti dok postavljate svoj otisak prsta."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Isključi zaslon"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Nastavi postavljanje"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Nastaviti s potvrđivanjem otiska prsta?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Pritisnuli ste tipku za uključivanje/isključivanje, čime se obično isključuje zaslon.\n\nPokušajte lagano dodirnuti da biste potvrdili svoj otisak prsta."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Isključi zaslon"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Nastavi"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"Izvodi se <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Dodirnite za povratak na igru"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Odabir igre"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 34f99630436f..4f040161709d 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Az alkalmazás az IMS-szolgáltatást használhatja híváskezdeményezéshez az Ön közbeavatkozása nélkül."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"telefonállapot és azonosító olvasása"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Lehetővé teszi az alkalmazás számára, hogy hozzáférjen az eszköz telefonálási funkcióihoz. Az engedéllyel rendelkező alkalmazás meghatározhatja a telefonszámot és eszközazonosítókat, hogy egy hívás aktív-e, valamint híváskor a másik fél telefonszámát."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"alapvető telefonállapot és identitás olvasása"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Lehetővé teszi az alkalmazás számára, hogy hozzáférjen az eszköz alapvető telefonos szolgáltatásaihoz."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"rendszeren keresztüli hívásirányítás"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"A telefonálási élmény javítása érdekében lehetővé teszi az alkalmazás számára a rendszeren keresztüli hívásirányítást."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"Hívások megtekintése és vezérlése a rendszeren keresztül"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Koppintson arcmodellje törléséhez, majd készítsen újat"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Az Arcalapú feloldás beállítása"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Feloldhatja a zárolást úgy, hogy ránéz a telefonra"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Az Arcalapú feloldás funkció használatához kapcsolja be a "<b>"Hozzáférés a kamerához"</b>" beállítást a Beállítások &gt; Adatvédelem szakaszban."</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"További feloldási módszerek beállítása"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Koppintson ide ujjlenyomat hozzáadásához"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Feloldás ujjlenyomattal"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"A(z) <xliff:g id="APPNAME">%1$s</xliff:g> előkészítése."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Kezdő alkalmazások."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Rendszerindítás befejezése."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Kikapcsolja a képernyőt?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Ujjlenyomata beállítása közben megnyomta a bekapcsológombot.\n\nEz általában kikapcsolja a képernyőt."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Kikapcsolás"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Mégse"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Folytatja a beállítást?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Megnyomta a bekapcsológombot — ezzel általában kikapcsol a képernyő.\n\nPróbáljon finoman rákoppintani az ujjlenyomat beállítása közben."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Kikapcsolom"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Beállítás folytatása"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Folytatja az ujjlenyomat ellenőrzését?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Megnyomta a bekapcsológombot — ezzel általában kikapcsol a képernyő.\n\nPróbáljon finoman rákoppintani az ujjlenyomat ellenőrzéséhez."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Kikapcsolom"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Tovább"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> fut"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Koppintson ide a játékhoz való visszatéréshez"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Játék kiválasztása"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 03f9e1d17e2c..141780e3a99d 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Թույլ է տալիս հավելվածին IMS ծառայության միջոցով կատարել զանգեր՝ առանց ձեր միջամտության:"</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"կարդալ հեռախոսի կարգավիճակը և ինքնությունը"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Թույլ է տալիս հավելվածին օգտագործել սարքի հեռախոսային գործիքները: Այս թույլտվությունը հավելվածին հնարավորություն է տալիս որոշել հեռախոսահամարը և սարքի ID-ները, արդյոք զանգը ակտիվ է և միացված զանգի հեռակա հեռախոսահամարը:"</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"Կարդալ հիմնական հեռախոսային գործառույթների կարգավիճակը և նույնականացնող տվյալներ"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Հավելվածին հասանելի է դարձնում սարքի հիմնական հեռախոսային գործառույթները:"</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"զանգերն ուղարկել համակարգի միջոցով"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Հավելվածին թույլ է տալիս իր զանգերն ուղարկել համակարգի միջոցով՝ կապի որակը բարձրացնելու նպատակով։"</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"Զանգերի դիտում և վերահսկում համակարգի միջոցով"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Հպեք՝ ձեր դեմքի նմուշը ջնջելու համար, այնուհետև նորից ավելացրեք այն:"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Կարգավորեք դեմքով ապակողպումը"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Ապակողպելու համար պարզապես նայեք հեռախոսին"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Դեմքով ապակողպումն օգտագործելու համար անցեք Կարգավորումներ &gt; Գաղտնիություն և տրամադրեք "<b>"տեսախցիկն օգտագործելու թույլտվություն"</b>"։"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Կարգավորեք ապակողպելու այլ եղանակներ"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Հպեք՝ մատնահետք ավելացնելու համար"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Մատնահետքով ապակողպում"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> հավելվածը պատրաստվում է:"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Հավելվածները մեկնարկում են:"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Բեռնումն ավարտվում է:"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Անջատե՞լ էկրանը"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Մատնահետքը կարգավորելու ժամանակ դուք սեղմել եք սնուցման կոճակը։\n\nՍովորաբար դրա արդյունքում էկրանն անջատվում է։"</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Անջատել"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Չեղարկել"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Շարունակե՞լ կարգավորումը"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Դուք սեղմել եք սնուցման կոճակը։ Սովորաբար դրա արդյունքում էկրանն անջատվում է։\n\nՄատնահետքը ավելացնելու համար թեթևակի հպեք կոճակին։"</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Անջատել էկրանը"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Շարունակել գրանցումը"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Շարունակե՞լ մատնահետքի սկանավորումը"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Դուք սեղմել եք սնուցման կոճակը։ Սովորաբար դրա արդյունքում էկրանն անջատվում է։\n\nՄատնահետքը սկանավորելու համար թեթևակի հպեք կոճակին։"</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Անջատել էկրանը"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Շարունակել"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g>-ն աշխատում է"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Հպեք՝ խաղին վերադառնալու համար"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Ընտրեք խաղ"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index db97591db32b..e1e8ea91a04c 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Memungkinkan aplikasi menggunakan layanan IMS untuk melakukan panggilan tanpa campur tangan Anda."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"baca identitas dan status ponsel"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Memungkinkan aplikasi mengakses fitur telepon perangkat. Izin ini memungkinkan aplikasi menentukan nomor telepon dan ID perangkat, apakah suatu panggilan aktif atau tidak, dan nomor jarak jauh yang terhubung oleh sebuah panggilan."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"membaca identitas dan status telepon dasar"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Memungkinkan aplikasi mengakses fitur telepon dasar perangkat."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"sambungkan panggilan telepon melalui sistem"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Mengizinkan aplikasi menyambungkan panggilan telepon melalui sistem untuk menyempurnakan pengalaman menelepon."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"melihat dan mengontrol panggilan melalui sistem."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Ketuk untuk menghapus model wajah, lalu tambahkan wajah Anda lagi"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Siapkan Face Unlock"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Buka kunci ponsel dengan melihat ke ponsel"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Untuk menggunakan Face Unlock, aktifkan "<b>"Akses kamera"</b>" di Setelan &gt; Privasi"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Siapkan lebih banyak cara untuk membuka kunci"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Ketuk untuk menambahkan sidik jari"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Fingerprint Unlock"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Menyiapkan <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Memulai aplikasi."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Menyelesaikan boot."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Nonaktifkan layar?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Saat menyiapkan sidik jari, Anda menekan Tombol daya.\n\nTindakan ini biasanya akan menonaktifkan layar."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Nonaktifkan"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Batal"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Lanjutkan penyiapan?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Anda menekan tombol daya; tindakan ini biasanya akan menonaktifkan layar.\n\nCoba ketuk lembut saat menyiapkan sidik jari Anda."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Nonaktifkan layar"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Lanjutkan penyiapan"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Lanjutkan verifikasi sidik jari?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Anda menekan tombol daya; tindakan ini biasanya akan menonaktifkan layar.\n\nCoba ketuk lembut untuk memverifikasi sidik jari Anda."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Nonaktifkan layar"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Lanjutkan"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> sedang berjalan"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Ketuk untuk kembali ke game"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Pilih game"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index d70a66bd6458..0678ab2034a2 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Leyfir forriti að nota IMS-þjónustu til að hringja án inngrips frá þér."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"lesa stöðu símans og auðkenni"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Leyfir forriti að fá aðgang að símaeiginleikum tækisins. Þessi heimild gerir forritinu kleift að komast að símanúmeri og auðkennum tækisins, hvort símtal er í gangi og símanúmeri viðmælanda í símtali."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"lesa stöðu og auðkenni grunneiginleika símaþjónustu"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Leyfir forritinu að fá aðgang að grunneiginleikum símaþjónustu tækisins."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"senda símtöl gegnum kerfið"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Heimilar forritinu að senda símtöl sín gegnum kerfið til að bæta gæði símtalsins."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"sjá og stjórna símtölum í gegnum kerfið."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Ýttu til að eyða andlitslíkaninu og skráðu svo andlitið aftur"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Setja upp andlitskenni"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Taktu símann úr lás með því að horfa á hann"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Þú verður að kveikja á "<b>"aðgangi að myndavél"</b>" í „Stillingar &gt; persónuvernd“ til að nota andlitskenni"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Settu upp fleiri leiðir til að taka úr lás"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Ýttu til að bæta við fingrafari"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Fingrafarskenni"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Undirbýr <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Ræsir forrit."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Lýkur ræsingu."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Slökkva á skjá?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Þú ýttir á aflrofann þegar þú varst að skrá fingrafarið þitt.\n\nYfirleitt slekkur það á skjánum."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Slökkva"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Hætta við"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Halda uppsetningu áfram?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Þú ýttir á aflrofann. Yfirleitt slekkur það á skjánum.\n\nPrófaðu að ýta laust þegar þú setur upp fingrafarið."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Slökkva á skjá"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Halda uppsetningu áfram"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Halda áfram að staðfesta fingrafarið?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Þú ýttir á aflrofann. Yfirleitt slekkur það á skjánum.\n\nPrófaðu að ýta laust til að staðfesta fingrafarið þitt."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Slökkva á skjá"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Áfram"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> er í gangi"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Ýttu til að fara aftur í leik"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Velja leik"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 91df4bdb7ad3..a63fbd82c510 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Consente all\'app di utilizzare il servizio IMS per fare chiamate senza il tuo intervento."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"lettura stato e identità telefono"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Consente all\'applicazione di accedere alle funzioni telefoniche del dispositivo. Questa autorizzazione consente all\'applicazione di determinare il numero di telefono e gli ID dei dispositivi, se una chiamata è attiva e il numero remoto connesso da una chiamata."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"lettura di identità e stato telefonia"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Consente all\'app di accedere alle funzionalità di telefonia di base del dispositivo."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"indirizzamento delle chiamate tramite il sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Consente all\'app di indirizzare le proprie chiamate tramite il sistema al fine di migliorare l\'esperienza di chiamata."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"visualizzazione e controllo delle chiamate tramite il sistema."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tocca per eliminare il tuo modello del volto e poi riaggiungi il tuo volto"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configura lo sblocco con il volto"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Sblocca il telefono guardandolo"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Per utilizzare lo sblocco con il volto, attiva "<b>"l\'accesso alla fotocamera"</b>" in Impostazioni &gt; Privacy"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configura altri modi per sbloccare"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tocca per aggiungere un\'impronta"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Sblocco con l\'impronta"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> in preparazione."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Avvio app."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Conclusione dell\'avvio."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Vuoi disattivare lo schermo?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Durante la configurazione della tua impronta hai premuto il tasto di accensione.\n\nGeneralmente questa azione comporta la disattivazione dello schermo."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Disattiva"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Annulla"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Vuoi continuare la configurazione?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Hai premuto il tasto di accensione; in genere questa azione disattiva lo schermo.\n\nProva a toccare leggermente il tasto di accensione durante la configurazione della tua impronta."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Disattiva lo schermo"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Continua configuraz."</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Vuoi continuare a verificare l\'impronta?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Hai premuto il tasto di accensione; in genere questa azione disattiva lo schermo.\n\nProva a toccare leggermente il tasto di accensione per verificare la tua impronta."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Disattiva lo schermo"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Continua"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> in esecuzione"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Tocca per tornare al gioco"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Scegli gioco"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index a16c682038be..7a594d821c62 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -477,6 +477,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"‏מאפשרת לאפליקציה להשתמש בשירות ה-IMS לביצוע שיחות ללא התערבות שלך."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"קריאת הסטטוס והזהות של הטלפון"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"מאפשרת לאפליקציה לגשת לתכונות הטלפון של המכשיר. ההרשאה הזו מתירה לאפליקציה לגלות את מספר הטלפון ואת מזהי המכשיר, אם השיחה פעילה ואת המספר המרוחק המחובר באמצעות שיחה."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"קריאה של זהות וסטטוס טלפוניים בסיסיים"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"ההרשאה מאפשרת לאפליקציה לגשת לתכונות הטלפוניות הבסיסיות של המכשיר."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"ניתוב שיחות דרך המערכת"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"מאפשרת לאפליקציה לנתב את השיחות דרך המערכת כדי לשפר את חוויית השיחה."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"הצגת שיחות ושליטה בהן באמצעות המערכת."</string>
@@ -628,6 +630,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"יש להקיש כדי למחוק את התבנית לזיהוי הפנים, ואז להוסיף תבנית חדשה לזיהוי הפנים"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"הגדרת התכונה \'פתיחה ע\"י זיהוי הפנים\'"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"יש להביט בטלפון כדי לבטל את נעילתו"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"‏כדי להשתמש בתכונה \'פתיחה ע\"י זיהוי הפנים\', יש להפעיל את ה"<b>"גישה למצלמה"</b>" בהגדרות &gt; פרטיות"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"אפשר להגדיר דרכים נוספות לביטול נעילה"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"יש להקיש כדי להוסיף טביעת אצבע"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ביטול הנעילה בטביעת אצבע"</string>
@@ -1316,10 +1319,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"המערכת מכינה את <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"מתבצעת הפעלה של אפליקציות."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"תהליך האתחול בשלבי סיום."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"לכבות את המסך?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"כשהגדרת את טביעת האצבע, לחצת על לחצן ההפעלה.\n\nלרוב, הפעולה הזו מכבה את המסך."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"כיבוי"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"ביטול"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"להמשיך בהגדרה?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"לחצת על לחצן ההפעלה – בדרך כלל הפעולה הזו מכבה את המסך.\n\nעליך לנסות להקיש בעדינות במהלך ההגדרה של טביעת האצבע שלך."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"כיבוי המסך"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"להמשך ההגדרה"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"להמשיך לאמת את טביעת האצבע שלך?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"לחצת על לחצן ההפעלה – בדרך כלל הפעולה הזו מכבה את המסך.\n\nעליך לנסות להקיש בעדינות כדי לאמת את טביעת האצבע שלך."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"כיבוי המסך"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"המשך"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"האפליקציה <xliff:g id="APP">%1$s</xliff:g> פועלת"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"יש להקיש כדי לחזור למשחק"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"בחירת משחק"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index ff7a868cdb70..241105f1b6fe 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"IMSサービスがユーザー操作なしで電話をかけることをアプリに許可します。"</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"デバイス情報と ID の読み取り"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"デバイスの電話機能へのアクセスをアプリに許可します。これにより、電話番号、デバイスID、通話中かどうか、通話相手の電話番号をアプリから特定できるようになります。"</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"電話の基本的なステータスと ID の読み取り"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"デバイスの基本的な電話機能へのアクセスをアプリに許可します。"</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"システム経由での通話転送"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"通話環境の改善のために、システム経由での通話転送をアプリに許可します。"</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"システム経由の通話の表示と操作。"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"タップして顔モデルを削除してから、改めて顔を追加してください"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"顔認証の設定"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"スマートフォンに顔を向けるとロックが解除されます"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"顔認証を使用するには、[設定] &gt; [プライバシー] で"<b>"カメラへのアクセス"</b>"を有効にしてください"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"その他のロック解除方法の設定"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"タップすると指紋が追加されます"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"指紋認証"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g>をペア設定しています。"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"アプリを起動しています。"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"ブートを終了しています。"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"画面を OFF にしますか?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"指紋の設定中に電源ボタンが押されました。\n\n通常、この操作により画面が OFF になります。"</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"OFF にする"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"キャンセル"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"設定を続行しますか?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"電源ボタンを押しました。通常、この操作で画面が OFF になります。\n\n指紋を設定する場合は電源ボタンに軽く触れてみましょう。"</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"画面を OFF にする"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"設定を続行"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"指紋の確認を続行しますか?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"電源ボタンを押しました。通常、この操作で画面が OFF になります。\n\n指紋を確認するには、電源ボタンに軽く触れてみましょう。"</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"画面を OFF にする"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"続行"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g>を実行中"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"タップするとゲームに戻ります"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"ゲームの選択"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index dbc7bf59dae0..2137e0feee53 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"აპს შეეძლება, გამოიყენოს IMS სერვისი ზარების თქვენი ჩარევის გარეშე განსახორციელებლად."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"ტელეფონის სტატუსისა და იდენტობის წაკითხვა"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"აპს შეეძლება ჰქონდეს წვდომა მოწყობილობის სატელეფონო ფუნქციებზე. აპმა მსგავსი უფლებით შეძლებს დაადგინოს ტელეფონის ნომერი, მისი სერიული გამოცემა, აქტიური ზარი, დაკავშირებული ნომერი და მსგავსი."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"წაიკითხეთ ტელეფონის ძირითადი სტატუსი და აიდი"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"აპს აძლევს მოწყობილობის ძირითად სატელეფონო ფუნქციებზე წვდომას."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"ზარების სისტემის მეშვეობით მარშრუტიზაცია"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"დარეკვის ხარისხის გაუმჯობესების მიზნით, აპს ზარების სისტემის მეშვეობით მარშრუტიზაციის საშუალებას აძლევს."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"ზარების ნახვა და გაკონტროლება სისტემის მეშვეობით."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"შეეხეთ თქვენი სახის მოდელის წასაშლელად, შემდეგ დაამატეთ სახე ხელახლა"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"სახით განბლოკვის პარამეტრების დაყენება"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"განბლოკეთ თქვენი ტელეფონი შეხედვით"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"იმისთვის, რომ სახით განბლოკვით ისარგებლოთ, ჩართეთ "<b>"კამერაზე წვდომა"</b>" პარამეტრებსა და კონფიდენციალურობაში"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"დააყენეთ განბლოკვის სხვა ხერხები"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"შეეხეთ თითის ანაბეჭდის დასამატებლად"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"თითის ანაბეჭდით განბლოკვა"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"ემზადება <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"აპების ჩართვა"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"ჩატვირთვის დასასრული."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"გამოირთოს ეკრანი?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"თითის ანაბეჭდის დაყენებისას ჩართვის ღილაკს დააჭირეთ.\n\nეს, ჩვეულებრივ, თქვენს ეკრანს გამორთავს."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"გამორთვა"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"გაუქმება"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"გსურთ დაყენების გაგრძელება?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"თქვენ დააჭირეთ ჩართვის ღილაკს — ჩვეულებრივ, ის ეკრანს გამორთავს.\n\nთქვენი თითის ანაბეჭდის დაყენებისას ცადეთ მსუბუქად შეხება."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"ეკრანის გამორთვა"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"დაყენების გაგრძელება"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"გსურთ ანაბეჭდის დადასტურების გაგრძელება?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"თქვენ დააჭირეთ ჩართვის ღილაკს — ჩვეულებრივ, ის ეკრანს გამორთავს.\n\nთქვენი თითის ანაბეჭდის დასადასტურებლად ცადეთ მსუბუქად შეხება."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"ეკრანის გამორთვა"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"გაგრძელება"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> გაშვებულია"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"შეეხეთ თამაშში დასაბრუნებლად"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"აირჩიეთ თამაში"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index c1d421fc4e85..3042c643f4ab 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Сіздің қатысуыңызсыз қоңыраулар соғу үшін қолданбаға IMS қызметін пайдалануға рұқсат етеді."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"телефон күйін оқу немесе анықтау"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Қолданбаға құрылғыдағы телефон функцияларына кіру мүмкіндігін береді. Бұл рұқсат қолданбаға телефон нөмірі, құрылғы жеке анықтағышы, қоңырау белсенділігі және сол қоңырауға байланысты қашықтағы нөмірді анықтау мүмкіндігін береді."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"негізгі телефония күйі мен сәйкестендіру деректерін оқу"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Қолданбаға құрылғының негізгі телефония функцияларын пайдалануға рұқсат береді."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"қоңырауларды жүйе арқылы бағыттау"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Қоңырау шалу тәжірибесін жақсарту үшін қолданба қоңырауларды жүйе арқылы бағыттай алады."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"жүйе арқылы қоңырауларды көру және басқару."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Бет үлгісін жою үшін түртіңіз, содан соң жаңа бет үлгісін қосыңыз."</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Бет тану функциясын реттеу"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Телефоныңызға қарап, оның құлпын ашыңыз."</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Face Unlock функциясын пайдалану үшін \"Параметрлер &gt; Құпиялылық\" бөлімінен "<b>"Камераны пайдалану рұқсатын"</b>" қосыңыз."</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Құлыпты ашудың басқа тәсілдерін реттеу"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Саусақ ізін қосу үшін түртіңіз."</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Құлыпты саусақ ізімен ашу"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> дайындалуда."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Қолданбалар іске қосылуда."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Қосуды аяқтауда."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Экранды өшіру керек пе?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Саусақ ізіңізді орнату кезінде қуат түймесін басып қалдыңыз.\n\nБұл әрекет әдетте экранды өшіреді."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Өшіру"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Бас тарту"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Реттеуді жалғастырасыз ба?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Қуат түймесін бастыңыз. Бұл әдетте экранды өшіреді.\n\nСаусақ ізін реттеу үшін, оны жайлап түртіп көріңіз."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Экранды өшіру"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Реттеуді жалғастыру"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Саусақ ізін растауды жалғастырасыз ба?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Қуат түймесін бастыңыз. Бұл әдетте экранды өшіреді.\n\nСаусақ ізін растау үшін, оны жайлап түртіп көріңіз."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Экранды өшіру"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Жалғастыру"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> қосылған"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Ойынды жалғастыру үшін түртіңіз"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Ойынды таңдаңыз"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 2889ac3c1b8f..81fbca5fe604 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"អនុញ្ញាតឲ្យកម្មវិធីនេះប្រើសេវាកម្ម IMS ដើម្បីធ្វើការហៅដោយគ្មានការអន្តរាគមន៍ពីអ្នក។"</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"អាន​ស្ថានភាព និង​អត្តសញ្ញាណ​ទូរស័ព្ទ"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"ឲ្យ​កម្មវិធី​ចូល​ដំណើរការ​លក្ខណៈ​ទូរស័ព្ទ​នៃ​ឧបករណ៍។ សិទ្ធិ​នេះ​​ឲ្យ​កម្មវិធី​កំណត់​លេខ​ទូរស័ព្ទ និង​លេខ​សម្គាល់​ឧបករណ៍ ថា​តើ​ការ​ហៅ​សកម្ម និង​លេខ​ពី​ចម្ងាយ​បាន​ភ្ជាប់​ដោយ​ការ​ហៅ។"</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"អានអត្តសញ្ញាណ និងស្ថានភាពទូរសព្ទគោល"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"អនុញ្ញាតឱ្យកម្មវិធីចូលប្រើមុខងារទូរសព្ទគោលរបស់ឧបករណ៍។"</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"បញ្ជូន​ការ​ហៅ​ទូរសព្ទ​តាមរយៈ​ប្រព័ន្ធ"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"អនុញ្ញាត​ឲ្យ​កម្មវិធី​នេះ​បញ្ជូន​ការ​ហៅ​ទូរសព្ទ​របស់វា​តាមរយៈ​ប្រព័ន្ធ ​ដើម្បី​ធ្វើ​ឲ្យ​ការ​ហៅ​ទូរសព្ទ​ប្រសើរ​ជាង​មុន។"</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"មើល និង​គ្រប់គ្រង​ការហៅទូរសព្ទ​តាមរយៈប្រព័ន្ធ។"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"ចុចដើម្បីលុប​គំរូមុខ​របស់អ្នក រួចបញ្ចូល​មុខរបស់អ្នក​ម្ដងទៀត"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"រៀបចំ​ការដោះសោ​តាមទម្រង់មុខ"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ដោះសោទូរសព្ទ​របស់អ្នកដោយសម្លឹងមើលវា"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"ដើម្បីប្រើមុខងារដោះសោតាមទម្រង់មុខ សូមបើក"<b>"ការចូលប្រើកាមេរ៉ា"</b>"នៅក្នុងការកំណត់ &gt; ឯកជនភាព"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"រៀបចំ​វិធីច្រើនទៀត​ដើម្បី​ដោះសោ"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"ចុច​ដើម្បីបញ្ចូល​ស្នាមម្រាមដៃ"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ការដោះសោ​ដោយប្រើ​ស្នាមម្រាមដៃ"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"កំពុងរៀបចំ <xliff:g id="APPNAME">%1$s</xliff:g>។"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"ចាប់ផ្ដើម​កម្មវិធី។"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"បញ្ចប់​ការ​ចាប់ផ្ដើម។"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"បិទ​អេក្រង់ឬ?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"នៅពេលរៀបចំ​ស្នាមម្រាមដៃ​របស់អ្នក អ្នកបានចុច​ប៊ូតុងថាមពល។\n\nជាធម្មតា ការធ្វើបែបនេះ​បិទអេក្រង់​របស់អ្នក។"</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"បិទ"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"បោះបង់"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"បន្ត​រៀបចំឬ?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"អ្នកបានចុចប៊ូតុងថាមពល — ជាធម្មតាការធ្វើបែបនេះនឹងបិទអេក្រង់។\n\nសាកល្បងចុចថ្នមៗ ខណៈពេលកំពុងរៀបចំស្នាមម្រាមដៃរបស់អ្នក។"</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"បិទ​អេក្រង់"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"បន្ត​រៀបចំ"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"បន្តផ្ទៀងផ្ទាត់ស្នាមម្រាមដៃរបស់អ្នកឬ?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"អ្នកបានចុចប៊ូតុងថាមពល — ជាធម្មតាការធ្វើបែបនេះនឹងបិទអេក្រង់។\n\nសាកល្បងចុចថ្នមៗ ដើម្បីផ្ទៀងផ្ទាត់ស្នាមម្រាមដៃរបស់អ្នក។"</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"បិទ​អេក្រង់"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"បន្ត"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> កំពុង​ដំណើរការ"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"ចុច​ដើម្បី​ត្រឡប់​ទៅ​ហ្គេមវិញ"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"ជ្រើសរើស​ហ្គេម"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index cc9771a23586..569f6a535349 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"ನಿಮ್ಮ ಮಧ್ಯಸ್ಥಿಕೆ ಇಲ್ಲದೆಯೇ ಕರೆಗಳನ್ನು ಮಾಡಲು IMS ಸೇವೆಯನ್ನು ಬಳಸಲು ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"ಫೋನ್ ಸ್ಥಿತಿ ಮತ್ತು ಗುರುತಿಸುವಿಕೆಯನ್ನು ಓದಿ"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"ಸಾಧನದ ಫೋನ್ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಪ್ರವೇಶಿಸಲು ಅಪ್ಲಿಕೇಶನ್‍‍ಗೆ ಅವಕಾಶ ಮಾಡಿಕೊಡುತ್ತದೆ. ಈ ಅನುಮತಿಯು ಫೋನ್ ಸಂಖ್ಯೆ ಮತ್ತು ಸಾಧನದ ID ಗಳನ್ನು ನಿರ್ಧರಿಸಲು, ಕರೆಯು ಸಕ್ರಿಯವಾಗಿದೆಯೇ ಮತ್ತು ಕರೆಯ ಮೂಲಕ ರಿಮೋಟ್ ಸಂಖ್ಯೆಯು ಸಂಪರ್ಕಗೊಂಡಿವೆಯೇ ಎಂಬುದನ್ನೂ ನಿರ್ಧರಿಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅವಕಾಶ ಕಲ್ಪಿಸುತ್ತದೆ."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"ಮೂಲ ಟೆಲಿಫೋನ್ ಸ್ಥಿತಿ ಮತ್ತು ಗುರುತನ್ನು ಓದಿ"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"ಸಾಧನದ ಮೂಲ ಟೆಲಿಫೋನ್ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಪ್ರವೇಶಿಸಲು ಆ್ಯಪ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"ಕರೆಗಳನ್ನು ಸಿಸ್ಟಂ ಮೂಲಕ ರವಾನಿಸಿ"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"ಕರೆಯ ಅನುಭವವನ್ನು ಸುಧಾರಿಸಲು ಅಪ್ಲಿಕೇಶನ್‌ನ ಕರೆಗಳನ್ನು ಸಿಸ್ಟಂ ಮೂಲಕ ರವಾನಿಸಲು ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"ಸಿಸ್ಟಂ ಮೂಲಕ ಕರೆಗಳನ್ನು ವೀಕ್ಷಿಸಿ ಮತ್ತು ನಿಯಂತ್ರಿಸಿ."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"ನಿಮ್ಮ ಫೇಸ್ ಮಾಡೆಲ್ ಅನ್ನು ಅಳಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ, ನಂತರ ನಿಮ್ಮ ಫೇಸ್ ಮಾಡೆಲ್ ಅನ್ನು ಪುನಃ ಸೇರಿಸಿ"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"ಫೇಸ್ ಅನ್‌ಲಾಕ್ ಅನ್ನು ಸೆಟಪ್ ಮಾಡಿ"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ಫೋನ್ ಅನ್ನು ನೋಡುವ ಮೂಲಕ ಅನ್‌ಲಾಕ್‌ ಮಾಡಿ"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"ಫೇಸ್ ಅನ್‌ಲಾಕ್ ಬಳಸಲು, ಸೆಟ್ಟಿಂಗ್‌ಗಳು &gt; ಗೌಪ್ಯತೆ ಎಂಬಲ್ಲಿ "<b>"ಕ್ಯಾಮರಾ ಪ್ರವೇಶವನ್ನು"</b>" ಆನ್ ಮಾಡಿ"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"ಅನ್‌ಲಾಕ್ ಮಾಡಲು ಹೆಚ್ಚಿನ ಮಾರ್ಗಗಳನ್ನು ಹೊಂದಿಸಿ"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"ಫಿಂಗರ್‌ ಪ್ರಿಂಟ್ ಸೇರಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಅನ್‌ಲಾಕ್"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> ಸಿದ್ಧಪಡಿಸಲಾಗುತ್ತಿದೆ."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಪ್ರಾರಂಭಿಸಲಾಗುತ್ತಿದೆ."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"ಬೂಟ್ ಪೂರ್ಣಗೊಳಿಸಲಾಗುತ್ತಿದೆ."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"ಸ್ಕ್ರೀನ್ ಆಫ್ ಮಾಡಬೇಕೇ?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"ಫಿಂಗರ್‌ ಪ್ರಿಂಟ್ ಅನ್ನು ಸೆಟ್ಟಪ್ ಮಾಡುವಾಗ ನೀವು ಪವರ್ ಬಟನ್‌ಅನ್ನು ಒತ್ತಿದ್ದೀರಿ \n\nಸಾಮಾನ್ಯವಾಗಿ ಇದರಿಂದ ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಆಫ್ ಆಗುತ್ತದೆ."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"ಆಫ್ ಮಾಡಿ"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"ರದ್ದುಗೊಳಿಸಿ"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"ಸೆಟಪ್ ಮುಂದುವರಿಸಬೇಕೆ?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"ನೀವು ಪವರ್ ಬಟನ್ ಒತ್ತಿದ್ದೀರಿ — ಇದು ಸಾಮಾನ್ಯವಾಗಿ ಸ್ಕ್ರೀನ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸುತ್ತದೆ.\n\nನಿಮ್ಮ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಅನ್ನು ಹೊಂದಿಸುವಾಗ ಲಘುವಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಲು ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"ಸ್ಕ್ರೀನ್ ಆಫ್ ಮಾಡಿ"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"ಸೆಟಪ್ ಮುಂದುವರಿಸಿ"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಪರಿಶೀಲನೆ ಮುಂದುವರಿಸುವುದೇ?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"ನೀವು ಪವರ್ ಬಟನ್ ಒತ್ತಿದ್ದೀರಿ — ಇದು ಸಾಮಾನ್ಯವಾಗಿ ಸ್ಕ್ರೀನ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸುತ್ತದೆ.\n\nನಿಮ್ಮ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಅನ್ನು ಪರಿಶೀಲಿಸಲು ಲಘುವಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಲು ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"ಸ್ಕ್ರೀನ್ ಆಫ್ ಮಾಡಿ"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"ಮುಂದುವರಿಸಿ"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> ರನ್ ಆಗುತ್ತಿದೆ"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"ಆಟಕ್ಕೆ ಹಿಂತಿರುಗಲು ಟ್ಯಾಪ್‌ ಮಾಡಿ"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"ಆಟವನ್ನು ಆಯ್ಕೆ ಮಾಡಿ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index aacc58bf0c26..01d040fa41a8 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"앱이 IMS 서비스를 사용하여 자동으로 전화를 걸 수 있도록 허용합니다."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"휴대전화 상태 및 ID 읽기"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"앱이 기기의 휴대전화 기능에 접근할 수 있도록 허용합니다. 이 권한을 사용하면 앱이 전화번호 및 기기의 ID, 활성 통화인지 여부, 통화가 연결된 원격 번호 등을 확인할 수 있습니다."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"기본 전화 상태 및 ID 읽기"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"앱이 기기의 기본 전화 기능에 액세스할 수 있도록 허용합니다."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"시스템을 통해 통화 연결"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"통화 환경을 개선하기 위해 앱이 시스템을 통해 통화를 연결하도록 허용합니다."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"시스템을 통해 통화 확인 및 제어"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"탭하여 얼굴 모델을 삭제한 후 다시 얼굴을 추가하세요"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"얼굴 인식 잠금 해제 설정"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"휴대전화의 화면을 응시하여 잠금 해제할 수 있습니다."</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"얼굴 인식 잠금 해제를 사용하려면 설정 &gt; 개인 정보 보호에서 "<b>"카메라 액세스"</b>"를 사용 설정하세요."</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"다른 잠금 해제 방법 설정"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"지문을 추가하려면 탭하세요."</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"지문 잠금 해제"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> 준비 중..."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"앱을 시작하는 중입니다."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"부팅 완료"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"화면을 끄시겠습니까?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"지문을 설정하는 중에 전원 버튼이 눌렸습니다.\n\n이렇게 하면 보통 화면이 꺼집니다."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"끄기"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"취소"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"설정을 계속하시겠어요?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"전원 버튼을 눌렀습니다. 이러면 보통 화면이 꺼집니다.\n\n지문 설정 중에 가볍게 탭하세요."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"화면 끄기"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"설정 계속"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"지문 인증을 계속할까요?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"전원 버튼을 눌렀습니다. 이러면 보통 화면이 꺼집니다.\n\n지문을 인식하려면 화면을 가볍게 탭하세요."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"화면 끄기"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"계속"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> 실행 중"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"게임으로 돌아가려면 탭하세요."</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"게임 선택"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 5399b4c4b733..e15377344230 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Колдонмого сизди катыштырбай туруп, IMS кызматынын жардамы менен, чалууларды жасоо мүмкүнчүлүгүн берет."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"телефондун абалын жана аныктыгын окуу"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Колдонмого түзмөктүн чалуу мүмкүнчүлүктөрүнө жетки алуу уруксатын берет. Бул уруксат колдонмого, телефондун номурун, түзмөктүн ID-син, чалуунун абалын жана байланышта чыккан номурду аныктоого жол берет."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"негизги телефония статусун окуу жана аныктоо"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Колдонмого түзмөктүн негизги телефония функцияларын колдонууга мүмкүнчүлүк берет."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"чалууларды тутум аркылуу өткөрүү"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Чалуунун сапатын жакшыртуу максатында колдонмого чалууларын тутум аркылуу өткөрүүгө уруксат берет."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"тутум аркылуу чалууларды көрүп, көзөмөлдөө."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Жүзүңүздүн үлгүсүн өчүрүү үчүн басып, жаңы үлгүнү кошуңуз"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Жүзүнөн таанып ачууну жөндөө"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Телефонуңузду карап туруп эле кулпусун ачып алыңыз"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Жүзүнөн таанып ачуу функциясын колдонуу үчүн Жөндөөлөр &gt; Купуялык бөлүмүнө өтүп, "<b>"Камераны колдонууну"</b>" күйгүзүңүз"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Кулпусун ачуунун көбүрөөк жолдорун жөндөңүз"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Манжа изин кошуу үчүн басыңыз"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Кулпуланган түзмөктү манжа изи менен ачуу"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> даярдалууда."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Колдонмолорду иштетип баштоо"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Жүктөлүүдө"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Экран өчүрүлсүнбү?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Манжаңыздын изин жөндөп жатканда күйгүзүү/өчүрүү баскычын басып алдыңыз.\n\nБул адатта экранды өчүрөт."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Өчүрүү"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Жок"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Жөндөөнү улантасызбы?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Кубат баскычын бастыңыз — адатта, бул экранды өчүрөт.\n\nМанжаңыздын изин жөндөп жатканда аны акырын басып көрүңүз."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Экранды өчүрүү"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Жөндөөнү улантуу"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Манжаңыздын изин ырастоону улантасызбы?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Кубат баскычын бастыңыз — адатта, бул экранды өчүрөт.\n\nМанжаңыздын изин ырастоо үчүн аны акырын басып көрүңүз."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Экранды өчүрүү"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Улантуу"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> иштеп жатат"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Оюнга кайтуу үчүн таптаңыз"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Оюн тандоо"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 88de60dd445c..b3cb5afea989 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"ອະ​ນຸ​ຍາດ​ໃຫ້​ແອັບ​ໃຊ້​ການ​ບໍ​ລິ​ການ IMS ເພື່ອ​ໂທ​ໂດຍ​ບໍ່​ມີ​ການ​ຊ່ວຍ​ເຫຼືອ​ຂອງ​ທ່ານ."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"ອ່ານສະຖານະ ແລະຂໍ້ມູນລະບຸໂຕຕົນຂອງໂທລະສັບ"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"ອະນຸຍາດໃຫ້ແອັບຯ ເຂົ້າເຖິງຄວາມສາມາດການໂທລະສັບຂອງອຸປະກອນ. ການກຳນົດສິດນີ້ເຮັດໃຫ້ແອັບຯສາມາດກວດສອບເບີໂທລະສັບ ແລະ ID ຂອງອຸປະກອນ, ບໍ່ວ່າການໂທຈະຍັງດຳເນີນຢູ່ ແລະເບີປາຍທາງເຊື່ອມຕໍ່ຢູ່ຫຼືບໍ່ກໍຕາມ."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"ອ່ານສະຖານະໂທລະສັບພື້ນຖານ ແລະ ຕົວຕົນ"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"ອະນຸຍາດໃຫ້ແອັບເຂົ້າເຖິງຄຸນສົມບັດໂທລະສັບພື້ນຖານຂອງອຸປະກອນໄດ້."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"route calls through the system"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Allows the app to route its calls through the system in order to improve the calling experience."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"ເຫັນ ແລະ ຄວບຄຸມການໂທຜ່ານລະບົບ."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"ແຕະເພື່ອລຶບຮູບແບບໃບໜ້າຂອງທ່ານ, ຈາກນັ້ນເພີ່ມໃບໜ້າຂອງທ່ານໃສ່ໃໝ່"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"ຕັ້ງຄ່າການປົດລັອກດ້ວຍໜ້າ"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ປົດລັອກໂທລະສັບຂອງທ່ານໂດຍການເບິ່ງມັນ"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"ເພື່ອໃຊ້ການປົດລັອກດ້ວຍໜ້າ, ກະລຸນາເປີດໃຊ້ "<b>"ສິດເຂົ້າເຖິງກ້ອງຖ່າຍຮູບ"</b>" ໃນການຕັ້ງຄ່າ &gt; ຄວາມເປັນສ່ວນຕົວ"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"ຕັ້ງຄ່າວິທີເພີ່ມເຕີມເພື່ອປົດລັອກ"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"ແຕະເພື່ອເພີ່ມລາຍນິ້ວມື"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ປົດລັອກດ້ວຍລາຍນິ້ວມື"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"ກຳ​ລັງ​ກຽມ <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"ກຳລັງເປີດແອັບຯ."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"ກຳລັງສຳເລັດການເປີດລະບົບ."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"ປິດໜ້າຈໍໄວ້ບໍ?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"ໃນລະຫວ່າງການຕັ້ງຄ່າລາຍນິ້ວມືຂອງທ່ານ, ທ່ານກົດປຸ່ມເປີດປິດ.\n\nໂດຍປົກກະຕິນີ້ຈະເປັນການປິດໜ້າຈໍຂອງທ່ານ."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"ປິດໄວ້"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"ຍົກເລີກ"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"ສືບຕໍ່ການຕັ້ງຄ່າບໍ?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"ທ່ານກົດປຸ່ມເປີດປິດ, ປົກກະຕິນີ້ຈະປິດໜ້າຈໍ.\n\nລອງແຕະຄ່ອຍໆໃນລະຫວ່າງຕັ້ງຄ່າລາຍນິ້ວມືຂອງທ່ານ."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"ປິດໜ້າຈໍ"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"ສືບຕໍ່ການຕັ້ງຄ່າ"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"ສືບຕໍ່ການຢັ້ງຢືນລາຍນິ້ວມືຂອງທ່ານບໍ?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"ທ່ານກົດປຸ່ມເປີດປິດ, ປົກກະຕິນີ້ຈະເປັນການປິດໜ້າຈໍ.\n\nໃຫ້ລອງແຕະຄ່ອຍໆເພື່ອຢັ້ງຢືນລາຍນິ້ວມືຂອງທ່ານ."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"ປິດໜ້າຈໍ"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"ສືບຕໍ່"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> ກຳລັງເຮັດວຽກ"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Tap to return to game"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Choose game"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 180e1022c8ed..70f0f424cb7a 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -477,6 +477,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Programai leidžiama naudoti IMS paslaugą, kad būtų galima atlikti skambučius be jūsų įsikišimo."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"skaityti telefono būseną ir tapatybę"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Leidžiama programai pasiekti telefono funkcijas įrenginyje. Šis leidimas suteikia teisę programai nustatyti telefono numerį ir įrenginio ID, tai, ar skambutis aktyvus, ir skambučiu prijungtą nuotolinį numerį."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"skaityti pagrindinę telefonijos būsenos ir tapatybės informaciją"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Programai leidžiama pasiekti pagrindines įrenginio telefonijos funkcijas."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"nukreipti skambučius per sistemą"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Programai leidžiama nukreipti jos skambučius per sistemą siekiant pagerinti skambinimo paslaugas."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"matyti ir valdyti skambučius per sistemą."</string>
@@ -628,6 +630,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Palieskite, kad ištrintumėte veido modelį, tada iš naujo pridėkite veidą"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Atrakinimo pagal veidą nustatymas"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Atrakinkite telefoną pažiūrėję į jį"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Jei norite naudoti atrakinimą pagal veidą, įjunkite parinktį "<b>"Prieiga prie fotoaparato"</b>" skiltyje „Nustatymai“ &gt; „Privatumas“"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Daugiau atrakinimo metodų nustatymas"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Palieskite, kad pridėtumėte kontrolinį kodą"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Atrakinimas kontroliniu kodu"</string>
@@ -1316,10 +1319,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Ruošiama „<xliff:g id="APPNAME">%1$s</xliff:g>“."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Paleidžiamos programos."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Užbaigiamas paleidimas."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Išjungti ekraną?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Nustatydami kontrolinį kodą paspaudėte maitinimo mygtuką.\n\nĮprastai juo išjungiamas ekranas."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Išjungti"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Atšaukti"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Tęsti sąranką?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Paspaudėte maitinimo mygtuką, taip paprastai išjungiamas ekranas.\n\nNustatydami kontrolinį kodą, pabandykite jį švelniai paliesti."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Išjungti ekraną"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Tęsti sąranką"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Tęsti kontrolinio kodo patvirtinimą?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Paspaudėte maitinimo mygtuką, taip paprastai išjungiamas ekranas.\n\nNorėdami patvirtinti kontrolinį kodą, pabandykite jį švelniai paliesti."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Išjungti ekraną"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Tęsti"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"Vykdoma „<xliff:g id="APP">%1$s</xliff:g>“"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Palieskite, kad grįžtumėte į žaidimą"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Pasirinkite žaidimą"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 8f3e989a31ce..1df77747f878 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -221,7 +221,7 @@
<string name="turn_on_radio" msgid="2961717788170634233">"Ieslēgt bezvadu tīklu"</string>
<string name="turn_off_radio" msgid="7222573978109933360">"Izslēgt bezvadu tīklu"</string>
<string name="screen_lock" msgid="2072642720826409809">"Bloķēt ekrānu"</string>
- <string name="power_off" msgid="4111692782492232778">"Strāvas padeve ir izslēgta."</string>
+ <string name="power_off" msgid="4111692782492232778">"Izslēgt tālruni"</string>
<string name="silent_mode_silent" msgid="5079789070221150912">"Zvanītājs izslēgts"</string>
<string name="silent_mode_vibrate" msgid="8821830448369552678">"Zvanītājs vibrācijas režīmā"</string>
<string name="silent_mode_ring" msgid="6039011004781526678">"Zvanītājs ieslēgts"</string>
@@ -245,7 +245,7 @@
<string name="global_actions" product="tv" msgid="3871763739487450369">"Android TV opcijas"</string>
<string name="global_actions" product="default" msgid="6410072189971495460">"Tālruņa opcijas"</string>
<string name="global_action_lock" msgid="6949357274257655383">"Ekrāna bloķētājs"</string>
- <string name="global_action_power_off" msgid="4404936470711393203">"Strāvas padeve izslēgta."</string>
+ <string name="global_action_power_off" msgid="4404936470711393203">"Izslēgt strāvas padevi"</string>
<string name="global_action_power_options" msgid="1185286119330160073">"Barošana"</string>
<string name="global_action_restart" msgid="4678451019561687074">"Restartēt"</string>
<string name="global_action_emergency" msgid="1387617624177105088">"Ārkārtas situācija"</string>
@@ -474,6 +474,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Ļauj lietotnei izmantot tūlītējās ziņojumapmaiņas pakalpojumu, lai veiktu zvanus bez jūsu ziņas."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"lasīt tālruņa statusu un identitāti"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Ļauj lietotnei piekļūt ierīces tālruņa funkcijām. Ar šo atļauju lietotne var noteikt tālruņa numuru un ierīču ID, zvana statusu un attālo numuru, ar ko ir izveidots savienojums, veicot zvanu."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"lasīt telefonijas statusa un identitātes pamatinformāciju"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Lietotnei tiek atļauta piekļuve telefonijas pamatfunkcijām ierīcē."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"maršrutēt zvanus sistēmā"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Ļauj lietotnei maršrutēt tās zvanus sistēmā, lai uzlabotu zvanīšanas pieredzi."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"skatīt un kontrolēt zvanus sistēmā."</string>
@@ -625,6 +627,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Pieskarieties, lai izdzēstu sejas modeli, un pēc tam vēlreiz pievienojiet seju"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Autorizācijas pēc sejas iestatīšana"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Atbloķējiet tālruni, skatoties uz to"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Lai izmantotu autorizāciju pēc sejas, sadaļā Iestatījumi &gt; Konfidencialitāte ieslēdziet opciju "<b>"Piekļuve kamerai"</b>"."</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Citi atbloķēšanas veidi"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Pieskarieties, lai pievienotu pirksta nospiedumu"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Autorizācija ar pirksta nospiedumu"</string>
@@ -1296,10 +1299,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Notiek lietotnes <xliff:g id="APPNAME">%1$s</xliff:g> sagatavošana."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Notiek lietotņu palaišana."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Tiek pabeigta sāknēšana."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Vai izslēgt ekrānu?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Iestatot pirksta nospiedumu, jūs nospiedāt barošanas pogu.\n\nTādējādi parasti tiek izslēgts ekrāns."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Izslēgt"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Atcelt"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Vai turpināt iestatīšanu?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Jūs nospiedāt barošanas pogu — tādējādi parasti tiek izslēgts ekrāns.\n\nMēģiniet viegli pieskarties, iestatot pirksta nospiedumu."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Izslēgt ekrānu"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Turpināt iestatīšanu"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Vai apstiprināt pirksta nospiedumu?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Jūs nospiedāt barošanas pogu — tādējādi parasti tiek izslēgts ekrāns.\n\nMēģiniet viegli pieskarties, lai apstiprinātu pirksta nospiedumu."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Izslēgt ekrānu"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Turpināt"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> darbojas"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Pieskarieties, lai atgrieztos spēlē"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Spēles izvēlēšanās"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 9717112d2b81..cc86005f4698 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Дозволува апликацијата да ја користи услугата IMS за повици без ваша интервенција."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"прочитај ги статусот и идентитетот на телефонот"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Овозможува апликацијата да пристапи кон карактеристиките на телефонот на уредот. Оваа дозвола овозможува апликацијата да ги утврди телефонскиот број и ID на уредот, дали повикот е активен и далечинскиот број поврзан со повикот."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"читање основен телефонски статус и идентитет"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Дозволете апликацијава да пристапи до основните телефонски функции на уредот."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"пренасочи повици преку системот"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Дозволете ѝ на апликацијата да ги пренасочи повиците преку системот за да го подобри искуството при јавувањето."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"гледање и контролирање повици преку системот."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Допрете за да го избришете вашиот модел на лице, а потоа повторно додајте го лицето"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Поставете „Отклучување со лик“"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Отклучете го телефонот со гледање во него"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"За да користите „Отклучување со лик“, вклучете "<b>"Пристап до камерата"</b>" во „Поставки &gt; Приватност“"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Поставете уште начини за отклучување"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Допрете за да додадете отпечаток"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Отклучување со отпечаток на прст"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Се подготвува <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Се стартуваат апликациите."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Подигањето завршува."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Да се исклучи екранот?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Додека го поставувавте вашиот отпечаток, го притиснавте копчето за вклучување.\n\nОва обично го исклучува екранот."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Исклучи"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Откажи"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Дали да продолжи поставувањето?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Го притиснавте копчето за вклучување — така обично се исклучува екранот.\n\nДопрете лесно додека го поставувате отпечатокот."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Исклучи го екранот"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Продолжи"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Да продолжи потврдувањето на отпечаток?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Го притиснавте копчето за вклучување — така обично се исклучува екранот.\n\nДопрете лесно за да го потврдите отпечатокот."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Исклучи го екранот"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Продолжи"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> работи"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Допрете за да се вратите во играта"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Избор на игра"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 12eb2a2e0ddd..251770422101 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"നിങ്ങളുടെ ഇടപെടൽ ഇല്ലാതെ കോളുകൾ ചെയ്യാൻ IMS സേവനം ഉപയോഗിക്കുന്നതിന് ആപ്പിനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"ഫോൺ നിലയും ഐഡന്റിറ്റിയും റീഡുചെയ്യുക"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"ഉപകരണത്തിന്റെ ഫോൺ സവിശേഷതകൾ ആക്‌സസ്സുചെയ്യാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഈ അനുമതി ഫോൺ നമ്പർ, ഉപകരണ ഐഡികൾ, ഒരു കോൾ സജീവമാണോയെന്നത്, ഒരു കോൾ കണക്റ്റുചെയ്‌ത വിദൂര നമ്പർ എന്നിവ നിർണ്ണയിക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"ടെലിഫോണിന്റെ അടിസ്ഥാന നിലയും ഐഡന്റിറ്റിയും വായിക്കാൻ"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"ഉപകരണത്തിലെ ടെലിഫോൺ സംബന്ധമായ അടിസ്ഥാന ഫീച്ചറുകൾ ആക്‌സസ് ചെയ്യാൻ ആപ്പിനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"കോളുകൾ സിസ്റ്റത്തിലൂടെ വിടുക"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"കോളിംഗ് അനുഭവം ‌മെച്ചപ്പെടുത്തുന്നതിനായി തങ്ങളുടെ ‌കോളുകൾ സിസ്റ്റത്തിലേയ്ക്ക് വഴിതിരിച്ചുവിടാൻ ആപ്പുകളെ അനുവദിക്കുന്നു."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"സിസ്‌റ്റത്തിലൂടെ കോളുകൾ കാണുകയും നിയന്ത്രിക്കുകയും ചെയ്യുക."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"നിങ്ങളുടെ മുഖ മോഡൽ ഇല്ലാതാക്കാൻ ടാപ്പ് ചെയ്യുക, തുടർന്ന് അത് വീണ്ടും ചേർക്കുക"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"ഫെയ്‌സ് അൺലോക്ക് സജ്ജീകരിക്കുക"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ഫോണിലേക്ക് നോക്കി അത് അൺലോക്ക് ചെയ്യുക"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"ഫെയ്‌സ് അൺലോക്ക് ഉപയോഗിക്കാൻ, ക്രമീകരണം &gt; സ്വകാര്യത എന്നതിൽ "<b>"ക്യാമറാ ആക്‌സസ്"</b>" ഓണാക്കുക"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"അൺലോക്ക് ചെയ്യുന്നതിനുള്ള കൂടുതൽ വഴികൾ സജ്ജീകരിക്കുക"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"ഫിംഗർപ്രിന്റ് ചേർക്കാൻ ടാപ്പ് ചെയ്യുക"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ഫിംഗർപ്രിന്റ് അൺലോക്ക്"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> തയ്യാറാക്കുന്നു."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"അപ്ലിക്കേഷനുകൾ ആരംഭിക്കുന്നു."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"ബൂട്ട് ചെയ്യൽ പൂർത്തിയാകുന്നു."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"സ്‌ക്രീൻ ഓഫാക്കണോ?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"ഫിംഗർപ്രിന്റ് സജ്ജീകരിച്ച് കൊണ്ടിരുന്നപ്പോൾ നിങ്ങൾ പവർ ബട്ടൺ അമർത്തി.\n\nഇത് സാധാരണയായി സ്ക്രീൻ ഓഫാകുന്നതിന് കാരണമാകും."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"ഓഫാക്കുക"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"റദ്ദാക്കുക"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"സജ്ജീകരണം തുടരണോ?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"നിങ്ങൾ പവർ ബട്ടൺ അമർത്തി — സാധാരണയായി ഇത് സ്ക്രീൻ ഓഫാകുന്നതിന് കാരണമാകും.\n\nനിങ്ങളുടെ ഫിംഗർപ്രിന്റ് സജ്ജീകരിക്കുമ്പോൾ മൃദുവായി ടാപ്പ് ചെയ്ത് നോക്കുക."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"സ്ക്രീൻ ഓഫാക്കുക"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"സജ്ജീകരണം തുടരുക"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"ഫിംഗർപ്രിന്റ് പരിശോധിച്ചുറപ്പിക്കൽ തുടരണോ?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"നിങ്ങൾ പവർ ബട്ടൺ അമർത്തി — സാധാരണയായി ഇത് സ്ക്രീൻ ഓഫാകുന്നതിന് കാരണമാകും.\n\nനിങ്ങളുടെ ഫിംഗർപ്രിന്റ് പരിശോധിച്ചുറപ്പിക്കാൻ മൃദുവായി ടാപ്പ് ചെയ്ത് നോക്കുക."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"സ്ക്രീൻ ഓഫാക്കുക"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"തുടരുക"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> പ്രവർത്തിക്കുന്നു"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"ഗെയിമിലേക്ക് മടങ്ങാൻ ടാപ്പ് ചെയ്യുക"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"ഗെയിം തിരഞ്ഞെടുക്കുക"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 68fdb072cfea..6be8243e8347 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Апп нь дуудлага хийхдээ таны оролцоогүйгээр IMS үйлчилгээг ашиглах боломжтой."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"утасны статус ба таниулбарыг унших"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Апп нь төхөөрөмжийн утасны функцд хандах боломжтой. Энэ зөвшөөрөл нь апп-д утасны дугаар болон төхөөрөмжийн ID-г, дуудлага идэвхтэй эсэх, холын дугаар дуудлагаар холбогдсон байгаа эсэхийг тогтоох боломжийг олгоно,"</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"утасны үндсэн статус болон таних мэдээллийг уншуулна уу"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Аппад төхөөрөмжийн утасны үндсэн онцлогт хандах боломжийг олгоно."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"системээр дамжуулах дуудлага"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Дуудлагыг сайжруулахын тулд дуудлагаа системээр дамжуулах зөвшөөрлийг апп-д олгодог."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"систем дэх дуудлагыг харах болон хянах."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Нүүрний загвараа устгахын тулд товшоод, дараа нь царайгаа дахин нэмнэ үү"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Царайгаар түгжээ тайлахыг тохируулах"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Утас руугаа харж түгжээг нь тайлна уу"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Царайгаар түгжээ тайлахыг ашиглахын тулд Тохиргоо &gt; Нууцлал хэсэгт "<b>" Камерын хандалтыг "</b>" асаана уу"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Түгжээ тайлах илүү олон арга тохируулна уу"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Хурууны хээ нэмэхийн тулд товшино уу"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Хурууны хээгээр түгжээ тайлах"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Бэлдэж байна <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Апп-г эхлүүлж байна."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Эхлэлийг дуусгаж байна."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Дэлгэцийг унтраах уу?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Та хурууны хээгээ тохируулж байх үед Асаах/унтраах товчийг дарсан байна.\n\nЭнэ нь ихэвчлэн таны дэлгэцийг унтраана."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Унтраах"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Цуцлах"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Тохируулгыг үргэлжлүүлэх үү?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Та асаах/унтраах товчийг дарсан байна — энэ нь ихэвчлэн дэлгэцийг унтраадаг.\n\nХурууны хээгээ тохируулж байх үедээ зөөлөн товшиж үзнэ үү."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Дэлгэцийг унтраах"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Тохируулгыг үргэлжлүүлэх"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Хурууны хээгээ үргэлжлүүлэн баталгаажуулах уу?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Та асаах/унтраах товчийг дарсан байна — энэ нь ихэвчлэн дэлгэцийг унтраадаг.\n\nХурууны хээгээ баталгаажуулахын тулд зөөлөн товшиж үзнэ үү."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Дэлгэцийг унтраах"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Үргэлжлүүлэх"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> ажиллаж байна"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Тоглоом руу буцахын тулд товших"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Тоглоом сонгох"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index bf9143d87ec0..69279ee78263 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"आपल्‍या हस्तक्षेपाशिवाय अ‍ॅपला कॉल करण्‍यासाठी IMS सेवा वापरण्याची अनुमती देते."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"फोन स्थिती आणि ओळख वाचा"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"डिव्हाइस च्या फोन वैशिष्ट्यांवर अ‍ॅक्सेस करण्यास ॲपला अनुमती देते. ही परवानगी कॉल ॲक्टिव्हेट असला किंवा नसला तरीही, फोन नंबर आणि डिव्हाइस आयडी आणि कॉलद्वारे कनेक्ट केलेला रिमोट नंबर निर्धारित करण्यासाठी ॲपला अनुमती देते."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"टेलिफोनी सुविधांशी संबंधित मूलभूत स्टेटस आणि ओळखीशी संबंधित माहिती वाचा"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"यामुळे ॲपला डिव्हाइसची मूलभूत टेलिफोनी वैशिष्ट्ये अ‍ॅक्सेस करण्याची अनुमती मिळते."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"प्रणालीच्या माध्यमातून कॉल रूट करा"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"कॉल करण्याचा अनुभव सुधारण्यासाठी ॲपला त्याचे कॉल प्रणालीच्या माध्यमातून रूट करू देते."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"सिस्टम वापरून कॉल पहा आणि नियंत्रण ठेवा."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"फेस मॉडेल हटवण्यासाठी टॅप करा, त्यानंतर तुमचा चेहरा पुन्हा जोडा"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"फेस अनलॉक सेट करा"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"तुमच्या फोनकडे पाहून तो अनलॉक करा"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"फेस अनलॉक वापरण्यासाठी, सेटिंग्ज &gt; गोपनीयता येथे "<b>"कॅमेरा अ‍ॅक्सेस"</b>" सुरू करा"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"अनलॉक करण्याच्या आणखी पद्धती सेट करा"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"फिंगरप्रिंट जोडण्यासाठी टॅप करा"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"फिंगरप्रिंट अनलॉक"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> तयार करत आहे."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"अ‍ॅप्स सुरू करत आहे."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"बूट समाप्त होत आहे."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"स्क्रीन बंद करायची आहे का?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"तुम्ही तुमची फिंगरप्रिंट सेट करत असताना पॉवर बटण दाबले.\n\nयामुळे सहसा तुमची स्क्रीन बंद होते."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"बंद करा"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"रद्द करा"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"सेट करणे पुढे सुरू ठेवायचे का?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"तुम्ही पॉवर बटण दाबले — हे सहसा स्क्रीन बंद करते.\n\nतुमचे फिंगरप्रिंट सेट करताना हलके टॅप करून पहा."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"स्क्रीन बंद करा"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"सेट करणे सुरू ठेवा"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"फिंगरप्रिंट पडताळणी सुरू ठेवायची का?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"तुम्ही पॉवर बटण दाबले — हे सहसा स्क्रीन बंद करते.\n\nतुमच्या फिंगरप्रिंटची पडताळणी करण्यासाठी हलके टॅप करून पहा."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"स्क्रीन बंद करा"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"पुढे सुरू ठेवा"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"रन होणारे <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"गेमवर परत जाण्यासाठी टॅप करा"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"गेम निवडा"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 05395c91fea7..27aac1ac2a74 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Membenarkan apl menggunakan perkhidmatan IMS untuk membuat panggilan tanpa campur tangan anda."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"baca status dan identiti telefon"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Membenarkan apl mengakses ciri telefon pada peranti. Kebenaran ini membolehkan apl menentukan nombor telefon dan ID peranti, sama ada panggilan aktif dan nombor jauh yang dihubungkan dengan panggilan."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"membaca status telefoni asas dan identiti"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Membenarkan apl mengakses ciri telefoni asas peranti."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"halakan panggilan menerusi sistem"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Membenarkan apl menghalakan panggilan menerusi sistem untuk meningkatkan pengalaman panggilan."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"lihat dan kawal panggilan melalui sistem."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Ketik untuk memadamkan model wajah anda, kemudian tambahkan wajah anda semula"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Sediakan Buka Kunci Wajah"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Buka kunci telefon anda dengan melihat telefon anda"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Untuk menggunakan Buka Kunci Wajah, hidupkan "<b>"akses Kamera"</b>" dalam Tetapan &gt; Privasi"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Sediakan lebih banyak cara untuk membuka kunci"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Ketik untuk menambahkan cap jari"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Buka Kunci Cap Jari"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Menyediakan <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Memulakan apl."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"But akhir."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Matikan skrin?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Semasa menyediakan cap jari anda, anda menekan butang Kuasa.\n\nTindakan ini biasanya mematikan skrin anda."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Matikan"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Batal"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Teruskan persediaan?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Anda menekan butang kuasa — tindakan ini biasanya mematikan skrin.\n\nCuba ketik dengan perlahan semasa menetapkan cap jari anda."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Matikan skrin"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Teruskan persediaan"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Terus mengesahkan cap jari anda?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Anda menekan butang kuasa — tindakan ini biasanya mematikan skrin.\n\nCuba ketik dengan perlahan untuk mengesahkan cap jari anda."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Matikan skrin"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Teruskan"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> dijalankan"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Ketik untuk kembali ke permainan"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Pilih permainan"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 4f97ef9b4da0..eefbe153d6ba 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"သင့်ရဲ့ဝင်ရောက်စွက်ဖက်မှုမပါဘဲ IMS ဝန်ဆောင်မှုကိုအသုံးပြုပြီး ဖုန်းခေါ်ဆိုနိုင်ရန် အပ်ဖ်ကို ခွင့်ပြုထားပါ။"</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"ဖုန်းရဲ့ အခြေအနေ နှင့် အမှတ်သညာအား ဖတ်ခြင်း"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"အပလီကေးရှင်းအား ဖုန်းရဲ့ စွမ်းဆောင်ချက်များအား သုံးခွင့်ပြုပါ။ အပလီကေးရှင်းအနေဖြင့် ဖုန်းနံပါတ်၊ စက်နံပါတ်၊ ဖုန်းခေါ်နေမှု ရှိမရှိနှင့် တဖက်မှ ဖုန်းနံပါတ် များအား သိရှိနိုင်ပါသည်"</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"အခြေခံတယ်လီဖုန်းအခြေအနေနှင့် အထောက်အထားကို ဖတ်ခြင်း"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"စက်၏ အခြေခံတယ်လီဖုန်းဝန်ဆောင်မှုများသုံးရန် အက်ပ်ကို ခွင့်ပြုသည်။"</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"ခေါ်ဆိုမှုများကို စနစ်မှတစ်ဆင့် ဖြတ်သန်းခွင့်ပြုပါ"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"ခေါ်ဆိုမှု အတွေ့အကြုံ ပိုမိုကောင်းမွန်လာစေရန်အတွက် အက်ပ်၏ ခေါ်ဆိုမှုအား စနစ်မှတစ်ဆင့် ဖြတ်သန်းရန် ခွင့်ပြုပါသည်။"</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"စနစ်မှတစ်ဆင့် ခေါ်ဆိုမှုများကို ကြည့်ရှုထိန်းချုပ်ပါ။"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"သင်၏မျက်နှာနမူနာကို ဖျက်ရန် တို့ပါ။ ထို့နောက် သင့်မျက်နှာကို ထပ်ထည့်ပါ"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"မျက်နှာပြ လော့ခ်ဖွင့်ခြင်းကို ထည့်သွင်းပါ"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"သင့်ဖုန်းကိုကြည့်၍ သော့ဖွင့်ပါ"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"မျက်နှာပြ လော့ခ်ဖွင့်ခြင်းကို သုံးရန် "<b>"ကင်မရာ သုံးခွင့်"</b>" ကို ‘ဆက်တင်များ &gt; ကန့်သတ်ဆက်တင်’ တွင်ဖွင့်ပါ"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"သော့ဖွင့်ရန် နောက်ထပ်နည်းလမ်းများကို စနစ်ထည့်သွင်းပါ"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"လက်ဗွေထည့်ရန် တို့ပါ"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"လက်ဗွေသုံး လော့ခ်ဖွင့်ခြင်း"</string>
@@ -1172,8 +1175,8 @@
<string name="delete" msgid="1514113991712129054">"ဖျက်ရန်"</string>
<string name="copyUrl" msgid="6229645005987260230">"URLအား ကူးခြင်း"</string>
<string name="selectTextMode" msgid="3225108910999318778">"စာသား ရွေးရန်"</string>
- <string name="undo" msgid="3175318090002654673">"တစ်ဆင့်နောက်ပြန်ရန်"</string>
- <string name="redo" msgid="7231448494008532233">"ထပ်လုပ်ပါ"</string>
+ <string name="undo" msgid="3175318090002654673">"နောက်ပြန်ရန်"</string>
+ <string name="redo" msgid="7231448494008532233">"ပြန်လုပ်ရန်"</string>
<string name="autofill" msgid="511224882647795296">"အော်တိုဖြည့်"</string>
<string name="textSelectionCABTitle" msgid="5151441579532476940">"စာတိုရွေးချယ်မှု"</string>
<string name="addToDictionary" msgid="8041821113480950096">"အဘိဓာန်ထဲ ထည့်ပါ"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> အားပြင်ဆင်နေသည်။"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"အက်ပ်များကို စတင်နေ"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"လုပ်ငန်းစနစ်ထည့်သွင်း၍ ပြန်လည်စတင်ရန် ပြီးပါပြီ"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"ဖန်သားပြင်ကို ပိတ်မလား။"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"သင့်လက်ဗွေကို စနစ်ထည့်သွင်းနေစဉ် ဖွင့်ပိတ်ခလုတ်ကို ဖိထားပါ။\n\n၎င်းက အများအားဖြင့် သင့်ဖန်သားပြင်ကို ပိတ်ပါသည်။"</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"ပိတ်ရန်"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"မလုပ်တော့"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"ဆက်လက်၍ စနစ်ထည့်သွင်းလိုပါသလား။"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"ဖွင့်ပိတ်ခလုတ်ကို သင်နှိပ်ခဲ့သည် — ၎င်းက ပုံမှန်အားဖြင့် စခရင်ကို ပိတ်စေသည်။\n\nသင့်လက်ဗွေကို ထည့်သွင်းသောအခါ ဖွဖွတို့ကြည့်ပါ။"</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"စခရင် ပိတ်ရန်"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"စနစ်ဆက်ထည့်သွင်းရန်"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"သင့်လက်ဗွေကို ဆက်၍ အတည်ပြုမလား။"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"ဖွင့်ပိတ်ခလုတ်ကို သင်နှိပ်ခဲ့သည် — ၎င်းက ပုံမှန်အားဖြင့် စခရင်ကို ပိတ်စေသည်။\n\nသင့်လက်ဗွေကို အတည်ပြုရန် ဖွဖွတို့ကြည့်ပါ။"</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"စခရင် ပိတ်ရန်"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"ရှေ့ဆက်ရန်"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> က အလုပ်လုပ်နေသည်"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"ဂိမ်းသို့ ပြန်သွားရန် တို့ပါ"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"ဂိမ်းကို ရွေးခြင်း"</string>
@@ -1549,7 +1556,7 @@
<string name="sync_too_many_deletes" msgid="6999440774578705300">"ပယ်ဖျက်မည့်ကန့်သတ်နှုန်းကျော်လွန်သည်"</string>
<string name="sync_too_many_deletes_desc" msgid="7409327940303504440">"<xliff:g id="TYPE_OF_SYNC">%2$s</xliff:g>၊ account <xliff:g id="ACCOUNT_NAME">%3$s</xliff:g> အတွက် စုစုပေါင်း <xliff:g id="NUMBER_OF_DELETED_ITEMS">%1$d</xliff:g> အရာဖျက်ထားပါသည်။ သင်ဘာလုပ်ချင်ပါလဲ?"</string>
<string name="sync_really_delete" msgid="5657871730315579051">"ဤအရာများကိုဖျက်ပါ"</string>
- <string name="sync_undo_deletes" msgid="5786033331266418896">"ဖျက်ပီးသည်များကို ပယ်ဖျက်ရန်"</string>
+ <string name="sync_undo_deletes" msgid="5786033331266418896">"အားလုံးကို မဖျက်တော့ရန်"</string>
<string name="sync_do_nothing" msgid="4528734662446469646">"လက်ရှိ ဘာမှမလုပ်ရန်"</string>
<string name="choose_account_label" msgid="5557833752759831548">"အကောင့် တစ်ခု ရွေးပါ"</string>
<string name="add_account_label" msgid="4067610644298737417">"အကောင့်တစ်ခုထည့်ရန်"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index ccf5e97ddfde..4d8d2b0534e4 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Lar appen bruke nettprattjenesten til å ringe uten at du gjør noe."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"lese telefonstatus og -identitet"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Lar appen bruke enhetens telefonfunksjoner. Med denne tillatelsen kan appen finne telefonnummer og enhets-ID-er, registrere om en samtale pågår, og se det eksterne nummeret det opprettes en forbindelse med via oppringing."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"lese grunnleggende telefonstatus og identitet"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Gir appen tilgang til de grunnleggende telefonfunksjonene på enheten."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"send anrop gjennom systemet"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Lar appen sende anrop gjennom systemet for å forbedre anropsopplevelsen."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"se og kontrollere anrop i systemet."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Trykk for å slette ansiktsmodellen din, og legg deretter til ansiktet på nytt"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Konfigurer ansiktslås"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Lås opp telefonen ved å se på den"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"For å bruke ansiktslås, slå på "<b>"Kameratilgang"</b>" i Innstillinger &gt; Personvern"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Konfigurer flere måter å låse opp på"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Trykk for å legge til et fingeravtrykk"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Opplåsing med fingeravtrykk"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Forbereder <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Starter apper."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Ferdigstiller oppstart."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Vil du slå av skjermen?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Da du konfigurerte fingeravtrykket, trykket du på av/på-knappen.\n\nDette slår vanligvis av skjermen."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Slå av"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Avbryt"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Vil du fortsette konfigureringen?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Du har trykket på av/på-knappen – dette slår vanligvis av skjermen.\n\nPrøv å trykke lett mens du konfigurerer fingeravtrykket ditt."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Slå av skjermen"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Fortsett konfig."</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Fortsett bekreftelse av fingeravtrykket?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Du har trykket på av/på-knappen – dette slår vanligvis av skjermen.\n\nPrøv å trykke lett for å bekrefte fingeravtrykket ditt."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Slå av skjermen"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Fortsett"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> kjører"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Trykk for å gå tilbake til spillet"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Velg et spill"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 99e3375bac63..12c4464843eb 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"तपाईँको हस्तक्षेप बिना नै कल गर्न IMS सेवा प्रयोग गर्न एपलाई अनुमति दिन्छ।"</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"फोन स्थिति र पहिचान पढ्नुहोस्"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"उपकरणको फोन विशेषताहरूको पहुँच गर्न एपलाई अनुमति दिन्छ। यस अनुमतिले फोन नम्बर र उपकरणको IDs, कल सक्षम छ कि छैन र कलद्वारा जोडिएको टाढाको नम्बर निर्धारण गर्न अनुमति दिन्छ।"</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"टेलिफोन सेवाका आधारभूत स्थिति र त्यसको पहिचानसम्बन्धी जानकारी रिड गर्ने"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"तपाईंले यो अनुमति दिनुभयो भने यो एपले यस डिभाइसको टेलिफोन सेवाका आधारभूत सुविधाहरू प्रयोग गर्न सक्छ।"</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"प्रणाली मार्फत कल गर्न दिनुहोस्‌"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"कल गर्दाको अनुभवलाई सुधार्न यस एपलाई प्रणाली मार्फत कलहरू गर्न अनुमति दिन्छ।"</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"प्रणालीमार्फत कलहरू हेर्नुका साथै तिनीहरूलाई नियन्त्रण गर्नुहोस्‌।"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"आफ्नो फेस मोडेल मेटाउन ट्याप गर्नुहोस् अनि आफ्नो अनुहार फेरि दर्ता गर्नुहोस्"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"फेस अनलक सेटअप गर्नुहोस्"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"फोनमा हेरेकै भरमा फोन अनलक गर्नुहोस्"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"फेस अनलक प्रयोग गर्न \"सेटिङ तथा गोपनीयता\" मा गई "<b>"क्यामेरा प्रयोग गर्ने अनुमति"</b>" दिनुहोस्"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"अनलक गर्ने अन्य तरिकाहरू सेटअप गर्नुहोस्"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"फिंगरप्रिन्ट हाल्न ट्याप गर्नुहोस्"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"फिंगरप्रिन्ट अनलक"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> तयारी गर्दै।"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"सुरुवात एपहरू।"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"बुट पुरा हुँदै।"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"स्क्रिन अफ गर्ने हो?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"आफ्नो फिंगरप्रिन्ट सेटअप गर्दा तपाईंले पावर बटन थिच्नुभयो।\n\nयसो गर्दा सामान्यतया तपाईंको स्क्रिन अफ हुन्छ।"</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"अफ गर्नुहोस्"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"रद्द गर्नुहोस्"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"सेटअप गर्ने प्रक्रिया जारी राख्ने हो?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"तपाईंले पावर बटन थिच्नुभयो — सामान्यतया स्क्रिन अफ गर्न यो बटन थिच्ने गरिन्छ।\n\nफिंगरप्रिन्ट सेटअप भइन्जेल हल्का तरिकाले यो बटन ट्याप गर्नुहोस्।"</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"स्क्रिन अफ गर्नुहोस्"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"सेटअप जारी राख्नुस्"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"फिंगरप्रिन्ट पुष्टि गर्ने क्रम जारी राख्ने हो?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"तपाईंले पावर बटन थिच्नुभयो — सामान्यतया स्क्रिन अफ गर्न यो बटन थिच्ने गरिन्छ।\n\nतपाईं आफ्नो फिंगरप्रिन्ट पुष्टि गर्न चाहनुहुन्छ भने हल्का तरिकाले यो बटन ट्याप गरी हेर्नुहोस्।"</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"स्क्रिन अफ गर्नुहोस्"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"जारी राख्नुहोस्"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> चलिरहेको छ"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"खेलमा फर्कन ट्याप गर्नुहोस्"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"खेल छनौट गर्नुहोस्‌"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index a1b45bfc3521..858ca009df05 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Hiermee kan de app de IMS-service gebruiken om te bellen zonder je tussenkomst."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"telefoonstatus en -identiteit lezen"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Hiermee kan de app toegang krijgen tot de telefoonfuncties van het apparaat, Met deze rechten kan de app het telefoonnummer en de apparaat-ID\'s bepalen, of een gesprek actief is, en het andere telefoonnummer waarmee wordt gebeld."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"basistelefoonstatus en -identiteit lezen"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Hiermee geef je de app toegang tot de basistelefoonfuncties van het apparaat."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"gesprekken doorschakelen via het systeem"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Hiermee kan de app de bijbehorende gesprekken doorschakelen via het systeem om de belfunctionaliteit te verbeteren."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"gesprekken via het systeem bekijken en beheren"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tik om je gezichtsmodel te verwijderen en voeg je gezicht opnieuw toe"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Ontgrendelen via gezichtsherkenning instellen"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Ontgrendel je telefoon door ernaar te kijken"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Als je Ontgrendelen via gezichtsherkenning wilt gebruiken, zet je "<b>"Cameratoegang"</b>" aan via Instellingen &gt; Privacy"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Stel meer manieren in om te ontgrendelen"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tik om een vingerafdruk toe te voegen"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Ontgrendelen met vingerafdruk"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> voorbereiden."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Apps starten."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Opstarten afronden."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Scherm uitzetten?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Toen je je vingerafdruk instelde, heb je op de aan/uit-knop gedrukt.\n\nDaarmee wordt het scherm meestal uitgezet."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Uitzetten"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Annuleren"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Doorgaan met instellen?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Je hebt op de aan/uit-knop gedrukt. Zo zet je meestal het scherm uit.\n\nRaak de knop voorzichtig aan terwijl je je vingerafdruk instelt."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Scherm uitzetten"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Doorgaan met instellen"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Doorgaan met verificatie van je vingerafdruk?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Je hebt op de aan/uit-knop gedrukt. Zo zet je meestal het scherm uit.\n\nRaak de knop voorzichtig aan om je vingerafdruk te verifiëren."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Scherm uitzetten"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Doorgaan"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> wordt uitgevoerd"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Tik om terug te keren naar de game"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Game kiezen"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index afe04419927e..52015c29027f 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"ଆପଣଙ୍କ ହସ୍ତକ୍ଷେପ ବିନା କଲ୍‍ କରିପାରିବା ପାଇଁ ଆପ୍‌କୁ IMS ସେବା ବ୍ୟବହାର କରିବାକୁ ଦିଏ।"</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"ଫୋନ୍‍ ସ୍ଥିତି ଓ ପରିଚୟ ପଢ଼ନ୍ତୁ"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"ଆପ୍‌କୁ ଡିଭାଇସ୍‌ର ଫୋନ୍‌ ବୈଶିଷ୍ଟ୍ୟ ଆକ୍ସେସ୍‍ କରିବାକୁ ଅନୁମତି ଦେଇଥାଏ। ଏହି ଅନୁମତି ଆପ୍‌କୁ ଫୋନ୍‌ ନମ୍ବର୍ ଓ ଡିଭାଇସ୍‌ IDଗୁଡ଼ିକୁ ନିର୍ଦ୍ଧାରଣ କରିବାକୁ ଅନୁମତି ଦେଇଥାଏ, କଲ୍ ସକ୍ରିୟ ଥିଲେ ବି ଏବଂ କଲ୍ ଦ୍ୱାରା ସଂଯୋଗ ଥିବା ରିମୋଟ୍‌ ନମ୍ବର୍‌କୁ ମଧ୍ୟ।"</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"ବେସିକ ଟେଲିଫୋନି ସ୍ଥିତି ଏବଂ ପରିଚୟ ପଢ଼ନ୍ତୁ"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"ଡିଭାଇସର ବେସିକ ଟେଲିଫୋନି ଫିଚରଗୁଡ଼ିକୁ ଆକ୍ସେସ କରିବା ପାଇଁ ଏହା ଆପକୁ ଅନୁମତି ଦିଏ।"</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"ସିଷ୍ଟମ୍‍ ଜରିଆରେ କଲ୍‌ର ମାର୍ଗ ବଦଳାଇପାରେ"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"କଲ୍‍ କରିବାର ଅନୁଭୂତି ବଢ଼ାଇବାକୁ ସିଷ୍ଟମ୍‍ ଜରିଆରେ ଆପର କଲ୍‍ଗୁଡ଼ିକୁ ରୁଟ୍‍ କରିବାକୁ ଏହାକୁ ଅନୁମତି ଦେଇଥାଏ।"</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"ସିଷ୍ଟମ୍‍ ମାଧ୍ୟମରେ କଲ୍‍ଗୁଡ଼ିକୁ ଦେଖିଥାଏ ଏବଂ ନିୟନ୍ତ୍ରଣ କରିଥାଏ।"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"ଆପଣଙ୍କ ଫେସ୍ ମଡେଲକୁ ଡିଲିଟ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ, ତା\'ପରେ ପୁଣି ଆପଣଙ୍କ ଫେସ୍ ଯୋଗ କରନ୍ତୁ"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"ଫେସ୍ ଅନଲକ୍ ସେଟ୍ ଅପ୍ କରନ୍ତୁ"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ଫୋନକୁ ଦେଖି ଏହାକୁ ଅନଲକ୍ କରନ୍ତୁ"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"ଫେସ ଅନଲକ ବ୍ୟବହାର କରିବା ପାଇଁ, ସେଟିଂସ ଏବଂ ଗୋପନୀୟତାରେ "<b>"କ୍ୟାମେରା ଆକ୍ସେସ"</b>"କୁ ଚାଲୁ କରନ୍ତୁ"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"ଅନଲକ୍ କରିବା ପାଇଁ ଆହୁରି ଅଧିକ ଉପାୟ ସେଟ୍ ଅପ୍ କରନ୍ତୁ"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"ଏକ ଟିପଚିହ୍ନ ଯୋଗ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ଫିଙ୍ଗରପ୍ରିଣ୍ଟ ଅନଲକ୍"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> ପ୍ରସ୍ତୁତ କରାଯାଉଛି।"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"ଆପ୍‍ ଆରମ୍ଭ କରାଯାଉଛି।"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"ବୁଟ୍‍ ସମାପ୍ତ କରୁଛି।"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"ସ୍କ୍ରିନ୍ ବନ୍ଦ କରିବେ?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"ଆପଣଙ୍କ ଟିପଚିହ୍ନ ସେଟ୍ ଅପ୍ କରିବା ସମୟରେ, ଆପଣ ପାୱାର ବଟନ୍ ଦବାଇଛନ୍ତି।\n\nଏହା ସାଧାରଣତଃ ଆପଣଙ୍କ ସ୍କ୍ରିନକୁ ବନ୍ଦ କରେ।"</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"ବନ୍ଦ କରନ୍ତୁ"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"ବାତିଲ୍ କରନ୍ତୁ"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"ସେଟଅପ କରିବା ଜାରି ରଖିବେ?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"ଆପଣ ପାୱାର ବଟନ ଦବାଇଛନ୍ତି — ଏହା ସାଧାରଣତଃ ଆପଣଙ୍କ ସ୍କ୍ରିନକୁ ବନ୍ଦ କରିଥାଏ।\n\nଆପଣଙ୍କ ଟିପଚିହ୍ନ ସେଟ ଅପ କରିବା ସମୟରେ ଧୀରେ ଟାପ କରିବାକୁ ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"ସ୍କ୍ରିନ ବନ୍ଦ କରନ୍ତୁ"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"ସେଟଅପ କରିବା ଜାରି ରଖ"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"ଆପଣଙ୍କ ଟିପଚିହ୍ନ ଯାଞ୍ଚ କରିବା ଜାରି ରଖିବେ?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"ଆପଣ ପାୱାର ବଟନ ଦବାଇଛନ୍ତି — ଏହା ସାଧାରଣତଃ ଆପଣଙ୍କ ସ୍କ୍ରିନକୁ ବନ୍ଦ କରିଥାଏ।\n\nଆପଣଙ୍କ ଟିପଚିହ୍ନ ଯାଞ୍ଚ କରିବା ପାଇଁ ଧୀରେ ଟାପ କରିବାକୁ ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"ସ୍କ୍ରିନ ବନ୍ଦ କରନ୍ତୁ"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"ଜାରି ରଖନ୍ତୁ"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> ଚାଲୁଛି"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"ଗେମ୍‌କୁ ଫେରିଆସିବା ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"ଗେମ୍ ଚୟନ କରନ୍ତୁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 1877ebecf597..6826a2d73cd7 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"ਐਪ ਨੂੰ ਤੁਹਾਡੇ ਦਖ਼ਲ ਤੋਂ ਬਿਨਾਂ ਕਾਲਾਂ ਕਰਨ ਲਈ IMS ਸੇਵਾ ਵਰਤਣ ਦੀ ਆਗਿਆ ਦਿੰਦੀ ਹੈ।"</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"ਫ਼ੋਨ ਸਥਿਤੀ ਅਤੇ ਪਛਾਣ ਪੜ੍ਹੋ"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"ਐਪ ਨੂੰ ਡੀਵਾਈਸ ਦੀਆਂ ਫ਼ੋਨ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਤੱਕ ਪਹੁੰਚ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਹ ਇਜਾਜ਼ਤ ਐਪ ਨੂੰ ਫ਼ੋਨ ਨੰਬਰ ਅਤੇ ਡੀਵਾਈਸ ਆਈ.ਡੀ. ਨਿਰਧਾਰਤ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦੀ ਹੈ, ਇੱਕ ਕਾਲ ਕਿਰਿਆਸ਼ੀਲ ਹੈ ਜਾਂ ਨਹੀਂ ਅਤੇ ਰਿਮੋਟ ਨੰਬਰ ਇੱਕ ਕਾਲ ਨਾਲ ਕਨੈਕਟ ਹੈ ਜਾਂ ਨਹੀਂ।"</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"ਮੂਲ ਟੈਲੀਫ਼ੋਨੀ ਸਥਿਤੀ ਅਤੇ ਪਛਾਣ ਨੂੰ ਪੜ੍ਹੋ"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"ਐਪ ਨੂੰ ਡੀਵਾਈਸ \'ਤੇ ਮੂਲ ਟੈਲੀਫ਼ੋਨੀ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦਿੰਦੀ ਹੈ।"</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"ਸਿਸਟਮ ਰਾਹੀਂ ਕਾਲਾਂ ਰੂਟ ਕਰੋ"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"ਕਾਲ ਕਰਨ ਦੇ ਅਨੁਭਵ ਨੂੰ ਬਿਹਤਰ ਬਣਾਉਣ ਲਈ ਐਪ ਨੂੰ ਇਸਦੀਆਂ ਕਾਲਾਂ ਨੂੰ ਸਿਸਟਮ ਰਾਹੀਂ ਰੂਟ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿੰਦੀ ਹੈ।"</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"ਸਿਸਟਮ ਰਾਹੀਂ ਕਾਲਾਂ ਦੇਖੋ ਅਤੇ ਕੰਟਰੋਲ ਕਰੋ।"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"ਆਪਣੇ ਚਿਹਰੇ ਦਾ ਮਾਡਲ ਮਿਟਾਉਣ ਲਈ ਟੈਪ ਕਰੋ, ਫਿਰ ਆਪਣਾ ਚਿਹਰਾ ਦੁਬਾਰਾ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"ਫ਼ੇਸ ਅਣਲਾਕ ਦਾ ਸੈੱਟਅੱਪ ਕਰੋ"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ਆਪਣੇ ਫ਼ੋਨ ਵੱਲ ਦੇਖ ਕੇ ਇਸਨੂੰ ਅਣਲਾਕ ਕਰੋ"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"ਫ਼ੇਸ ਅਣਲਾਕ ਨੂੰ ਵਰਤਣ ਲਈ, ਸੈਟਿੰਗਾਂ &gt; ਪਰਦੇਦਾਰੀ ਵਿੱਚ ਜਾ ਕੇ "<b>"ਕੈਮਰਾ ਪਹੁੰਚ"</b>" ਨੂੰ ਚਾਲੂ ਕਰੋ"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"ਅਣਲਾਕ ਕਰਨ ਦੇ ਹੋਰ ਤਰੀਕਿਆਂ ਦਾ ਸੈੱਟਅੱਪ ਕਰੋ"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਸ਼ਾਮਲ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਅਣਲਾਕ"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> ਤਿਆਰ ਕਰ ਰਿਹਾ ਹੈ।"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"ਐਪਸ ਚਾਲੂ ਕਰ ਰਿਹਾ ਹੈ।"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"ਬੂਟ ਪੂਰਾ ਕਰ ਰਿਹਾ ਹੈ।"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"ਕੀ ਸਕ੍ਰੀਨ ਬੰਦ ਕਰਨੀ ਹੈ?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"ਆਪਣੇ ਫਿੰਗਰਪ੍ਰਿੰਟ ਦਾ ਸੈੱਟਅੱਪ ਕਰਨ ਵੇਲੇ, ਤੁਸੀਂ ਪਾਵਰ ਬਟਨ ਨੂੰ ਦਬਾਇਆ ਹੈ।\n\nਇਹ ਆਮ ਤੌਰ \'ਤੇ ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ ਨੂੰ ਬੰਦ ਕਰ ਦਿੰਦਾ ਹੈ।"</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"ਬੰਦ ਕਰੋ"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"ਰੱਦ ਕਰੋ"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"ਕੀ ਸੈੱਟਅੱਪ ਜਾਰੀ ਰੱਖਣਾ ਹੈ?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"ਤੁਸੀਂ ਪਾਵਰ ਬਟਨ ਨੂੰ ਦਬਾਇਆ ਹੈ — ਇਹ ਆਮ ਤੌਰ \'ਤੇ ਸਕ੍ਰੀਨ ਨੂੰ ਬੰਦ ਕਰ ਦਿੰਦਾ ਹੈ।\n\nਆਪਣੇ ਫਿੰਗਰਪ੍ਰਿੰਟ ਦਾ ਸੈੱਟਅੱਪ ਕਰਦੇ ਸਮੇਂ ਹਲਕਾ ਜਿਹਾ ਟੈਪ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"ਸਕ੍ਰੀਨ ਬੰਦ ਕਰੋ"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"ਸੈੱਟਅੱਪ ਜਾਰੀ ਰੱਖੋ"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"ਕੀ ਆਪਣੇ ਫਿੰਗਰਪ੍ਰਿੰਟ ਦੀ ਪੁਸ਼ਟੀ ਕਰਨਾ ਜਾਰੀ ਰੱਖਣਾ ਹੈ?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"ਤੁਸੀਂ ਪਾਵਰ ਬਟਨ ਨੂੰ ਦਬਾਇਆ ਹੈ — ਇਹ ਆਮ ਤੌਰ \'ਤੇ ਸਕ੍ਰੀਨ ਨੂੰ ਬੰਦ ਕਰ ਦਿੰਦਾ ਹੈ।\n\nਆਪਣੇ ਫਿੰਗਰਪ੍ਰਿੰਟ ਦੀ ਪੁਸ਼ਟੀ ਕਰਨ ਲਈ ਹਲਕਾ ਜਿਹਾ ਟੈਪ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"ਸਕ੍ਰੀਨ ਬੰਦ ਕਰੋ"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"ਜਾਰੀ ਰੱਖੋ"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> ਚੱਲ ਰਿਹਾ ਹੈ"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"ਗੇਮ \'ਤੇ ਵਾਪਸ ਜਾਣ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"ਗੇਮ ਚੁਣੋ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 6f2198ecd74c..e16d2afcb190 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -477,6 +477,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Zezwala aplikacji na korzystanie z usługi komunikatora, by nawiązywać połączenia bez Twojego udziału."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"odczytywanie stanu i informacji o telefonie"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Pozwala aplikacji na dostęp do funkcji telefonicznych urządzenia. Aplikacja z tym uprawnieniem może odczytać numer telefonu i identyfikator urządzenia, sprawdzić, czy połączenie jest aktywne, oraz poznać numer, z którym jest nawiązane połączenie."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"odczyt podstawowych informacji na temat stanu usług telefonicznych i tożsamości"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Zezwól na dostęp aplikacji do podstawowych funkcji telefonicznych na urządzeniu."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"przekazywanie połączeń przez system"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Zezwala aplikacji na przekazywanie połączeń przez system, by poprawić ich jakość."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"przeglądanie i kontrolowanie połączeń w systemie."</string>
@@ -628,6 +630,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Kliknij, aby usunąć model twarzy, a następnie ponownie dodaj skan twarzy"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Skonfiguruj rozpoznawanie twarzy"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Popatrz na ekran telefonu, aby go odblokować"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Aby używać rozpoznawania twarzy, włącz "<b>"dostęp do aparatu"</b>" w Ustawieniach i prywatności"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Skonfiguruj więcej sposobów odblokowywania"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Kliknij, aby dodać odcisk palca"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Odblokowywanie odciskiem palca"</string>
@@ -1316,10 +1319,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Przygotowuję aplikację <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Uruchamianie aplikacji."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Kończenie uruchamiania."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Wyłączyć ekran?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Podczas konfigurowania odcisku palca naciśnięto przycisk zasilania.\n\nZwykle powoduje to wyłączenie ekranu."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Wyłącz"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Anuluj"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Kontynuować konfigurowanie?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Naciśnięto przycisk zasilania — zwykle powoduje to wyłączenie ekranu.\n\nKlikaj delikatnie podczas konfigurowania odcisku palca."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Wyłącz ekran"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Konfiguruj dalej"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Kontynuować weryfikację odcisku palca?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Naciśnięto przycisk zasilania — zwykle powoduje to wyłączenie ekranu.\n\nKliknij delikatnie, aby zweryfikować odcisk palca."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Wyłącz ekran"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Dalej"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"Działa <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Kliknij, by wrócić do gry"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Wybierz grę"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 8e7cc3dd6a9a..52d7992d456c 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Permite que o app use o serviço de mensagens instantâneas para fazer chamadas sem sua intervenção."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"ler status e identidade do telefone"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Permite que o app acesse os recursos de telefonia do dispositivo. Esta permissão autoriza o app a determinar o número de telefone e IDs de dispositivo, quando uma chamada está ativa, e o número remoto conectado a uma chamada."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"ler status e identidade básicos de telefonia"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Permite que o app acesse os recursos básicos de telefonia do dispositivo."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"encaminhar chamadas pelo sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Permite que o app encaminhe suas chamadas por meio do sistema para melhorar a experiência com chamadas."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"ver e controlar chamadas pelo sistema."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Toque para excluir seu modelo de rosto e crie um novo"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configurar o Desbloqueio facial"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Desbloqueie o smartphone olhando para ele"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Para usar o Desbloqueio facial, ative o "<b>"acesso à câmera"</b>" em Configurações &gt; Privacidade"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configure mais formas de desbloquear a tela"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Toque para adicionar uma impressão digital"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Desbloqueio por impressão digital"</string>
@@ -1208,7 +1211,7 @@
<string name="whichOpenLinksWith" msgid="1120936181362907258">"Abrir links com"</string>
<string name="whichOpenLinksWithApp" msgid="6917864367861910086">"Abrir links com <xliff:g id="APPLICATION">%1$s</xliff:g>"</string>
<string name="whichOpenHostLinksWithApp" msgid="2401668560768463004">"Abrir links do domínio <xliff:g id="HOST">%1$s</xliff:g> usando <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
- <string name="whichGiveAccessToApplicationLabel" msgid="7805857277166106236">"Conceder acesso"</string>
+ <string name="whichGiveAccessToApplicationLabel" msgid="7805857277166106236">"Permitir acesso"</string>
<string name="whichEditApplication" msgid="6191568491456092812">"Editar com"</string>
<string name="whichEditApplicationNamed" msgid="8096494987978521514">"Editar com %1$s"</string>
<string name="whichEditApplicationLabel" msgid="1463288652070140285">"Editar"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Preparando <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Iniciando apps."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Concluindo a inicialização."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Desligar a tela?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Durante a configuração da sua impressão digital, você pressionou o botão liga/desliga.\n\nNormalmente, essa ação desliga a tela."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Desligar"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Cancelar"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Continuar a configuração?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Você pressionou o botão liga/desliga. Normalmente, essa ação desliga a tela.\n\nToque levemente na tela durante a configuração da impressão digital."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Desligar a tela"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Continuar configuração"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Continuar a verificação da digital?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Você pressionou o botão liga/desliga. Normalmente, essa ação desliga a tela.\n\nToque levemente na tela para verificar sua impressão digital."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Desligar a tela"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Continuar"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> em execução"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Toque para voltar ao jogo"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Escolha o jogo"</string>
@@ -2110,12 +2117,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Esta notificação foi rebaixada a Silenciosa. Toque para enviar seu feedback."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Esta notificação foi classificada com maior prioridade. Toque para enviar seu feedback."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Esta notificação foi classificada com menor prioridade. Toque para enviar seu feedback."</string>
- <string name="nas_upgrade_notification_title" msgid="8436359459300146555">"Notificações aprimoradas"</string>
- <string name="nas_upgrade_notification_content" msgid="5157550369837103337">"As ações e respostas sugeridas agora são fornecidas pelas notificações aprimoradas. As Notificações adaptáveis do Android não estão mais disponíveis."</string>
+ <string name="nas_upgrade_notification_title" msgid="8436359459300146555">"Notificações avançadas"</string>
+ <string name="nas_upgrade_notification_content" msgid="5157550369837103337">"As ações e respostas sugeridas agora são fornecidas pelas notificações avançadas. As Notificações adaptáveis do Android não estão mais disponíveis."</string>
<string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"OK"</string>
<string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"Desativar"</string>
<string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Saiba mais"</string>
- <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"No Android 12, as notificações aprimoradas substituíram as notificações adaptáveis. Esse recurso exibe ações e respostas sugeridas, além de organizar suas notificações.\n\nAs notificações aprimoradas podem acessar o conteúdo das notificações, incluindo informações pessoais como nomes de contatos e mensagens. Elas também podem dispensar ou responder às notificações, como atender chamadas telefônicas e controlar o Não perturbe."</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"No Android 12, as notificações avançadas substituíram as notificações adaptáveis. Esse recurso exibe ações e respostas sugeridas, além de organizar suas notificações.\n\nAs notificações avançadas podem acessar o conteúdo das notificações, incluindo informações pessoais como nomes de contatos e mensagens. Elas também podem dispensar ou responder às notificações, como atender chamadas telefônicas e controlar o Não perturbe."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notificação de informação do modo rotina"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"A bateria pode acabar antes da recarga normal"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"A Economia de bateria foi ativada para aumentar a duração da carga"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index bf8ff8e81a8c..2ea9f39046d1 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Permite que a app utilize o serviço IMS para fazer chamadas sem a sua intervenção."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"ler o estado e a identidade do telemóvel"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Permite que a app aceda às funcionalidades de telefone do dispositivo. Esta autorização permite que a app determine o número de telefone e IDs do dispositivo, se alguma chamada está ativa e qual o número remoto ligado por uma chamada."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"ler o estado e a identidade de telefonia básicos"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Permite à app aceder às funcionalidades básicas de telefonia do dispositivo."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"encaminhar chamadas através do sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Permite que a app encaminhe as respetivas chamadas através do sistema de modo a melhorar a experiência da chamada."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"ver e controlar chamadas através do sistema."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Toque para eliminar o seu modelo de rosto e, em seguida, adicione o seu rosto novamente"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configure o Desbloqueio facial"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Desbloqueie o telemóvel ao olhar para ele"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Para utilizar o Desbloqueio facial, ative o "<b>"Acesso à câmara"</b>" em Definições &gt; Privacidade"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configure mais formas de desbloquear"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Toque para adicionar uma impressão digital"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Desbloqueio por impressão digital"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"A preparar o <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"A iniciar aplicações"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"A concluir o arranque."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Pretende desligar o ecrã?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Quando configurou a sua impressão digital, premiu o botão ligar/desligar.\n\nGeralmente, esta ação desativa o seu ecrã."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Desligar"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Cancelar"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Continuar a configuração?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Premiu o botão ligar/desligar. Geralmente, esta ação desliga o ecrã.\n\nExperimente tocar levemente ao configurar a sua impressão digital."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Desligar ecrã"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Continuar configur."</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Continuar a validar a impressão digital?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Premiu o botão ligar/desligar. Geralmente, esta ação desliga o ecrã.\n\nExperimente tocar levemente para validar a sua impressão digital."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Desligar ecrã"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Continuar"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> em execução"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Toque para regressar ao jogo."</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Selecionar jogo"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 8e7cc3dd6a9a..52d7992d456c 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Permite que o app use o serviço de mensagens instantâneas para fazer chamadas sem sua intervenção."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"ler status e identidade do telefone"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Permite que o app acesse os recursos de telefonia do dispositivo. Esta permissão autoriza o app a determinar o número de telefone e IDs de dispositivo, quando uma chamada está ativa, e o número remoto conectado a uma chamada."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"ler status e identidade básicos de telefonia"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Permite que o app acesse os recursos básicos de telefonia do dispositivo."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"encaminhar chamadas pelo sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Permite que o app encaminhe suas chamadas por meio do sistema para melhorar a experiência com chamadas."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"ver e controlar chamadas pelo sistema."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Toque para excluir seu modelo de rosto e crie um novo"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configurar o Desbloqueio facial"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Desbloqueie o smartphone olhando para ele"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Para usar o Desbloqueio facial, ative o "<b>"acesso à câmera"</b>" em Configurações &gt; Privacidade"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configure mais formas de desbloquear a tela"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Toque para adicionar uma impressão digital"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Desbloqueio por impressão digital"</string>
@@ -1208,7 +1211,7 @@
<string name="whichOpenLinksWith" msgid="1120936181362907258">"Abrir links com"</string>
<string name="whichOpenLinksWithApp" msgid="6917864367861910086">"Abrir links com <xliff:g id="APPLICATION">%1$s</xliff:g>"</string>
<string name="whichOpenHostLinksWithApp" msgid="2401668560768463004">"Abrir links do domínio <xliff:g id="HOST">%1$s</xliff:g> usando <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
- <string name="whichGiveAccessToApplicationLabel" msgid="7805857277166106236">"Conceder acesso"</string>
+ <string name="whichGiveAccessToApplicationLabel" msgid="7805857277166106236">"Permitir acesso"</string>
<string name="whichEditApplication" msgid="6191568491456092812">"Editar com"</string>
<string name="whichEditApplicationNamed" msgid="8096494987978521514">"Editar com %1$s"</string>
<string name="whichEditApplicationLabel" msgid="1463288652070140285">"Editar"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Preparando <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Iniciando apps."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Concluindo a inicialização."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Desligar a tela?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Durante a configuração da sua impressão digital, você pressionou o botão liga/desliga.\n\nNormalmente, essa ação desliga a tela."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Desligar"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Cancelar"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Continuar a configuração?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Você pressionou o botão liga/desliga. Normalmente, essa ação desliga a tela.\n\nToque levemente na tela durante a configuração da impressão digital."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Desligar a tela"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Continuar configuração"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Continuar a verificação da digital?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Você pressionou o botão liga/desliga. Normalmente, essa ação desliga a tela.\n\nToque levemente na tela para verificar sua impressão digital."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Desligar a tela"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Continuar"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> em execução"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Toque para voltar ao jogo"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Escolha o jogo"</string>
@@ -2110,12 +2117,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Esta notificação foi rebaixada a Silenciosa. Toque para enviar seu feedback."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Esta notificação foi classificada com maior prioridade. Toque para enviar seu feedback."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Esta notificação foi classificada com menor prioridade. Toque para enviar seu feedback."</string>
- <string name="nas_upgrade_notification_title" msgid="8436359459300146555">"Notificações aprimoradas"</string>
- <string name="nas_upgrade_notification_content" msgid="5157550369837103337">"As ações e respostas sugeridas agora são fornecidas pelas notificações aprimoradas. As Notificações adaptáveis do Android não estão mais disponíveis."</string>
+ <string name="nas_upgrade_notification_title" msgid="8436359459300146555">"Notificações avançadas"</string>
+ <string name="nas_upgrade_notification_content" msgid="5157550369837103337">"As ações e respostas sugeridas agora são fornecidas pelas notificações avançadas. As Notificações adaptáveis do Android não estão mais disponíveis."</string>
<string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"OK"</string>
<string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"Desativar"</string>
<string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Saiba mais"</string>
- <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"No Android 12, as notificações aprimoradas substituíram as notificações adaptáveis. Esse recurso exibe ações e respostas sugeridas, além de organizar suas notificações.\n\nAs notificações aprimoradas podem acessar o conteúdo das notificações, incluindo informações pessoais como nomes de contatos e mensagens. Elas também podem dispensar ou responder às notificações, como atender chamadas telefônicas e controlar o Não perturbe."</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"No Android 12, as notificações avançadas substituíram as notificações adaptáveis. Esse recurso exibe ações e respostas sugeridas, além de organizar suas notificações.\n\nAs notificações avançadas podem acessar o conteúdo das notificações, incluindo informações pessoais como nomes de contatos e mensagens. Elas também podem dispensar ou responder às notificações, como atender chamadas telefônicas e controlar o Não perturbe."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notificação de informação do modo rotina"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"A bateria pode acabar antes da recarga normal"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"A Economia de bateria foi ativada para aumentar a duração da carga"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 8f05f9f7c68d..ac5eca7f80b1 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -474,6 +474,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Permite aplicației să folosească serviciul IMS pentru apeluri, fără intervenția dvs."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"citește starea și identitatea telefonului"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Permite aplicației să acceseze funcțiile de telefon ale dispozitivului. Cu această permisiune aplicația stabilește numărul de telefon și ID-urile de dispozitiv, dacă un apel este activ, precum și numărul de la distanță conectat printr-un apel."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"să citească informații de bază, precum activitatea și starea telefonului"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Permite ca aplicația să acceseze funcțiile de telefonie de bază ale dispozitivului."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"să direcționeze apelurile prin intermediul sistemului"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Permiteți aplicației să direcționeze apelurile prin intermediul sistemului pentru a îmbunătăți calitatea apelurilor."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"Vedeți și controlați apelurile prin intermediul sistemului."</string>
@@ -625,6 +627,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Atingeți pentru a șterge modelul facial, apoi adăugați din nou fața"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configurați Deblocarea facială"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Deblocați-vă telefonul uitându-vă la acesta"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Pentru a folosi Deblocarea facială, activați "<b>"Accesul la cameră"</b>" în Setări și confidențialitate"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configurați mai multe moduri de deblocare"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Atingeți ca să adăugați o amprentă"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Deblocare cu amprenta"</string>
@@ -1296,10 +1299,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Se pregătește <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Se pornesc aplicațiile."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Se finalizează pornirea."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Dezactivați ecranul?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Ați apăsat butonul de pornire în timpul configurării amprentei.\n\nDe obicei, această acțiune dezactivează ecranul."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Dezactivați"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Anulați"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Continuați configurarea?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Ați apăsat butonul de pornire. De obicei, această acțiune dezactivează ecranul.\n\nAtingeți ușor când vă configurați amprenta."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Dezactivați ecranul"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Continuați configurarea"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Continuați cu verificarea amprentei?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Ați apăsat butonul de pornire. De obicei, această acțiune dezactivează ecranul.\n\nAtingeți ușor pentru verificarea amprentei."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Dezactivați ecranul"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Continuați"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"Rulează <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Atingeți pentru a reveni la joc"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Alegeți jocul"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 927ec93b7683..618283273b61 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -477,6 +477,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Позволяет приложению совершать звонки с помощью службы IMS без вашего вмешательства."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"Получение данных о статусе телефона"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Приложение получит доступ к функциям телефона на устройстве. Кроме того, оно сможет определять номера телефонов и серийные номера моделей, состояние активности вызова, а также удаленные номера, с которыми установлено соединение."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"Считывание статуса и идентификационной информации телефонии"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Приложение получит доступ к основным функциям телефонии на устройстве."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"перенаправлять звонки в системе"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Приложение сможет перенаправлять звонки в системе с целью улучшения качества связи."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"Управление вызовами через систему"</string>
@@ -628,6 +630,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Нажмите, чтобы удалить модель лица, а затем добавьте ее снова."</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Настройка фейсконтроля"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Вы сможете разблокировать телефон, просто посмотрев на него."</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Чтобы использовать фейсконтроль, разрешите "<b>"доступ к камере"</b>". Для этого перейдите в настройки и нажмите \"Конфиденциальность\"."</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Настройте другие способы разблокировки"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Нажмите, чтобы добавить отпечаток пальца."</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Разблокировка по отпечатку пальца"</string>
@@ -1316,10 +1319,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Подготовка приложения \"<xliff:g id="APPNAME">%1$s</xliff:g>\"..."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Запуск приложений."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Окончание загрузки..."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Отключить экран?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Во время настройки отпечатка пальца вы нажали кнопку питания.\n\nОбычно это действие приводит к отключению экрана."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Отключить"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Отмена"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Продолжить настройку?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Вы нажали кнопку питания. Обычно это приводит к отключению экрана.\n\nПри добавлении отпечатка пальца слегка прикоснитесь к кнопке."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Отключить экран"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Продолжить настройку"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Продолжить сканирование отпечатка?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Вы нажали кнопку питания. Обычно это приводит к отключению экрана.\n\nЧтобы отсканировать отпечаток пальца, слегка прикоснитесь к кнопке."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Отключить экран"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Продолжить"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"Приложение <xliff:g id="APP">%1$s</xliff:g> запущено"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Нажмите, чтобы вернуться в игру."</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Выберите игру"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index c63708fbc459..c5e76c8afb7f 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"ඔබේ මැදිහත්වීමකින් තොරව ඇමතුම් සිදු කිරීමට IMS සේවාව භාවිතයට යෙදුමට ඉඩ දෙන්න."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"දුරකථනයේ තත්වය සහ අනන්‍යතාවය කියවීම"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"උපාංගයේ දුරකථන විශේෂාංග වෙත ප්‍රවේශයට යෙදුමට ඉඩ දෙයි. ඇමතුම සක්‍රිය වුවත්, සහ ඇමතුමකින් දුරස්ථ අංකය සම්බන්ධ වුවත් දුරකථන අංකය සහ උපාංග ID හඳුනා ගැනීමට මෙම අවසරය යෙදුමට ඉඩ දෙයි."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"මූලික දුරකථන තත්ත්වය සහ අනන්‍යතාව කියවන්න"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"උපාංගයේ මූලික දුරකථන විශේෂාංග වෙත ප්‍රවේශ වීමට යෙදුමට ඉඩ දෙයි."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"පද්ධතිය හරහා ඇමතුම් මාර්ගගත කරන්න"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"ඇමතුම් අත්දැකීම වැඩිදියුණු කිරීම සඳහා යෙදුමට පද්ධතිය හරහා එහි ඇමතුම් මාර්ගගත කිරීමට ඉඩ දෙයි."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"පද්ධතිය හරහා ඇමතුම් බලන්න සහ පාලනය කරන්න."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"ඔබගේ මුහුණත ආකෘතිය මැකීමට තට්ටු කරන්න, අනතුරුව ඔබගේ මුහුණ නැවත එක් කරන්න"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"මුහුණෙන් අගුළු හැරීම පිහිටුවන්න"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ඔබගේ දුරකථනය දෙස බැලීමෙන් එහි අගුලු හරින්න"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"මුහුණෙන් අගුලු හැරීම භාවිත කිරීමට, සැකසීම් &gt; පෞද්ගලිකත්වය තුළ "<b>"කැමරා ප්‍රවේශය"</b>" ක්‍රියාත්මක කරන්න"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"අගුලු හැරීමට තවත් ක්‍රම සකසන්න"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"ඇඟිලි සලකුණක් එක් කිරීමට තට්ටු කරන්න"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ඇඟිලි සලකුණු අගුළු හැරීම"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> සූදානම් කරමින්."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"යෙදුම් ආරම්භ කරමින්."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"ඇරඹුම අවසාන කරමින්."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"තිරය ක්‍රියාවිරහිත කරන්නද?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"ඔබගේ ඇඟිලි සලකුණ පිහිටුවන අතරතුර ඔබ බල බොත්තම එබුවේය.\n\nමෙය සාමාන්‍යයෙන් ඔබගේ තිරය ක්‍රියාවිරහිත කරයි."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"ක්‍රියාවිරහිත කරන්න"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"අවලංගු කරන්න"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"පිහිටුවීම දිගටම කරන්නද?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"ඔබ බල බොත්තම එබුවේය — සාමාන්‍යයෙන් මෙය තිරය ක්‍රියාවිරහිත කරයි.\n\nඔබගේ ඇඟිලි සලකුණ පිහිටුවන අතරතුර සැහැල්ලුවෙන් තට්ටු කිරීමට උත්සාහ කරන්න."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"තිරය අක්‍රිය කරන්න"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"පිහිටුවීම දිගටම කර."</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"ඔබගේ ඇඟිලි සලකුණ සත්‍යාපනය දිගටම කරන්නද?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"ඔබ බල බොත්තම එබුවේය — සාමාන්‍යයෙන් මෙය තිරය ක්‍රියාවිරහිත කරයි.\n\nඔබගේ ඇඟිලි සලකුණ සත්‍යාපනය කිරීමට සැහැල්ලුවෙන් තට්ටු කිරීමට උත්සාහ කරන්න."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"තිරය අක්‍රිය කරන්න"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"ඉදිරියට යන්න"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> ධාවනය වෙමින්"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"ක්‍රීඩාව වෙත ආපසු යාමට තට්ටු කරන්න"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"ක්‍රීඩාව තෝරන්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 66451763c07f..dfd34e7ce7b2 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -477,6 +477,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Umožňuje aplikácii používať službu okamžitých správ (IMS) na volanie bez intervencie používateľa."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"čítať stav a identitu telefónu"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Umožňuje aplikácii pristupovať k telefónnym funkciám zariadenia. Aplikácia s týmto povolením môže určiť telefónne číslo a ID zariadenia, či práve prebieha hovor, a vzdialené číslo, s ktorým je prostredníctvom hovoru nadviazané spojenie."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"čítanie základného stavu a identity súvisiacich s telefonovaním"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Umožňuje aplikácii prístup k základným telefonickým funkciám zariadenia."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"presmerovanie hovorov cez systém"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Umožňuje aplikácii presmerovať hovory cez systém na účely zlepšenia kvality hovorov."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"zobrazenie a kontrola hovorov prostredníctvom systému."</string>
@@ -628,6 +630,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Klepnutím odstráňte model tváre a potom znova pridajte svoju tvár"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Nastavte odomknutie tvárou"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Odomykajte telefón tak, že sa naň pozriete"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Ak chcete používať odomknutie tvárou, v sekcii Nastavenia &gt; Ochrana súkromia zapnite "<b>"prístup ku kamere"</b></string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Nastavte viac spôsobov odomknutia"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Klepnutím pridajte odtlačok prsta"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Odomknutie odtlačkom prsta"</string>
@@ -1316,10 +1319,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Pripravuje sa aplikácia <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Prebieha spúšťanie aplikácií."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Prebieha dokončovanie spúšťania."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Chcete vypnúť obrazovku?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Pri nastavovaní odtlačku prsta ste stlačili vypínač.\n\nObrazovka sa tým zvyčajne vypne."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Vypnúť"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Zrušiť"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Chcete pokračovať v nastavovaní?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Stlačili ste vypínač. Obvykle tým vypnete obrazovku.\n\nPri nastavovaní odtlačku prsta skúste klepnúť jemne."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Vypnúť obrazovku"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Pokračovať v nastav."</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Pokračovať v overovaní odtlačku prsta?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Stlačili ste vypínač. Obvykle tým vypnete obrazovku.\n\nAk chcete overiť odtlačok prsta, skúste klepnúť jemne."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Vypnúť obrazovku"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Pokračovať"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"Spustená aplikácia: <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Klepnutím prejdete späť do hry"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Vyberte hru"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 935b1e88752d..931fc5b3bcfb 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -477,6 +477,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Aplikaciji dovoljuje uporabo storitev IMS za opravljanje klicev brez vašega posredovanja."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"branje stanja in identitete telefona"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Aplikaciji omogoča dostop do funkcij telefona v napravi. S tem dovoljenjem lahko aplikacija določi telefonsko številko in ID-je naprave, določi lahko tudi, ali je klic aktiven, in oddaljeno številko, s katero je klic povezan."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"branje stanja osnovne telefonije in identitete"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Aplikaciji omogoča dostop do osnovnih funkcij telefonije v napravi."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"Usmeri klice prek sistema"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Dovoli, da aplikacija usmeri klice prek sistema, da se tako izboljša izkušnja klicanja."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"ogled in nadzor klicev prek sistema."</string>
@@ -628,6 +630,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Dotaknite se, da izbrišete model obraza, in nato znova dodajte obraz."</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Nastavitev odklepanja z obrazom"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Odklenite telefon tako, da ga pogledate."</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Če želite uporabljati odklepanje z obrazom, v meniju »Nastavitve« &gt; »Zasebnost« vklopite možnost "<b>"Dostop do fotoaparata"</b>"."</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Nastavite več načinov odklepanja"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Dotaknite se, da dodate prstni odtis."</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Odklepanje s prstnim odtisom"</string>
@@ -1316,10 +1319,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Pripravljanje aplikacije <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Zagon aplikacij."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Dokončevanje zagona."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Želite izklopiti zaslon?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Pri nastavljanju prstnega odtisa ste pritisnili gumb za vklop.\n\nS tem običajno izklopite zaslon."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Izklopi"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Prekliči"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Ali želite nadaljevati nastavitev?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Pritisnili ste gumb za vklop, s čimer običajno izklopite zaslon.\n\nPoskusite se narahlo dotakniti med nastavljanjem prstnega odtisa."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Izklopi zaslon"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Nadaljuj nastavitev"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Želite nadaljevati preverjanje prstnega odtisa?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Pritisnili ste gumb za vklop, s čimer običajno izklopite zaslon.\n\nZa preverjanje prstnega odtisa se poskusite narahlo dotakniti."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Izklopi zaslon"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Nadaljuj"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> se izvaja"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Dotaknite se za vrnitev v igro"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Izberite igro"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 7bd3a53cdd68..937332b12027 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Lejon aplikacionin të përdorë shërbimin IMS për të kryer telefonata pa ndërhyrjen tënde."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"lexo statusin e telefonit dhe identitetin"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Lejon aplikacionin të hyjë në funksionet telefonike të pajisjes. Kjo leje i mundëson aplikacionit të përcaktojë numrin e telefonit dhe ID-të e pajisjes, nëse një telefonatë është aktive apo nëse numri në distancë është i lidhur me një telefonatë."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"lexo statusin dhe identitetin telefonik bazë"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Lejo që aplikacioni të ketë qasje në veçoritë telefonike bazë në pajisje."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"kalon telefonatat përmes sistemit"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Lejon që aplikacioni të kalojë telefonatat përmes sistemit për të përmirësuar përvojën e telefonatës."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"Shiko dhe kontrollo telefonatat nëpërmjet sistemit."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Trokit për të fshirë modelin tënd të fytyrës, pastaj shtoje përsëri fytyrën tënde"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Konfiguro \"Shkyçjen me fytyrë\""</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Shkyçe telefonin duke parë tek ai"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Për të përdorur \"Shkyçjen me fytyrë\", aktivizo "<b>"Qasjen te kamera"</b>" te \"Cilësimet\" &gt; \"Privatësia\""</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Konfiguri më shumë mënyra për të shkyçur"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Trokit për të shtuar një gjurmë gishti"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Shkyçja me gjurmën e gishtit"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Po përgatit <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Aplikacionet e fillimit."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Po përfundon nisjen."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Të fiket ekrani?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Gjatë konfigurimit të gjurmës së gishtit tënd, ke shtypur butonin e \"Energjisë\".\n\nKjo zakonisht fik ekranin tënd."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Fik"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Anulo"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Të vazhdohet konfigurimi?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Shtype butonin e energjisë — zakonisht, kjo e fik ekranin.\n\nProvo të trokasësh lehtë ndërkohë që konfiguron gjurmën e gishtit."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Fik ekranin"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Vazhdo konfigurimin"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Të vazhdohet verifikimi i gjurmës?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Shtype butonin e energjisë — zakonisht, kjo e fik ekranin.\n\nProvo të trokasësh lehtë për të verifikuar gjurmën e gishtit."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Fik ekranin"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Vazhdo"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> është në punë"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Trokit për t\'u kthyer te loja"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Zgjidh një lojë"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 97b233c0af75..72fd908fc7ae 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -474,6 +474,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Дозвољава апликацији да користи услугу размене тренутних порука да би упућивала позиве без ваше интервенције."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"читање статуса и идентитета телефона"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Дозвољава апликацији да приступа функцијама телефона на уређају. Ова дозвола омогућава апликацији да утврди број телефона и ИД-ове уређаја, затим да ли је позив активан, као и број даљинског уређаја са којим је успостављен позив."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"очитавање основног телефонског статуса и идентитета"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Омогућава апликацији да приступа основним телефонским функцијама уређаја."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"преусмеравање позива преко система"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Дозвољава апликацији да преусмерава позиве преко система да би побољшала доживљај позивања."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"преглед и контрола позива преко система."</string>
@@ -625,6 +627,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Додирните да бисте избрисали модел лица, па поново додајте своје лице"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Подесите откључавање лицем"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Откључајте телефон тако што ћете га погледати"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Да бисте користили откључавање лицем, укључите "<b>"приступ камери"</b>" у одељку Подешавања &gt; Приватност"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Подесите још начина за откључавање"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Додирните да бисте додали отисак прста"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Откључавање отиском прста"</string>
@@ -1296,10 +1299,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Припрема се <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Покретање апликација."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Завршавање покретања."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Желите да искључите екран?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Притисли сте дугме за укључивање током подешавања отиска прста.\n\nТако се најчешће искључује екран."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Искључи"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Откажи"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Желите ли да наставите са подешавањем?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Притиснули сте дугме за укључивање – тиме обично искључујете екран.\n\nПробајте лагано да додирнете док подешавате отисак прста."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Искључи екран"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Настави подешавање"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Настављате верификацију отиска прста?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Притиснули сте дугме за укључивање – тиме обично искључујете екран.\n\nПробајте лагано да додирнете да бисте верификовали отисак прста."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Искључи екран"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Настави"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"Апликација <xliff:g id="APP">%1$s</xliff:g> је покренута"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Додирните да бисте се вратили у игру"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Одаберите игру"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 740cfb977c5d..e078da0510da 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Tillåter att appen använder tjänsten för snabbmeddelanden för att ringa samtal utan åtgärd från dig."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"läsa telefonens status och identitet"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Tillåter att appen kommer åt enhetens telefonfunktioner. Med den här behörigheten tillåts appen att identifiera mobilens telefonnummer och enhets-ID, om ett samtal pågår och vilket nummer samtalet är kopplat till."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"läsa grundläggande uppgifter om telefonistatus och identitet"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Ger appen åtkomst till grundläggande telefonifunktioner på enheten."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"dirigera samtal via systemet"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Appen tillåts att dirigera samtal via systemet för att förbättra samtalsupplevelsen."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"visa och styra samtal via systemet."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tryck för att radera ansiktsmodellen och lägg sedan till ansiktet igen"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Konfigurera ansiktslås"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Lås upp telefonen genom att titta på den"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Om du vill använda ansiktslås aktiverar du "<b>"Kameraåtkomst"</b>" i Inställningar &gt; Integritet"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Konfigurera fler sätt att låsa upp"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tryck för att lägga till ett fingeravtryck"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Fingeravtryckslås"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> förbereds."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Appar startas."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Uppgraderingen är klar."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Vill du stänga av skärmen?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Du tryckte på strömbrytaren när du skulle konfigurera fingeravtrycket.\n\nDet brukar leda till att skärmen stängs av."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Stäng av"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Avbryt"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Vill du fortsätta med konfigureringen?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Du tryckte på strömbrytaren, vilket vanligtvis stänger av skärmen.\n\nTesta att trycka lätt när du konfigurerar fingeravtrycket."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Stäng av skärmen"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Fortsätt konfigurera"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Vill du verifiera ditt fingeravtryck?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Du tryckte på strömbrytaren, vilket vanligtvis stänger av skärmen.\n\nTesta att trycka lätt för att verifiera ditt fingeravtryck."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Stäng av skärmen"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Fortsätt"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> körs"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Återgå till spelet genom att trycka här"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Välj spel"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index fd1faa080a47..99fd1079476f 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Huruhusu programu kutumia huduma ya IMS kupiga simu bila udhibiti wako."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"kusoma hali na kitambulisho cha simu"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Huruhusu programu kufikia vipengele vya simu vilivyo kwenye kifaa. Idhini hii inaruhusu programu kutambua nambari ya simu na kifaa, kama kuna simu inayopigwa, na nambari ya mbali iliyounganishwa kwenye simu."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"kusoma utambulisho na hali ya msingi ya simu"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Huruhusu programu kufikia vipengele vya msingi vya simu kwenye kifaa."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"elekeza simu kupitia mfumo"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Huruhusu programu kuelekeza simu zake kupitia mfumo ili kuboresha hali ya kupiga simu."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"kuona na kudhibiti simu kupitia mfumo."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Gusa ili ufute muundo wa uso wako, kisha uweke uso wako tena"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Weka mipangilio ya Kufungua kwa Uso"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Fungua simu yako kwa kuiangalia"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Ili utumie kipengele cha kufungua kwa uso, washa kipengele cha "<b>"ufikiaji wa Kamera"</b>" katika Mipangilio na Faragha"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Weka mipangilio ya mbinu zaidi za kufungua"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Gusa ili uweke alama ya kidole"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Kufungua kwa Alama ya Kidole"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Inaandaa <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Programu zinaanza"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Inamaliza kuwasha."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Ungependa kuzima skrini?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Ulipokuwa ukiweka alama ya kidole chako, ulibonyeza Kitufe cha kuwasha/kuzima.\n\nKwa kawaida, hatua hii huzima skrini yako."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Zima"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Ghairi"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Ungependa kuendelea kuweka mipangilio?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Umebonyeza kitufe cha kuwasha/kuzima — kwa kawaida, hali hii huzima skrini.\n\nJaribu kugusa taratibu unapoweka mipangilio ya alama ya kidole chako."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Zima skrini"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Endelea kuweka mipangilio"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Utaendelea kuthibitisha alama ya kidole chako?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Umebonyeza kitufe cha kuwasha/kuzima — kwa kawaida, hali hii huzima skrini.\n\nJaribu kugusa taratibu ili uthibitishe alama ya kidole chako."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Zima skrini"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Endelea"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> inaendelea"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Gusa ili urudi kwenye mchezo"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Chagua mchezo"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 0690c319bbf6..0be25cd879ed 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"உங்கள் குறுக்கீடின்றி IMS சேவையைப் பயன்படுத்தி அழைப்பதற்கு, ஆப்ஸை அனுமதிக்கும்."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"மொபைல் நிலை மற்றும் அடையாளத்தைப் படித்தல்"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"சாதனத்தின் மொபைல் அம்சங்களை அணுகப் ஆப்ஸை அனுமதிக்கிறது. மொபைல் மற்றும் சாதன ஐடிகள், அழைப்பு செயலில் உள்ளதா மற்றும் அழைப்பு மூலம் இணைக்கப்பட்ட தொலைக் கட்டுப்பாட்டு எண் ஆகியவற்றைத் தீர்மானிக்க இந்த அனுமதி ஆப்ஸை அனுமதிக்கிறது."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"அடிப்படையான டெலிஃபோனி நிலையையும் அடையாளத்தையும் படித்தல்"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"சாதனத்திலுள்ள அடிப்படையான டெலிஃபோனி அம்சங்களை அணுக ஆப்ஸை அனுமதிக்கும்."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"சிஸ்டம் மூலம் அழைப்புகளை ரூட் செய்தல்"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"அழைக்கும் அனுபவத்தை மேம்படுத்தும் பொருட்டு, சிஸ்டம் மூலம் தனது அழைப்புகளை ரூட் செய்ய ஆப்ஸை அனுமதிக்கும்."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"சிஸ்டம் மூலமாக அழைப்புகளைப் பார்த்தலும் கட்டுப்படுத்துதலும்."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"முகத் தோற்றப் பதிவைத் தட்டி நீக்கிவிட்டு உங்கள் முகத்தை மீண்டும் சேர்க்கவும்"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"\'முகம் காட்டித் திறத்தல்\' அம்சத்தை அமைத்தல்"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"மொபைலைப் பார்ப்பதன் மூலம் அதை அன்லாக் செய்யலாம்"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"முகம் காட்டித் திறத்தல் அம்சத்தைப் பயன்படுத்த, அமைப்புகள் &gt; தனியுரிமை என்பதற்குச் சென்று "<b>"கேமரா அணுகலை"</b>" இயக்கவும்"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"அன்லாக் செய்ய மேலும் பல வழிகளை அமையுங்கள்"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"கைரேகையைச் சேர்க்கத் தட்டுங்கள்"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"கைரேகை அன்லாக்"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g>ஐத் தயார்செய்கிறது."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"ஆப்ஸ் தொடங்கப்படுகின்றன."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"துவக்குதலை முடிக்கிறது."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"திரையை ஆஃப் செய்யவா?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"கைரேகையை அமைக்கும்போது பவர் பட்டனை அழுத்திவிட்டீர்கள்.\n\nஇது திரையை ஆஃப் செய்துவிடும்."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"ஆஃப் செய்"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"ரத்துசெய்"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"அமைவைத் தொடரவா?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"நீங்கள் பவர் பட்டனை அழுத்தியுள்ளீர்கள் — வழக்கமாக இது திரையை ஆஃப் செய்யும்.\n\nஉங்கள் கைரேகையை அமைக்கும்போது மெதுவாகத் தொடுங்கள்."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"திரையை ஆஃப் செய்"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"அமைவைத் தொடர்க"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"கைரேகைச் சரிபார்ப்பைத் தொடரவா?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"நீங்கள் பவர் பட்டனை அழுத்தியுள்ளீர்கள் — வழக்கமாக இது திரையை ஆஃப் செய்யும்.\n\nஉங்கள் கைரேகையைச் சரிபார்க்க மெதுவாகத் தொடுங்கள்."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"திரையை ஆஃப் செய்"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"தொடர்க"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> இயங்குகிறது"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"கேமிற்குச் செல்ல, தட்டவும்"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"கேமைத் தேர்வுசெய்க"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 313ce593c77a..3aa7b045153c 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -422,7 +422,7 @@
<string name="permdesc_writeContacts" product="default" msgid="8304795696237065281">"మీ ఫోన్‌లో నిల్వ చేసి ఉన్న కాంటాక్ట్‌లకు సంబంధించిన డేటాను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. ఈ అనుమతి, కాంటాక్ట్ డేటాను తొలగించడానికి యాప్‌లను అనుమతిస్తుంది."</string>
<string name="permlab_readCallLog" msgid="1739990210293505948">"కాల్ లాగ్‌ను చదవడం"</string>
<string name="permdesc_readCallLog" msgid="8964770895425873433">"ఈ యాప్‌ మీ కాల్ చరిత్రను చదవగలదు."</string>
- <string name="permlab_writeCallLog" msgid="670292975137658895">"కాల్ లాగ్‌ను వ్రాయడం"</string>
+ <string name="permlab_writeCallLog" msgid="670292975137658895">"కాల్ లాగ్‌ను రాయడం"</string>
<string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"ఇన్‌కమింగ్ మరియు అవుట్‌గోయింగ్ కాల్స్‌ల గురించిన డేటాతో సహా మీ టాబ్లెట్ యొక్క కాల్ లాగ్‌ను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. హానికరమైన యాప్‌లు మీ కాల్ లాగ్‌ను ఎరేజ్ చేయడానికి లేదా సవరించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"ఇన్‌కమింగ్ మరియు అవుట్‌గోయింగ్ కాల్స్‌కు సంబంధించిన డేటాతో సహా మీ Android TV పరికరం కాల్ లాగ్‌ను సవరించడానికి యాప్‌ని అనుమతిస్తుంది. హానికరమైన యాప్‌లు మీ కాల్ లాగ్‌ను తీసివేయడానికి లేదా సవరించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
<string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"ఇన్‌కమింగ్ మరియు అవుట్‌గోయింగ్ కాల్స్‌ల గురించిన డేటాతో సహా మీ ఫోన్ యొక్క కాల్ లాగ్‌ను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. హానికరమైన యాప్‌లు మీ కాల్ లాగ్‌ను ఎరేజ్ చేయడానికి లేదా సవరించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"మీ ప్రమేయం లేకుండా కాల్స్‌ చేయడం కోసం IMS సేవను ఉపయోగించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"ఫోన్ స్టేటస్‌ మరియు గుర్తింపుని చదవడం"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"పరికరం యొక్క ఫోన్ ఫీచర్‌లను యాక్సెస్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. ఈ అనుమతి ఫోన్ నంబర్ మరియు పరికరం IDలను, కాల్ సక్రియంగా ఉందా లేదా అనే విషయాన్ని మరియు కాల్ ద్వారా కనెక్ట్ చేయబడిన రిమోట్ నంబర్‌ను కనుగొనడానికి యాప్‌ను అనుమతిస్తుంది."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"ప్రాథమిక టెలిఫోన్ స్టేటస్, గుర్తింపును చదవండి"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"పరికరం తాలూకు ప్రాథమిక టెలిఫోన్ ఫీచర్‌లను యాక్సెస్ చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"కాల్స్‌ను సిస్టమ్ ద్వారా వెళ్లేలా చేయి"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"కాలింగ్ అనుభవాన్ని మెరుగుపరచడం కోసం తన కాల్స్‌ను సిస్టమ్ ద్వారా వెళ్లేలా చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"సిస్టమ్ ద్వారా కాల్స్‌ను చూసి, నియంత్రించండి."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"ఫేస్ మోడల్‌ను తొలగించడానికి నొక్కండి, ఆపై మీ ముఖాన్ని మళ్లీ జోడించండి"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"ఫేస్ అన్‌లాక్‌ను సెటప్ చేయండి"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"మీ ఫోన్‌ను చూడటం ద్వారా దాన్ని అన్‌లాక్ చేయండి"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"ఫేస్ అన్‌లాక్‌ను ఉపయోగించడానికి, సెట్టింగ్‌లు &gt; గోప్యతలో "<b>"కెమెరా యాక్సెస్"</b>"ను ఆన్ చేయండి"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"అన్‌లాక్ చేయడానికి మరిన్ని మార్గాలను సెటప్ చేయండి"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"వేలిముద్రను జోడించడానికి ట్యాప్ చేయండి"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"వేలిముద్ర అన్‌లాక్"</string>
@@ -725,7 +728,7 @@
<string name="permlab_bindCarrierServices" msgid="2395596978626237474">"క్యారియర్ సేవలకు అనుబంధించడం"</string>
<string name="permdesc_bindCarrierServices" msgid="9185614481967262900">"క్యారియర్ సేవలకు అనుబంధించడానికి హోల్డర్‌ను అనుమతిస్తుంది. సాధారణ యాప్‌లకు ఎప్పటికీ అవసరం ఉండదు."</string>
<string name="permlab_access_notification_policy" msgid="5524112842876975537">"అంతరాయం కలిగించవద్దును యాక్సెస్ చేయడం"</string>
- <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"అంతరాయం కలిగించవద్దు ఎంపిక కాన్ఫిగరేషన్ చదవడానికి మరియు వ్రాయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
+ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"అంతరాయం కలిగించవద్దు ఎంపిక కాన్ఫిగరేషన్ చదవడానికి మరియు రాయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"వీక్షణ అనుమతి వినియోగాన్ని ప్రారంభించండి"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"యాప్‌నకు అనుమతి వినియోగాన్ని ప్రారంభించడానికి హోల్డర్‌‌ను అనుమతిస్తుంది. సాధారణ యాప్‌లకు ఎప్పటికీ ఇటువంటి అనుమతి అవసరం ఉండదు."</string>
<string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"యాప్ ఫీచర్‌లను చూడటాన్ని ప్రారంభించండి"</string>
@@ -1020,7 +1023,7 @@
<string name="autofill_emirate" msgid="2544082046790551168">"ఎమిరేట్"</string>
<string name="permlab_readHistoryBookmarks" msgid="9102293913842539697">"మీ వెబ్ బుక్‌మార్క్‌లు మరియు చరిత్రను చదవడం"</string>
<string name="permdesc_readHistoryBookmarks" msgid="2323799501008967852">"బ్రౌజర్ సందర్శించిన అన్ని URLల చరిత్ర గురించి మరియు అన్ని బ్రౌజర్ బుక్‌మార్క్‌ల గురించి చదవడానికి యాప్‌ను అనుమతిస్తుంది. గమనిక: ఈ అనుమతి మూడవ పక్షం బ్రౌజర్‌లు లేదా వెబ్ బ్రౌజింగ్ సామర్థ్యాలు గల ఇతర యాప్‌ల ద్వారా అమలు చేయబడకపోవచ్చు."</string>
- <string name="permlab_writeHistoryBookmarks" msgid="6090259925187986937">"వెబ్ బుక్‌మార్క్‌లు మరియు చరిత్రను వ్రాయడం"</string>
+ <string name="permlab_writeHistoryBookmarks" msgid="6090259925187986937">"వెబ్ బుక్‌మార్క్‌లు మరియు చరిత్రను రాయడం"</string>
<string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="573341025292489065">"మీ టాబ్లెట్‌లో నిల్వ చేయబడిన బ్రౌజర్ హిస్టరీని, బుక్‌మార్క్‌లను ఎడిట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. ఇది బ్రౌజర్ డేటాను ఎరేజ్ చేయడానికి లేదా ఎడిట్ చేయడానికి యాప్‌ను అనుమతించవచ్చు. గమనిక: ఈ అనుమతిని థర్డ్ పార్టీ బ్రౌజర్‌లు లేదా వెబ్ బ్రౌజింగ్ సామర్థ్యాలు గల ఇతర యాప్‌లు అమలు చేయకపోవచ్చు."</string>
<string name="permdesc_writeHistoryBookmarks" product="tv" msgid="88642768580408561">"మీ Android TV పరికరంలో నిల్వ చేసిన బ్రౌజర్ చరిత్ర లేదా బుక్‌మార్క్‌లను సవరించడానికి యాప్‌ని అనుమతిస్తుంది. ఇది బ్రౌజర్ డేటాను తీసివేయడానికి లేదా సవరించడానికి యాప్‌ని అనుమతించవచ్చు. గమనిక: ఈ అనుమతి మూడవ-పక్ష బ్రౌజర్‌లు లేదా వెబ్ బ్రౌజింగ్ సామర్థ్యాలు గల ఇతర యాప్‌ల ద్వారా అమలు కాకపోవచ్చు."</string>
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="2245203087160913652">"మీ ఫోన్‌లో నిల్వ చేయబడిన బ్రౌజర్ చరిత్రను లేదా బుక్‌మార్క్‌లను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. ఇది బ్రౌజర్ డేటాను ఎరేజ్ చేయడానికి లేదా సవరించడానికి యాప్‌ను అనుమతించవచ్చు. గమనిక: ఈ అనుమతి మూడవ పక్షం బ్రౌజర్‌లు లేదా వెబ్ బ్రౌజింగ్ సామర్థ్యాలు గల ఇతర యాప్‌ల ద్వారా అమలు చేయబడకపోవచ్చు."</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g>ని సిద్ధం చేస్తోంది."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"యాప్‌లను ప్రారంభిస్తోంది."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"బూట్‌ను ముగిస్తోంది."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"స్క్రీన్‌ను ఆఫ్ చేయాలా?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"మీ వేలిముద్రను సెటప్ చేస్తున్నప్పుడు, మీరు పవర్ బటన్‌ను నొక్కారు.\n\nఇది సాధారణంగా మీ స్క్రీన్‌ను ఆఫ్ చేస్తుంది."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"ఆఫ్ చేయండి"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"రద్దు చేయండి"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"సెటప్‌ను కొనసాగించాలా?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"మీరు పవర్ బటన్‌ను నొక్కారు — ఇది సాధారణంగా స్క్రీన్‌ను ఆఫ్ చేస్తుంది.\n\nమీ వేలిముద్రను సెటప్ చేస్తున్నప్పుడు తేలికగా ట్యాప్ చేయడానికి ట్రై చేయండి."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"స్క్రీన్‌ను ఆఫ్ చేయి"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"సెటప్‌ను కొనసాగించు"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"మీ వేలిముద్ర వెరిఫై‌ను కొనసాగించాలా?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"మీరు పవర్ బటన్‌ను నొక్కారు — ఇది సాధారణంగా స్క్రీన్‌ను ఆఫ్ చేస్తుంది.\n\nమీ వేలిముద్రను వెరిఫై చేయడానికి తేలికగా ట్యాప్ చేయడం ట్రై చేయండి."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"స్క్రీన్‌ను ఆఫ్ చేయి"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"కొనసాగించండి"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> అమలవుతోంది"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"గేమ్‌కి తిరిగి రావడానికి నొక్కండి"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"గేమ్‌ను ఎంచుకోండి"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index b8d8506febf2..41adb26abfaa 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"อนุญาตให้แอปใช้บริการ IMS เพื่อโทรออกโดยคุณไม่ต้องดำเนินการใดๆ เลย"</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"อ่านสถานะและข้อมูลระบุตัวตนของโทรศัพท์"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"อนุญาตให้แอปพลิเคชันเข้าถึงฟีเจอร์โทรศัพท์ของอุปกรณ์ การอนุญาตนี้ทำให้แอปพลิเคชันสามารถตรวจสอบหมายเลขโทรศัพท์และรหัสอุปกรณ์ ตรวจสอบว่ามีการโทรที่ทำงานอยู่หรือไม่ และตรวจสอบหมายเลขระยะไกลที่เชื่อมต่อด้วยการโทร"</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"อ่านสถานะการใช้โทรศัพท์เบื้องต้นและข้อมูลระบุตัวตน"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"อนุญาตให้แอปเข้าถึงฟีเจอร์พื้นฐานในการใช้โทรศัพท์ของอุปกรณ์นี้"</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"กำหนดเส้นทางการโทรผ่านระบบ"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"อนุญาตให้แอปกำหนดเส้นทางการโทรของแอปผ่านระบบเพื่อปรับปรุงประสบการณ์ในการโทร"</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"ดูและจัดการการติดต่อผ่านระบบ"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"แตะเพื่อลบรูปแบบใบหน้า แล้วเพิ่มใบหน้าอีกครั้ง"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"ตั้งค่าการปลดล็อกด้วยใบหน้า"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ปลดล็อกโทรศัพท์โดยมองไปที่โทรศัพท์"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"หากต้องการใช้ปลดล็อกด้วยใบหน้า ให้เปิด"<b>"การเข้าถึงกล้อง"</b>"ในการตั้งค่าและความเป็นส่วนตัว"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"ตั้งค่าการปลดล็อกด้วยวิธีอื่น"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"แตะเพื่อเพิ่มลายนิ้วมือ"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ปลดล็อกด้วยลายนิ้วมือ"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"กำลังเตรียม <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"กำลังเริ่มต้นแอปพลิเคชัน"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"เสร็จสิ้นการบูต"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"ปิดหน้าจอใช่ไหม"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"ขณะตั้งค่าลายนิ้วมือคุณจะต้องกดปุ่มเปิด/ปิด\n\nซึ่งโดยปกติจะเป็นการปิดหน้าจอด้วย"</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"ปิด"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"ยกเลิก"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"ตั้งค่าต่อไหม"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"คุณกดปุ่มเปิด/ปิดซึ่งโดยปกติจะเป็นการปิดหน้าจอ\n\nลองแตะเบาๆ ขณะตั้งค่าลายนิ้วมือ"</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"ปิดหน้าจอ"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"ตั้งค่าต่อ"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"ยืนยันลายนิ้วมือต่อไหม"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"คุณกดปุ่มเปิด/ปิดซึ่งโดยปกติจะเป็นการปิดหน้าจอ\n\nลองแตะเบาๆ เพื่อยืนยันลายนิ้วมือ"</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"ปิดหน้าจอ"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"ต่อไป"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> กำลังทำงาน"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"แตะเพื่อกลับไปที่เกม"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"เลือกเกม"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 30350a5ab8a7..7f39769d4e77 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Pinapahintulutan ang app na gamitin ang serbisyo ng IMS upang tumawag nang walang pahintulot mo."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"basahin ang katayuan at pagkakakilanlan ng telepono"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Pinapayagan ang app na i-access ang mga tampok ng telepono ng device. Pinapayagan ng pahintulot na ito ang app na tukuyin ang numero ng telepono at mga ID ng device, kung aktibo man ang isang tawag, at ang malayuang numerong ikinonekta ng isang tawag."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"basahin ang pangunahing status at pagkakakilanlan sa telephony"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Pinapayagan ang app na i-access ang mga pangunahing feature sa telephony ng device."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"iruta ang mga tawag sa pamamagitan ng system"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Pinapayagan ang app na iruta ang mga tawag nito sa pamamagitan ng system upang mapahusay ang karanasan sa pagtawag."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"tingnan at kontrolin ang mga tawag sa pamamagitan ng system."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"I-tap para i-delete ang iyong face model, pagkatapos ay idagdag ulit ang mukha mo"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"I-set up ang Pag-unlock Gamit ang Mukha"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"I-unlock ang iyong telepono sa pamamagitan ng pagtingin dito"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Para magamit ang Pag-unlock Gamit ang Mukha, i-on ang "<b>"Access sa camera"</b>" sa Mga Setting &gt; Privacy"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Mag-set up ng higit pang paraan para mag-unlock"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"I-tap para magdagdag ng fingerprint"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Pag-unlock Gamit ang Fingerprint"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Ihinahanda ang <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Sinisimulan ang apps."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Pagtatapos ng pag-boot."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"I-off ang screen?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Habang sine-set up ang iyong fingerprint, pinindot mo ang Power button.\n\nKaraniwan nitong ino-off ang iyong screen."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"I-off"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Kanselahin"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Ituloy ang pag-set up?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Pinindot mo ang power button — karaniwan nitong ino-off ang screen.\n\nSubukang i-tap habang sine-set up ang iyong fingerprint."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"I-off ang screen"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Ituloy ang setup"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Magpatuloy sa pag-verify ng fingerprint?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Pinindot mo ang power button — karaniwan nitong ino-off ang screen.\n\nSubukang i-tap para i-verify ang iyong fingerprint."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"I-off ang screen"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Magpatuloy"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"Tumatakbo ang <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Mag-tap upang bumalik sa laro"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Pumili ng laro"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index e676c8959aae..bc26fedd0ab9 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Uygulamanın, sizin müdahaleniz olmadan telefon etmek için IMS hizmetini kullanmasına izin verir."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"telefonun durumunu ve kimliğini okuma"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Uygulamaya cihazdaki telefon özelliklerine erişme izni verir. Bu izin, uygulamanın telefon numarasını ve cihaz kimliğini, etkin bir çağrı olup olmadığını ve çağrıda bağlanılan karşı tarafın numarasını öğrenmesine olanak sağlar."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"temel telefon durumunu ve kimliğini okuma"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Uygulamanın, cihazdaki temel telefon özelliklerine erişmesine izin verir."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"aramaları sistem üzerinden yönlendir"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Uygulamanın, çağrı deneyimini iyileştirmek için çağrılarını sistem üzerinden yönlendirmesine olanak tanır."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"aramaları sistemde görüp denetleme."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Yüz modelinizi silmek için dokunup ardından yüzünüzü yeniden ekleyin"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Yüz Tanıma Kilidi\'ni kurma"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Telefonunuza bakarak kilidini açın"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Yüz Tanıma Kilidi\'ni kullanmak için Ayarlar &gt; Gizlilik bölümünden "<b>"Kamera erişimi"</b>"\'ni açın"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Kilidi açmak için daha fazla yöntem ayarlayın"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Parmak izi eklemek için dokunun"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Parmak İzi Kilidi"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> hazırlanıyor."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Uygulamalar başlatılıyor"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Açılış tamamlanıyor."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Ekran kapatılsın mı?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Parmak izinizi ayarlarken Güç düğmesine bastınız.\n\nBu hareket genellikle ekranınızın kapanmasına neden olur."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Kapat"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"İptal"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Kuruluma devam edilsin mi?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Güç düğmesine bastınız. Bu düğmeye basıldığında genellikle ekran kapanır.\n\nParmak izinizi tanımlarken hafifçe dokunmayı deneyin."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Ekranı kapat"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Kuruluma devam et"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Parmak izi doğrulamaya devam edilsin mi?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Güç düğmesine bastınız. Bu düğmeye basıldığında genellikle ekran kapanır.\n\nParmak izinizi doğrulamak için hafifçe dokunmayı deneyin."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Ekranı kapat"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Devam"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> çalışıyor"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Oyuna geri dönmek için dokunun"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Oyun seçin"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 18c5600738d1..5ce1407f1737 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -477,6 +477,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Додаток зможе телефонувати за допомогою служби IMS без вашого відома."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"читати статус та ідентифікаційну інформацію телефону"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Дозволяє програмі отримувати доступ до телефонних функцій пристрою. Такий дозвіл дає програмі змогу визначати номер телефону й ідентифікатори пристрою, активність виклику, а також віддалений номер, на який здійснюється виклик."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"зчитувати статус та ідентифікаційну інформацію телефонії"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Дозволяє додатку отримувати доступ до базових функцій телефонії на пристрої."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"маршрутизувати виклики через систему"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Дозволяє додатку маршрутизувати виклики через систему, щоб було зручніше телефонувати."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"Переглядати виклики через систему й керувати ними."</string>
@@ -628,6 +630,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Натисніть, щоб видалити свою модель обличчя, а потім знову додайте її"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Налаштування фейсконтролю"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Ви зможете розблоковувати телефон, подивившись на нього"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Щоб використовувати фейсконтроль, увімкніть "<b>"Доступ до камери"</b>" в розділі \"Налаштування\" &gt; \"Конфіденційність\""</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Налаштуйте більше способів розблокування"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Натисніть, щоб додати відбиток пальця"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Розблокування відбитком пальця"</string>
@@ -1316,10 +1319,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Підготовка додатка <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Запуск програм."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Завершення завантаження."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Вимкнути екран?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Налаштовуючи відбиток пальця, ви натиснули кнопку живлення.\n\nЗазвичай після цього вимикається екран."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Вимкнути"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Скасувати"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Продовжити реєстрацію?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Ви натиснули кнопку живлення – зазвичай після цього вимикається екран.\n\nЩоб зареєструвати відбиток пальця, спробуйте лише злегка торкнутися датчика."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Вимкнути екран"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Додати відбиток"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Продовжити підтвердження відбитка?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Ви натиснули кнопку живлення – зазвичай після цього вимикається екран.\n\nЩоб підтвердити відбиток пальця, спробуйте лише злегка торкнутися датчика."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Вимкнути екран"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Продовжити"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"Працює <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Торкніться, щоб повернутися в гру"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Виберіть гру"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index fdb8ddd0e1fe..7ceaa992d0d1 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"‏آپ کی مداخلت کے بغیر کالیں کرنے کیلئے ایپ کو IMS سروس استعمال کرنے کی اجازت دیتی ہے۔"</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"فون کے اسٹیٹس اور شناخت کو پڑھیں"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"‏ایپ کو آلے کی فون والی خصوصیات تک رسائی حاصل کرنے کی اجازت دیتا ہے۔ یہ اجازت ایپ کو فون نمبر اور آلے کے IDs کا تعین کرنے، آیا کوئی کال فعال ہے، اور کال کے ذریعہ مربوط ریموٹ نمبر کا تعین کرنے دیتی ہے۔"</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"بنیادی ٹیلیفونی صورتحال اور شناخت کو پڑھیں"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"اس سے ایپ کو آلے کی بنیادی ٹیلیفونی خصوصیات تک رسائی کی اجازت ملتی ہے۔"</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"سسٹم کے ذریعہ کالز روٹ کریں"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"کالںگ کا تجربہ بہتر بنانے کے لیے سسٹم کے ذریعہ ایپ کو کالز روٹ کرنے کی اجازت دیتا ہے۔"</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"سسٹم کے ذریعے کالز دیکھیں اور کنٹرول کریں۔"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"اپنے چہرے کا ماڈل حذف کرنے کے لیے تھپتھپائیں پھر اپنا چہرہ دوبارہ شامل کریں"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"فیس اَنلاک سیٹ اپ کریں"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"اپنے فون کی طرف دیکھ کر اسے غیر مقفل کریں"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"فیس اَنلاک کا استعمال کرنے کے لیے، ترتیبات اور رازداری میں "<b>"کیمرے تک رسائی"</b>" کو آن کریں"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"غیر مقفل کرنے کے مزید طریقے سیٹ اپ کریں"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"فنگر پرنٹ شامل کرنے کیلئے تھپتھپائیں"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"فنگر پرنٹ اَن لاک"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> تیار ہو رہی ہے۔"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"ایپس شروع ہو رہی ہیں۔"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"بوٹ مکمل ہو رہا ہے۔"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"اسکرین آف کریں؟"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"اپنی فنگر پرنٹ سیٹ اپ کرنے کے دوران آپ نے پاور بٹن دبایا۔\n\n یہ عام طور پر آپ کی اسکرین کو آف کرتی ہے۔"</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"آف کریں"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"منسوخ کریں"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"سیٹ اپ جاری رکھیں؟"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"آپ نے پاور بٹن دبایا — اس سے عام طور پر اسکرین آف ہو جاتی ہے۔\n\nاپنے فنگر پرنٹ کو سیٹ اپ کرنے کے دوران ہلکا سا تھپتھپانے کی کوشش کریں۔"</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"اسکرین آف کریں"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"سیٹ اپ جاری رکھیں"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"اپنے فنگر پرنٹ کی توثیق کرنا جاری رکھیں؟"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"آپ نے پاور بٹن دبایا — اس سے عام طور پر اسکرین آف ہو جاتی ہے۔\n\nاپنے فنگر پرنٹ کی توثیق کرنے کے لیے ہلکا سا تھپتھپانے کی کوشش کریں۔"</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"اسکرین آف کریں"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"جاری رکھیں"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> چل رہی ہے"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"گیم پر واپس جانے کے لیے تھپتھپائیں"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"گیم کا انتخاب کریں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 73cc4961bcc2..a40b67f19934 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Ilovaga sizning ishtirokingizsiz qo‘ng‘iroqlarni amalga oshirish uchun IMS xizmatidan foydalanishga ruxsat beradi."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"telefon holati haqidagi ma’lumotlarni olish"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Ilovaga qurilmangizdagi telefon xususiyatlariga kirishga ruxsat beradi. Bu ruxsat ilovaga telefon raqami va qurilma nomlari, qo‘ng‘iroq faol yoki faolsizligi va masofadagi raqam qo‘ng‘rioq orqali bog‘langanligini aniqlashga imkon beradi."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"oddiy telefoniya holatini oʻqish va aniqlash"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Ilovaga qurilmadagi oddiy telefoniya funksiyalaridan foydalanish imkonini beradi."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"chaqiruvlarni tizim orqali yo‘naltirish"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Ilova aloqa sifatini yaxshilash maqsadida chaqiruvlarni tizim orqali yo‘naltirishi mumkin."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"tizim orqali chaqiruvlarni tekshirish va boshqarish."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Yuz modelini oʻchirish uchun bosing va keyin yana yuzni qoʻshing"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Yuz bilan ochishni sozlash"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Telefoningizni yuz tekshiruvi yordamida qulfdan chiqaring"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Yuz bilan ochish uchun Sozlamalar va maxfiylik orqali "<b>"kameraga kirishga ruxsat bering"</b></string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Qulfdan chiqarishning boshqa usullarini sozlang"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Barmoq izi kiritish uchun bosing"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Barmoq izi bilan ochish"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> tayyorlanmoqda."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Ilovalar ishga tushirilmoqda."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Tizimni yuklashni tugatish."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Ekran oʻchirilsinmi?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Barmoq izini sozlayotganda Quvvat tugmasini bosdingiz.\n\nBunda odatda ekran oʻchib qoladi."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Oʻchirish"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Bekor qilish"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Sozlashni davom ettirasizmi?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Quvvat tugmasini bosdingiz — bu odatda ekranni oʻchiradi.\n\nBarmoq izini qoʻshish vaqtida tugmaga yengilgina tegining."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Ekranni oʻchirish"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Sozlashni davom ettirish"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Barmoq izi tasdiqlashda davom etilsinmi?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Quvvat tugmasini bosdingiz. Bu odatda ekranni oʻchiradi.\n\nBarmoq izingizni tasdiqlash uchun tugmaga yengilgina tegining."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Ekranni oʻchirish"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Davom etish"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> ishlamoqda"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"O‘yinga qaytish uchun bosing"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"O‘yinni tanlang"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index d47b4f484e5c..1731b3b2c4db 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Cho phép ứng dụng sử dụng dịch vụ IMS để thực hiện cuộc gọi mà không có sự can thiệp của bạn."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"đọc trạng thái và nhận dạng của điện thoại"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Cho phép ứng dụng truy cập vào các tính năng điện thoại của thiết bị. Quyền này cho phép ứng dụng xác định số điện thoại và ID thiết bị, cho dù cuộc gọi có hiện hoạt hay không và số từ xa có được kết nối bằng một cuộc gọi hay không."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"đọc danh tính và trạng thái cơ bản của điện thoại"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Cho phép ứng dụng dùng các tính năng cơ bản của điện thoại trên thiết bị."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"định tuyến cuộc gọi thông qua hệ thống"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Cho phép ứng dụng định tuyến cuộc gọi thông qua hệ thống nhằm cải thiện trải nghiệm gọi điện."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"xem và kiểm soát cuộc gọi thông qua hệ thống."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Nhấn để xóa mẫu khuôn mặt, sau đó thêm lại khuôn mặt của bạn"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Thiết lập tính năng Mở khóa bằng khuôn mặt"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Mở khóa điện thoại bằng cách nhìn vào điện thoại"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Để dùng tính năng Mở khoá bằng khuôn mặt, hãy bật tuỳ chọn "<b>"Truy cập máy ảnh"</b>" trong phần Cài đặt &gt; Quyền riêng tư"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Thiết lập thêm những cách mở khóa khác"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Nhấn để thêm vân tay"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Mở khóa bằng vân tay"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Đang chuẩn bị <xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Khởi động ứng dụng."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Hoàn tất khởi động."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Tắt màn hình?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Trong khi thiết lập vân tay, bạn đã nhấn vào nút Nguồn.\n\nThông thường, thao tác này sẽ tắt màn hình."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Tắt"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Hủy"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Tiếp tục thiết lập?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Bạn đã nhấn nút nguồn – thao tác này thường tắt màn hình.\n\nHãy thử nhấn nhẹ khi thiết lập vân tay của bạn."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Tắt màn hình"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Tiếp tục thiết lập"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Tiếp tục xác minh vân tay của bạn?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Bạn đã nhấn nút nguồn – thao tác này thường tắt màn hình.\n\nHãy thử nhấn nhẹ để xác minh vân tay."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Tắt màn hình"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Tiếp tục"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> đang hoạt động"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Nhấn để quay lại trò chơi"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Chọn trò chơi"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 17ce14b90136..8ce9ab2e4370 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"允许应用自行使用即时通讯服务拨打电话。"</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"读取手机状态和身份"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"允许该应用访问设备的电话功能。此权限可让该应用确定本机号码和设备 ID、是否正处于通话状态以及拨打的号码。"</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"读取基本电话状态和身份信息"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"允许该应用使用设备的基本电话功能。"</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"通过系统转接来电"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"允许该应用通过系统转接来电,以改善通话体验。"</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"查看并控制通过系统拨打的电话。"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"请点按以删除您的脸部模型,然后再添加您的脸部模型"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"设置人脸解锁"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"脸部对准手机即可将其解锁"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"如需使用人脸解锁功能,请在“设置”&gt;“隐私权”中开启"<b>"摄像头使用权限"</b></string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"设置更多解锁方式"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"点按即可添加指纹"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"指纹解锁"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"正在准备升级<xliff:g id="APPNAME">%1$s</xliff:g>。"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"正在启动应用。"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"即将完成启动。"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"要关闭屏幕吗?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"您在设置指纹时按了电源按钮。\n\n此操作通常会关闭屏幕。"</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"关闭"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"取消"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"要继续设置吗?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"您已按电源按钮,这通常会关闭屏幕。\n\n请尝试在设置指纹时轻轻按一下。"</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"关闭屏幕"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"继续设置"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"要继续验证您的指纹吗?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"您已按电源按钮,这通常会关闭屏幕。\n\n请尝试轻轻按一下来验证您的指纹。"</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"关闭屏幕"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"继续"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g>正在运行"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"点按即可返回游戏"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"选择游戏"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 35a11b2eef96..49850e14d91a 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"允許應用程式自行使用 IMS 服務撥打電話。"</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"讀取手機狀態和識別碼"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"允許應用程式使用裝置的電話功能。這項權限允許應用程式確定手機號碼和裝置編號、是否正在通話中,以及所撥打的對方號碼。"</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"讀取基本電話狀態和身分"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"允許應用程式存取裝置的基本電話功能。"</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"透過系統轉接來電"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"允許應用程式透過系統轉接來電,以改善通話體驗。"</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"透過系統查看和控制通話。"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"請輕按這裡刪除面部模型,然後再重新新增"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"設定「面孔解鎖」"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"直望手機即可解鎖"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"如要使用「面孔解鎖」,請在 [設定] &gt; [私隱] 開啟"<b>"相機存取權"</b></string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"設定更多解鎖方法"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"輕按即可新增指紋"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"指紋解鎖"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"正在準備 <xliff:g id="APPNAME">%1$s</xliff:g>。"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"正在啟動應用程式。"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"啟動完成。"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"要關閉螢幕嗎?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"您在設定指紋時按了開關按鈕。\n\n螢幕通常會因此而關閉。"</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"關閉"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"取消"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"要繼續設定嗎?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"您已按下開關按鈕,這麼做通常會關閉螢幕。\n\n設定指紋時請嘗試輕按開關按鈕。"</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"關閉螢幕"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"繼續設定"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"要繼續驗證指紋嗎?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"您已按下開關按鈕,這麼做通常會關閉螢幕。\n\n嘗試輕按開關按鈕以驗證指紋。"</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"關閉螢幕"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"繼續"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"正在執行 <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"輕按即可返回遊戲"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"選擇遊戲"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 42f0586a13af..329b5dc15661 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"允許應用程式自動使用 IMS 服務撥打電話。"</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"讀取手機狀態和識別碼"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"允許應用程式使用裝置的電話功能。這項權限可讓應用程式判讀手機號碼和裝置 ID、是否正在通話中,以及所撥打的對方號碼。"</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"讀取基本電話通訊狀態和識別資訊"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"允許應用程式存取裝置的基本電話通訊功能。"</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"透過系統接通來電"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"允許應用程式透過系統接通來電,以改善通話品質。"</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"查看及控管透過系統撥打的電話。"</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"請輕觸這裡刪除臉部模型,然後再重新新增"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"設定人臉解鎖功能"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"看著手機就能解鎖"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"如要使用人臉解鎖功能,請前往「設定」&gt;「隱私權」開啟"<b>"攝影機存取權"</b></string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"設定更多解鎖方式"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"輕觸即可新增指紋"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"指紋解鎖"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"正在準備升級「<xliff:g id="APPNAME">%1$s</xliff:g>」。"</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"正在啟動應用程式。"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"啟動完成。"</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"要關閉螢幕嗎?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"設定指紋時必須按下電源鍵。\n\n這項操作通常會將螢幕關閉。"</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"關閉"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"取消"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"要繼續設定嗎?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"你已按下電源鍵,這麼做通常會關閉螢幕。\n\n設定指紋時請試著減輕觸碰電源鍵的力道。"</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"關閉螢幕"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"繼續設定"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"要繼續驗證指紋嗎?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"你已按下電源鍵,這麼做通常會關閉螢幕。\n\n驗證指紋時,請試著減輕觸碰電源鍵的力道。"</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"關閉螢幕"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"繼續"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> 執行中"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"輕觸即可返回遊戲"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"選擇遊戲"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 3f7e37d4ea5d..8c7d5ea1a6bc 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -471,6 +471,8 @@
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Ivumela uhlelo lokusebenza ukuthi lusebenzise isevisi ye-IMS ukuze yenze amakholi ngaphandle kokungenelela kwakho."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"funda isimo sefoni kanye nesazisi"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Ivumela uhlelo lokusebenza ukufinyelela izici zefoni zedivayisi. Le mvume ivumela uhlelo lokusebenza ukucacisa inombolo yefoni nobunikazi bedivayisi, ukuthi noma ikholi iyasebenza, futhi nenombolo yesilawuli kude zixhunywe ngekholi."</string>
+ <string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"funda isimo nokuhlonza ucingo okuyisisekelo"</string>
+ <string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Ivumela i-app ukufinyelela izakhi zocingo eziyisisekelo zedivayisi."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"yanza imizila yamakholi ngesistimu"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Ivumela uhlelo lokusebenza ukwenza imizila yamakholi ngesistimu ukuze ithuthukise umuzwa wokushaya."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"bona futhi ulawule amakholi ngesistimu."</string>
@@ -622,6 +624,7 @@
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Thepha ukuze usule imodeli yakho yobuso, bese wengeza futhi ubuso"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Setha Ukuvula ngobuso"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Vula ifoni yakho ngokuyibheka"</string>
+ <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Ukuze usebenzise Ukuvula ngobuso, vula "<b>"Ukufinyelela kwekhamera"</b>" kokuthi Amasethingi &gt; Ubumfihlo"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Setha izindlela eziningi zokuvula"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Thepha ukuze ungeze izigxivizo zomunwe"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Ukuvula ngesigxivizo somunwe"</string>
@@ -1276,10 +1279,14 @@
<string name="android_preparing_apk" msgid="589736917792300956">"Ukulungisela i-<xliff:g id="APPNAME">%1$s</xliff:g>."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Qalisa izinhlelo zokusebenza."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Qedela ukuqala kabusha."</string>
- <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Vala isikrini?"</string>
- <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Lapho usetha izigxivizo zeminwe yakho, ucindezele Inkinobho yamandla.\n\nLokhu kuvame ukuvala isikrini sakho."</string>
- <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Vala"</string>
- <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Khansela"</string>
+ <string name="fp_power_button_enrollment_title" msgid="3574363228413259548">"Qhubeka nokusetha?"</string>
+ <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Ucindezele inkinobho yamandla — lokhu kuvame ukuvala isikrini.\n\nZama ukuthepha kancane ngenkathi usetha isigxivizo sakho somunwe."</string>
+ <string name="fp_power_button_enrollment_positive_button" msgid="2095415838459356833">"Vala isikrini"</string>
+ <string name="fp_power_button_enrollment_negative_button" msgid="6558436406362486747">"Qhubeka nokusetha"</string>
+ <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Qhubeka uqinisekise isigxivizo sakho somunwe?"</string>
+ <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Ucindezele inkinobho yamandla — lokhu kuvame ukuvala isikrini.\n\nZama ukuthepha kancane ukuze uqinisekise isigxivizo sakho somunwe."</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Vala isikrini"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Qhubeka"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> iyasebenza"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Thepha ukuze ubuyele emuva kwigeyimu"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Khetha igeyimu"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 60766459f1cb..8aa92018aa7c 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3638,6 +3638,9 @@
to re-retrieve all resources (including view layouts, drawables, etc)
to correctly handle any configuration change.-->
<attr name="configChanges" />
+ <!-- Specifies whether the IME supports Handwriting using stylus. Defaults to false. -->
+ <attr name="supportsStylusHandwriting" format="boolean" />
+
</declare-styleable>
<!-- This is the subtype of InputMethod. Subtype can describe locales (for example, en_US and
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 0a6ef7cf6f21..06f347f4daff 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2584,6 +2584,9 @@
<declare-styleable name="AndroidManifestProcess" parent="AndroidManifestProcesses">
<!-- Required name of the process that is allowed -->
<attr name="process" />
+ <!-- custom Application class name. We use call it "name", not "className", to be
+ consistent with the Application tag. -->
+ <attr name="name" />
<attr name="gwpAsanMode" />
<attr name="memtagMode" />
<attr name="nativeHeapZeroInitialized" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index ddec469f63ad..be32c42eb30d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3333,6 +3333,20 @@
and one pSIM) -->
<integer name="config_num_physical_slots">1</integer>
+ <!--The default "usage setting" indicating that the device is either a voice-centric
+ device (1) or a data-centric device (2). A voice-centric device will require that any cellular
+ service that it uses provides access to voice capability, and a data-centric device will
+ likewise require that the network provides access to data services. These settings are
+ sent to the cellular modem and control the behavior in accordance with 3gpp TS 24.301 sec 4.3
+ (and equivalent functionality in other generations of cellular).-->
+ <integer name="config_default_cellular_usage_setting">1</integer>
+
+ <!--The list of supported cellular usage settings for this device.-->
+ <integer-array translatable="false" name="config_supported_cellular_usage_settings">
+ <item>1</item> <!-- USAGE_SETTING_VOICE_CENTRIC -->
+ <item>2</item> <!-- USAGE_SETTING_DATA_CENTRIC -->
+ </integer-array>
+
<!-- When a radio power off request is received, we will delay completing the request until
either IMS moves to the deregistered state or the timeout defined by this configuration
elapses. If 0, this feature is disabled and we do not delay radio power off requests.-->
@@ -4019,6 +4033,11 @@
-->
<string name="config_defaultSystemCaptionsManagerService" translatable="false"></string>
+ <!-- Indicates whether the system wide captions service should also support
+ call captioning.
+ -->
+ <bool name="config_systemCaptionsServiceCallsEnabled" translatable="false"></bool>
+
<!-- The package name for the incident report approver app.
This app is usually PermissionController or an app that replaces it. When
a bugreport or incident report with EXPLICT-level sharing flags is going to be
@@ -5049,6 +5068,10 @@
If given value is outside of this range, the option 1 (center) is assummed. -->
<integer name="config_letterboxDefaultPositionForReachability">1</integer>
+ <!-- Whether a camera compat controller is enabled to allow the user to apply or revert
+ treatment for stretched issues in camera viewfinder. -->
+ <bool name="config_isCameraCompatControlForStretchedIssuesEnabled">false</bool>
+
<!-- If true, hide the display cutout with display area -->
<bool name="config_hideDisplayCutoutWithDisplayArea">false</bool>
@@ -5518,4 +5541,13 @@
</string-array>
<integer name="config_chooser_max_targets_per_row">4</integer>
+
+ <!-- Package that provides the supervised user creation flow. This package must include an
+ activity with an intent filter for {@link UserManager.ACTION_CREATE_SUPERVISED_USER}.
+ When this resource is defined, an extra button in user settings screen will be shown
+ with a title defined in @*android:string/supervised_user_creation_label
+ and an icon defined in @*android:drawable/ic_add_supervised_user.
+ That button will fire an intent targeted for this package with the mentioned action.
+ When this resource is empty, that button will not be shown. -->
+ <string name="config_supervisedUserCreationPackage" translatable="false"></string>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 1aa3ac235a6a..3d3c86006871 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3249,6 +3249,7 @@
<public name="canDisplayOnRemoteDevices" />
<public name="supportedTypes" />
<public name="resetEnabledSettingsOnAppDataCleared" />
+ <public name="supportsStylusHandwriting" />
</staging-public-group>
<staging-public-group type="id" first-id="0x01de0000">
@@ -3308,6 +3309,8 @@
</staging-public-group>
<staging-public-group type="bool" first-id="0x01cf0000">
+ <!-- @hide @SystemApi -->
+ <public name="config_systemCaptionsServiceCallsEnabled" />
</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 769e667a2b25..b16e462192f5 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5374,6 +5374,8 @@
<string name="user_creation_account_exists">Allow <xliff:g id="app" example="Gmail">%1$s</xliff:g> to create a new User with <xliff:g id="account" example="foobar@gmail.com">%2$s</xliff:g> (a User with this account already exists) ?</string>
<!-- Message to user that app is trying to create user for a specified account. [CHAR LIMIT=none] -->
<string name="user_creation_adding">Allow <xliff:g id="app" example="Gmail">%1$s</xliff:g> to create a new User with <xliff:g id="account" example="foobar@gmail.com">%2$s</xliff:g> ?</string>
+ <!-- String label displayed on buttons that trigger the flow for creating supervised users. [CHAR LIMIT=35] -->
+ <string name="supervised_user_creation_label">Add supervised user</string>
<!-- Locale picker strings -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 55bf24b25b0d..527865f77257 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -492,6 +492,8 @@
<java-symbol type="string" name="config_deviceSpecificDevicePolicyManagerService" />
<java-symbol type="string" name="config_deviceSpecificAudioService" />
<java-symbol type="integer" name="config_num_physical_slots" />
+ <java-symbol type="integer" name="config_default_cellular_usage_setting" />
+ <java-symbol type="array" name="config_supported_cellular_usage_settings" />
<java-symbol type="integer" name="config_delay_for_ims_dereg_millis" />
<java-symbol type="array" name="config_integrityRuleProviderPackages" />
<java-symbol type="bool" name="config_useAssistantVolume" />
@@ -4292,6 +4294,7 @@
<java-symbol type="dimen" name="config_letterboxHorizontalPositionMultiplier" />
<java-symbol type="bool" name="config_letterboxIsReachabilityEnabled" />
<java-symbol type="integer" name="config_letterboxDefaultPositionForReachability" />
+ <java-symbol type="bool" name="config_isCameraCompatControlForStretchedIssuesEnabled" />
<java-symbol type="bool" name="config_hideDisplayCutoutWithDisplayArea" />
@@ -4629,4 +4632,6 @@
<java-symbol type="integer" name="config_mashPressVibrateTimeOnPowerButton" />
<java-symbol type="string" name="config_systemGameService" />
+
+ <java-symbol type="string" name="config_supervisedUserCreationPackage"/>
</resources>
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 64b303b724dc..32d72b37b8af 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -23,6 +23,7 @@ android_test {
],
aidl: {
+ generate_get_transaction_name: true,
local_include_dirs: ["aidl"],
},
diff --git a/core/tests/coretests/src/android/os/AidlTest.java b/core/tests/coretests/src/android/os/AidlTest.java
index 4c5141537c6a..5f54b093e5e5 100644
--- a/core/tests/coretests/src/android/os/AidlTest.java
+++ b/core/tests/coretests/src/android/os/AidlTest.java
@@ -27,11 +27,12 @@ import java.util.List;
public class AidlTest extends TestCase {
private IAidlTest mRemote;
+ private AidlObject mLocal;
@Override
protected void setUp() throws Exception {
super.setUp();
- AidlObject mLocal = new AidlObject();
+ mLocal = new AidlObject();
mRemote = IAidlTest.Stub.asInterface(mLocal);
}
@@ -417,5 +418,24 @@ public class AidlTest extends TestCase {
}
assertEquals(good, true);
}
+
+ @SmallTest
+ public void testGetTransactionName() throws Exception {
+ assertEquals(15, mLocal.getMaxTransactionId());
+
+ assertEquals("booleanArray",
+ mLocal.getTransactionName(IAidlTest.Stub.TRANSACTION_booleanArray));
+ assertEquals("voidSecurityException",
+ mLocal.getTransactionName(IAidlTest.Stub.TRANSACTION_voidSecurityException));
+ assertEquals("parcelableIn",
+ mLocal.getTransactionName(IAidlTest.Stub.TRANSACTION_parcelableIn));
+
+ assertEquals("IAidlTest:booleanArray",
+ mLocal.getTransactionTraceName(IAidlTest.Stub.TRANSACTION_booleanArray));
+ assertEquals("IAidlTest:voidSecurityException",
+ mLocal.getTransactionTraceName(IAidlTest.Stub.TRANSACTION_voidSecurityException));
+ assertEquals("IAidlTest:parcelableIn",
+ mLocal.getTransactionTraceName(IAidlTest.Stub.TRANSACTION_parcelableIn));
+ }
}
diff --git a/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java b/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java
index 16dcff5e0c8c..7ccbb0150cb6 100644
--- a/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java
+++ b/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java
@@ -16,6 +16,10 @@
package android.os.storage;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
import android.content.Context;
import android.content.res.Resources;
import android.test.InstrumentationTestCase;
@@ -23,6 +27,10 @@ import android.util.Log;
import libcore.io.Streams;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
@@ -39,6 +47,7 @@ public class StorageManagerBaseTest extends InstrumentationTestCase {
protected Context mContext = null;
protected StorageManager mSm = null;
+ @Mock private File mFile;
private static String LOG_TAG = "StorageManagerBaseTest";
protected static final long MAX_WAIT_TIME = 120*1000;
protected static final long WAIT_TIME_INCR = 5*1000;
@@ -121,12 +130,50 @@ public class StorageManagerBaseTest extends InstrumentationTestCase {
*/
@Override
public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
mContext = getInstrumentation().getContext();
mSm = (StorageManager)mContext.getSystemService(android.content.Context.STORAGE_SERVICE);
}
/**
+ * Tests the space reserved for cache when system has high free space i.e. more than
+ * StorageManager.STORAGE_THRESHOLD_PERCENT_HIGH of total space.
+ */
+ @Test
+ public void testGetStorageCacheBytesUnderHighStorage() throws Exception {
+ when(mFile.getUsableSpace()).thenReturn(10000L);
+ when(mFile.getTotalSpace()).thenReturn(15000L);
+ long result = mSm.getStorageCacheBytes(mFile, 0);
+ assertThat(result).isEqualTo(1500L);
+ }
+
+ /**
+ * Tests the space reserved for cache when system has low free space i.e. less than
+ * StorageManager.STORAGE_THRESHOLD_PERCENT_LOW of total space.
+ */
+ @Test
+ public void testGetStorageCacheBytesUnderLowStorage() throws Exception {
+ when(mFile.getUsableSpace()).thenReturn(10000L);
+ when(mFile.getTotalSpace()).thenReturn(250000L);
+ long result = mSm.getStorageCacheBytes(mFile, 0);
+ assertThat(result).isEqualTo(5000L);
+ }
+
+ /**
+ * Tests the space reserved for cache when system has moderate free space i.e.more than
+ * StorageManager.STORAGE_THRESHOLD_PERCENT_LOW of total space but less than
+ * StorageManager.STORAGE_THRESHOLD_PERCENT_HIGH of total space.
+ */
+ @Test
+ public void testGetStorageCacheBytesUnderModerateStorage() throws Exception {
+ when(mFile.getUsableSpace()).thenReturn(10000L);
+ when(mFile.getTotalSpace()).thenReturn(100000L);
+ long result = mSm.getStorageCacheBytes(mFile, 0);
+ assertThat(result).isEqualTo(4666L);
+ }
+
+ /**
* Creates an OBB file (with the given name), into the app's standard files directory
*
* @param name The name of the OBB file we want to create/write to
@@ -526,4 +573,4 @@ public class StorageManagerBaseTest extends InstrumentationTestCase {
doValidateIntContents(path + File.separator + "subdir2" + File.separator + "subdir2a"
+ File.separator + "subdir2a1", "OneToOneThousandInts", 0, 1000);
}
-} \ No newline at end of file
+}
diff --git a/core/tests/coretests/src/android/view/MotionEventTest.java b/core/tests/coretests/src/android/view/MotionEventTest.java
index 78a8f7b3f32e..c4c983d24af9 100644
--- a/core/tests/coretests/src/android/view/MotionEventTest.java
+++ b/core/tests/coretests/src/android/view/MotionEventTest.java
@@ -16,6 +16,7 @@
package android.view;
+import static android.view.InputDevice.SOURCE_CLASS_POINTER;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_POINTER_DOWN;
import static android.view.MotionEvent.TOOL_TYPE_FINGER;
@@ -214,4 +215,27 @@ public class MotionEventTest {
rotInvalid.transform(mat);
assertEquals(-1, rotInvalid.getSurfaceRotation());
}
+
+ @Test
+ public void testUsesPointerSourceByDefault() {
+ final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */,
+ ACTION_DOWN, 0 /* x */, 0 /* y */, 0 /* metaState */);
+ assertTrue(event.isFromSource(SOURCE_CLASS_POINTER));
+ }
+
+ @Test
+ public void testLocationOffsetOnlyAppliedToNonPointerSources() {
+ final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */,
+ ACTION_DOWN, 10 /* x */, 20 /* y */, 0 /* metaState */);
+ event.offsetLocation(40, 50);
+
+ // The offset should be applied since a pointer source is used by default.
+ assertEquals(50, (int) event.getX());
+ assertEquals(70, (int) event.getY());
+
+ // The offset should not be applied if the source is changed to a non-pointer source.
+ event.setSource(InputDevice.SOURCE_JOYSTICK);
+ assertEquals(10, (int) event.getX());
+ assertEquals(20, (int) event.getY());
+ }
}
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
index 3ce79444f1f5..ad1f298e57a1 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
@@ -16,6 +16,7 @@
package android.view.accessibility;
+import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.IAccessibilityServiceConnection;
import android.accessibilityservice.MagnificationConfig;
@@ -144,6 +145,10 @@ public class AccessibilityServiceConnectionImpl extends IAccessibilityServiceCon
return false;
}
+ public int setInputMethodEnabled(String imeId, boolean enabled) {
+ return AccessibilityService.SoftKeyboardController.ENABLE_IME_FAIL_UNKNOWN;
+ }
+
public boolean isAccessibilityButtonAvailable() {
return false;
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
index 4733f862ebba..69e617aaac19 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
@@ -83,7 +83,7 @@ public class BatteryUsageStatsTest {
final Parcel parcel = Parcel.obtain();
parcel.writeParcelable(outBatteryUsageStats, 0);
- assertThat(parcel.dataSize()).isLessThan(5000);
+ assertThat(parcel.dataSize()).isLessThan(5500);
parcel.setDataPosition(0);
diff --git a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
index 48a1da15d574..8d9d79d0c4d9 100644
--- a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
@@ -26,6 +26,8 @@ import static org.mockito.Mockito.when;
import android.net.NetworkCapabilities;
import android.net.NetworkStats;
import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.BatteryUsageStatsQuery;
import android.os.Process;
import android.os.UidBatteryConsumer;
import android.telephony.DataConnectionRealTimeInfo;
@@ -37,12 +39,15 @@ import android.telephony.TelephonyManager;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.google.common.collect.Range;
+
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
+@SuppressWarnings("GuardedBy")
public class MobileRadioPowerCalculatorTest {
private static final double PRECISION = 0.00001;
private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
@@ -55,7 +60,7 @@ public class MobileRadioPowerCalculatorTest {
.setAveragePower(PowerProfile.POWER_RADIO_SCANNING, 720.0)
.setAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX, 1440.0)
.setAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX,
- new double[] {720.0, 1080.0, 1440.0, 1800.0, 2160.0})
+ new double[]{720.0, 1080.0, 1440.0, 1800.0, 2160.0})
.initMeasuredEnergyStatsLocked();
@Test
@@ -81,7 +86,7 @@ public class MobileRadioPowerCalculatorTest {
// Note established network
stats.noteNetworkInterfaceForTransports("cellular",
- new int[] { NetworkCapabilities.TRANSPORT_CELLULAR });
+ new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
// Note application network activity
NetworkStats networkStats = new NetworkStats(10000, 1)
@@ -89,7 +94,7 @@ public class MobileRadioPowerCalculatorTest {
mStatsRule.setNetworkStats(networkStats);
ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
- new int[] {100, 200, 300, 400, 500}, 600);
+ new int[]{100, 200, 300, 400, 500}, 600);
stats.noteModemControllerActivity(mai, POWER_DATA_UNAVAILABLE, 10000, 10000);
mStatsRule.setTime(12_000_000, 12_000_000);
@@ -119,6 +124,90 @@ public class MobileRadioPowerCalculatorTest {
}
@Test
+ public void testTimerBasedModel_byProcessState() {
+ BatteryStatsImpl stats = mStatsRule.getBatteryStats();
+ BatteryStatsImpl.Uid uid = stats.getUidStatsLocked(APP_UID);
+
+ mStatsRule.setTime(1000, 1000);
+ uid.setProcessStateForTest(
+ BatteryStats.Uid.PROCESS_STATE_FOREGROUND, 1000);
+
+ // Scan for a cell
+ stats.notePhoneStateLocked(ServiceState.STATE_OUT_OF_SERVICE,
+ TelephonyManager.SIM_STATE_READY,
+ 2000, 2000);
+
+ // Found a cell
+ stats.notePhoneStateLocked(ServiceState.STATE_IN_SERVICE, TelephonyManager.SIM_STATE_READY,
+ 5000, 5000);
+
+ // Note cell signal strength
+ SignalStrength signalStrength = mock(SignalStrength.class);
+ when(signalStrength.getLevel()).thenReturn(SignalStrength.SIGNAL_STRENGTH_MODERATE);
+ stats.notePhoneSignalStrengthLocked(signalStrength, 5000, 5000);
+
+ stats.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH,
+ 8_000_000_000L, APP_UID, 8000, 8000);
+
+ // Note established network
+ stats.noteNetworkInterfaceForTransports("cellular",
+ new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
+
+ // Note application network activity
+ mStatsRule.setNetworkStats(new NetworkStats(10000, 1)
+ .insertEntry("cellular", APP_UID, 0, 0, 1000, 100, 2000, 20, 100));
+
+ stats.noteModemControllerActivity(null, POWER_DATA_UNAVAILABLE, 10000, 10000);
+
+ uid.setProcessStateForTest(
+ BatteryStats.Uid.PROCESS_STATE_BACKGROUND, 11000);
+
+ mStatsRule.setNetworkStats(new NetworkStats(12000, 1)
+ .insertEntry("cellular", APP_UID, 0, 0, 1000, 250, 2000, 80, 200));
+
+ stats.noteModemControllerActivity(null, POWER_DATA_UNAVAILABLE, 12000, 12000);
+
+ assertThat(uid.getMobileRadioMeasuredBatteryConsumptionUC()).isAtMost(0);
+ // 12000-8000 = 4000 ms == 4_000_000 us
+ assertThat(uid.getMobileRadioActiveTimeInProcessState(BatteryConsumer.PROCESS_STATE_ANY))
+ .isEqualTo(4_000_000);
+ assertThat(uid.getMobileRadioActiveTimeInProcessState(
+ BatteryConsumer.PROCESS_STATE_FOREGROUND))
+ .isEqualTo(3_000_000);
+ assertThat(uid.getMobileRadioActiveTimeInProcessState(
+ BatteryConsumer.PROCESS_STATE_BACKGROUND))
+ .isEqualTo(1_000_000);
+ assertThat(uid.getMobileRadioActiveTimeInProcessState(
+ BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE))
+ .isEqualTo(0);
+
+ MobileRadioPowerCalculator calculator =
+ new MobileRadioPowerCalculator(mStatsRule.getPowerProfile());
+
+ mStatsRule.apply(new BatteryUsageStatsQuery.Builder()
+ .powerProfileModeledOnly()
+ .includePowerModels()
+ .includeProcessStateData()
+ .build(), calculator);
+
+ UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
+
+ final BatteryConsumer.Key foreground = uidConsumer.getKey(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND);
+ final BatteryConsumer.Key background = uidConsumer.getKey(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
+ BatteryConsumer.PROCESS_STATE_BACKGROUND);
+ final BatteryConsumer.Key fgs = uidConsumer.getKey(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE);
+
+ assertThat(uidConsumer.getConsumedPower(foreground)).isWithin(PRECISION).of(1.2);
+ assertThat(uidConsumer.getConsumedPower(background)).isWithin(PRECISION).of(0.4);
+ assertThat(uidConsumer.getConsumedPower(fgs)).isWithin(PRECISION).of(0);
+ }
+
+ @Test
public void testMeasuredEnergyBasedModel() {
BatteryStatsImpl stats = mStatsRule.getBatteryStats();
@@ -141,7 +230,7 @@ public class MobileRadioPowerCalculatorTest {
// Note established network
stats.noteNetworkInterfaceForTransports("cellular",
- new int[] { NetworkCapabilities.TRANSPORT_CELLULAR });
+ new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
// Note application network activity
NetworkStats networkStats = new NetworkStats(10000, 1)
@@ -149,7 +238,7 @@ public class MobileRadioPowerCalculatorTest {
mStatsRule.setNetworkStats(networkStats);
ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
- new int[] {100, 200, 300, 400, 500}, 600);
+ new int[]{100, 200, 300, 400, 500}, 600);
stats.noteModemControllerActivity(mai, 10_000_000, 10000, 10000);
mStatsRule.setTime(12_000_000, 12_000_000);
@@ -177,4 +266,87 @@ public class MobileRadioPowerCalculatorTest {
assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
.isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
}
+
+ @Test
+ public void testMeasuredEnergyBasedModel_byProcessState() {
+ BatteryStatsImpl stats = mStatsRule.getBatteryStats();
+ BatteryStatsImpl.Uid uid = stats.getUidStatsLocked(APP_UID);
+
+ mStatsRule.setTime(1000, 1000);
+ uid.setProcessStateForTest(
+ BatteryStats.Uid.PROCESS_STATE_FOREGROUND, 1000);
+
+ // Scan for a cell
+ stats.notePhoneStateLocked(ServiceState.STATE_OUT_OF_SERVICE,
+ TelephonyManager.SIM_STATE_READY,
+ 2000, 2000);
+
+ // Found a cell
+ stats.notePhoneStateLocked(ServiceState.STATE_IN_SERVICE, TelephonyManager.SIM_STATE_READY,
+ 5000, 5000);
+
+ // Note cell signal strength
+ SignalStrength signalStrength = mock(SignalStrength.class);
+ when(signalStrength.getLevel()).thenReturn(SignalStrength.SIGNAL_STRENGTH_MODERATE);
+ stats.notePhoneSignalStrengthLocked(signalStrength, 5000, 5000);
+
+ stats.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH,
+ 8_000_000_000L, APP_UID, 8000, 8000);
+
+ // Note established network
+ stats.noteNetworkInterfaceForTransports("cellular",
+ new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
+
+ // Note application network activity
+ mStatsRule.setNetworkStats(new NetworkStats(10000, 1)
+ .insertEntry("cellular", APP_UID, 0, 0, 1000, 100, 2000, 20, 100));
+
+ stats.noteModemControllerActivity(null, 10_000_000, 10000, 10000);
+
+ uid.setProcessStateForTest(
+ BatteryStats.Uid.PROCESS_STATE_BACKGROUND, 11000);
+
+ mStatsRule.setNetworkStats(new NetworkStats(12000, 1)
+ .insertEntry("cellular", APP_UID, 0, 0, 1000, 250, 2000, 80, 200));
+
+ stats.noteModemControllerActivity(null, 15_000_000, 12000, 12000);
+
+ mStatsRule.setTime(20000, 20000);
+
+ assertThat(uid.getMobileRadioMeasuredBatteryConsumptionUC())
+ .isIn(Range.open(20_000_000L, 21_000_000L));
+ assertThat(uid.getMobileRadioMeasuredBatteryConsumptionUC(
+ BatteryConsumer.PROCESS_STATE_FOREGROUND))
+ .isIn(Range.open(13_000_000L, 14_000_000L));
+ assertThat(uid.getMobileRadioMeasuredBatteryConsumptionUC(
+ BatteryConsumer.PROCESS_STATE_BACKGROUND))
+ .isIn(Range.open(7_000_000L, 8_000_000L));
+ assertThat(uid.getMobileRadioMeasuredBatteryConsumptionUC(
+ BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE))
+ .isEqualTo(0);
+
+ MobileRadioPowerCalculator calculator =
+ new MobileRadioPowerCalculator(mStatsRule.getPowerProfile());
+
+ mStatsRule.apply(new BatteryUsageStatsQuery.Builder()
+ .includePowerModels()
+ .includeProcessStateData()
+ .build(), calculator);
+
+ UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
+
+ final BatteryConsumer.Key foreground = uidConsumer.getKey(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND);
+ final BatteryConsumer.Key background = uidConsumer.getKey(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
+ BatteryConsumer.PROCESS_STATE_BACKGROUND);
+ final BatteryConsumer.Key fgs = uidConsumer.getKey(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE);
+
+ assertThat(uidConsumer.getConsumedPower(foreground)).isWithin(PRECISION).of(3.62064);
+ assertThat(uidConsumer.getConsumedPower(background)).isWithin(PRECISION).of(2.08130);
+ assertThat(uidConsumer.getConsumedPower(fgs)).isWithin(PRECISION).of(0);
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index e4c83f175a71..4faf349aad7e 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -233,6 +233,11 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl {
public Future<?> scheduleSyncDueToBatteryLevelChange(long delayMillis) {
return null;
}
+
+ @Override
+ public Future<?> scheduleSyncDueToProcessStateChange(long delayMillis) {
+ return null;
+ }
}
}
diff --git a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
index dc5bc9708f6a..dbb2cf15dd8e 100644
--- a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
@@ -465,16 +465,23 @@ public class MeasuredEnergyStatsTest {
supportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = false;
supportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = true;
+ final int[] supportedMultiStateBuckets = new int[]{POWER_BUCKET_SCREEN_ON};
final MeasuredEnergyStats.Config config =
new MeasuredEnergyStats.Config(supportedStandardBuckets, customBucketNames,
- new int[0], new String[]{"s"});
+ supportedMultiStateBuckets, new String[]{"s1", "s2"});
final MeasuredEnergyStats stats = new MeasuredEnergyStats(config);
- stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 10);
- stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 5);
+ stats.setState(1, 0);
+ stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 10, 1000);
+ stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 5, 2000);
stats.updateStandardBucket(POWER_BUCKET_SCREEN_OTHER, 40);
stats.updateCustomBucket(0, 50);
stats.updateCustomBucket(1, 60);
+ assertThat(stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_ON, 0))
+ .isEqualTo(0);
+ assertThat(stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_ON, 1))
+ .isEqualTo(15);
+
MeasuredEnergyStats.resetIfNotNull(stats);
// All charges should be reset to 0
for (int i = 0; i < NUMBER_STANDARD_POWER_BUCKETS; i++) {
@@ -485,7 +492,13 @@ public class MeasuredEnergyStatsTest {
assertFalse(stats.isStandardBucketSupported(i));
assertEquals(POWER_DATA_UNAVAILABLE, stats.getAccumulatedStandardBucketCharge(i));
}
+
}
+ assertThat(stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_ON, 0))
+ .isEqualTo(0);
+ assertThat(stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_ON, 1))
+ .isEqualTo(0);
+
for (int i = 0; i < customBucketNames.length; i++) {
assertEquals(0, stats.getAccumulatedCustomBucketCharge(i));
}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 81db63eaa039..e61a665307f8 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -394,6 +394,8 @@ applications that come with the platform
<!-- Permissions required for Incremental CTS tests -->
<permission name="com.android.permission.USE_INSTALLER_V2"/>
<permission name="android.permission.LOADER_USAGE_STATS"/>
+ <!-- Permissions required for Package Verifier tests -->
+ <permission name="android.permission.PACKAGE_VERIFICATION_AGENT" />
<!-- Permission required to test system only camera devices. -->
<permission name="android.permission.SYSTEM_CAMERA" />
<!-- Permission required to test ExplicitHealthCheckServiceImpl. -->
@@ -425,7 +427,6 @@ applications that come with the platform
<permission name="android.permission.COMPANION_APPROVE_WIFI_CONNECTIONS" />
<permission name="android.permission.MANAGE_COMPANION_DEVICES" />
<permission name="android.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING" />
- <permission name="android.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION" />
<permission name="android.permission.REQUEST_COMPANION_PROFILE_WATCH" />
<permission name="android.permission.REQUEST_COMPANION_SELF_MANAGED" />
<!-- Permission required for testing registering pull atom callbacks. -->
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 6bfbd8d55ab0..9584994774a9 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1201,6 +1201,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-846931068": {
+ "message": "Update camera compat control state to %s for taskId=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_ORGANIZER",
+ "at": "com\/android\/server\/wm\/TaskOrganizerController.java"
+ },
"-846078709": {
"message": "Configuration doesn't matter in finishing %s",
"level": "VERBOSE",
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index a612265793a3..425a37891afb 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -67,7 +67,7 @@ public abstract class BaseCanvas {
* @hide
*/
protected int mDensity = Bitmap.DENSITY_NONE;
- private boolean mAllowHwBitmapsInSwMode = false;
+ private boolean mAllowHwFeaturesInSwMode = false;
protected void throwIfCannotDraw(Bitmap bitmap) {
if (bitmap.isRecycled()) {
@@ -101,14 +101,14 @@ public abstract class BaseCanvas {
public void drawArc(float left, float top, float right, float bottom, float startAngle,
float sweepAngle, boolean useCenter, @NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawArc(mNativeCanvasWrapper, left, top, right, bottom, startAngle, sweepAngle,
useCenter, paint.getNativeInstance());
}
public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter,
@NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
drawArc(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle, useCenter,
paint);
}
@@ -119,14 +119,14 @@ public abstract class BaseCanvas {
public void drawBitmap(@NonNull Bitmap bitmap, float left, float top, @Nullable Paint paint) {
throwIfCannotDraw(bitmap);
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawBitmap(mNativeCanvasWrapper, bitmap.getNativeInstance(), left, top,
paint != null ? paint.getNativeInstance() : 0, mDensity, mScreenDensity,
bitmap.mDensity);
}
public void drawBitmap(@NonNull Bitmap bitmap, @NonNull Matrix matrix, @Nullable Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawBitmapMatrix(mNativeCanvasWrapper, bitmap.getNativeInstance(), matrix.ni(),
paint != null ? paint.getNativeInstance() : 0);
}
@@ -137,7 +137,7 @@ public abstract class BaseCanvas {
throw new NullPointerException();
}
throwIfCannotDraw(bitmap);
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
int left, top, right, bottom;
@@ -163,7 +163,7 @@ public abstract class BaseCanvas {
throw new NullPointerException();
}
throwIfCannotDraw(bitmap);
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
float left, top, right, bottom;
@@ -202,7 +202,7 @@ public abstract class BaseCanvas {
|| (lastScanline + width > length)) {
throw new ArrayIndexOutOfBoundsException();
}
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
// quick escape if there's nothing to draw
if (width == 0 || height == 0) {
return;
@@ -226,7 +226,7 @@ public abstract class BaseCanvas {
if ((meshWidth | meshHeight | vertOffset | colorOffset) < 0) {
throw new ArrayIndexOutOfBoundsException();
}
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
if (meshWidth == 0 || meshHeight == 0) {
return;
}
@@ -243,7 +243,7 @@ public abstract class BaseCanvas {
}
public void drawCircle(float cx, float cy, float radius, @NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawCircle(mNativeCanvasWrapper, cx, cy, radius, paint.getNativeInstance());
}
@@ -275,23 +275,23 @@ public abstract class BaseCanvas {
public void drawLine(float startX, float startY, float stopX, float stopY,
@NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawLine(mNativeCanvasWrapper, startX, startY, stopX, stopY, paint.getNativeInstance());
}
public void drawLines(@Size(multiple = 4) @NonNull float[] pts, int offset, int count,
@NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawLines(mNativeCanvasWrapper, pts, offset, count, paint.getNativeInstance());
}
public void drawLines(@Size(multiple = 4) @NonNull float[] pts, @NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
drawLines(pts, 0, pts.length, paint);
}
public void drawOval(float left, float top, float right, float bottom, @NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawOval(mNativeCanvasWrapper, left, top, right, bottom, paint.getNativeInstance());
}
@@ -299,18 +299,19 @@ public abstract class BaseCanvas {
if (oval == null) {
throw new NullPointerException();
}
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
drawOval(oval.left, oval.top, oval.right, oval.bottom, paint);
}
public void drawPaint(@NonNull Paint paint) {
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawPaint(mNativeCanvasWrapper, paint.getNativeInstance());
}
public void drawPatch(@NonNull NinePatch patch, @NonNull Rect dst, @Nullable Paint paint) {
Bitmap bitmap = patch.getBitmap();
throwIfCannotDraw(bitmap);
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
nDrawNinePatch(mNativeCanvasWrapper, bitmap.getNativeInstance(), patch.mNativeChunk,
dst.left, dst.top, dst.right, dst.bottom, nativePaint,
@@ -320,7 +321,7 @@ public abstract class BaseCanvas {
public void drawPatch(@NonNull NinePatch patch, @NonNull RectF dst, @Nullable Paint paint) {
Bitmap bitmap = patch.getBitmap();
throwIfCannotDraw(bitmap);
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
nDrawNinePatch(mNativeCanvasWrapper, bitmap.getNativeInstance(), patch.mNativeChunk,
dst.left, dst.top, dst.right, dst.bottom, nativePaint,
@@ -328,7 +329,7 @@ public abstract class BaseCanvas {
}
public void drawPath(@NonNull Path path, @NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
if (path.isSimplePath && path.rects != null) {
nDrawRegion(mNativeCanvasWrapper, path.rects.mNativeRegion, paint.getNativeInstance());
} else {
@@ -337,18 +338,18 @@ public abstract class BaseCanvas {
}
public void drawPoint(float x, float y, @NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawPoint(mNativeCanvasWrapper, x, y, paint.getNativeInstance());
}
public void drawPoints(@Size(multiple = 2) float[] pts, int offset, int count,
@NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawPoints(mNativeCanvasWrapper, pts, offset, count, paint.getNativeInstance());
}
public void drawPoints(@Size(multiple = 2) @NonNull float[] pts, @NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
drawPoints(pts, 0, pts.length, paint);
}
@@ -359,7 +360,7 @@ public abstract class BaseCanvas {
if (index < 0 || index + count > text.length || count * 2 > pos.length) {
throw new IndexOutOfBoundsException();
}
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
for (int i = 0; i < count; i++) {
drawText(text, index + i, 1, pos[i * 2], pos[i * 2 + 1], paint);
}
@@ -368,22 +369,22 @@ public abstract class BaseCanvas {
@Deprecated
public void drawPosText(@NonNull String text, @NonNull @Size(multiple = 2) float[] pos,
@NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
drawPosText(text.toCharArray(), 0, text.length(), pos, paint);
}
public void drawRect(float left, float top, float right, float bottom, @NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawRect(mNativeCanvasWrapper, left, top, right, bottom, paint.getNativeInstance());
}
public void drawRect(@NonNull Rect r, @NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
drawRect(r.left, r.top, r.right, r.bottom, paint);
}
public void drawRect(@NonNull RectF rect, @NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawRect(mNativeCanvasWrapper,
rect.left, rect.top, rect.right, rect.bottom, paint.getNativeInstance());
}
@@ -394,13 +395,13 @@ public abstract class BaseCanvas {
public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
@NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawRoundRect(mNativeCanvasWrapper, left, top, right, bottom, rx, ry,
paint.getNativeInstance());
}
public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
drawRoundRect(rect.left, rect.top, rect.right, rect.bottom, rx, ry, paint);
}
@@ -410,7 +411,7 @@ public abstract class BaseCanvas {
*/
public void drawDoubleRoundRect(@NonNull RectF outer, float outerRx, float outerRy,
@NonNull RectF inner, float innerRx, float innerRy, @NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
float outerLeft = outer.left;
float outerTop = outer.top;
float outerRight = outer.right;
@@ -431,7 +432,7 @@ public abstract class BaseCanvas {
*/
public void drawDoubleRoundRect(@NonNull RectF outer, @NonNull float[] outerRadii,
@NonNull RectF inner, @NonNull float[] innerRadii, @NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
if (innerRadii == null || outerRadii == null
|| innerRadii.length != 8 || outerRadii.length != 8) {
throw new IllegalArgumentException("Both inner and outer radii arrays must contain "
@@ -509,7 +510,7 @@ public abstract class BaseCanvas {
(text.length - index - count)) < 0) {
throw new IndexOutOfBoundsException();
}
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawText(mNativeCanvasWrapper, text, index, count, x, y, paint.mBidiFlags,
paint.getNativeInstance());
}
@@ -519,7 +520,7 @@ public abstract class BaseCanvas {
if ((start | end | (end - start) | (text.length() - end)) < 0) {
throw new IndexOutOfBoundsException();
}
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
if (text instanceof String || text instanceof SpannedString ||
text instanceof SpannableString) {
nDrawText(mNativeCanvasWrapper, text.toString(), start, end, x, y,
@@ -537,7 +538,7 @@ public abstract class BaseCanvas {
}
public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawText(mNativeCanvasWrapper, text, 0, text.length(), x, y, paint.mBidiFlags,
paint.getNativeInstance());
}
@@ -547,7 +548,7 @@ public abstract class BaseCanvas {
if ((start | end | (end - start) | (text.length() - end)) < 0) {
throw new IndexOutOfBoundsException();
}
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawText(mNativeCanvasWrapper, text, start, end, x, y, paint.mBidiFlags,
paint.getNativeInstance());
}
@@ -557,7 +558,7 @@ public abstract class BaseCanvas {
if (index < 0 || index + count > text.length) {
throw new ArrayIndexOutOfBoundsException();
}
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawTextOnPath(mNativeCanvasWrapper, text, index, count,
path.readOnlyNI(), hOffset, vOffset,
paint.mBidiFlags, paint.getNativeInstance());
@@ -566,7 +567,7 @@ public abstract class BaseCanvas {
public void drawTextOnPath(@NonNull String text, @NonNull Path path, float hOffset,
float vOffset, @NonNull Paint paint) {
if (text.length() > 0) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawTextOnPath(mNativeCanvasWrapper, text, path.readOnlyNI(), hOffset, vOffset,
paint.mBidiFlags, paint.getNativeInstance());
}
@@ -587,7 +588,7 @@ public abstract class BaseCanvas {
throw new IndexOutOfBoundsException();
}
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawTextRun(mNativeCanvasWrapper, text, index, count, contextIndex, contextCount,
x, y, isRtl, paint.getNativeInstance(), 0 /* measured text */);
}
@@ -606,7 +607,7 @@ public abstract class BaseCanvas {
throw new IndexOutOfBoundsException();
}
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
if (text instanceof String || text instanceof SpannedString ||
text instanceof SpannableString) {
nDrawTextRun(mNativeCanvasWrapper, text.toString(), start, end, contextStart,
@@ -664,7 +665,7 @@ public abstract class BaseCanvas {
if (indices != null) {
checkRange(indices.length, indexOffset, indexCount);
}
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawVertices(mNativeCanvasWrapper, mode.nativeInt, vertexCount, verts,
vertOffset, texs, texOffset, colors, colorOffset,
indices, indexOffset, indexCount, paint.getNativeInstance());
@@ -680,50 +681,52 @@ public abstract class BaseCanvas {
/**
* @hide
*/
- public void setHwBitmapsInSwModeEnabled(boolean enabled) {
- mAllowHwBitmapsInSwMode = enabled;
+ public void setHwFeaturesInSwModeEnabled(boolean enabled) {
+ mAllowHwFeaturesInSwMode = enabled;
}
/**
* @hide
*/
- public boolean isHwBitmapsInSwModeEnabled() {
- return mAllowHwBitmapsInSwMode;
+ public boolean isHwFeaturesInSwModeEnabled() {
+ return mAllowHwFeaturesInSwMode;
}
/**
+ * If true throw an exception
* @hide
*/
- protected void onHwBitmapInSwMode() {
- if (!mAllowHwBitmapsInSwMode) {
- throw new IllegalArgumentException(
- "Software rendering doesn't support hardware bitmaps");
- }
+ protected boolean onHwFeatureInSwMode() {
+ return !mAllowHwFeaturesInSwMode;
}
private void throwIfHwBitmapInSwMode(Bitmap bitmap) {
- if (!isHardwareAccelerated() && bitmap.getConfig() == Bitmap.Config.HARDWARE) {
- onHwBitmapInSwMode();
+ if (!isHardwareAccelerated() && bitmap.getConfig() == Bitmap.Config.HARDWARE
+ && onHwFeatureInSwMode()) {
+ throw new IllegalArgumentException(
+ "Software rendering doesn't support hardware bitmaps");
}
}
- private void throwIfHasHwBitmapInSwMode(Paint p) {
+ private void throwIfHasHwFeaturesInSwMode(Paint p) {
if (isHardwareAccelerated() || p == null) {
return;
}
- throwIfHasHwBitmapInSwMode(p.getShader());
+ throwIfHasHwFeaturesInSwMode(p.getShader());
}
- private void throwIfHasHwBitmapInSwMode(Shader shader) {
+ private void throwIfHasHwFeaturesInSwMode(Shader shader) {
if (shader == null) {
return;
}
if (shader instanceof BitmapShader) {
throwIfHwBitmapInSwMode(((BitmapShader) shader).mBitmap);
- }
- if (shader instanceof ComposeShader) {
- throwIfHasHwBitmapInSwMode(((ComposeShader) shader).mShaderA);
- throwIfHasHwBitmapInSwMode(((ComposeShader) shader).mShaderB);
+ } else if (shader instanceof RuntimeShader && onHwFeatureInSwMode()) {
+ throw new IllegalArgumentException(
+ "Software rendering doesn't support RuntimeShader");
+ } else if (shader instanceof ComposeShader) {
+ throwIfHasHwFeaturesInSwMode(((ComposeShader) shader).mShaderA);
+ throwIfHasHwFeaturesInSwMode(((ComposeShader) shader).mShaderB);
}
}
diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java
index 390d3d414346..ee4165b8da05 100644
--- a/graphics/java/android/graphics/Picture.java
+++ b/graphics/java/android/graphics/Picture.java
@@ -124,7 +124,7 @@ public class Picture {
public void endRecording() {
verifyValid();
if (mRecordingCanvas != null) {
- mRequiresHwAcceleration = mRecordingCanvas.mHoldsHwBitmap;
+ mRequiresHwAcceleration = mRecordingCanvas.mUsesHwFeature;
mRecordingCanvas = null;
nativeEndRecording(mNativePicture);
}
@@ -182,8 +182,10 @@ public class Picture {
if (mRecordingCanvas != null) {
endRecording();
}
- if (mRequiresHwAcceleration && !canvas.isHardwareAccelerated()) {
- canvas.onHwBitmapInSwMode();
+ if (mRequiresHwAcceleration && !canvas.isHardwareAccelerated()
+ && canvas.onHwFeatureInSwMode()) {
+ throw new IllegalArgumentException("Software rendering not supported for Pictures that"
+ + " require hardware acceleration");
}
nativeDraw(canvas.getNativeCanvasWrapper(), mNativePicture);
}
@@ -242,7 +244,7 @@ public class Picture {
private static class PictureCanvas extends Canvas {
private final Picture mPicture;
- boolean mHoldsHwBitmap;
+ boolean mUsesHwFeature;
public PictureCanvas(Picture pict, long nativeCanvas) {
super(nativeCanvas);
@@ -265,8 +267,9 @@ public class Picture {
}
@Override
- protected void onHwBitmapInSwMode() {
- mHoldsHwBitmap = true;
+ protected boolean onHwFeatureInSwMode() {
+ mUsesHwFeature = true;
+ return false;
}
}
}
diff --git a/graphics/java/android/graphics/RuntimeShader.java b/graphics/java/android/graphics/RuntimeShader.java
index 1ace32277f0a..9ca8e3b46317 100644
--- a/graphics/java/android/graphics/RuntimeShader.java
+++ b/graphics/java/android/graphics/RuntimeShader.java
@@ -16,13 +16,15 @@
package android.graphics;
+import android.annotation.ColorInt;
+import android.annotation.ColorLong;
import android.annotation.NonNull;
import libcore.util.NativeAllocationRegistry;
/**
- * Shader that calculates pixel output with a program (fragment shader) running on a GPU.
- * @hide
+ * Shader that calculates per-pixel color via a user defined Android Graphics Shading Language
+ * (AGSL) function.
*/
public class RuntimeShader extends Shader {
@@ -32,7 +34,7 @@ public class RuntimeShader extends Shader {
RuntimeShader.class.getClassLoader(), nativeGetFinalizer());
}
- private boolean mIsOpaque;
+ private boolean mForceOpaque;
/**
* Current native shader builder instance.
@@ -42,57 +44,270 @@ public class RuntimeShader extends Shader {
/**
* Creates a new RuntimeShader.
*
- * @param sksl The text of SKSL program to run on the GPU.
- * @param uniforms Array of parameters passed by the SKSL shader. Array size depends
- * on number of uniforms declared by sksl.
- * @param isOpaque True if all pixels have alpha 1.0f.
+ * @param shader The text of AGSL shader program to run.
*/
- public RuntimeShader(@NonNull String sksl, boolean isOpaque) {
+ public RuntimeShader(@NonNull String shader) {
+ this(shader, false);
+ }
+
+ /**
+ * Creates a new RuntimeShader.
+ *
+ * @param shader The text of AGSL shader program to run.
+ * @param forceOpaque If true then all pixels produced by the AGSL shader program will have an
+ * alpha of 1.0f.
+ */
+ public RuntimeShader(@NonNull String shader, boolean forceOpaque) {
+ // colorspace is required, but the RuntimeShader always produces colors in the destination
+ // buffer's colorspace regardless of the value specified here.
super(ColorSpace.get(ColorSpace.Named.SRGB));
- mIsOpaque = isOpaque;
- mNativeInstanceRuntimeShaderBuilder = nativeCreateBuilder(sksl);
+ if (shader == null) {
+ throw new NullPointerException("RuntimeShader requires a non-null AGSL string");
+ }
+ mForceOpaque = forceOpaque;
+ mNativeInstanceRuntimeShaderBuilder = nativeCreateBuilder(shader);
NoImagePreloadHolder.sRegistry.registerNativeAllocation(
this, mNativeInstanceRuntimeShaderBuilder);
}
+ public boolean isForceOpaque() {
+ return mForceOpaque;
+ }
+
+ /**
+ * Sets the uniform color value corresponding to this shader. If the shader does not have a
+ * uniform with that name or if the uniform is declared with a type other than vec3 or vec4 and
+ * corresponding layout(color) annotation then an IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the color uniform declared in the AGSL shader program
+ * @param color the provided sRGB color will be transformed into the shader program's output
+ * colorspace and will be available as a vec4 uniform in the program.
+ */
+ public void setColorUniform(@NonNull String uniformName, @ColorInt int color) {
+ setUniform(uniformName, Color.valueOf(color).getComponents(), true);
+ }
+
+ /**
+ * Sets the uniform color value corresponding to this shader. If the shader does not have a
+ * uniform with that name or if the uniform is declared with a type other than vec3 or vec4 and
+ * corresponding layout(color) annotation then an IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the color uniform declared in the AGSL shader program
+ * @param color the provided sRGB color will be transformed into the shader program's output
+ * colorspace and will be available as a vec4 uniform in the program.
+ */
+ public void setColorUniform(@NonNull String uniformName, @ColorLong long color) {
+ Color exSRGB = Color.valueOf(color).convert(ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB));
+ setUniform(uniformName, exSRGB.getComponents(), true);
+ }
+
+ /**
+ * Sets the uniform color value corresponding to this shader. If the shader does not have a
+ * uniform with that name or if the uniform is declared with a type other than vec3 or vec4 and
+ * corresponding layout(color) annotation then an IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the color uniform declared in the AGSL shader program
+ * @param color the provided sRGB color will be transformed into the shader program's output
+ * colorspace and will be available as a vec4 uniform in the program.
+ */
+ public void setColorUniform(@NonNull String uniformName, @NonNull Color color) {
+ if (color == null) {
+ throw new NullPointerException("The color parameter must not be null");
+ }
+ Color exSRGB = color.convert(ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB));
+ setUniform(uniformName, exSRGB.getComponents(), true);
+ }
+
+ /**
+ * Sets the uniform value corresponding to this shader. If the shader does not have a uniform
+ * with that name or if the uniform is declared with a type other than a float or float[1]
+ * then an IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the uniform declared in the AGSL shader program
+ */
+ public void setFloatUniform(@NonNull String uniformName, float value) {
+ setFloatUniform(uniformName, value, 0.0f, 0.0f, 0.0f, 1);
+ }
+
/**
* Sets the uniform value corresponding to this shader. If the shader does not have a uniform
- * with that name or if the uniform is declared with a type other than float then an
+ * with that name or if the uniform is declared with a type other than vec2 or float[2] then an
* IllegalArgumentException is thrown.
*
- * @param uniformName name matching the uniform declared in the SKSL shader
- * @param value
+ * @param uniformName name matching the uniform declared in the AGSL shader program
*/
- public void setUniform(@NonNull String uniformName, float value) {
- setUniform(uniformName, new float[] {value});
+ public void setFloatUniform(@NonNull String uniformName, float value1, float value2) {
+ setFloatUniform(uniformName, value1, value2, 0.0f, 0.0f, 2);
+
}
/**
* Sets the uniform value corresponding to this shader. If the shader does not have a uniform
- * with that name or if the uniform is declared with a type other than float2/vec2 then an
+ * with that name or if the uniform is declared with a type other than vec3 or float[3] then an
* IllegalArgumentException is thrown.
*
- * @param uniformName name matching the uniform declared in the SKSL shader
- * @param value1
- * @param value2
+ * @param uniformName name matching the uniform declared in the AGSL shader program
*/
- public void setUniform(@NonNull String uniformName, float value1, float value2) {
- setUniform(uniformName, new float[] {value1, value2});
+ public void setFloatUniform(@NonNull String uniformName, float value1, float value2,
+ float value3) {
+ setFloatUniform(uniformName, value1, value2, value3, 0.0f, 3);
+
+ }
+
+ /**
+ * Sets the uniform value corresponding to this shader. If the shader does not have a uniform
+ * with that name or if the uniform is declared with a type other than vec4 or float[4] then an
+ * IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the uniform declared in the AGSL shader program
+ */
+ public void setFloatUniform(@NonNull String uniformName, float value1, float value2,
+ float value3, float value4) {
+ setFloatUniform(uniformName, value1, value2, value3, value4, 4);
}
/**
* Sets the uniform value corresponding to this shader. If the shader does not have a uniform
- * with that name or if the uniform is declared with a type other than a vecN/floatN where N is
- * the size of the values array then an IllegalArgumentException is thrown.
+ * with that name or if the uniform is declared with a type other than a float (for N=1), vecN,
+ * or float[N] where N is the length of the values param then an IllegalArgumentException is
+ * thrown.
*
- * @param uniformName name matching the uniform declared in the SKSL shader
- * @param values
+ * @param uniformName name matching the uniform declared in the AGSL shader program
*/
+ public void setFloatUniform(@NonNull String uniformName, @NonNull float[] values) {
+ setUniform(uniformName, values, false);
+ }
+
+ /**
+ * Old method signature used by some callers within the platform code
+ * @hide
+ * @deprecated use setFloatUniform instead
+ */
+ @Deprecated
public void setUniform(@NonNull String uniformName, float[] values) {
+ setFloatUniform(uniformName, values);
+ }
+
+ /**
+ * Old method signature used by some callers within the platform code
+ * @hide
+ * @deprecated use setFloatUniform instead
+ */
+ @Deprecated
+ public void setUniform(@NonNull String uniformName, float value) {
+ setFloatUniform(uniformName, value);
+ }
+
+ /**
+ * Old method signature used by some callers within the platform code
+ * @hide
+ * @deprecated use setFloatUniform instead
+ */
+ @Deprecated
+ public void setUniform(@NonNull String uniformName, float value1, float value2) {
+ setFloatUniform(uniformName, value1, value2);
+ }
+
+ private void setFloatUniform(@NonNull String uniformName, float value1, float value2,
+ float value3, float value4, int count) {
+ if (uniformName == null) {
+ throw new NullPointerException("The uniformName parameter must not be null");
+ }
+
+ nativeUpdateUniforms(mNativeInstanceRuntimeShaderBuilder, uniformName, value1, value2,
+ value3, value4, count);
+ discardNativeInstance();
+ }
+
+ private void setUniform(@NonNull String uniformName, @NonNull float[] values, boolean isColor) {
+ if (uniformName == null) {
+ throw new NullPointerException("The uniformName parameter must not be null");
+ }
+ if (values == null) {
+ throw new NullPointerException("The uniform values parameter must not be null");
+ }
+
+ nativeUpdateUniforms(mNativeInstanceRuntimeShaderBuilder, uniformName, values, isColor);
+ discardNativeInstance();
+ }
+
+ /**
+ * Sets the uniform value corresponding to this shader. If the shader does not have a uniform
+ * with that name or if the uniform is declared with a type other than an int or int[1]
+ * then an IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the uniform declared in the AGSL shader program
+ */
+ public void setIntUniform(@NonNull String uniformName, int value) {
+ setIntUniform(uniformName, value, 0, 0, 0, 1);
+ }
+
+ /**
+ * Sets the uniform value corresponding to this shader. If the shader does not have a uniform
+ * with that name or if the uniform is declared with a type other than ivec2 or int[2] then an
+ * IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the uniform declared in the AGSL shader program
+ */
+ public void setIntUniform(@NonNull String uniformName, int value1, int value2) {
+ setIntUniform(uniformName, value1, value2, 0, 0, 2);
+
+ }
+
+ /**
+ * Sets the uniform value corresponding to this shader. If the shader does not have a uniform
+ * with that name or if the uniform is declared with a type other than ivec3 or int[3] then an
+ * IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the uniform declared in the AGSL shader program
+ */
+ public void setIntUniform(@NonNull String uniformName, int value1, int value2, int value3) {
+ setIntUniform(uniformName, value1, value2, value3, 0, 3);
+
+ }
+
+ /**
+ * Sets the uniform value corresponding to this shader. If the shader does not have a uniform
+ * with that name or if the uniform is declared with a type other than ivec4 or int[4] then an
+ * IllegalArgumentException is thrown.
+ *
+ * @param uniformName name matching the uniform declared in the AGSL shader program
+ */
+ public void setIntUniform(@NonNull String uniformName, int value1, int value2,
+ int value3, int value4) {
+ setIntUniform(uniformName, value1, value2, value3, value4, 4);
+ }
+
+ /**
+ * Sets the uniform value corresponding to this shader. If the shader does not have a uniform
+ * with that name or if the uniform is declared with a type other than an int (for N=1), ivecN,
+ * or int[N] where N is the length of the values param then an IllegalArgumentException is
+ * thrown.
+ *
+ * @param uniformName name matching the uniform declared in the AGSL shader program
+ */
+ public void setIntUniform(@NonNull String uniformName, @NonNull int[] values) {
+ if (uniformName == null) {
+ throw new NullPointerException("The uniformName parameter must not be null");
+ }
+ if (values == null) {
+ throw new NullPointerException("The uniform values parameter must not be null");
+ }
nativeUpdateUniforms(mNativeInstanceRuntimeShaderBuilder, uniformName, values);
discardNativeInstance();
}
+ private void setIntUniform(@NonNull String uniformName, int value1, int value2, int value3,
+ int value4, int count) {
+ if (uniformName == null) {
+ throw new NullPointerException("The uniformName parameter must not be null");
+ }
+
+ nativeUpdateUniforms(mNativeInstanceRuntimeShaderBuilder, uniformName, value1, value2,
+ value3, value4, count);
+ discardNativeInstance();
+ }
+
/**
* Sets the uniform shader that is declares as input to this shader. If the shader does not
* have a uniform shader with that name then an IllegalArgumentException is thrown.
@@ -101,6 +316,12 @@ public class RuntimeShader extends Shader {
* @param shader shader passed into the SKSL shader for sampling
*/
public void setInputShader(@NonNull String shaderName, @NonNull Shader shader) {
+ if (shaderName == null) {
+ throw new NullPointerException("The shaderName parameter must not be null");
+ }
+ if (shader == null) {
+ throw new NullPointerException("The shader parameter must not be null");
+ }
nativeUpdateShader(
mNativeInstanceRuntimeShaderBuilder, shaderName, shader.getNativeInstance());
discardNativeInstance();
@@ -109,23 +330,28 @@ public class RuntimeShader extends Shader {
/** @hide */
@Override
protected long createNativeInstance(long nativeMatrix, boolean filterFromPaint) {
- return nativeCreateShader(mNativeInstanceRuntimeShaderBuilder, nativeMatrix, mIsOpaque);
+ return nativeCreateShader(mNativeInstanceRuntimeShaderBuilder, nativeMatrix, mForceOpaque);
}
- public long getNativeShaderBuilder() {
+ /** @hide */
+ protected long getNativeShaderBuilder() {
return mNativeInstanceRuntimeShaderBuilder;
}
- public boolean isOpaque() {
- return mIsOpaque;
- }
-
private static native long nativeGetFinalizer();
- private static native long nativeCreateBuilder(String sksl);
+ private static native long nativeCreateBuilder(String agsl);
private static native long nativeCreateShader(
long shaderBuilder, long matrix, boolean isOpaque);
private static native void nativeUpdateUniforms(
- long shaderBuilder, String uniformName, float[] uniforms);
+ long shaderBuilder, String uniformName, float[] uniforms, boolean isColor);
+ private static native void nativeUpdateUniforms(
+ long shaderBuilder, String uniformName, float value1, float value2, float value3,
+ float value4, int count);
+ private static native void nativeUpdateUniforms(
+ long shaderBuilder, String uniformName, int[] uniforms);
+ private static native void nativeUpdateUniforms(
+ long shaderBuilder, String uniformName, int value1, int value2, int value3,
+ int value4, int count);
private static native void nativeUpdateShader(
long shaderBuilder, String shaderName, long shader);
}
diff --git a/keystore/java/android/security/KeyStoreException.java b/keystore/java/android/security/KeyStoreException.java
index 30389a29d342..6db2745840f4 100644
--- a/keystore/java/android/security/KeyStoreException.java
+++ b/keystore/java/android/security/KeyStoreException.java
@@ -16,25 +16,448 @@
package android.security;
+import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.annotation.TestApi;
+import android.security.keymaster.KeymasterDefs;
+import android.system.keystore2.ResponseCode;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.Map;
/**
- * KeyStore/keymaster exception with positive error codes coming from the KeyStore and negative
- * ones from keymaster.
+ * Exception containing information about the failure at the Keystore / KeyMint layer while
+ * generating or using a key.
*
- * @hide
+ * The public error codes indicate the cause of the error and the methods indicate whether
+ * it's a system/key issue and whether re-trying the operation (with the same key or a new key)
+ * is likely to succeed.
*/
-@TestApi
public class KeyStoreException extends Exception {
+ /**
+ * This error code is for mapping errors that the caller will not know about. If the caller is
+ * targeting an API level earlier than the one the error was introduced in, then the error will
+ * be mapped to this one.
+ * In API level 33 no errors map to this error.
+ */
+ public static final int ERROR_OTHER = 1;
+ /**
+ * Indicating the key could not be used because the user needs to authenticate first.
+ * See
+ * {@link android.security.keystore.KeyGenParameterSpec.Builder#setUserAuthenticationRequired(boolean)}.
+ */
+ public static final int ERROR_USER_AUTHENTICATION_REQUIRED = 2;
+ /**
+ * Indicating that {@code load()} has not been called on the Keystore instance, or an attempt
+ * has been made to generate an authorization bound key while the user has not set a lock
+ * screen knowledge factor (LSKF). Instruct the user to set an LSKF and retry.
+ */
+ public static final int ERROR_KEYSTORE_UNINITIALIZED = 3;
+ /**
+ * An internal system error - refer to {@link #isTransientFailure()} to determine whether
+ * re-trying the operation is likely to yield different results.
+ */
+ public static final int ERROR_INTERNAL_SYSTEM_ERROR = 4;
+ /**
+ * The caller has requested key parameters or operation which are only available to system
+ * or privileged apps.
+ */
+ public static final int ERROR_PERMISSION_DENIED = 5;
+ /**
+ * The key the operation refers to doesn't exist.
+ */
+ public static final int ERROR_KEY_DOES_NOT_EXIST = 6;
+ /**
+ * The key is corrupted and could not be recovered.
+ */
+ public static final int ERROR_KEY_CORRUPTED = 7;
+ /**
+ * The error related to inclusion of device identifiers in the attestation record.
+ */
+ public static final int ERROR_ID_ATTESTATION_FAILURE = 8;
+ /**
+ * The attestation challenge specified is too large.
+ */
+ public static final int ERROR_ATTESTATION_CHALLENGE_TOO_LARGE = 9;
+ /**
+ * General error in the KeyMint layer.
+ */
+ public static final int ERROR_KEYMINT_FAILURE = 10;
+ /**
+ * Failure in the Keystore layer.
+ */
+ public static final int ERROR_KEYSTORE_FAILURE = 11;
+ /**
+ * The feature the caller is trying to use is not implemented by the underlying
+ * KeyMint implementation.
+ * This could happen when an unsupported algorithm is requested, or when trying to import
+ * a key in a format other than raw or PKCS#8.
+ */
+ public static final int ERROR_UNIMPLEMENTED = 12;
+ /**
+ * The feature the caller is trying to use is not compatible with the parameters used to
+ * generate the key. For example, trying to use a key generated for a different signature
+ * algorithm, or a digest not specified during key creation.
+ * Another case is the attempt to generate a symmetric AES key and requesting key attestation.
+ */
+ public static final int ERROR_INCORRECT_USAGE = 13;
+ /**
+ * The key is not currently valid: Either at has expired or it will be valid for use in the
+ * future.
+ */
+ public static final int ERROR_KEY_NOT_TEMPORALLY_VALID = 14;
+ /**
+ * The crypto object the caller has been using held a reference to a KeyMint operation that
+ * has been evacuated (likely due to other concurrent operations taking place).
+ * The caller should re-create the crypto object and try again.
+ */
+ public static final int ERROR_KEY_OPERATION_EXPIRED = 15;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, prefix = {"ERROR_"}, value = {
+ ERROR_OTHER,
+ ERROR_USER_AUTHENTICATION_REQUIRED,
+ ERROR_KEYSTORE_UNINITIALIZED,
+ ERROR_INTERNAL_SYSTEM_ERROR,
+ ERROR_PERMISSION_DENIED,
+ ERROR_KEY_DOES_NOT_EXIST,
+ ERROR_KEY_CORRUPTED,
+ ERROR_ID_ATTESTATION_FAILURE,
+ ERROR_ATTESTATION_CHALLENGE_TOO_LARGE,
+ ERROR_KEYMINT_FAILURE,
+ ERROR_KEYSTORE_FAILURE,
+ ERROR_UNIMPLEMENTED,
+ ERROR_INCORRECT_USAGE,
+ ERROR_KEY_NOT_TEMPORALLY_VALID,
+ ERROR_KEY_OPERATION_EXPIRED
+ })
+ public @interface PublicErrorCode {
+ }
+ // Constants for encoding information about the error encountered:
+ // Whether the error relates to the system state/implementation as a whole, or a specific key.
+ private static final int IS_SYSTEM_ERROR = 1 << 1;
+ // Whether the error is permanent.
+ private static final int IS_TRANSIENT_ERROR = 1 << 2;
+ // Whether the cause of the error is the user not having authenticated recently.
+ private static final int REQUIRES_USER_AUTHENTICATION = 1 << 3;
+
+ // The internal error code. NOT to be returned directly to callers or made part of the
+ // public API.
private final int mErrorCode;
- public KeyStoreException(int errorCode, String message) {
+ /**
+ * @hide
+ */
+ public KeyStoreException(int errorCode, @Nullable String message) {
super(message);
mErrorCode = errorCode;
}
+ /**
+ * Returns the internal error code. Only for use by the platform.
+ *
+ * @hide
+ */
+ @TestApi
public int getErrorCode() {
return mErrorCode;
}
+
+ /**
+ * Returns one of the error codes exported by the class.
+ *
+ * @return a public error code, one of the values in {@link PublicErrorCode}.
+ */
+ @PublicErrorCode
+ public int getNumericErrorCode() {
+ PublicErrorInformation failureInfo = getErrorInformation(mErrorCode);
+ return failureInfo.errorCode;
+ }
+
+ /**
+ * Returns true if the failure is a transient failure - that is, performing the same operation
+ * again at a late time is likely to succeed.
+ *
+ * If {@link #isSystemError()} returns true, the transient nature of the failure relates to the
+ * device, otherwise relates to the key (so a permanent failure with an existing key likely
+ * requires creating another key to repeat the operation with).
+ */
+ public boolean isTransientFailure() {
+ PublicErrorInformation failureInfo = getErrorInformation(mErrorCode);
+ return (failureInfo.indicators & IS_TRANSIENT_ERROR) != 0;
+ }
+
+ /**
+ * Indicates whether the failure is due to the device being locked.
+ *
+ * @return true if the key operation failed because the user has to authenticate
+ * (e.g. by unlocking the device).
+ */
+ public boolean requiresUserAuthentication() {
+ PublicErrorInformation failureInfo = getErrorInformation(mErrorCode);
+ return (failureInfo.indicators & REQUIRES_USER_AUTHENTICATION) != 0;
+ }
+
+ /**
+ * Indicates whether the error related to the Keystore/KeyMint implementation and not
+ * a specific key.
+ *
+ * @return true if the error is related to the system, not the key in use. System
+ * errors indicate a feature isn't working, whereas key-related errors are likely
+ * to succeed with a new key.
+ */
+ public boolean isSystemError() {
+ PublicErrorInformation failureInfo = getErrorInformation(mErrorCode);
+ return (failureInfo.indicators & IS_SYSTEM_ERROR) != 0;
+ }
+
+ @Override
+ public String toString() {
+ String errorCodes = String.format(" (public error code: %d internal Keystore code: %d)",
+ getNumericErrorCode(), mErrorCode);
+ return super.toString() + errorCodes;
+ }
+
+ private static PublicErrorInformation getErrorInformation(int internalErrorCode) {
+ PublicErrorInformation errorInfo = sErrorCodeToFailureInfo.get(internalErrorCode);
+ if (errorInfo != null) {
+ return errorInfo;
+ }
+
+ /**
+ * KeyStore/keymaster exception with positive error codes coming from the KeyStore and
+ * negative ones from keymaster.
+ * This is a safety fall-back: All error codes should be present in the map.
+ */
+ if (internalErrorCode > 0) {
+ return GENERAL_KEYSTORE_ERROR;
+ } else {
+ return GENERAL_KEYMINT_ERROR;
+ }
+ }
+
+ private static final class PublicErrorInformation {
+ public final int indicators;
+ public final int errorCode;
+
+ PublicErrorInformation(int indicators, @PublicErrorCode int errorCode) {
+ this.indicators = indicators;
+ this.errorCode = errorCode;
+ }
+ }
+
+ private static final PublicErrorInformation GENERAL_KEYMINT_ERROR =
+ new PublicErrorInformation(0, ERROR_KEYMINT_FAILURE);
+
+ private static final PublicErrorInformation GENERAL_KEYSTORE_ERROR =
+ new PublicErrorInformation(0, ERROR_KEYSTORE_FAILURE);
+
+ private static final PublicErrorInformation KEYMINT_UNIMPLEMENTED_ERROR =
+ new PublicErrorInformation(IS_SYSTEM_ERROR, ERROR_UNIMPLEMENTED);
+
+ private static final PublicErrorInformation KEYMINT_RETRYABLE_ERROR =
+ new PublicErrorInformation(IS_SYSTEM_ERROR | IS_TRANSIENT_ERROR,
+ ERROR_KEYMINT_FAILURE);
+
+ private static final PublicErrorInformation KEYMINT_INCORRECT_USAGE_ERROR =
+ new PublicErrorInformation(0, ERROR_INCORRECT_USAGE);
+
+ private static final PublicErrorInformation KEYMINT_TEMPORAL_VALIDITY_ERROR =
+ new PublicErrorInformation(0, ERROR_KEY_NOT_TEMPORALLY_VALID);
+
+
+ private static final Map<Integer, PublicErrorInformation> sErrorCodeToFailureInfo =
+ new HashMap();
+
+ /**
+ * @hide
+ */
+ @TestApi
+ public static boolean hasFailureInfoForError(int internalErrorCode) {
+ return sErrorCodeToFailureInfo.containsKey(internalErrorCode);
+ }
+
+ static {
+ // KeyMint error codes
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_OK, GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_ROOT_OF_TRUST_ALREADY_SET,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_PURPOSE,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INCOMPATIBLE_PURPOSE,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_ALGORITHM,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INCOMPATIBLE_ALGORITHM,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_KEY_SIZE,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_BLOCK_MODE,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INCOMPATIBLE_BLOCK_MODE,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_MAC_LENGTH,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_PADDING_MODE,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INCOMPATIBLE_PADDING_MODE,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_DIGEST,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INCOMPATIBLE_DIGEST,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_EXPIRATION_TIME,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_USER_ID,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_AUTHORIZATION_TIMEOUT,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_KEY_FORMAT,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INCOMPATIBLE_KEY_FORMAT,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_KEY_VERIFICATION_ALGORITHM,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_KEY_EXPORT_OPTIONS_INVALID,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_DELEGATION_NOT_ALLOWED,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_KEY_NOT_YET_VALID,
+ KEYMINT_TEMPORAL_VALIDITY_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_KEY_EXPIRED,
+ KEYMINT_TEMPORAL_VALIDITY_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_KEY_USER_NOT_AUTHENTICATED,
+ new PublicErrorInformation(REQUIRES_USER_AUTHENTICATION,
+ ERROR_USER_AUTHENTICATION_REQUIRED));
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_OUTPUT_PARAMETER_NULL,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_OPERATION_HANDLE,
+ new PublicErrorInformation(IS_SYSTEM_ERROR | IS_TRANSIENT_ERROR,
+ ERROR_KEY_OPERATION_EXPIRED));
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INSUFFICIENT_BUFFER_SPACE,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_VERIFICATION_FAILED,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_TOO_MANY_OPERATIONS,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNEXPECTED_NULL_POINTER,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_KEY_BLOB,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_IMPORTED_KEY_NOT_ENCRYPTED,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_IMPORTED_KEY_DECRYPTION_FAILED,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_IMPORTED_KEY_NOT_SIGNED,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_IMPORTED_KEY_VERIFICATION_FAILED,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_ARGUMENT,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_TAG,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_TAG,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_MEMORY_ALLOCATION_FAILED,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_IMPORT_PARAMETER_MISMATCH,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_SECURE_HW_ACCESS_DENIED,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_OPERATION_CANCELLED,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_CONCURRENT_ACCESS_CONFLICT,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_SECURE_HW_BUSY,
+ KEYMINT_RETRYABLE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_SECURE_HW_COMMUNICATION_FAILED,
+ KEYMINT_RETRYABLE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_EC_FIELD,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_MISSING_NONCE,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_NONCE,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_MISSING_MAC_LENGTH,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_KEY_RATE_LIMIT_EXCEEDED,
+ KEYMINT_RETRYABLE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_CALLER_NONCE_PROHIBITED,
+ GENERAL_KEYMINT_ERROR);
+ // Error related to MAX_USES_PER_BOOT, restricting the number of uses per boot.
+ // It is not re-tryable.
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_KEY_MAX_OPS_EXCEEDED,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_MAC_LENGTH,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_MISSING_MIN_MAC_LENGTH,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_KDF,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_EC_CURVE,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_ATTESTATION_CHALLENGE_MISSING,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_KEYMINT_NOT_CONFIGURED,
+ new PublicErrorInformation(IS_SYSTEM_ERROR, ERROR_KEYMINT_FAILURE));
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_ATTESTATION_APPLICATION_ID_MISSING,
+ KEYMINT_RETRYABLE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_CANNOT_ATTEST_IDS,
+ new PublicErrorInformation(IS_SYSTEM_ERROR,
+ ERROR_ID_ATTESTATION_FAILURE));
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_ROLLBACK_RESISTANCE_UNAVAILABLE,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_HARDWARE_TYPE_UNAVAILABLE,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_DEVICE_LOCKED,
+ new PublicErrorInformation(IS_SYSTEM_ERROR | REQUIRES_USER_AUTHENTICATION,
+ ERROR_USER_AUTHENTICATION_REQUIRED));
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_STORAGE_KEY_UNSUPPORTED,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INCOMPATIBLE_MGF_DIGEST,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_MGF_DIGEST,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_MISSING_NOT_BEFORE,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_MISSING_NOT_AFTER,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ // This should not be exposed to apps as it's handled by Keystore.
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_HARDWARE_NOT_YET_AVAILABLE,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNIMPLEMENTED,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNKNOWN_ERROR,
+ new PublicErrorInformation(IS_SYSTEM_ERROR,
+ ERROR_KEYMINT_FAILURE));
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_VERSION_MISMATCH, GENERAL_KEYMINT_ERROR);
+
+ // Keystore error codes
+ sErrorCodeToFailureInfo.put(ResponseCode.LOCKED,
+ new PublicErrorInformation(REQUIRES_USER_AUTHENTICATION,
+ ERROR_USER_AUTHENTICATION_REQUIRED));
+ sErrorCodeToFailureInfo.put(ResponseCode.UNINITIALIZED,
+ new PublicErrorInformation(IS_SYSTEM_ERROR, ERROR_KEYSTORE_UNINITIALIZED));
+ sErrorCodeToFailureInfo.put(ResponseCode.SYSTEM_ERROR,
+ new PublicErrorInformation(IS_SYSTEM_ERROR,
+ ERROR_INTERNAL_SYSTEM_ERROR));
+ sErrorCodeToFailureInfo.put(ResponseCode.PERMISSION_DENIED,
+ new PublicErrorInformation(0, ERROR_PERMISSION_DENIED));
+ sErrorCodeToFailureInfo.put(ResponseCode.KEY_NOT_FOUND,
+ new PublicErrorInformation(0, ERROR_KEY_DOES_NOT_EXIST));
+ sErrorCodeToFailureInfo.put(ResponseCode.VALUE_CORRUPTED,
+ new PublicErrorInformation(0, ERROR_KEY_CORRUPTED));
+ sErrorCodeToFailureInfo.put(ResponseCode.KEY_PERMANENTLY_INVALIDATED,
+ new PublicErrorInformation(0, ERROR_KEY_DOES_NOT_EXIST));
+ }
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index af19bd0a79ac..b8e8b0114b47 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -295,11 +295,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
@NonNull TaskFragmentContainer primaryContainer, @NonNull Activity primaryActivity,
@NonNull TaskFragmentContainer secondaryContainer,
@NonNull SplitRule splitRule) {
+ SplitContainer splitContainer = new SplitContainer(primaryContainer, primaryActivity,
+ secondaryContainer, splitRule);
+ // Remove container later to prevent pinning escaping toast showing in lock task mode.
if (splitRule instanceof SplitPairRule && ((SplitPairRule) splitRule).shouldClearTop()) {
removeExistingSecondaryContainers(wct, primaryContainer);
}
- SplitContainer splitContainer = new SplitContainer(primaryContainer, primaryActivity,
- secondaryContainer, splitRule);
mSplitContainers.add(splitContainer);
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index 81be21cbd7aa..ade573132eef 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -112,8 +112,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
secondaryContainer.setLastRequestedBounds(secondaryRectBounds);
// Set adjacent to each other so that the containers below will be invisible.
- setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(),
- secondaryContainer.getTaskFragmentToken(), rule);
+ setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule);
mController.registerSplit(wct, primaryContainer, primaryActivity, secondaryContainer, rule);
@@ -149,8 +148,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
secondaryActivity, secondaryRectBounds, primaryContainer);
// Set adjacent to each other so that the containers below will be invisible.
- setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(),
- secondaryContainer.getTaskFragmentToken(), rule);
+ setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule);
mController.registerSplit(wct, primaryContainer, primaryActivity, secondaryContainer, rule);
@@ -269,8 +267,22 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
final TaskFragmentContainer secondaryContainer = splitContainer.getSecondaryContainer();
resizeTaskFragmentIfRegistered(wct, secondaryContainer, secondaryRectBounds);
- setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(),
- secondaryContainer.getTaskFragmentToken(), rule);
+ setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule);
+ }
+
+ private void setAdjacentTaskFragments(@NonNull WindowContainerTransaction wct,
+ @NonNull TaskFragmentContainer primaryContainer,
+ @NonNull TaskFragmentContainer secondaryContainer, @NonNull SplitRule splitRule) {
+ final Rect parentBounds = getParentContainerBounds(primaryContainer);
+ // Clear adjacent TaskFragments if the container is shown in fullscreen, or the
+ // secondaryContainer could not be finished.
+ if (!shouldShowSideBySide(parentBounds, splitRule)) {
+ setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(),
+ null /* secondary */, null /* splitRule */);
+ } else {
+ setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(),
+ secondaryContainer.getTaskFragmentToken(), splitRule);
+ }
}
/**
diff --git a/libs/WindowManager/Shell/res/color/size_compat_background_ripple.xml b/libs/WindowManager/Shell/res/color/compat_background_ripple.xml
index 329e5b9b31a0..329e5b9b31a0 100644
--- a/libs/WindowManager/Shell/res/color/size_compat_background_ripple.xml
+++ b/libs/WindowManager/Shell/res/color/compat_background_ripple.xml
diff --git a/libs/WindowManager/Shell/res/drawable/camera_compat_dismiss_button.xml b/libs/WindowManager/Shell/res/drawable/camera_compat_dismiss_button.xml
new file mode 100644
index 000000000000..1c8cb914af81
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/camera_compat_dismiss_button.xml
@@ -0,0 +1,33 @@
+<!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="43dp"
+ android:viewportWidth="48"
+ android:viewportHeight="43">
+ <group>
+ <clip-path
+ android:pathData="M48,43l-48,-0l-0,-43l48,-0z"/>
+ <path
+ android:pathData="M24,43C37.2548,43 48,32.2548 48,19L48,0L0,-0L0,19C0,32.2548 10.7452,43 24,43Z"
+ android:fillColor="@color/compat_controls_background"
+ android:strokeAlpha="0.8"
+ android:fillAlpha="0.8"/>
+ <path
+ android:pathData="M31,12.41L29.59,11L24,16.59L18.41,11L17,12.41L22.59,18L17,23.59L18.41,25L24,19.41L29.59,25L31,23.59L25.41,18L31,12.41Z"
+ android:fillColor="@color/compat_controls_text"/>
+ </group>
+</vector>
diff --git a/packages/CompanionDeviceManager/res/layout/profile_summary.xml b/libs/WindowManager/Shell/res/drawable/camera_compat_dismiss_ripple.xml
index 80fec59fbc45..c81013966c35 100644
--- a/packages/CompanionDeviceManager/res/layout/profile_summary.xml
+++ b/libs/WindowManager/Shell/res/drawable/camera_compat_dismiss_ripple.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2020 The Android Open Source Project
+ ~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -14,17 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-
-
-<TextView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/profile_summary"
- android:layout_below="@+id/title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="16dp"
- android:layout_marginEnd="16dp"
- android:textColor="?android:attr/textColorSecondary"
- android:textSize="14sp"
- android:gravity="center"
-/> \ No newline at end of file
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/compat_background_ripple">
+ <item android:drawable="@drawable/camera_compat_dismiss_button"/>
+</ripple> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_applied_button.xml b/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_applied_button.xml
new file mode 100644
index 000000000000..c796b5967f98
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_applied_button.xml
@@ -0,0 +1,32 @@
+<!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="43dp"
+ android:viewportWidth="48"
+ android:viewportHeight="43">
+ <path
+ android:pathData="M24,0C10.7452,0 0,10.7452 0,24V43H48V24C48,10.7452 37.2548,0 24,0Z"
+ android:fillColor="@color/compat_controls_background"
+ android:strokeAlpha="0.8"
+ android:fillAlpha="0.8"/>
+ <path
+ android:pathData="M32,17H28.83L27,15H21L19.17,17H16C14.9,17 14,17.9 14,19V31C14,32.1 14.9,33 16,33H32C33.1,33 34,32.1 34,31V19C34,17.9 33.1,17 32,17ZM32,31H16V19H32V31Z"
+ android:fillColor="@color/compat_controls_text"/>
+ <path
+ android:pathData="M24.6618,22C23.0436,22 21.578,22.6187 20.4483,23.625L18.25,21.375V27H23.7458L21.5353,24.7375C22.3841,24.0125 23.4649,23.5625 24.6618,23.5625C26.8235,23.5625 28.6616,25.0062 29.3028,27L30.75,26.5125C29.9012,23.8938 27.5013,22 24.6618,22Z"
+ android:fillColor="@color/compat_controls_text"/>
+</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_applied_ripple.xml b/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_applied_ripple.xml
new file mode 100644
index 000000000000..3e9fe6dc3b99
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_applied_ripple.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/compat_background_ripple">
+ <item android:drawable="@drawable/camera_compat_treatment_applied_button"/>
+</ripple>
diff --git a/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_suggested_button.xml b/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_suggested_button.xml
new file mode 100644
index 000000000000..af505d1cb73c
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_suggested_button.xml
@@ -0,0 +1,53 @@
+<!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="43dp"
+ android:viewportWidth="48"
+ android:viewportHeight="43">
+ <path
+ android:pathData="M24,0C10.7452,0 0,10.7452 0,24V43H48V24C48,10.7452 37.2548,0 24,0Z"
+ android:fillColor="@color/compat_controls_background"
+ android:strokeAlpha="0.8"
+ android:fillAlpha="0.8"/>
+ <path
+ android:pathData="M32,17H28.83L27,15H21L19.17,17H16C14.9,17 14,17.9 14,19V31C14,32.1 14.9,33 16,33H32C33.1,33 34,32.1 34,31V19C34,17.9 33.1,17 32,17ZM32,31H16V19H32V31Z"
+ android:fillColor="@color/compat_controls_text"/>
+ <path
+ android:pathData="M18,29L18,25.5L19.5,25.5L19.5,29L18,29Z"
+ android:fillColor="@color/compat_controls_text"/>
+ <path
+ android:pathData="M30,29L30,25.5L28.5,25.5L28.5,29L30,29Z"
+ android:fillColor="@color/compat_controls_text"/>
+ <path
+ android:pathData="M30,21L30,24.5L28.5,24.5L28.5,21L30,21Z"
+ android:fillColor="@color/compat_controls_text"/>
+ <path
+ android:pathData="M18,21L18,24.5L19.5,24.5L19.5,21L18,21Z"
+ android:fillColor="@color/compat_controls_text"/>
+ <path
+ android:pathData="M18,27.5L21.5,27.5L21.5,29L18,29L18,27.5Z"
+ android:fillColor="@color/compat_controls_text"/>
+ <path
+ android:pathData="M30,27.5L26.5,27.5L26.5,29L30,29L30,27.5Z"
+ android:fillColor="@color/compat_controls_text"/>
+ <path
+ android:pathData="M30,22.5L26.5,22.5L26.5,21L30,21L30,22.5Z"
+ android:fillColor="@color/compat_controls_text"/>
+ <path
+ android:pathData="M18,22.5L21.5,22.5L21.5,21L18,21L18,22.5Z"
+ android:fillColor="@color/compat_controls_text"/>
+</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_suggested_ripple.xml b/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_suggested_ripple.xml
new file mode 100644
index 000000000000..c0f1c89b0cbb
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/camera_compat_treatment_suggested_ripple.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/compat_background_ripple">
+ <item android:drawable="@drawable/camera_compat_treatment_suggested_button"/>
+</ripple>
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
index ab74e43472c3..e6ae28207970 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
+++ b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
@@ -21,7 +21,9 @@
android:viewportHeight="48">
<path
android:fillColor="@color/compat_controls_background"
- android:pathData="M0,24 a24,24 0 1,0 48,0 a24,24 0 1,0 -48,0" />
+ android:strokeAlpha="0.8"
+ android:fillAlpha="0.8"
+ android:pathData="M0,24 a24,24 0 1,0 48,0 a24,24 0 1,0 -48,0"/>
<group
android:translateX="12"
android:translateY="12">
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button_ripple.xml b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button_ripple.xml
index 95decff24ac4..6551edf6d0e6 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button_ripple.xml
+++ b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button_ripple.xml
@@ -15,6 +15,6 @@
~ limitations under the License.
-->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="@color/size_compat_background_ripple">
+ android:color="@color/compat_background_ripple">
<item android:drawable="@drawable/size_compat_restart_button"/>
</ripple> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml b/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml
index c04e258ea784..4ac972c6cfa7 100644
--- a/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml
+++ b/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml
@@ -16,7 +16,7 @@
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:clipToPadding="false"
@@ -26,7 +26,7 @@
<TextView
android:id="@+id/compat_mode_hint_text"
- android:layout_width="188dp"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:lineSpacingExtra="4sp"
android:background="@drawable/compat_hint_bubble"
diff --git a/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml b/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
index 6f946b25eaa5..c99f3fe89563 100644
--- a/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
+++ b/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
@@ -21,24 +21,51 @@
android:orientation="vertical"
android:gravity="bottom|end">
- <include android:id="@+id/size_compat_hint"
- layout="@layout/compat_mode_hint"/>
+ <include android:id="@+id/camera_compat_hint"
+ android:visibility="gone"
+ android:layout_width="@dimen/camera_compat_hint_width"
+ android:layout_height="wrap_content"
+ layout="@layout/compat_mode_hint"/>
- <FrameLayout
- android:layout_width="@dimen/size_compat_button_width"
- android:layout_height="@dimen/size_compat_button_height"
+ <LinearLayout
+ android:id="@+id/camera_compat_control"
+ android:visibility="gone"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:clipToPadding="false"
- android:paddingBottom="16dp">
+ android:layout_marginEnd="16dp"
+ android:layout_marginBottom="16dp"
+ android:orientation="vertical">
+
+ <ImageButton
+ android:id="@+id/camera_compat_treatment_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@android:color/transparent"/>
<ImageButton
- android:id="@+id/size_compat_restart_button"
+ android:id="@+id/camera_compat_dismiss_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:src="@drawable/size_compat_restart_button_ripple"
+ android:src="@drawable/camera_compat_dismiss_ripple"
android:background="@android:color/transparent"
- android:contentDescription="@string/restart_button_description"/>
+ android:contentDescription="@string/camera_compat_dismiss_button_description"/>
+
+ </LinearLayout>
+
+ <include android:id="@+id/size_compat_hint"
+ android:visibility="gone"
+ android:layout_width="@dimen/size_compat_hint_width"
+ android:layout_height="wrap_content"
+ layout="@layout/compat_mode_hint"/>
- </FrameLayout>
+ <ImageButton
+ android:id="@+id/size_compat_restart_button"
+ android:visibility="gone"
+ android:layout_width="@dimen/size_compat_button_width"
+ android:layout_height="@dimen/size_compat_button_height"
+ android:src="@drawable/size_compat_restart_button_ripple"
+ android:background="@android:color/transparent"
+ android:contentDescription="@string/restart_button_description"/>
</com.android.wm.shell.compatui.CompatUILayout>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 18e91f41a698..d338e3bd74f9 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -216,6 +216,12 @@
- compat_hint_corner_radius - compat_hint_point_width /2). -->
<dimen name="compat_hint_padding_end">7dp</dimen>
+ <!-- The width of the size compat hint. -->
+ <dimen name="size_compat_hint_width">188dp</dimen>
+
+ <!-- The width of the camera compat hint. -->
+ <dimen name="camera_compat_hint_width">143dp</dimen>
+
<!-- The width of the brand image on staring surface. -->
<dimen name="starting_surface_brand_image_width">200dp</dimen>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index c88fc16e218e..ab0013a2b0b4 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -158,4 +158,17 @@
<!-- Description of the restart button in the hint of size compatibility mode. [CHAR LIMIT=NONE] -->
<string name="restart_button_description">Tap to restart this app and go full screen.</string>
+
+ <!-- Description of the camera compat button for applying stretched issues treatment in the hint for
+ compatibility control. [CHAR LIMIT=NONE] -->
+ <string name="camera_compat_treatment_suggested_button_description">Camera issues?\nTap to refit</string>
+
+ <!-- Description of the camera compat button for reverting stretched issues treatment in the hint for
+ compatibility control. [CHAR LIMIT=NONE] -->
+ <string name="camera_compat_treatment_applied_button_description">Didn\u2019t fix it?\nTap to revert</string>
+
+ <!-- Accessibillity description of the camera dismiss button for stretched issues in the hint for
+ compatibility control. [CHAR LIMIT=NONE] -->
+ <string name="camera_compat_dismiss_button_description">No camera issues? Tap to dismiss.</string>
+
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index 8b3a35688f11..91ea436f81b6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -458,7 +458,7 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
newListener.onTaskInfoChanged(taskInfo);
}
notifyLocusVisibilityIfNeeded(taskInfo);
- if (updated || !taskInfo.equalsForSizeCompat(data.getTaskInfo())) {
+ if (updated || !taskInfo.equalsForCompatUi(data.getTaskInfo())) {
// Notify the compat UI if the listener or task info changed.
notifyCompatUI(taskInfo, newListener);
}
@@ -607,6 +607,19 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
restartTaskTopActivityProcessIfVisible(info.getTaskInfo().token);
}
+ @Override
+ public void onCameraControlStateUpdated(
+ int taskId, @TaskInfo.CameraCompatControlState int state) {
+ final TaskAppearedInfo info;
+ synchronized (mLock) {
+ info = mTasks.get(taskId);
+ }
+ if (info == null) {
+ return;
+ }
+ updateCameraCompatControlState(info.getTaskInfo().token, state);
+ }
+
private void logSizeCompatRestartButtonEventReported(@NonNull TaskAppearedInfo info,
int event) {
ActivityInfo topActivityInfo = info.getTaskInfo().topActivityInfo;
@@ -633,14 +646,11 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
// The task is vanished or doesn't support compat UI, notify to remove compat UI
// on this Task if there is any.
if (taskListener == null || !taskListener.supportCompatUI()
- || !taskInfo.topActivityInSizeCompat || !taskInfo.isVisible) {
- mCompatUI.onCompatInfoChanged(taskInfo.displayId, taskInfo.taskId,
- null /* taskConfig */, null /* taskListener */);
+ || !taskInfo.hasCompatUI() || !taskInfo.isVisible) {
+ mCompatUI.onCompatInfoChanged(taskInfo, null /* taskListener */);
return;
}
-
- mCompatUI.onCompatInfoChanged(taskInfo.displayId, taskInfo.taskId,
- taskInfo.configuration, taskListener);
+ mCompatUI.onCompatInfoChanged(taskInfo, taskListener);
}
private TaskListener getTaskListener(RunningTaskInfo runningTaskInfo) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
index d92e2ccc77bd..686fbbfd6f7c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
@@ -15,23 +15,22 @@
*/
package com.android.wm.shell.bubbles;
-import static android.graphics.Paint.ANTI_ALIAS_FLAG;
-import static android.graphics.Paint.DITHER_FLAG;
-import static android.graphics.Paint.FILTER_BITMAP_FLAG;
-
+import android.annotation.DrawableRes;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Outline;
-import android.graphics.Paint;
-import android.graphics.PaintFlagsDrawFilter;
import android.graphics.Path;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.PathParser;
+import android.view.Gravity;
import android.view.View;
import android.view.ViewOutlineProvider;
+import android.widget.FrameLayout;
import android.widget.ImageView;
import com.android.launcher3.icons.DotRenderer;
@@ -47,7 +46,7 @@ import java.util.EnumSet;
* Badge = the icon associated with the app that created this bubble, this will show work profile
* badge if appropriate.
*/
-public class BadgedImageView extends ImageView {
+public class BadgedImageView extends FrameLayout {
/** Same value as Launcher3 dot code */
public static final float WHITE_SCRIM_ALPHA = 0.54f;
@@ -74,6 +73,9 @@ public class BadgedImageView extends ImageView {
private final EnumSet<SuppressionFlag> mDotSuppressionFlags =
EnumSet.of(SuppressionFlag.FLYOUT_VISIBLE);
+ private final ImageView mBubbleIcon;
+ private final ImageView mAppIcon;
+
private float mDotScale = 0f;
private float mAnimatingToDotScale = 0f;
private boolean mDotIsAnimating = false;
@@ -86,7 +88,6 @@ public class BadgedImageView extends ImageView {
private DotRenderer.DrawParams mDrawParams;
private int mDotColor;
- private Paint mPaint = new Paint(ANTI_ALIAS_FLAG);
private Rect mTempBounds = new Rect();
public BadgedImageView(Context context) {
@@ -104,6 +105,17 @@ public class BadgedImageView extends ImageView {
public BadgedImageView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+
+ mBubbleIcon = new ImageView(context);
+ addView(mBubbleIcon);
+ mAppIcon = new ImageView(context);
+ addView(mAppIcon);
+
+ final TypedArray ta = mContext.obtainStyledAttributes(attrs, new int[]{android.R.attr.src},
+ defStyleAttr, defStyleRes);
+ mBubbleIcon.setImageResource(ta.getResourceId(0, 0));
+ ta.recycle();
+
mDrawParams = new DotRenderer.DrawParams();
setFocusable(true);
@@ -135,7 +147,6 @@ public class BadgedImageView extends ImageView {
public void showDotAndBadge(boolean onLeft) {
removeDotSuppressionFlag(BadgedImageView.SuppressionFlag.BEHIND_STACK);
animateDotBadgePositions(onLeft);
-
}
public void hideDotAndBadge(boolean onLeft) {
@@ -149,6 +160,7 @@ public class BadgedImageView extends ImageView {
*/
public void setRenderedBubble(BubbleViewProvider bubble) {
mBubble = bubble;
+ mBubbleIcon.setImageBitmap(bubble.getBubbleIcon());
if (mDotSuppressionFlags.contains(SuppressionFlag.BEHIND_STACK)) {
hideBadge();
} else {
@@ -176,6 +188,20 @@ public class BadgedImageView extends ImageView {
mDotRenderer.draw(canvas, mDrawParams);
}
+ /**
+ * Set drawable resource shown as the icon
+ */
+ public void setIconImageResource(@DrawableRes int drawable) {
+ mBubbleIcon.setImageResource(drawable);
+ }
+
+ /**
+ * Get icon drawable
+ */
+ public Drawable getIconDrawable() {
+ return mBubbleIcon.getDrawable();
+ }
+
/** Adds a dot suppression flag, updating dot visibility if needed. */
void addDotSuppressionFlag(SuppressionFlag flag) {
if (mDotSuppressionFlags.add(flag)) {
@@ -279,7 +305,6 @@ public class BadgedImageView extends ImageView {
showBadge();
}
-
/** Whether to draw the dot in onDraw(). */
private boolean shouldDrawDot() {
// Always render the dot if it's animating, since it could be animating out. Otherwise, show
@@ -325,29 +350,28 @@ public class BadgedImageView extends ImageView {
void showBadge() {
Bitmap badge = mBubble.getAppBadge();
if (badge == null) {
- setImageBitmap(mBubble.getBubbleIcon());
+ mAppIcon.setVisibility(GONE);
return;
}
- Canvas bubbleCanvas = new Canvas();
- Bitmap noBadgeBubble = mBubble.getBubbleIcon();
- Bitmap bubble = noBadgeBubble.copy(noBadgeBubble.getConfig(), /* isMutable */ true);
- bubbleCanvas.setDrawFilter(new PaintFlagsDrawFilter(DITHER_FLAG, FILTER_BITMAP_FLAG));
- bubbleCanvas.setBitmap(bubble);
- final int bubbleSize = bubble.getWidth();
+ final int bubbleSize = mBubble.getBubbleIcon().getWidth();
final int badgeSize = (int) (ICON_BADGE_SCALE * bubbleSize);
- Rect dest = new Rect();
+
+ FrameLayout.LayoutParams appIconParams = (LayoutParams) mAppIcon.getLayoutParams();
+ appIconParams.height = badgeSize;
+ appIconParams.width = badgeSize;
if (mOnLeft) {
- dest.set(0, bubbleSize - badgeSize, badgeSize, bubbleSize);
+ appIconParams.gravity = Gravity.BOTTOM | Gravity.LEFT;
} else {
- dest.set(bubbleSize - badgeSize, bubbleSize - badgeSize, bubbleSize, bubbleSize);
+ appIconParams.gravity = Gravity.BOTTOM | Gravity.RIGHT;
}
- bubbleCanvas.drawBitmap(badge, null /* src */, dest, mPaint);
- bubbleCanvas.setBitmap(null);
- setImageBitmap(bubble);
+ mAppIcon.setLayoutParams(appIconParams);
+
+ mAppIcon.setImageBitmap(badge);
+ mAppIcon.setVisibility(VISIBLE);
}
void hideBadge() {
- setImageBitmap(mBubble.getBubbleIcon());
+ mAppIcon.setVisibility(GONE);
}
}
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 f979f4d3b41f..ce1f8707da31 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
@@ -89,6 +89,8 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.onehanded.OneHandedController;
+import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.pip.PinnedStackListenerForwarder;
import java.io.FileDescriptor;
@@ -98,6 +100,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
+import java.util.Optional;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.IntConsumer;
@@ -198,6 +201,9 @@ public class BubbleController {
/** True when user is in status bar unlock shade. */
private boolean mIsStatusBarShade = true;
+ /** One handed mode controller to register transition listener. */
+ private Optional<OneHandedController> mOneHandedOptional;
+
/**
* Creates an instance of the BubbleController.
*/
@@ -212,6 +218,7 @@ public class BubbleController {
UiEventLogger uiEventLogger,
ShellTaskOrganizer organizer,
DisplayController displayController,
+ Optional<OneHandedController> oneHandedOptional,
ShellExecutor mainExecutor,
Handler mainHandler,
TaskViewTransitions taskViewTransitions,
@@ -222,8 +229,9 @@ public class BubbleController {
return new BubbleController(context, data, synchronizer, floatingContentCoordinator,
new BubbleDataRepository(context, launcherApps, mainExecutor),
statusBarService, windowManager, windowManagerShellWrapper, launcherApps,
- logger, taskStackListener, organizer, positioner, displayController, mainExecutor,
- mainHandler, taskViewTransitions, syncQueue);
+ logger, taskStackListener, organizer, positioner, displayController,
+ oneHandedOptional, mainExecutor, mainHandler, taskViewTransitions,
+ syncQueue);
}
/**
@@ -244,6 +252,7 @@ public class BubbleController {
ShellTaskOrganizer organizer,
BubblePositioner positioner,
DisplayController displayController,
+ Optional<OneHandedController> oneHandedOptional,
ShellExecutor mainExecutor,
Handler mainHandler,
TaskViewTransitions taskViewTransitions,
@@ -271,9 +280,25 @@ public class BubbleController {
mBubbleIconFactory = new BubbleIconFactory(context);
mDisplayController = displayController;
mTaskViewTransitions = taskViewTransitions;
+ mOneHandedOptional = oneHandedOptional;
mSyncQueue = syncQueue;
}
+ private static void registerOneHandedState(OneHandedController oneHanded) {
+ oneHanded.registerTransitionCallback(
+ new OneHandedTransitionCallback() {
+ @Override
+ public void onStartFinished(Rect bounds) {
+ // TODO(b/198403767) mStackView.offSetY(int bounds.top)
+ }
+
+ @Override
+ public void onStopFinished(Rect bounds) {
+ // TODO(b/198403767) mStackView.offSetY(int bounds.top)
+ }
+ });
+ }
+
public void initialize() {
mBubbleData.setListener(mBubbleDataListener);
mBubbleData.setSuppressionChangedListener(this::onBubbleNotificationSuppressionChanged);
@@ -397,6 +422,8 @@ public class BubbleController {
}
}
});
+
+ mOneHandedOptional.ifPresent(BubbleController::registerOneHandedState);
}
@VisibleForTesting
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index 519a856538c7..51b7eaa8b0e4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -328,6 +328,7 @@ public class BubbleData {
if (prevBubble == null) {
// Create a new bubble
bubble.setSuppressFlyout(suppressFlyout);
+ bubble.markUpdatedAt(mTimeSource.currentTimeMillis());
doAdd(bubble);
trim();
} else {
@@ -561,17 +562,10 @@ public class BubbleData {
overflowBubble(reason, bubbleToRemove);
if (mBubbles.size() == 1) {
- if (hasOverflowBubbles() && (mPositioner.showingInTaskbar() || isExpanded())) {
- // No more active bubbles but we have stuff in the overflow -- select that view
- // if we're already expanded or always showing.
- setShowingOverflow(true);
- setSelectedBubbleInternal(mOverflow);
- } else {
- setExpandedInternal(false);
- // Don't use setSelectedBubbleInternal because we don't want to trigger an
- // applyUpdate
- mSelectedBubble = null;
- }
+ setExpandedInternal(false);
+ // Don't use setSelectedBubbleInternal because we don't want to trigger an
+ // applyUpdate
+ mSelectedBubble = null;
}
if (indexToRemove < mBubbles.size() - 1) {
// Removing anything but the last bubble means positions will change.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
index 5161092fb96c..a175929cf498 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
@@ -66,7 +66,7 @@ class BubbleOverflow(
updateResources()
getExpandedView()?.applyThemeAttrs()
// Apply inset and new style to fresh icon drawable.
- getIconView()?.setImageResource(R.drawable.bubble_ic_overflow_button)
+ getIconView()?.setIconImageResource(R.drawable.bubble_ic_overflow_button)
updateBtnTheme()
}
@@ -89,19 +89,19 @@ class BubbleOverflow(
dotColor = colorAccent
val shapeColor = res.getColor(android.R.color.system_accent1_1000)
- overflowBtn?.drawable?.setTint(shapeColor)
+ overflowBtn?.iconDrawable?.setTint(shapeColor)
val iconFactory = BubbleIconFactory(context)
// Update bitmap
- val fg = InsetDrawable(overflowBtn?.drawable, overflowIconInset)
+ val fg = InsetDrawable(overflowBtn?.iconDrawable, overflowIconInset)
bitmap = iconFactory.createBadgedIconBitmap(
AdaptiveIconDrawable(ColorDrawable(colorAccent), fg)).icon
// Update dot path
dotPath = PathParser.createPathFromPathData(
res.getString(com.android.internal.R.string.config_icon_mask))
- val scale = iconFactory.normalizer.getScale(iconView!!.drawable,
+ val scale = iconFactory.normalizer.getScale(iconView!!.iconDrawable,
null /* outBounds */, null /* path */, null /* outMaskShape */)
val radius = BadgedImageView.DEFAULT_PATH_SIZE / 2f
val matrix = Matrix()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
index f0f78748e343..19d513f81cab 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
@@ -346,6 +346,9 @@ public class ExpandedAnimationController
* bubble is dragged back into the row.
*/
public void dragBubbleOut(View bubbleView, float x, float y) {
+ if (mMagnetizedBubbleDraggingOut == null) {
+ return;
+ }
if (mSpringToTouchOnNextMotionEvent) {
springBubbleTo(mMagnetizedBubbleDraggingOut.getUnderlyingObject(), x, y);
mSpringToTouchOnNextMotionEvent = false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
index ffda1f92ec90..c32733d4f73c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
@@ -27,7 +27,6 @@ import androidx.annotation.BinderThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
-import java.util.ArrayList;
import java.util.concurrent.CopyOnWriteArrayList;
/**
@@ -71,12 +70,18 @@ public class DisplayChangeController {
mRotationListener.remove(listener);
}
+ /** Query all listeners for changes that should happen on rotation. */
+ public void dispatchOnRotateDisplay(WindowContainerTransaction outWct, int displayId,
+ final int fromRotation, final int toRotation) {
+ for (OnDisplayChangingListener c : mRotationListener) {
+ c.onRotateDisplay(displayId, fromRotation, toRotation, outWct);
+ }
+ }
+
private void onRotateDisplay(int displayId, final int fromRotation, final int toRotation,
IDisplayWindowRotationCallback callback) {
WindowContainerTransaction t = new WindowContainerTransaction();
- for (OnDisplayChangingListener c : mRotationListener) {
- c.onRotateDisplay(displayId, fromRotation, toRotation, t);
- }
+ dispatchOnRotateDisplay(t, displayId, fromRotation, toRotation);
try {
callback.continueRotateDisplay(toRotation, t);
} catch (RemoteException e) {
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 a1fb658ccb9d..7db49f05a5dc 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
@@ -76,6 +76,11 @@ public class DisplayController {
}
}
+ /** Get the DisplayChangeController. */
+ public DisplayChangeController getChangeController() {
+ return mChangeController;
+ }
+
/**
* Gets a display by id from DisplayManager.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
index eea6e3cb35db..c4bd73ba1b4a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
@@ -16,7 +16,6 @@
package com.android.wm.shell.common;
-import android.graphics.GraphicBuffer;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.view.SurfaceControl;
@@ -63,8 +62,6 @@ public class ScreenshotUtils {
if (buffer == null || buffer.getHardwareBuffer() == null) {
return;
}
- final GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(
- buffer.getHardwareBuffer());
mScreenshot = new SurfaceControl.Builder()
.setName("ScreenshotUtils screenshot")
.setFormat(PixelFormat.TRANSLUCENT)
@@ -73,7 +70,7 @@ public class ScreenshotUtils {
.setBLASTLayer()
.build();
- mTransaction.setBuffer(mScreenshot, graphicBuffer);
+ mTransaction.setBuffer(mScreenshot, buffer.getHardwareBuffer());
mTransaction.setColorSpace(mScreenshot, buffer.getColorSpace());
mTransaction.reparent(mScreenshot, mSurfaceControl);
mTransaction.setLayer(mScreenshot, mLayer);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 040fffae3e84..b8ac87fa5d4c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -93,7 +93,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
private final InsetsState mInsetsState = new InsetsState();
private Context mContext;
- private DividerSnapAlgorithm mDividerSnapAlgorithm;
+ @VisibleForTesting DividerSnapAlgorithm mDividerSnapAlgorithm;
private WindowContainerToken mWinToken1;
private WindowContainerToken mWinToken2;
private int mDividePosition;
@@ -294,20 +294,22 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
mSplitLayoutHandler.onLayoutSizeChanging(this);
}
- void setDividePosition(int position) {
+ void setDividePosition(int position, boolean applyLayoutChange) {
mDividePosition = position;
updateBounds(mDividePosition);
- mSplitLayoutHandler.onLayoutSizeChanged(this);
+ if (applyLayoutChange) {
+ mSplitLayoutHandler.onLayoutSizeChanged(this);
+ }
}
- /** Sets divide position base on the ratio within root bounds. */
+ /** Updates divide position and split bounds base on the ratio within root bounds. */
public void setDivideRatio(float ratio) {
final int position = isLandscape()
? mRootBounds.left + (int) (mRootBounds.width() * ratio)
: mRootBounds.top + (int) (mRootBounds.height() * ratio);
- DividerSnapAlgorithm.SnapTarget snapTarget =
+ final DividerSnapAlgorithm.SnapTarget snapTarget =
mDividerSnapAlgorithm.calculateNonDismissingSnapTarget(position);
- setDividePosition(snapTarget.position);
+ setDividePosition(snapTarget.position, false /* applyLayoutChange */);
}
/** Resets divider position. */
@@ -336,7 +338,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
break;
default:
flingDividePosition(currentPosition, snapTarget.position,
- () -> setDividePosition(snapTarget.position));
+ () -> setDividePosition(snapTarget.position, true /* applyLayoutChange */));
break;
}
}
@@ -389,7 +391,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
@Override
public void onAnimationCancel(Animator animation) {
- setDividePosition(to);
+ setDividePosition(to, true /* applyLayoutChange */);
}
});
animator.start();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index e0b23873a980..8f4cfb0a49a4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -17,6 +17,8 @@
package com.android.wm.shell.compatui;
import android.annotation.Nullable;
+import android.app.TaskInfo;
+import android.app.TaskInfo.CameraCompatControlState;
import android.content.Context;
import android.content.res.Configuration;
import android.hardware.display.DisplayManager;
@@ -53,12 +55,14 @@ import java.util.function.Predicate;
public class CompatUIController implements OnDisplaysChangedListener,
DisplayImeController.ImePositionProcessor {
- /** Callback for size compat UI interaction. */
+ /** Callback for compat UI interaction. */
public interface CompatUICallback {
/** Called when the size compat restart button appears. */
void onSizeCompatRestartButtonAppeared(int taskId);
/** Called when the size compat restart button is clicked. */
void onSizeCompatRestartButtonClicked(int taskId);
+ /** Called when the camera compat control state is updated. */
+ void onCameraControlStateUpdated(int taskId, @CameraCompatControlState int state);
}
private static final String TAG = "CompatUIController";
@@ -86,10 +90,12 @@ public class CompatUIController implements OnDisplaysChangedListener,
private CompatUICallback mCallback;
- /** Only show once automatically in the process life. */
- private boolean mHasShownHint;
- /** Indicates if the keyguard is currently occluded, in which case compat UIs shouldn't
- * be shown. */
+ // Only show once automatically in the process life.
+ private boolean mHasShownSizeCompatHint;
+ private boolean mHasShownCameraCompatHint;
+
+ // Indicates if the keyguard is currently occluded, in which case compat UIs shouldn't
+ // be shown.
private boolean mKeyguardOccluded;
public CompatUIController(Context context,
@@ -122,23 +128,20 @@ public class CompatUIController implements OnDisplaysChangedListener,
* Called when the Task info changed. Creates and updates the compat UI if there is an
* activity in size compat, or removes the UI if there is no size compat activity.
*
- * @param displayId display the task and activity are in.
- * @param taskId task the activity is in.
- * @param taskConfig task config to place the compat UI with.
+ * @param taskInfo {@link TaskInfo} task the activity is in.
* @param taskListener listener to handle the Task Surface placement.
*/
- public void onCompatInfoChanged(int displayId, int taskId,
- @Nullable Configuration taskConfig,
+ public void onCompatInfoChanged(TaskInfo taskInfo,
@Nullable ShellTaskOrganizer.TaskListener taskListener) {
- if (taskConfig == null || taskListener == null) {
+ if (taskInfo.configuration == null || taskListener == null) {
// Null token means the current foreground activity is not in compatibility mode.
- removeLayout(taskId);
- } else if (mActiveLayouts.contains(taskId)) {
+ removeLayout(taskInfo.taskId);
+ } else if (mActiveLayouts.contains(taskInfo.taskId)) {
// UI already exists, update the UI layout.
- updateLayout(taskId, taskConfig, taskListener);
+ updateLayout(taskInfo, taskListener);
} else {
// Create a new compat UI.
- createLayout(displayId, taskId, taskConfig, taskListener);
+ createLayout(taskInfo, taskListener);
}
}
@@ -215,38 +218,45 @@ public class CompatUIController implements OnDisplaysChangedListener,
return mDisplaysWithIme.contains(displayId);
}
- private void createLayout(int displayId, int taskId, Configuration taskConfig,
- ShellTaskOrganizer.TaskListener taskListener) {
- final Context context = getOrCreateDisplayContext(displayId);
+ private void createLayout(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener) {
+ final Context context = getOrCreateDisplayContext(taskInfo.displayId);
if (context == null) {
- Log.e(TAG, "Cannot get context for display " + displayId);
+ Log.e(TAG, "Cannot get context for display " + taskInfo.displayId);
return;
}
final CompatUIWindowManager compatUIWindowManager =
- createLayout(context, displayId, taskId, taskConfig, taskListener);
- mActiveLayouts.put(taskId, compatUIWindowManager);
- compatUIWindowManager.createLayout(showOnDisplay(displayId));
+ createLayout(context, taskInfo, taskListener);
+ mActiveLayouts.put(taskInfo.taskId, compatUIWindowManager);
+ compatUIWindowManager.createLayout(showOnDisplay(taskInfo.displayId),
+ taskInfo.topActivityInSizeCompat, taskInfo.cameraCompatControlState);
}
@VisibleForTesting
- CompatUIWindowManager createLayout(Context context, int displayId, int taskId,
- Configuration taskConfig, ShellTaskOrganizer.TaskListener taskListener) {
+ CompatUIWindowManager createLayout(Context context, TaskInfo taskInfo,
+ ShellTaskOrganizer.TaskListener taskListener) {
final CompatUIWindowManager compatUIWindowManager = new CompatUIWindowManager(context,
- taskConfig, mSyncQueue, mCallback, taskId, taskListener,
- mDisplayController.getDisplayLayout(displayId), mHasShownHint);
- // Only show hint for the first time.
- mHasShownHint = true;
+ taskInfo.configuration, mSyncQueue, mCallback, taskInfo.taskId, taskListener,
+ mDisplayController.getDisplayLayout(taskInfo.displayId), mHasShownSizeCompatHint,
+ mHasShownCameraCompatHint);
+ // Only show hints for the first time.
+ if (taskInfo.topActivityInSizeCompat) {
+ mHasShownSizeCompatHint = true;
+ }
+ if (taskInfo.hasCameraCompatControl()) {
+ mHasShownCameraCompatHint = true;
+ }
return compatUIWindowManager;
}
- private void updateLayout(int taskId, Configuration taskConfig,
- ShellTaskOrganizer.TaskListener taskListener) {
- final CompatUIWindowManager layout = mActiveLayouts.get(taskId);
+ private void updateLayout(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener) {
+ final CompatUIWindowManager layout = mActiveLayouts.get(taskInfo.taskId);
if (layout == null) {
return;
}
- layout.updateCompatInfo(taskConfig, taskListener, showOnDisplay(layout.getDisplayId()));
+ layout.updateCompatInfo(taskInfo.configuration, taskListener,
+ showOnDisplay(layout.getDisplayId()), taskInfo.topActivityInSizeCompat,
+ taskInfo.cameraCompatControlState);
}
private void removeLayout(int taskId) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java
index ea4f20968438..29b2baa221e7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java
@@ -16,6 +16,9 @@
package com.android.wm.shell.compatui;
+import android.annotation.IdRes;
+import android.app.TaskInfo;
+import android.app.TaskInfo.CameraCompatControlState;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
@@ -53,21 +56,59 @@ public class CompatUILayout extends LinearLayout {
mWindowManager = windowManager;
}
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- // Need to relayout after changes like hiding / showing a hint since they affect size.
- // Doing this directly in setSizeCompatHintVisibility can result in flaky animation.
- mWindowManager.relayout();
+ void updateCameraTreatmentButton(@CameraCompatControlState int newState) {
+ int buttonBkgId = newState == TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED
+ ? R.drawable.camera_compat_treatment_suggested_ripple
+ : R.drawable.camera_compat_treatment_applied_ripple;
+ int hintStringId = newState == TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED
+ ? R.string.camera_compat_treatment_suggested_button_description
+ : R.string.camera_compat_treatment_applied_button_description;
+ final ImageButton button = findViewById(R.id.camera_compat_treatment_button);
+ button.setImageResource(buttonBkgId);
+ button.setContentDescription(getResources().getString(hintStringId));
+ final LinearLayout hint = findViewById(R.id.camera_compat_hint);
+ ((TextView) hint.findViewById(R.id.compat_mode_hint_text)).setText(hintStringId);
}
void setSizeCompatHintVisibility(boolean show) {
- final LinearLayout sizeCompatHint = findViewById(R.id.size_compat_hint);
+ setViewVisibility(R.id.size_compat_hint, show);
+ }
+
+ void setCameraCompatHintVisibility(boolean show) {
+ setViewVisibility(R.id.camera_compat_hint, show);
+ }
+
+ void setRestartButtonVisibility(boolean show) {
+ setViewVisibility(R.id.size_compat_restart_button, show);
+ // Hint should never be visible without button.
+ if (!show) {
+ setSizeCompatHintVisibility(/* show= */ false);
+ }
+ }
+
+ void setCameraControlVisibility(boolean show) {
+ setViewVisibility(R.id.camera_compat_control, show);
+ // Hint should never be visible without button.
+ if (!show) {
+ setCameraCompatHintVisibility(/* show= */ false);
+ }
+ }
+
+ private void setViewVisibility(@IdRes int resId, boolean show) {
+ final View view = findViewById(resId);
int visibility = show ? View.VISIBLE : View.GONE;
- if (sizeCompatHint.getVisibility() == visibility) {
+ if (view.getVisibility() == visibility) {
return;
}
- sizeCompatHint.setVisibility(visibility);
+ view.setVisibility(visibility);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ // Need to relayout after changes like hiding / showing a hint since they affect size.
+ // Doing this directly in setSizeCompatHintVisibility can result in flaky animation.
+ mWindowManager.relayout();
}
@Override
@@ -85,5 +126,26 @@ public class CompatUILayout extends LinearLayout {
((TextView) sizeCompatHint.findViewById(R.id.compat_mode_hint_text))
.setText(R.string.restart_button_description);
sizeCompatHint.setOnClickListener(view -> setSizeCompatHintVisibility(/* show= */ false));
+
+ final ImageButton cameraTreatmentButton =
+ findViewById(R.id.camera_compat_treatment_button);
+ cameraTreatmentButton.setOnClickListener(
+ view -> mWindowManager.onCameraTreatmentButtonClicked());
+ cameraTreatmentButton.setOnLongClickListener(view -> {
+ mWindowManager.onCameraButtonLongClicked();
+ return true;
+ });
+
+ final ImageButton cameraDismissButton = findViewById(R.id.camera_compat_dismiss_button);
+ cameraDismissButton.setOnClickListener(
+ view -> mWindowManager.onCameraDismissButtonClicked());
+ cameraDismissButton.setOnLongClickListener(view -> {
+ mWindowManager.onCameraButtonLongClicked();
+ return true;
+ });
+
+ final LinearLayout cameraCompatHint = findViewById(R.id.camera_compat_hint);
+ cameraCompatHint.setOnClickListener(
+ view -> setCameraCompatHintVisibility(/* show= */ false));
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
index 997ad04e3b57..44526b00bf0d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
@@ -16,6 +16,10 @@
package com.android.wm.shell.compatui;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
@@ -23,6 +27,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERL
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import android.annotation.Nullable;
+import android.app.TaskInfo.CameraCompatControlState;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
@@ -63,8 +68,17 @@ class CompatUIWindowManager extends WindowlessWindowManager {
private ShellTaskOrganizer.TaskListener mTaskListener;
private DisplayLayout mDisplayLayout;
+ // Remember the last reported states in case visibility changes due to keyguard or
+ // IME updates.
@VisibleForTesting
- boolean mShouldShowHint;
+ boolean mHasSizeCompat;
+ @CameraCompatControlState
+ private int mCameraCompatControlState = CAMERA_COMPAT_CONTROL_HIDDEN;
+
+ @VisibleForTesting
+ boolean mShouldShowSizeCompatHint;
+ @VisibleForTesting
+ boolean mShouldShowCameraCompatHint;
@Nullable
@VisibleForTesting
@@ -78,7 +92,7 @@ class CompatUIWindowManager extends WindowlessWindowManager {
CompatUIWindowManager(Context context, Configuration taskConfig,
SyncTransactionQueue syncQueue, CompatUIController.CompatUICallback callback,
int taskId, ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout,
- boolean hasShownHint) {
+ boolean hasShownSizeCompatHint, boolean hasShownCameraCompatHint) {
super(taskConfig, null /* rootSurface */, null /* hostInputToken */);
mContext = context;
mSyncQueue = syncQueue;
@@ -88,7 +102,8 @@ class CompatUIWindowManager extends WindowlessWindowManager {
mTaskId = taskId;
mTaskListener = taskListener;
mDisplayLayout = displayLayout;
- mShouldShowHint = !hasShownHint;
+ mShouldShowSizeCompatHint = !hasShownSizeCompatHint;
+ mShouldShowCameraCompatHint = !hasShownCameraCompatHint;
mStableBounds = new Rect();
mDisplayLayout.getStableBounds(mStableBounds);
}
@@ -113,7 +128,10 @@ class CompatUIWindowManager extends WindowlessWindowManager {
}
/** Creates the layout for compat controls. */
- void createLayout(boolean show) {
+ void createLayout(boolean show, boolean hasSizeCompat,
+ @CameraCompatControlState int cameraCompatControlState) {
+ mHasSizeCompat = hasSizeCompat;
+ mCameraCompatControlState = cameraCompatControlState;
if (!show || mCompatUILayout != null) {
// Wait until compat controls should be visible.
return;
@@ -122,16 +140,27 @@ class CompatUIWindowManager extends WindowlessWindowManager {
initCompatUi();
updateSurfacePosition();
- mCallback.onSizeCompatRestartButtonAppeared(mTaskId);
+ if (hasSizeCompat) {
+ mCallback.onSizeCompatRestartButtonAppeared(mTaskId);
+ }
+ }
+
+ private void createLayout(boolean show) {
+ createLayout(show, mHasSizeCompat, mCameraCompatControlState);
}
/** Called when compat info changed. */
void updateCompatInfo(Configuration taskConfig,
- ShellTaskOrganizer.TaskListener taskListener, boolean show) {
+ ShellTaskOrganizer.TaskListener taskListener, boolean show, boolean hasSizeCompat,
+ @CameraCompatControlState int cameraCompatControlState) {
final Configuration prevTaskConfig = mTaskConfig;
final ShellTaskOrganizer.TaskListener prevTaskListener = mTaskListener;
mTaskConfig = taskConfig;
mTaskListener = taskListener;
+ final boolean prevHasSizeCompat = mHasSizeCompat;
+ final int prevCameraCompatControlState = mCameraCompatControlState;
+ mHasSizeCompat = hasSizeCompat;
+ mCameraCompatControlState = cameraCompatControlState;
// Update configuration.
mContext = mContext.createConfigurationContext(taskConfig);
@@ -144,6 +173,11 @@ class CompatUIWindowManager extends WindowlessWindowManager {
return;
}
+ if (prevHasSizeCompat != mHasSizeCompat
+ || prevCameraCompatControlState != mCameraCompatControlState) {
+ updateVisibilityOfViews();
+ }
+
if (!taskConfig.windowConfiguration.getBounds()
.equals(prevTaskConfig.windowConfiguration.getBounds())) {
// Reposition the UI surfaces.
@@ -155,6 +189,7 @@ class CompatUIWindowManager extends WindowlessWindowManager {
mCompatUILayout.setLayoutDirection(taskConfig.getLayoutDirection());
updateSurfacePosition();
}
+
}
/** Called when the visibility of the UI should change. */
@@ -195,6 +230,34 @@ class CompatUIWindowManager extends WindowlessWindowManager {
mCallback.onSizeCompatRestartButtonClicked(mTaskId);
}
+ /** Called when the camera treatment button is clicked. */
+ void onCameraTreatmentButtonClicked() {
+ if (!shouldShowCameraControl()) {
+ Log.w(TAG, "Camera compat shouldn't receive clicks in the hidden state.");
+ return;
+ }
+ // When a camera control is shown, only two states are allowed: "treament applied" and
+ // "treatment suggested". Clicks on the conrol's treatment button toggle between these
+ // two states.
+ mCameraCompatControlState =
+ mCameraCompatControlState == CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED
+ ? CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED
+ : CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
+ mCallback.onCameraControlStateUpdated(mTaskId, mCameraCompatControlState);
+ mCompatUILayout.updateCameraTreatmentButton(mCameraCompatControlState);
+ }
+
+ /** Called when the camera dismiss button is clicked. */
+ void onCameraDismissButtonClicked() {
+ if (!shouldShowCameraControl()) {
+ Log.w(TAG, "Camera compat shouldn't receive clicks in the hidden state.");
+ return;
+ }
+ mCameraCompatControlState = CAMERA_COMPAT_CONTROL_DISMISSED;
+ mCallback.onCameraControlStateUpdated(mTaskId, CAMERA_COMPAT_CONTROL_DISMISSED);
+ mCompatUILayout.setCameraControlVisibility(/* show= */ false);
+ }
+
/** Called when the restart button is long clicked. */
void onRestartButtonLongClicked() {
if (mCompatUILayout == null) {
@@ -203,6 +266,14 @@ class CompatUIWindowManager extends WindowlessWindowManager {
mCompatUILayout.setSizeCompatHintVisibility(/* show= */ true);
}
+ /** Called when either dismiss or treatment camera buttons is long clicked. */
+ void onCameraButtonLongClicked() {
+ if (mCompatUILayout == null) {
+ return;
+ }
+ mCompatUILayout.setCameraCompatHintVisibility(/* show= */ true);
+ }
+
int getDisplayId() {
return mDisplayId;
}
@@ -213,6 +284,8 @@ class CompatUIWindowManager extends WindowlessWindowManager {
/** Releases the surface control and tears down the view hierarchy. */
void release() {
+ // Hiding before releasing to avoid flickering when transitioning to the Home screen.
+ mCompatUILayout.setVisibility(View.GONE);
mCompatUILayout = null;
if (mViewHost != null) {
@@ -283,12 +356,35 @@ class CompatUIWindowManager extends WindowlessWindowManager {
mCompatUILayout = inflateCompatUILayout();
mCompatUILayout.inject(this);
- mCompatUILayout.setSizeCompatHintVisibility(mShouldShowHint);
+ updateVisibilityOfViews();
mViewHost.setView(mCompatUILayout, getWindowLayoutParams());
+ }
+
+ private void updateVisibilityOfViews() {
+ // Size Compat mode restart button.
+ mCompatUILayout.setRestartButtonVisibility(mHasSizeCompat);
+ if (mHasSizeCompat && mShouldShowSizeCompatHint) {
+ mCompatUILayout.setSizeCompatHintVisibility(/* show= */ true);
+ // Only show by default for the first time.
+ mShouldShowSizeCompatHint = false;
+ }
+
+ // Camera control for stretched issues.
+ mCompatUILayout.setCameraControlVisibility(shouldShowCameraControl());
+ if (shouldShowCameraControl() && mShouldShowCameraCompatHint) {
+ mCompatUILayout.setCameraCompatHintVisibility(/* show= */ true);
+ // Only show by default for the first time.
+ mShouldShowCameraCompatHint = false;
+ }
+ if (shouldShowCameraControl()) {
+ mCompatUILayout.updateCameraTreatmentButton(mCameraCompatControlState);
+ }
+ }
- // Only show by default for the first time.
- mShouldShowHint = false;
+ private boolean shouldShowCameraControl() {
+ return mCameraCompatControlState != CAMERA_COMPAT_CONTROL_HIDDEN
+ && mCameraCompatControlState != CAMERA_COMPAT_CONTROL_DISMISSED;
}
@VisibleForTesting
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 6d158d591011..23d9b8b14159 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
@@ -362,7 +362,6 @@ public abstract class WMShellBaseModule {
return Optional.empty();
}
-
//
// Task to Surface communication
//
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 dff563589e5b..d681a7780295 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
@@ -107,6 +107,7 @@ public class WMShellModule {
UiEventLogger uiEventLogger,
ShellTaskOrganizer organizer,
DisplayController displayController,
+ @DynamicOverride Optional<OneHandedController> oneHandedOptional,
@ShellMainThread ShellExecutor mainExecutor,
@ShellMainThread Handler mainHandler,
TaskViewTransitions taskViewTransitions,
@@ -114,8 +115,8 @@ public class WMShellModule {
return BubbleController.create(context, null /* synchronizer */,
floatingContentCoordinator, statusBarService, windowManager,
windowManagerShellWrapper, launcherApps, taskStackListener,
- uiEventLogger, organizer, displayController, mainExecutor, mainHandler,
- taskViewTransitions, syncQueue);
+ uiEventLogger, organizer, displayController, oneHandedOptional,
+ mainExecutor, mainHandler, taskViewTransitions, syncQueue);
}
//
@@ -142,12 +143,10 @@ public class WMShellModule {
static OneHandedController provideOneHandedController(Context context,
WindowManager windowManager, DisplayController displayController,
DisplayLayout displayLayout, TaskStackListenerImpl taskStackListener,
- UiEventLogger uiEventLogger,
- @ShellMainThread ShellExecutor mainExecutor,
+ UiEventLogger uiEventLogger, @ShellMainThread ShellExecutor mainExecutor,
@ShellMainThread Handler mainHandler) {
- return OneHandedController.create(context, windowManager,
- displayController, displayLayout, taskStackListener, uiEventLogger, mainExecutor,
- mainHandler);
+ return OneHandedController.create(context, windowManager, displayController, displayLayout,
+ taskStackListener, uiEventLogger, mainExecutor, mainHandler);
}
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
index b65a2e4ffca6..8e6c05d83906 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
@@ -151,7 +151,13 @@ public class DragAndDropPolicy {
final Rect rightHitRegion = new Rect();
final Rect rightDrawRegion = bottomOrRightBounds;
- displayRegion.splitVertically(leftHitRegion, rightHitRegion);
+ // If we have existing split regions use those bounds, otherwise split it 50/50
+ if (inSplitScreen) {
+ leftHitRegion.set(topOrLeftBounds);
+ rightHitRegion.set(bottomOrRightBounds);
+ } else {
+ displayRegion.splitVertically(leftHitRegion, rightHitRegion);
+ }
mTargets.add(new Target(TYPE_SPLIT_LEFT, leftHitRegion, leftDrawRegion));
mTargets.add(new Target(TYPE_SPLIT_RIGHT, rightHitRegion, rightDrawRegion));
@@ -162,8 +168,13 @@ public class DragAndDropPolicy {
final Rect bottomHitRegion = new Rect();
final Rect bottomDrawRegion = bottomOrRightBounds;
- displayRegion.splitHorizontally(
- topHitRegion, bottomHitRegion);
+ // If we have existing split regions use those bounds, otherwise split it 50/50
+ if (inSplitScreen) {
+ topHitRegion.set(topOrLeftBounds);
+ bottomHitRegion.set(bottomOrRightBounds);
+ } else {
+ displayRegion.splitHorizontally(topHitRegion, bottomHitRegion);
+ }
mTargets.add(new Target(TYPE_SPLIT_TOP, topHitRegion, topDrawRegion));
mTargets.add(new Target(TYPE_SPLIT_BOTTOM, bottomHitRegion, bottomDrawRegion));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
index 67f9062b0a66..fd3be2b11c15 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
@@ -17,7 +17,9 @@
package com.android.wm.shell.draganddrop;
import static android.app.StatusBarManager.DISABLE_NONE;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import android.animation.Animator;
@@ -32,11 +34,11 @@ import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.Insets;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.RemoteException;
import android.view.DragEvent;
import android.view.SurfaceControl;
-import android.view.ViewGroup;
import android.view.WindowInsets;
import android.view.WindowInsets.Type;
import android.widget.LinearLayout;
@@ -73,6 +75,7 @@ public class DragLayout extends LinearLayout {
private DropZoneView mDropZoneView2;
private int mDisplayMargin;
+ private int mDividerSize;
private Insets mInsets = Insets.NONE;
private boolean mIsShowing;
@@ -89,13 +92,15 @@ public class DragLayout extends LinearLayout {
mDisplayMargin = context.getResources().getDimensionPixelSize(
R.dimen.drop_layout_display_margin);
+ mDividerSize = context.getResources().getDimensionPixelSize(
+ R.dimen.split_divider_bar_width);
mDropZoneView1 = new DropZoneView(context);
mDropZoneView2 = new DropZoneView(context);
- addView(mDropZoneView1, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
- addView(mDropZoneView2, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
+ addView(mDropZoneView1, new LinearLayout.LayoutParams(MATCH_PARENT,
+ MATCH_PARENT));
+ addView(mDropZoneView2, new LinearLayout.LayoutParams(MATCH_PARENT,
+ MATCH_PARENT));
((LayoutParams) mDropZoneView1.getLayoutParams()).weight = 1;
((LayoutParams) mDropZoneView2.getLayoutParams()).weight = 1;
updateContainerMargins();
@@ -165,42 +170,80 @@ public class DragLayout extends LinearLayout {
mHasDropped = false;
mCurrentTarget = null;
- List<ActivityManager.RunningTaskInfo> tasks = null;
- // Figure out the splashscreen info for the existing task(s).
- try {
- tasks = ActivityTaskManager.getService().getTasks(2,
- false /* filterOnlyVisibleRecents */,
- false /* keepIntentExtra */);
- } catch (RemoteException e) {
- // don't show an icon / will just use the defaults
- }
- if (tasks != null && !tasks.isEmpty()) {
- ActivityManager.RunningTaskInfo taskInfo1 = tasks.get(0);
- Drawable icon1 = mIconProvider.getIcon(taskInfo1.topActivityInfo);
- int bgColor1 = getResizingBackgroundColor(taskInfo1);
-
- boolean alreadyInSplit = mSplitScreenController != null
- && mSplitScreenController.isSplitScreenVisible();
- if (alreadyInSplit && tasks.size() > 1) {
- ActivityManager.RunningTaskInfo taskInfo2 = tasks.get(1);
- Drawable icon2 = mIconProvider.getIcon(taskInfo2.topActivityInfo);
- int bgColor2 = getResizingBackgroundColor(taskInfo2);
-
- // figure out which task is on which side
- int splitPosition1 = mSplitScreenController.getSplitPosition(taskInfo1.taskId);
- boolean isTask1TopOrLeft = splitPosition1 == SPLIT_POSITION_TOP_OR_LEFT;
- if (isTask1TopOrLeft) {
- mDropZoneView1.setAppInfo(bgColor1, icon1);
- mDropZoneView2.setAppInfo(bgColor2, icon2);
- } else {
- mDropZoneView2.setAppInfo(bgColor1, icon1);
- mDropZoneView1.setAppInfo(bgColor2, icon2);
- }
- } else {
+ boolean alreadyInSplit = mSplitScreenController != null
+ && mSplitScreenController.isSplitScreenVisible();
+ if (!alreadyInSplit) {
+ List<ActivityManager.RunningTaskInfo> tasks = null;
+ // Figure out the splashscreen info for the existing task.
+ try {
+ tasks = ActivityTaskManager.getService().getTasks(1,
+ false /* filterOnlyVisibleRecents */,
+ false /* keepIntentExtra */);
+ } catch (RemoteException e) {
+ // don't show an icon / will just use the defaults
+ }
+ if (tasks != null && !tasks.isEmpty()) {
+ ActivityManager.RunningTaskInfo taskInfo1 = tasks.get(0);
+ Drawable icon1 = mIconProvider.getIcon(taskInfo1.topActivityInfo);
+ int bgColor1 = getResizingBackgroundColor(taskInfo1);
mDropZoneView1.setAppInfo(bgColor1, icon1);
mDropZoneView2.setAppInfo(bgColor1, icon1);
+ updateDropZoneSizes(null, null); // passing null splits the views evenly
}
+ } else {
+ // We're already in split so get taskInfo from the controller to populate icon / color.
+ ActivityManager.RunningTaskInfo topOrLeftTask =
+ mSplitScreenController.getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT);
+ ActivityManager.RunningTaskInfo bottomOrRightTask =
+ mSplitScreenController.getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT);
+ if (topOrLeftTask != null && bottomOrRightTask != null) {
+ Drawable topOrLeftIcon = mIconProvider.getIcon(topOrLeftTask.topActivityInfo);
+ int topOrLeftColor = getResizingBackgroundColor(topOrLeftTask);
+ Drawable bottomOrRightIcon = mIconProvider.getIcon(
+ bottomOrRightTask.topActivityInfo);
+ int bottomOrRightColor = getResizingBackgroundColor(bottomOrRightTask);
+ mDropZoneView1.setAppInfo(topOrLeftColor, topOrLeftIcon);
+ mDropZoneView2.setAppInfo(bottomOrRightColor, bottomOrRightIcon);
+ }
+
+ // Update the dropzones to match existing split sizes
+ Rect topOrLeftBounds = new Rect();
+ Rect bottomOrRightBounds = new Rect();
+ mSplitScreenController.getStageBounds(topOrLeftBounds, bottomOrRightBounds);
+ updateDropZoneSizes(topOrLeftBounds, bottomOrRightBounds);
+ }
+ }
+
+ /**
+ * Sets the size of the two drop zones based on the provided bounds. The divider sits between
+ * the views and its size is included in the calculations.
+ *
+ * @param bounds1 bounds to apply to the first dropzone view, null if split in half.
+ * @param bounds2 bounds to apply to the second dropzone view, null if split in half.
+ */
+ private void updateDropZoneSizes(Rect bounds1, Rect bounds2) {
+ final int orientation = getResources().getConfiguration().orientation;
+ final boolean isPortrait = orientation == Configuration.ORIENTATION_PORTRAIT;
+ final int halfDivider = mDividerSize / 2;
+ final LinearLayout.LayoutParams dropZoneView1 =
+ (LayoutParams) mDropZoneView1.getLayoutParams();
+ final LinearLayout.LayoutParams dropZoneView2 =
+ (LayoutParams) mDropZoneView2.getLayoutParams();
+ if (isPortrait) {
+ dropZoneView1.width = MATCH_PARENT;
+ dropZoneView2.width = MATCH_PARENT;
+ dropZoneView1.height = bounds1 != null ? bounds1.height() + halfDivider : MATCH_PARENT;
+ dropZoneView2.height = bounds2 != null ? bounds2.height() + halfDivider : MATCH_PARENT;
+ } else {
+ dropZoneView1.width = bounds1 != null ? bounds1.width() + halfDivider : MATCH_PARENT;
+ dropZoneView2.width = bounds2 != null ? bounds2.width() + halfDivider : MATCH_PARENT;
+ dropZoneView1.height = MATCH_PARENT;
+ dropZoneView2.height = MATCH_PARENT;
}
+ dropZoneView1.weight = bounds1 != null ? 0 : 1;
+ dropZoneView2.weight = bounds2 != null ? 0 : 1;
+ mDropZoneView1.setLayoutParams(dropZoneView1);
+ mDropZoneView2.setLayoutParams(dropZoneView2);
}
public void show() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index 72bb6552ff3d..507204ce0229 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -198,7 +198,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
OneHandedSettingsUtil settingsUtil = new OneHandedSettingsUtil();
OneHandedAccessibilityUtil accessibilityUtil = new OneHandedAccessibilityUtil(context);
OneHandedTimeoutHandler timeoutHandler = new OneHandedTimeoutHandler(mainExecutor);
- OneHandedState transitionState = new OneHandedState();
+ OneHandedState oneHandedState = new OneHandedState();
OneHandedTutorialHandler tutorialHandler = new OneHandedTutorialHandler(context,
settingsUtil, windowManager);
OneHandedAnimationController animationController =
@@ -216,7 +216,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
ServiceManager.getService(Context.OVERLAY_SERVICE));
return new OneHandedController(context, displayController,
oneHandedBackgroundPanelOrganizer, organizer, touchHandler, tutorialHandler,
- settingsUtil, accessibilityUtil, timeoutHandler, transitionState,
+ settingsUtil, accessibilityUtil, timeoutHandler, oneHandedState,
oneHandedUiEventsLogger, overlayManager, taskStackListener, mainExecutor,
mainHandler);
}
@@ -392,8 +392,10 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
mEventCallback = callback;
}
- @VisibleForTesting
- void registerTransitionCallback(OneHandedTransitionCallback callback) {
+ /**
+ * Registers {@link OneHandedTransitionCallback} to monitor the transition status
+ */
+ public void registerTransitionCallback(OneHandedTransitionCallback callback) {
mDisplayAreaOrganizer.registerTransitionCallback(callback);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
index 6d4773bdeb1f..c0734e95ecb7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
@@ -112,11 +112,17 @@ public interface Pip {
default void showPictureInPictureMenu() {}
/**
- * Called by NavigationBar in order to listen in for PiP bounds change. This is mostly used
- * for times where the PiP bounds could conflict with SystemUI elements, such as a stashed
- * PiP and the Back-from-Edge gesture.
+ * Called by NavigationBar and TaskbarDelegate in order to listen in for PiP bounds change. This
+ * is mostly used for times where the PiP bounds could conflict with SystemUI elements, such as
+ * a stashed PiP and the Back-from-Edge gesture.
*/
- default void setPipExclusionBoundsChangeListener(Consumer<Rect> listener) { }
+ default void addPipExclusionBoundsChangeListener(Consumer<Rect> listener) { }
+
+ /**
+ * Remove a callback added previously. This is used when NavigationBar is removed from the
+ * view hierarchy or destroyed.
+ */
+ default void removePipExclusionBoundsChangeListener(Consumer<Rect> listener) { }
/**
* Dump the current state and information if need.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
index e3674dc920d5..b3558ad4b91e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
@@ -38,6 +38,8 @@ import com.android.wm.shell.common.DisplayLayout;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
@@ -89,7 +91,7 @@ public final class PipBoundsState {
private @Nullable Runnable mOnMinimalSizeChangeCallback;
private @Nullable TriConsumer<Boolean, Integer, Boolean> mOnShelfVisibilityChangeCallback;
- private @Nullable Consumer<Rect> mOnPipExclusionBoundsChangeCallback;
+ private List<Consumer<Rect>> mOnPipExclusionBoundsChangeCallbacks = new ArrayList<>();
public PipBoundsState(@NonNull Context context) {
mContext = context;
@@ -108,8 +110,8 @@ public final class PipBoundsState {
/** Set the current PIP bounds. */
public void setBounds(@NonNull Rect bounds) {
mBounds.set(bounds);
- if (mOnPipExclusionBoundsChangeCallback != null) {
- mOnPipExclusionBoundsChangeCallback.accept(bounds);
+ for (Consumer<Rect> callback : mOnPipExclusionBoundsChangeCallbacks) {
+ callback.accept(bounds);
}
}
@@ -407,17 +409,25 @@ public final class PipBoundsState {
}
/**
- * Set a callback to watch out for PiP bounds. This is mostly used by SystemUI's
+ * Add a callback to watch out for PiP bounds. This is mostly used by SystemUI's
* Back-gesture handler, to avoid conflicting with PiP when it's stashed.
*/
- public void setPipExclusionBoundsChangeCallback(
+ public void addPipExclusionBoundsChangeCallback(
@Nullable Consumer<Rect> onPipExclusionBoundsChangeCallback) {
- mOnPipExclusionBoundsChangeCallback = onPipExclusionBoundsChangeCallback;
- if (mOnPipExclusionBoundsChangeCallback != null) {
- mOnPipExclusionBoundsChangeCallback.accept(getBounds());
+ mOnPipExclusionBoundsChangeCallbacks.add(onPipExclusionBoundsChangeCallback);
+ for (Consumer<Rect> callback : mOnPipExclusionBoundsChangeCallbacks) {
+ callback.accept(getBounds());
}
}
+ /**
+ * Remove a callback that was previously added.
+ */
+ public void removePipExclusionBoundsChangeCallback(
+ @Nullable Consumer<Rect> onPipExclusionBoundsChangeCallback) {
+ mOnPipExclusionBoundsChangeCallbacks.remove(onPipExclusionBoundsChangeCallback);
+ }
+
/** Source of truth for the current bounds of PIP that may be in motion. */
public static class MotionBoundsState {
/** The bounds used when PIP is in motion (e.g. during a drag or animation) */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 79e3444a0d82..d3dc91515921 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -843,9 +843,16 @@ public class PipController implements PipTransitionController.PipTransitionCallb
}
@Override
- public void setPipExclusionBoundsChangeListener(Consumer<Rect> listener) {
+ public void addPipExclusionBoundsChangeListener(Consumer<Rect> listener) {
mMainExecutor.execute(() -> {
- mPipBoundsState.setPipExclusionBoundsChangeCallback(listener);
+ mPipBoundsState.addPipExclusionBoundsChangeCallback(listener);
+ });
+ }
+
+ @Override
+ public void removePipExclusionBoundsChangeListener(Consumer<Rect> listener) {
+ mMainExecutor.execute(() -> {
+ mPipBoundsState.removePipExclusionBoundsChangeCallback(listener);
});
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index cf4e56e128bf..afd51171bdd7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -186,6 +186,15 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
return mStageCoordinator.isSplitScreenVisible();
}
+ @Nullable
+ public ActivityManager.RunningTaskInfo getTaskInfo(@SplitPosition int splitPosition) {
+ if (isSplitScreenVisible()) {
+ int taskId = mStageCoordinator.getTaskId(splitPosition);
+ return mTaskOrganizer.getRunningTaskInfo(taskId);
+ }
+ return null;
+ }
+
public boolean isTaskInSplitScreen(int taskId) {
return isSplitScreenVisible()
&& mStageCoordinator.getStageOfTask(taskId) != STAGE_TYPE_UNDEFINED;
@@ -309,8 +318,15 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
startIntentLegacy(intent, fillInIntent, position, options);
return;
}
- mStageCoordinator.startIntent(intent, fillInIntent, STAGE_TYPE_UNDEFINED, position, options,
- null /* remote */);
+
+ try {
+ options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options,
+ null /* wct */);
+ intent.send(mContext, 0, fillInIntent, null /* onFinished */, null /* handler */,
+ null /* requiredPermission */, options);
+ } catch (PendingIntent.CanceledException e) {
+ Slog.e(TAG, "Failed to launch task", e);
+ }
}
private void startIntentLegacy(PendingIntent intent, Intent fillInIntent,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index 86e7b0e4cb7f..16f4c7234e72 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -23,6 +23,10 @@ import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.TransitionInfo.FLAG_FIRST_CUSTOM;
+import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
+import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS_SNAP;
import static com.android.wm.shell.transition.Transitions.isOpeningType;
@@ -40,7 +44,9 @@ import android.window.TransitionInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.transition.OneShotRemoteHandler;
import com.android.wm.shell.transition.Transitions;
@@ -57,7 +63,7 @@ class SplitScreenTransitions {
private final Transitions mTransitions;
private final Runnable mOnFinish;
- IBinder mPendingDismiss = null;
+ DismissTransition mPendingDismiss = null;
IBinder mPendingEnter = null;
private IBinder mAnimatingTransition = null;
@@ -127,12 +133,6 @@ class SplitScreenTransitions {
}
// TODO(shell-transitions): screenshot here
final Rect startBounds = new Rect(change.getStartAbsBounds());
- if (info.getType() == TRANSIT_SPLIT_DISMISS_SNAP) {
- // Dismissing split via snap which means the still-visible task has been
- // dragged to its end position at animation start so reflect that here.
- startBounds.offsetTo(change.getEndAbsBounds().left,
- change.getEndAbsBounds().top);
- }
final Rect endBounds = new Rect(change.getEndAbsBounds());
startBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y);
endBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y);
@@ -184,12 +184,20 @@ class SplitScreenTransitions {
return transition;
}
- /** Starts a transition for dismissing split after dragging the divider to a screen edge */
- IBinder startSnapToDismiss(@NonNull WindowContainerTransaction wct,
- @NonNull Transitions.TransitionHandler handler) {
- final IBinder transition = mTransitions.startTransition(
- TRANSIT_SPLIT_DISMISS_SNAP, wct, handler);
- mPendingDismiss = transition;
+ /** Starts a transition to dismiss split. */
+ IBinder startDismissTransition(@Nullable IBinder transition, WindowContainerTransaction wct,
+ Transitions.TransitionHandler handler, @SplitScreen.StageType int dismissTop,
+ @SplitScreenController.ExitReason int reason) {
+ final int type = reason == EXIT_REASON_DRAG_DIVIDER
+ ? TRANSIT_SPLIT_DISMISS_SNAP : TRANSIT_SPLIT_DISMISS;
+ if (transition == null) {
+ transition = mTransitions.startTransition(type, wct, handler);
+ }
+ mPendingDismiss = new DismissTransition(transition, reason, dismissTop);
+
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
+ + " deduced Dismiss due to %s. toTop=%s",
+ exitReasonToString(reason), stageTypeToString(dismissTop));
return transition;
}
@@ -206,7 +214,7 @@ class SplitScreenTransitions {
if (mAnimatingTransition == mPendingEnter) {
mPendingEnter = null;
}
- if (mAnimatingTransition == mPendingDismiss) {
+ if (mPendingDismiss != null && mPendingDismiss.mTransition == mAnimatingTransition) {
mPendingDismiss = null;
}
mAnimatingTransition = null;
@@ -295,4 +303,20 @@ class SplitScreenTransitions {
mAnimations.add(va);
mTransitions.getAnimExecutor().execute(va::start);
}
+
+ /** Bundled information of dismiss transition. */
+ static class DismissTransition {
+ IBinder mTransition;
+
+ int mReason;
+
+ @SplitScreen.StageType
+ int mDismissTop;
+
+ DismissTransition(IBinder transition, int reason, int dismissTop) {
+ this.mTransition = transition;
+ this.mReason = reason;
+ this.mDismissTop = dismissTop;
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index ed5de0d698f3..43a1d74ba49c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -18,9 +18,10 @@ package com.android.wm.shell.splitscreen;
import static android.app.ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
-import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.transitTypeToString;
@@ -32,7 +33,6 @@ import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSIT
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
-import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_FINISHED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
@@ -40,11 +40,11 @@ import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_UNKNOWN;
import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString;
import static com.android.wm.shell.splitscreen.SplitScreenTransitions.FLAG_IS_DIVIDER_BAR;
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS_SNAP;
-import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
import static com.android.wm.shell.transition.Transitions.isClosingType;
import static com.android.wm.shell.transition.Transitions.isOpeningType;
@@ -54,10 +54,8 @@ import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
-import android.app.PendingIntent;
import android.app.WindowConfiguration;
import android.content.Context;
-import android.content.Intent;
import android.graphics.Rect;
import android.hardware.devicestate.DeviceStateManager;
import android.os.Bundle;
@@ -124,9 +122,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private static final String TAG = StageCoordinator.class.getSimpleName();
- /** internal value for mDismissTop that represents no dismiss */
- private static final int NO_DISMISS = -2;
-
private final SurfaceSession mSurfaceSession = new SurfaceSession();
private final MainStage mMainStage;
@@ -159,9 +154,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private boolean mKeyguardOccluded;
private boolean mDeviceSleep;
- @StageType
- private int mDismissTop = NO_DISMISS;
-
/** The target stage to dismiss to when unlock after folded. */
@StageType
private int mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
@@ -174,7 +166,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
setDividerVisibility(false);
mSplitLayout.resetDividerPosition();
}
- mDismissTop = NO_DISMISS;
};
private final SplitWindowManager.ParentContainerCallbacks mParentContainerCallbacks =
@@ -342,12 +333,12 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
sideOptions = sideOptions != null ? sideOptions : new Bundle();
setSideStagePosition(sidePosition, wct);
+ mSplitLayout.setDivideRatio(splitRatio);
// Build a request WCT that will launch both apps such that task 0 is on the main stage
// while task 1 is on the side stage.
mMainStage.activate(getMainStageBounds(), wct, false /* reparent */);
mSideStage.setBounds(getSideStageBounds(), wct);
- mSplitLayout.setDivideRatio(splitRatio);
// Make sure the launch options will put tasks in the corresponding split roots
addActivityOptions(mainOptions, mMainStage);
addActivityOptions(sideOptions, mSideStage);
@@ -436,17 +427,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mTaskOrganizer.applyTransaction(wct);
}
- public void startIntent(PendingIntent intent, Intent fillInIntent,
- @StageType int stage, @SplitPosition int position,
- @androidx.annotation.Nullable Bundle options,
- @Nullable RemoteTransition remoteTransition) {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- options = resolveStartStage(stage, position, options, wct);
- wct.sendPendingIntent(intent, fillInIntent, options);
- mSplitTransitions.startEnterTransition(
- TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, wct, remoteTransition, this);
- }
-
/**
* Collects all the current child tasks of a specific split and prepares transaction to evict
* them to display.
@@ -475,8 +455,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
options = resolveStartStage(STAGE_TYPE_SIDE, position, options, wct);
}
} else {
- // Exit split-screen and launch fullscreen since stage wasn't specified.
- prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
+ Slog.w(TAG,
+ "No stage type nor split position specified to resolve start stage");
}
break;
}
@@ -522,6 +502,14 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return SplitLayout.reversePosition(mSideStagePosition);
}
+ int getTaskId(@SplitPosition int splitPosition) {
+ if (mSideStagePosition == splitPosition) {
+ return mSideStage.getTopVisibleChildTaskId();
+ } else {
+ return mMainStage.getTopVisibleChildTaskId();
+ }
+ }
+
void setSideStagePosition(@SplitPosition int sideStagePosition,
@Nullable WindowContainerTransaction wct) {
setSideStagePosition(sideStagePosition, true /* updateBounds */, wct);
@@ -920,23 +908,22 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
}
- @VisibleForTesting
- IBinder onSnappedToDismissTransition(boolean mainStageToTop) {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- prepareExitSplitScreen(mainStageToTop ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE, wct);
- return mSplitTransitions.startSnapToDismiss(wct, this);
- }
-
@Override
public void onSnappedToDismiss(boolean bottomOrRight) {
final boolean mainStageToTop =
bottomOrRight ? mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT
: mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT;
- if (ENABLE_SHELL_TRANSITIONS) {
- onSnappedToDismissTransition(mainStageToTop);
+
+ if (!ENABLE_SHELL_TRANSITIONS) {
+ exitSplitScreen(mainStageToTop ? mMainStage : mSideStage, EXIT_REASON_DRAG_DIVIDER);
return;
}
- exitSplitScreen(mainStageToTop ? mMainStage : mSideStage, EXIT_REASON_DRAG_DIVIDER);
+
+ final int dismissTop = mainStageToTop ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ prepareExitSplitScreen(dismissTop, wct);
+ mSplitTransitions.startDismissTransition(
+ null /* transition */, wct, this, dismissTop, EXIT_REASON_DRAG_DIVIDER);
}
@Override
@@ -1115,14 +1102,25 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@Nullable TransitionRequestInfo request) {
final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
if (triggerTask == null) {
- // still want to monitor everything while in split-screen, so return non-null.
+ // Still want to monitor everything while in split-screen, so return non-null.
return isSplitScreenVisible() ? new WindowContainerTransaction() : null;
+ } else if (triggerTask.displayId != mDisplayId) {
+ // Skip handling task on the other display.
+ return null;
}
WindowContainerTransaction out = null;
final @WindowManager.TransitionType int type = request.getType();
+ final boolean isOpening = isOpeningType(type);
+ final boolean inFullscreen = triggerTask.getWindowingMode() == WINDOWING_MODE_FULLSCREEN;
+
+ if (isOpening && inFullscreen) {
+ // One task is opening into fullscreen mode, remove the corresponding split record.
+ mRecentTasks.ifPresent(recentTasks -> recentTasks.removeSplitPair(triggerTask.taskId));
+ }
+
if (isSplitScreenVisible()) {
- // try to handle everything while in split-screen, so return a WCT even if it's empty.
+ // Try to handle everything while in split-screen, so return a WCT even if it's empty.
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " split is active so using split"
+ "Transition to handle request. triggerTask=%d type=%s mainChildren=%d"
+ " sideChildren=%d", triggerTask.taskId, transitTypeToString(type),
@@ -1130,33 +1128,35 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
out = new WindowContainerTransaction();
final StageTaskListener stage = getStageOfTask(triggerTask);
if (stage != null) {
- // dismiss split if the last task in one of the stages is going away
+ // Dismiss split if the last task in one of the stages is going away
if (isClosingType(type) && stage.getChildCount() == 1) {
// The top should be the opposite side that is closing:
- mDismissTop = getStageType(stage) == STAGE_TYPE_MAIN
- ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN;
+ int dismissTop = getStageType(stage) == STAGE_TYPE_MAIN ? STAGE_TYPE_SIDE
+ : STAGE_TYPE_MAIN;
+ prepareExitSplitScreen(dismissTop, out);
+ mSplitTransitions.startDismissTransition(transition, out, this, dismissTop,
+ EXIT_REASON_APP_FINISHED);
}
- } else {
- if (triggerTask.getActivityType() == ACTIVITY_TYPE_HOME && isOpeningType(type)) {
- // Going home so dismiss both.
- mDismissTop = STAGE_TYPE_UNDEFINED;
+ } else if (isOpening && inFullscreen) {
+ final int activityType = triggerTask.getActivityType();
+ if (activityType == ACTIVITY_TYPE_ASSISTANT) {
+ // We don't want assistant panel to dismiss split screen, so do nothing.
+ } else {
+ // Going home or occluded by the other fullscreen task, so dismiss both.
+ prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, out);
+ final int exitReason = activityType == ACTIVITY_TYPE_HOME
+ ? EXIT_REASON_RETURN_HOME : EXIT_REASON_UNKNOWN;
+ mSplitTransitions.startDismissTransition(transition, out, this,
+ STAGE_TYPE_UNDEFINED, exitReason);
}
}
- if (mDismissTop != NO_DISMISS) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
- + " deduced Dismiss from request. toTop=%s",
- stageTypeToString(mDismissTop));
- prepareExitSplitScreen(mDismissTop, out);
- mSplitTransitions.mPendingDismiss = transition;
- }
} else {
- // Not in split mode, so look for an open into a split stage just so we can whine and
- // complain about how this isn't a supported operation.
- if ((type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT)) {
- if (getStageOfTask(triggerTask) != null) {
- throw new IllegalStateException("Entering split implicitly with only one task"
- + " isn't supported.");
- }
+ if (isOpening && getStageOfTask(triggerTask) != null) {
+ // One task is appearing into split, prepare to enter split screen.
+ out = new WindowContainerTransaction();
+ mMainStage.activate(getMainStageBounds(), out, true /* includingTopTask */);
+ mSideStage.moveToTop(getSideStageBounds(), out);
+ mSplitTransitions.mPendingEnter = transition;
}
}
return out;
@@ -1168,8 +1168,9 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
- if (transition != mSplitTransitions.mPendingDismiss
- && transition != mSplitTransitions.mPendingEnter) {
+ if (transition != mSplitTransitions.mPendingEnter && (
+ mSplitTransitions.mPendingDismiss == null
+ || mSplitTransitions.mPendingDismiss.mTransition != transition)) {
// Not entering or exiting, so just do some house-keeping and validation.
// If we're not in split-mode, just abort so something else can handle it.
@@ -1212,8 +1213,10 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
boolean shouldAnimate = true;
if (mSplitTransitions.mPendingEnter == transition) {
shouldAnimate = startPendingEnterAnimation(transition, info, startTransaction);
- } else if (mSplitTransitions.mPendingDismiss == transition) {
- shouldAnimate = startPendingDismissAnimation(transition, info, startTransaction);
+ } else if (mSplitTransitions.mPendingDismiss != null
+ && mSplitTransitions.mPendingDismiss.mTransition == transition) {
+ shouldAnimate = startPendingDismissAnimation(
+ mSplitTransitions.mPendingDismiss, info, startTransaction);
}
if (!shouldAnimate) return false;
@@ -1224,60 +1227,65 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private boolean startPendingEnterAnimation(@NonNull IBinder transition,
@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) {
- if (info.getType() == TRANSIT_SPLIT_SCREEN_PAIR_OPEN) {
- // First, verify that we actually have opened 2 apps in split.
- TransitionInfo.Change mainChild = null;
- TransitionInfo.Change sideChild = null;
- for (int iC = 0; iC < info.getChanges().size(); ++iC) {
- final TransitionInfo.Change change = info.getChanges().get(iC);
- final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
- if (taskInfo == null || !taskInfo.hasParentTask()) continue;
- final @StageType int stageType = getStageType(getStageOfTask(taskInfo));
- if (stageType == STAGE_TYPE_MAIN) {
- mainChild = change;
- } else if (stageType == STAGE_TYPE_SIDE) {
- sideChild = change;
- }
- }
- if (mainChild == null || sideChild == null) {
- throw new IllegalStateException("Launched 2 tasks in split, but didn't receive"
- + " 2 tasks in transition. Possibly one of them failed to launch");
- // TODO: fallback logic. Probably start a new transition to exit split before
- // applying anything here. Ideally consolidate with transition-merging.
+ // First, verify that we actually have opened apps in both splits.
+ TransitionInfo.Change mainChild = null;
+ TransitionInfo.Change sideChild = null;
+ for (int iC = 0; iC < info.getChanges().size(); ++iC) {
+ final TransitionInfo.Change change = info.getChanges().get(iC);
+ final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+ if (taskInfo == null || !taskInfo.hasParentTask()) continue;
+ final @StageType int stageType = getStageType(getStageOfTask(taskInfo));
+ if (stageType == STAGE_TYPE_MAIN) {
+ mainChild = change;
+ } else if (stageType == STAGE_TYPE_SIDE) {
+ sideChild = change;
}
+ }
+ if (mainChild == null || sideChild == null) {
+ throw new IllegalStateException("Launched 2 tasks in split, but didn't receive"
+ + " 2 tasks in transition. Possibly one of them failed to launch");
+ // TODO: fallback logic. Probably start a new transition to exit split before
+ // applying anything here. Ideally consolidate with transition-merging.
+ }
- // Update local states (before animating).
- setDividerVisibility(true);
- setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, false /* updateBounds */,
- null /* wct */);
- setSplitsVisible(true);
-
- addDividerBarToTransition(info, t, true /* show */);
-
- // Make some noise if things aren't totally expected. These states shouldn't effect
- // transitions locally, but remotes (like Launcher) may get confused if they were
- // depending on listener callbacks. This can happen because task-organizer callbacks
- // aren't serialized with transition callbacks.
- // TODO(b/184679596): Find a way to either include task-org information in
- // the transition, or synchronize task-org callbacks.
- if (!mMainStage.containsTask(mainChild.getTaskInfo().taskId)) {
- Log.w(TAG, "Expected onTaskAppeared on " + mMainStage
- + " to have been called with " + mainChild.getTaskInfo().taskId
- + " before startAnimation().");
- }
- if (!mSideStage.containsTask(sideChild.getTaskInfo().taskId)) {
- Log.w(TAG, "Expected onTaskAppeared on " + mSideStage
- + " to have been called with " + sideChild.getTaskInfo().taskId
- + " before startAnimation().");
- }
- return true;
- } else {
- // TODO: other entry method animations
- throw new RuntimeException("Unsupported split-entry");
+ // Update local states (before animating).
+ setDividerVisibility(true);
+ setSplitsVisible(true);
+
+ addDividerBarToTransition(info, t, true /* show */);
+
+ // Make some noise if things aren't totally expected. These states shouldn't effect
+ // transitions locally, but remotes (like Launcher) may get confused if they were
+ // depending on listener callbacks. This can happen because task-organizer callbacks
+ // aren't serialized with transition callbacks.
+ // TODO(b/184679596): Find a way to either include task-org information in
+ // the transition, or synchronize task-org callbacks.
+ if (!mMainStage.containsTask(mainChild.getTaskInfo().taskId)) {
+ Log.w(TAG, "Expected onTaskAppeared on " + mMainStage
+ + " to have been called with " + mainChild.getTaskInfo().taskId
+ + " before startAnimation().");
+ }
+ if (!mSideStage.containsTask(sideChild.getTaskInfo().taskId)) {
+ Log.w(TAG, "Expected onTaskAppeared on " + mSideStage
+ + " to have been called with " + sideChild.getTaskInfo().taskId
+ + " before startAnimation().");
+ }
+
+ mShouldUpdateRecents = true;
+ updateRecentTasksSplitPair();
+
+ if (!mLogger.hasStartedSession()) {
+ mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
+ getMainStagePosition(), mMainStage.getTopChildTaskUid(),
+ getSideStagePosition(), mSideStage.getTopChildTaskUid(),
+ mSplitLayout.isLandscape());
}
+
+ return true;
}
- private boolean startPendingDismissAnimation(@NonNull IBinder transition,
+ private boolean startPendingDismissAnimation(
+ @NonNull SplitScreenTransitions.DismissTransition dismissTransition,
@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) {
// Make some noise if things aren't totally expected. These states shouldn't effect
// transitions locally, but remotes (like Launcher) may get confused if they were
@@ -1306,6 +1314,21 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
+ "] before startAnimation().");
}
+ mRecentTasks.ifPresent(recentTasks -> {
+ // Notify recents if we are exiting in a way that breaks the pair, and disable further
+ // updates to splits in the recents until we enter split again
+ if (shouldBreakPairedTaskInRecents(dismissTransition.mReason) && mShouldUpdateRecents) {
+ for (TransitionInfo.Change change : info.getChanges()) {
+ final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+ if (taskInfo != null
+ && taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
+ recentTasks.removeSplitPair(taskInfo.taskId);
+ }
+ }
+ }
+ });
+ mShouldUpdateRecents = false;
+
// Update local states.
setSplitsVisible(false);
// Wait until after animation to update divider
@@ -1316,15 +1339,17 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
t.setWindowCrop(mSideStage.mRootLeash, null);
}
- if (mDismissTop == STAGE_TYPE_UNDEFINED) {
- // Going home (dismissing both splits)
-
+ if (dismissTransition.mDismissTop == STAGE_TYPE_UNDEFINED) {
+ logExit(dismissTransition.mReason);
// TODO: Have a proper remote for this. Until then, though, reset state and use the
// normal animation stuff (which falls back to the normal launcher remote).
t.hide(mSplitLayout.getDividerLeash());
setDividerVisibility(false);
mSplitTransitions.mPendingDismiss = null;
return false;
+ } else {
+ logExitToStage(dismissTransition.mReason,
+ dismissTransition.mDismissTop == STAGE_TYPE_MAIN);
}
addDividerBarToTransition(info, t, false /* show */);
@@ -1468,7 +1493,15 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@Override
public void onNoLongerSupportMultiWindow() {
if (mMainStage.isActive()) {
- StageCoordinator.this.exitSplitScreen(null /* childrenToTop */,
+ if (!ENABLE_SHELL_TRANSITIONS) {
+ StageCoordinator.this.exitSplitScreen(null /* childrenToTop */,
+ EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
+ }
+
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
+ mSplitTransitions.startDismissTransition(null /* transition */, wct,
+ StageCoordinator.this, STAGE_TYPE_UNDEFINED,
EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenTransitions.java
index af9a5aa501e8..018365420177 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenTransitions.java
@@ -127,12 +127,6 @@ class SplitScreenTransitions {
}
// TODO(shell-transitions): screenshot here
final Rect startBounds = new Rect(change.getStartAbsBounds());
- if (info.getType() == TRANSIT_SPLIT_DISMISS_SNAP) {
- // Dismissing split via snap which means the still-visible task has been
- // dragged to its end position at animation start so reflect that here.
- startBounds.offsetTo(change.getEndAbsBounds().left,
- change.getEndAbsBounds().top);
- }
final Rect endBounds = new Rect(change.getEndAbsBounds());
startBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y);
endBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index 6643ca176280..4ecc0b685626 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -380,9 +380,7 @@ public class TaskSnapshotWindow {
}
private void drawSizeMatchSnapshot() {
- GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(
- mSnapshot.getHardwareBuffer());
- mTransaction.setBuffer(mSurfaceControl, graphicBuffer)
+ mTransaction.setBuffer(mSurfaceControl, mSnapshot.getHardwareBuffer())
.setColorSpace(mSurfaceControl, mSnapshot.getColorSpace())
.apply();
}
@@ -428,20 +426,20 @@ public class TaskSnapshotWindow {
// Scale the mismatch dimensions to fill the task bounds
mSnapshotMatrix.setRectToRect(mTmpSnapshotSize, mTmpDstFrame, Matrix.ScaleToFit.FILL);
mTransaction.setMatrix(childSurfaceControl, mSnapshotMatrix, mTmpFloat9);
- GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(
- mSnapshot.getHardwareBuffer());
mTransaction.setColorSpace(childSurfaceControl, mSnapshot.getColorSpace());
- mTransaction.setBuffer(childSurfaceControl, graphicBuffer);
+ mTransaction.setBuffer(childSurfaceControl, mSnapshot.getHardwareBuffer());
if (aspectRatioMismatch) {
GraphicBuffer background = GraphicBuffer.create(mFrame.width(), mFrame.height(),
PixelFormat.RGBA_8888,
GraphicBuffer.USAGE_HW_TEXTURE | GraphicBuffer.USAGE_HW_COMPOSER
| GraphicBuffer.USAGE_SW_WRITE_RARELY);
+ // TODO: Support this on HardwareBuffer
final Canvas c = background.lockCanvas();
drawBackgroundAndBars(c, frame);
background.unlockCanvasAndPost(c);
- mTransaction.setBuffer(mSurfaceControl, background);
+ mTransaction.setBuffer(mSurfaceControl,
+ HardwareBuffer.createFromGraphicBuffer(background));
}
mTransaction.apply();
childSurfaceControl.release();
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 e40472415635..a1c086415063 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
@@ -313,7 +313,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
final int anim = getRotationAnimation(info);
if (!(isSeamless || anim == ROTATION_ANIMATION_JUMPCUT)) {
mRotationAnimation = new ScreenRotationAnimation(mContext, mSurfaceSession,
- mTransactionPool, startTransaction, change, info.getRootLeash());
+ mTransactionPool, startTransaction, change, info.getRootLeash(),
+ anim);
mRotationAnimation.startAnimation(animations, onAnimFinish,
mTransitionAnimationScaleSetting, mMainExecutor, mAnimExecutor);
continue;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
index 45d8be1eb905..46f73fda37a1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
@@ -19,6 +19,8 @@ package com.android.wm.shell.transition;
import static android.hardware.HardwareBuffer.RGBA_8888;
import static android.hardware.HardwareBuffer.USAGE_PROTECTED_CONTENT;
import static android.util.RotationUtils.deltaRotation;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT;
import static android.view.WindowManagerPolicyConstants.SCREEN_FREEZE_LAYER_BASE;
import static com.android.wm.shell.transition.DefaultTransitionHandler.startSurfaceAnimation;
@@ -59,7 +61,7 @@ import java.util.Arrays;
* This class handles the rotation animation when the device is rotated.
*
* <p>
- * The screen rotation animation is composed of 4 different part:
+ * The screen rotation animation is composed of 3 different part:
* <ul>
* <li> The screenshot: <p>
* A screenshot of the whole screen prior the change of orientation is taken to hide the
@@ -75,10 +77,6 @@ import java.util.Arrays;
* To have the animation seem more seamless, we add a color transitioning background behind the
* exiting and entering layouts. We compute the brightness of the start and end
* layouts and transition from the two brightness values as grayscale underneath the animation
- *
- * <li> The entering Blackframe: <p>
- * The enter Blackframe is similar to the exit Blackframe but is only used when a custom
- * rotation animation is used and matches the new content size instead of the screenshot.
* </ul>
*/
class ScreenRotationAnimation {
@@ -94,6 +92,7 @@ class ScreenRotationAnimation {
private final Rect mStartBounds = new Rect();
private final Rect mEndBounds = new Rect();
+ private final int mAnimHint;
private final int mStartWidth;
private final int mStartHeight;
private final int mEndWidth;
@@ -117,6 +116,7 @@ class ScreenRotationAnimation {
// rotations.
private Animation mRotateExitAnimation;
private Animation mRotateEnterAnimation;
+ private Animation mRotateAlphaAnimation;
/** Intensity of light/whiteness of the layout before rotation occurs. */
private float mStartLuma;
@@ -124,9 +124,10 @@ class ScreenRotationAnimation {
private float mEndLuma;
ScreenRotationAnimation(Context context, SurfaceSession session, TransactionPool pool,
- Transaction t, TransitionInfo.Change change, SurfaceControl rootLeash) {
+ Transaction t, TransitionInfo.Change change, SurfaceControl rootLeash, int animHint) {
mContext = context;
mTransactionPool = pool;
+ mAnimHint = animHint;
mSurfaceControl = change.getLeash();
mStartWidth = change.getStartAbsBounds().width();
@@ -160,13 +161,6 @@ class ScreenRotationAnimation {
return;
}
- mBackColorSurface = new SurfaceControl.Builder(session)
- .setParent(rootLeash)
- .setColorLayer()
- .setCallsite("ShellRotationAnimation")
- .setName("BackColorSurface")
- .build();
-
mScreenshotLayer = new SurfaceControl.Builder(session)
.setParent(mAnimLeash)
.setBLASTLayer()
@@ -175,17 +169,9 @@ class ScreenRotationAnimation {
.setName("RotationLayer")
.build();
- HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer();
- mStartLuma = getMedianBorderLuma(hardwareBuffer, screenshotBuffer.getColorSpace());
-
GraphicBuffer buffer = GraphicBuffer.createFromHardwareBuffer(
screenshotBuffer.getHardwareBuffer());
- t.setLayer(mBackColorSurface, -1);
- t.setColor(mBackColorSurface, new float[]{mStartLuma, mStartLuma, mStartLuma});
- t.setAlpha(mBackColorSurface, 1);
- t.show(mBackColorSurface);
-
t.setLayer(mAnimLeash, SCREEN_FREEZE_LAYER_BASE);
t.setPosition(mAnimLeash, 0, 0);
t.setAlpha(mAnimLeash, 1);
@@ -195,6 +181,23 @@ class ScreenRotationAnimation {
t.setColorSpace(mScreenshotLayer, screenshotBuffer.getColorSpace());
t.show(mScreenshotLayer);
+ if (!isCustomRotate()) {
+ mBackColorSurface = new SurfaceControl.Builder(session)
+ .setParent(rootLeash)
+ .setColorLayer()
+ .setCallsite("ShellRotationAnimation")
+ .setName("BackColorSurface")
+ .build();
+
+ HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer();
+ mStartLuma = getMedianBorderLuma(hardwareBuffer, screenshotBuffer.getColorSpace());
+
+ t.setLayer(mBackColorSurface, -1);
+ t.setColor(mBackColorSurface, new float[]{mStartLuma, mStartLuma, mStartLuma});
+ t.setAlpha(mBackColorSurface, 1);
+ t.show(mBackColorSurface);
+ }
+
} catch (Surface.OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate freeze surface", e);
}
@@ -203,6 +206,10 @@ class ScreenRotationAnimation {
t.apply();
}
+ private boolean isCustomRotate() {
+ return mAnimHint == ROTATION_ANIMATION_CROSSFADE || mAnimHint == ROTATION_ANIMATION_JUMPCUT;
+ }
+
private void setRotation(SurfaceControl.Transaction t) {
// Compute the transformation matrix that must be applied
// to the snapshot to make it stay in the same original position
@@ -244,33 +251,44 @@ class ScreenRotationAnimation {
// color frame animation.
//mEndLuma = getLumaOfSurfaceControl(mEndBounds, mSurfaceControl);
- // Figure out how the screen has moved from the original rotation.
- int delta = deltaRotation(mEndRotation, mStartRotation);
- switch (delta) { /* Counter-Clockwise Rotations */
- case Surface.ROTATION_0:
- mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
- R.anim.screen_rotate_0_exit);
- mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
- R.anim.rotation_animation_enter);
- break;
- case Surface.ROTATION_90:
- mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
- R.anim.screen_rotate_plus_90_exit);
- mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
- R.anim.screen_rotate_plus_90_enter);
- break;
- case Surface.ROTATION_180:
- mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
- R.anim.screen_rotate_180_exit);
- mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
- R.anim.screen_rotate_180_enter);
- break;
- case Surface.ROTATION_270:
- mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
- R.anim.screen_rotate_minus_90_exit);
- mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
- R.anim.screen_rotate_minus_90_enter);
- break;
+ final boolean customRotate = isCustomRotate();
+ if (customRotate) {
+ mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
+ mAnimHint == ROTATION_ANIMATION_JUMPCUT ? R.anim.rotation_animation_jump_exit
+ : R.anim.rotation_animation_xfade_exit);
+ mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.rotation_animation_enter);
+ mRotateAlphaAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.screen_rotate_alpha);
+ } else {
+ // Figure out how the screen has moved from the original rotation.
+ int delta = deltaRotation(mEndRotation, mStartRotation);
+ switch (delta) { /* Counter-Clockwise Rotations */
+ case Surface.ROTATION_0:
+ mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.screen_rotate_0_exit);
+ mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.rotation_animation_enter);
+ break;
+ case Surface.ROTATION_90:
+ mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.screen_rotate_plus_90_exit);
+ mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.screen_rotate_plus_90_enter);
+ break;
+ case Surface.ROTATION_180:
+ mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.screen_rotate_180_exit);
+ mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.screen_rotate_180_enter);
+ break;
+ case Surface.ROTATION_270:
+ mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.screen_rotate_minus_90_exit);
+ mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.screen_rotate_minus_90_enter);
+ break;
+ }
}
mRotateExitAnimation.initialize(mEndWidth, mEndHeight, mStartWidth, mStartHeight);
@@ -281,9 +299,20 @@ class ScreenRotationAnimation {
mRotateEnterAnimation.scaleCurrentDuration(animationScale);
mTransaction = mTransactionPool.acquire();
- startDisplayRotation(animations, finishCallback, mainExecutor, animExecutor);
- startScreenshotRotationAnimation(animations, finishCallback, mainExecutor, animExecutor);
- //startColorAnimation(mTransaction, animationScale);
+ if (customRotate) {
+ mRotateAlphaAnimation.initialize(mEndWidth, mEndHeight, mStartWidth, mStartHeight);
+ mRotateAlphaAnimation.restrictDuration(MAX_ANIMATION_DURATION);
+ mRotateAlphaAnimation.scaleCurrentDuration(animationScale);
+
+ startScreenshotAlphaAnimation(animations, finishCallback, mainExecutor,
+ animExecutor);
+ startDisplayRotation(animations, finishCallback, mainExecutor, animExecutor);
+ } else {
+ startDisplayRotation(animations, finishCallback, mainExecutor, animExecutor);
+ startScreenshotRotationAnimation(animations, finishCallback, mainExecutor,
+ animExecutor);
+ //startColorAnimation(mTransaction, animationScale);
+ }
return true;
}
@@ -304,6 +333,14 @@ class ScreenRotationAnimation {
0 /* cornerRadius */, null /* clipRect */);
}
+ private void startScreenshotAlphaAnimation(@NonNull ArrayList<Animator> animations,
+ @NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor,
+ @NonNull ShellExecutor animExecutor) {
+ startSurfaceAnimation(animations, mRotateAlphaAnimation, mAnimLeash, finishCallback,
+ mTransactionPool, mainExecutor, animExecutor, null /* position */,
+ 0 /* cornerRadius */, null /* clipRect */);
+ }
+
private void startColorAnimation(float animationScale, @NonNull ShellExecutor animExecutor) {
int colorTransitionMs = mContext.getResources().getInteger(
R.integer.config_screen_rotation_color_transition);
@@ -351,13 +388,12 @@ class ScreenRotationAnimation {
t.remove(mScreenshotLayer);
}
mScreenshotLayer = null;
-
- if (mBackColorSurface != null) {
- if (mBackColorSurface.isValid()) {
- t.remove(mBackColorSurface);
- }
- mBackColorSurface = null;
+ }
+ if (mBackColorSurface != null) {
+ if (mBackColorSurface.isValid()) {
+ t.remove(mBackColorSurface);
}
+ mBackColorSurface = null;
}
t.apply();
mTransactionPool.release(t);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 804e449decf8..b8cbfd9680fd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -89,12 +89,16 @@ public class Transitions implements RemoteCallable<Transitions> {
/** Transition type for entering split by opening an app into side-stage. */
public static final int TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE = TRANSIT_FIRST_CUSTOM + 5;
+ /** Transition type for dismissing split-screen. */
+ public static final int TRANSIT_SPLIT_DISMISS = TRANSIT_FIRST_CUSTOM + 6;
+
private final WindowOrganizer mOrganizer;
private final Context mContext;
private final ShellExecutor mMainExecutor;
private final ShellExecutor mAnimExecutor;
private final TransitionPlayerImpl mPlayerImpl;
private final RemoteTransitionHandler mRemoteTransitionHandler;
+ private final DisplayController mDisplayController;
private final ShellTransitionImpl mImpl = new ShellTransitionImpl();
/** List of possible handlers. Ordered by specificity (eg. tapped back to front). */
@@ -122,6 +126,7 @@ public class Transitions implements RemoteCallable<Transitions> {
mContext = context;
mMainExecutor = mainExecutor;
mAnimExecutor = animExecutor;
+ mDisplayController = displayController;
mPlayerImpl = new TransitionPlayerImpl();
// The very last handler (0 in the list) should be the default one.
mHandlers.add(new DefaultTransitionHandler(displayController, pool, context, mainExecutor,
@@ -147,6 +152,7 @@ public class Transitions implements RemoteCallable<Transitions> {
mContext = null;
mMainExecutor = null;
mAnimExecutor = null;
+ mDisplayController = null;
mPlayerImpl = null;
mRemoteTransitionHandler = null;
}
@@ -547,6 +553,17 @@ public class Transitions implements RemoteCallable<Transitions> {
break;
}
}
+ if (request.getDisplayChange() != null) {
+ TransitionRequestInfo.DisplayChange change = request.getDisplayChange();
+ if (change.getEndRotation() != change.getStartRotation()) {
+ // Is a rotation, so dispatch to all displayChange listeners
+ if (wct == null) {
+ wct = new WindowContainerTransaction();
+ }
+ mDisplayController.getChangeController().dispatchOnRotateDisplay(wct,
+ change.getDisplayId(), change.getStartRotation(), change.getEndRotation());
+ }
+ }
active.mToken = mOrganizer.startTransition(
request.getType(), transitionToken, wct);
mActiveTransitions.add(active);
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
index 2a53bef8cf2e..3121218c7c37 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
@@ -30,7 +30,6 @@ import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION
import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
-import org.junit.Assert.assertEquals
import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
@@ -79,13 +78,42 @@ class SetRequestedOrientationWhilePinnedTest(
pipApp.launchViaIntent(wmHelper)
wmHelper.waitForFullScreenApp(pipApp.component)
wmHelper.waitForRotation(Surface.ROTATION_90)
- assertEquals(Surface.ROTATION_90, device.displayRotation)
}
}
+ @Presubmit
+ @Test
+ fun displayEndsAt90Degrees() {
+ // This test doesn't work in shell transitions because of b/208576418
+ assumeFalse(isShellTransitionsEnabled)
+ testSpec.assertWmEnd {
+ hasRotation(Surface.ROTATION_90)
+ }
+ }
+
+ @Presubmit
+ @Test
+ override fun navBarLayerIsVisible() {
+ // This test doesn't work in shell transitions because of b/208576418
+ assumeFalse(isShellTransitionsEnabled)
+ super.navBarLayerIsVisible()
+ }
+
+ @Presubmit
+ @Test
+ override fun statusBarLayerIsVisible() {
+ // This test doesn't work in shell transitions because of b/208576418
+ assumeFalse(isShellTransitionsEnabled)
+ super.statusBarLayerIsVisible()
+ }
+
@FlakyTest
@Test
- override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+ override fun navBarLayerRotatesAndScales() {
+ // This test doesn't work in shell transitions because of b/208576418
+ assumeFalse(isShellTransitionsEnabled)
+ super.navBarLayerRotatesAndScales()
+ }
@Presubmit
@Test
@@ -98,6 +126,8 @@ class SetRequestedOrientationWhilePinnedTest(
@Presubmit
@Test
fun pipWindowInsideDisplay() {
+ // This test doesn't work in shell transitions because of b/208576418
+ assumeFalse(isShellTransitionsEnabled)
testSpec.assertWmStart {
frameRegion(pipApp.component).coversAtMost(startingBounds)
}
@@ -106,6 +136,8 @@ class SetRequestedOrientationWhilePinnedTest(
@Presubmit
@Test
fun pipAppShowsOnTop() {
+ // This test doesn't work in shell transitions because of b/208576418
+ assumeFalse(isShellTransitionsEnabled)
testSpec.assertWmEnd {
isAppWindowOnTop(pipApp.component)
}
@@ -114,6 +146,8 @@ class SetRequestedOrientationWhilePinnedTest(
@Presubmit
@Test
fun pipLayerInsideDisplay() {
+ // This test doesn't work in shell transitions because of b/208576418
+ assumeFalse(isShellTransitionsEnabled)
testSpec.assertLayersStart {
visibleRegion(pipApp.component).coversAtMost(startingBounds)
}
@@ -121,13 +155,19 @@ class SetRequestedOrientationWhilePinnedTest(
@Presubmit
@Test
- fun pipAlwaysVisible() = testSpec.assertWm {
- this.isAppWindowVisible(pipApp.component)
+ fun pipAlwaysVisible() {
+ // This test doesn't work in shell transitions because of b/208576418
+ assumeFalse(isShellTransitionsEnabled)
+ testSpec.assertWm {
+ this.isAppWindowVisible(pipApp.component)
+ }
}
@Presubmit
@Test
fun pipAppLayerCoversFullScreen() {
+ // This test doesn't work in shell transitions because of b/208576418
+ assumeFalse(isShellTransitionsEnabled)
testSpec.assertLayersEnd {
visibleRegion(pipApp.component).coversExactly(endingBounds)
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index a3b98a8fc880..825320b4e784 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -37,6 +37,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import android.app.ActivityManager.RunningTaskInfo;
+import android.app.TaskInfo;
import android.content.Context;
import android.content.LocusId;
import android.content.pm.ParceledListSlice;
@@ -334,8 +335,7 @@ public class ShellTaskOrganizerTests {
mOrganizer.onTaskAppeared(taskInfo1, null);
// sizeCompatActivity is null if top activity is not in size compat.
- verify(mCompatUI).onCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
- null /* taskConfig */, null /* taskListener */);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo1, null /* taskListener */);
// sizeCompatActivity is non-null if top activity is in size compat.
clearInvocations(mCompatUI);
@@ -345,8 +345,7 @@ public class ShellTaskOrganizerTests {
taskInfo2.topActivityInSizeCompat = true;
taskInfo2.isVisible = true;
mOrganizer.onTaskInfoChanged(taskInfo2);
- verify(mCompatUI).onCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
- taskInfo1.configuration, taskListener);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo2, taskListener);
// Not show size compat UI if task is not visible.
clearInvocations(mCompatUI);
@@ -356,13 +355,82 @@ public class ShellTaskOrganizerTests {
taskInfo3.topActivityInSizeCompat = true;
taskInfo3.isVisible = false;
mOrganizer.onTaskInfoChanged(taskInfo3);
- verify(mCompatUI).onCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
- null /* taskConfig */, null /* taskListener */);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo3, null /* taskListener */);
clearInvocations(mCompatUI);
mOrganizer.onTaskVanished(taskInfo1);
- verify(mCompatUI).onCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
- null /* taskConfig */, null /* taskListener */);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo1, null /* taskListener */);
+ }
+
+ @Test
+ public void testOnCameraCompatActivityChanged() {
+ final RunningTaskInfo taskInfo1 = createTaskInfo(1, WINDOWING_MODE_FULLSCREEN);
+ taskInfo1.displayId = DEFAULT_DISPLAY;
+ taskInfo1.cameraCompatControlState = TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
+ final TrackingTaskListener taskListener = new TrackingTaskListener();
+ mOrganizer.addListenerForType(taskListener, TASK_LISTENER_TYPE_FULLSCREEN);
+ mOrganizer.onTaskAppeared(taskInfo1, null);
+
+ // Task listener sent to compat UI is null if top activity doesn't request a camera
+ // compat control.
+ verify(mCompatUI).onCompatInfoChanged(taskInfo1, null /* taskListener */);
+
+ // Task linster is non-null when request a camera compat control for a visible task.
+ clearInvocations(mCompatUI);
+ final RunningTaskInfo taskInfo2 =
+ createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
+ taskInfo2.displayId = taskInfo1.displayId;
+ taskInfo2.cameraCompatControlState = TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
+ taskInfo2.isVisible = true;
+ mOrganizer.onTaskInfoChanged(taskInfo2);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo2, taskListener);
+
+ // CompatUIController#onCompatInfoChanged is called when requested state for a camera
+ // compat control changes for a visible task.
+ clearInvocations(mCompatUI);
+ final RunningTaskInfo taskInfo3 =
+ createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
+ taskInfo3.displayId = taskInfo1.displayId;
+ taskInfo3.cameraCompatControlState = TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
+ taskInfo3.isVisible = true;
+ mOrganizer.onTaskInfoChanged(taskInfo3);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo3, taskListener);
+
+ // CompatUIController#onCompatInfoChanged is called when a top activity goes in size compat
+ // mode for a visible task that has a compat control.
+ clearInvocations(mCompatUI);
+ final RunningTaskInfo taskInfo4 =
+ createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
+ taskInfo4.displayId = taskInfo1.displayId;
+ taskInfo4.topActivityInSizeCompat = true;
+ taskInfo4.cameraCompatControlState = TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
+ taskInfo4.isVisible = true;
+ mOrganizer.onTaskInfoChanged(taskInfo4);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo4, taskListener);
+
+ // Task linster is null when a camera compat control is dimissed for a visible task.
+ clearInvocations(mCompatUI);
+ final RunningTaskInfo taskInfo5 =
+ createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
+ taskInfo5.displayId = taskInfo1.displayId;
+ taskInfo5.cameraCompatControlState = TaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
+ taskInfo5.isVisible = true;
+ mOrganizer.onTaskInfoChanged(taskInfo5);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo5, null /* taskListener */);
+
+ // Task linster is null when request a camera compat control for a invisible task.
+ clearInvocations(mCompatUI);
+ final RunningTaskInfo taskInfo6 =
+ createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
+ taskInfo6.displayId = taskInfo1.displayId;
+ taskInfo6.cameraCompatControlState = TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
+ taskInfo6.isVisible = false;
+ mOrganizer.onTaskInfoChanged(taskInfo6);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo6, null /* taskListener */);
+
+ clearInvocations(mCompatUI);
+ mOrganizer.onTaskVanished(taskInfo1);
+ verify(mCompatUI).onCompatInfoChanged(taskInfo1, null /* taskListener */);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
index 2b5cd601b200..51eec27cfc0e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
@@ -18,6 +18,7 @@ package com.android.wm.shell;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -36,6 +37,7 @@ public final class TestRunningTaskInfoBuilder {
private WindowContainerToken mToken = createMockWCToken();
private int mParentTaskId = INVALID_TASK_ID;
private @WindowConfiguration.ActivityType int mActivityType = ACTIVITY_TYPE_STANDARD;
+ private @WindowConfiguration.WindowingMode int mWindowingMode = WINDOWING_MODE_UNDEFINED;
public static WindowContainerToken createMockWCToken() {
final IWindowContainerToken itoken = mock(IWindowContainerToken.class);
@@ -60,6 +62,12 @@ public final class TestRunningTaskInfoBuilder {
return this;
}
+ public TestRunningTaskInfoBuilder setWindowingMode(
+ @WindowConfiguration.WindowingMode int windowingMode) {
+ mWindowingMode = windowingMode;
+ return this;
+ }
+
public ActivityManager.RunningTaskInfo build() {
final ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
info.parentTaskId = INVALID_TASK_ID;
@@ -67,6 +75,7 @@ public final class TestRunningTaskInfoBuilder {
info.parentTaskId = mParentTaskId;
info.configuration.windowConfiguration.setBounds(mBounds);
info.configuration.windowConfiguration.setActivityType(mActivityType);
+ info.configuration.windowConfiguration.setWindowingMode(mWindowingMode);
info.token = mToken;
info.isResizeable = true;
info.supportsMultiWindow = true;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index bc701d0c70bc..185479b145af 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -39,6 +39,7 @@ import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.util.Log;
import android.util.Pair;
import android.view.WindowManager;
@@ -793,7 +794,7 @@ public class BubbleDataTest extends ShellTestCase {
}
@Test
- public void test_expanded_removeLastBubble_showsOverflowIfNotEmpty() {
+ public void test_expanded_removeLastBubble_collapsesIfOverflowNotEmpty() {
// Setup
sendUpdatedEntryAtTime(mEntryA1, 1000);
changeExpandedStateAtTime(true, 2000);
@@ -803,7 +804,7 @@ public class BubbleDataTest extends ShellTestCase {
mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_USER_GESTURE);
verifyUpdateReceived();
assertThat(mBubbleData.getOverflowBubbles().size()).isGreaterThan(0);
- assertSelectionChangedTo(mBubbleData.getOverflow());
+ assertExpandedChangedTo(false);
}
@Test
@@ -913,6 +914,31 @@ public class BubbleDataTest extends ShellTestCase {
assertSelectionChangedTo(mBubbleA2);
}
+ /**
+ * - have a maxed out bubble stack & all of the bubbles have been recently accessed
+ * - bubble a notification that was posted before any of those bubbles were accessed
+ * => that bubble should be added
+ *
+ */
+ @Test
+ public void test_addOldNotifWithNewerBubbles() {
+ sendUpdatedEntryAtTime(mEntryA1, 2000);
+ sendUpdatedEntryAtTime(mEntryA2, 3000);
+ sendUpdatedEntryAtTime(mEntryA3, 4000);
+ sendUpdatedEntryAtTime(mEntryB1, 5000);
+ sendUpdatedEntryAtTime(mEntryB2, 6000);
+
+ mBubbleData.setListener(mListener);
+ sendUpdatedEntryAtTime(mEntryB3, 1000 /* postTime */, 7000 /* currentTime */);
+ verifyUpdateReceived();
+
+ // B3 is in the stack
+ assertThat(mBubbleData.getBubbleInStackWithKey(mBubbleB3.getKey())).isNotNull();
+ // A1 is the oldest so it's in the overflow
+ assertThat(mBubbleData.getOverflowBubbleWithKey(mEntryA1.getKey())).isNotNull();
+ assertOrderChangedTo(mBubbleB3, mBubbleB2, mBubbleB1, mBubbleA3, mBubbleA2);
+ }
+
private void verifyUpdateReceived() {
verify(mListener).applyUpdate(mUpdateCaptor.capture());
reset(mListener);
@@ -1014,6 +1040,12 @@ public class BubbleDataTest extends ShellTestCase {
}
private void sendUpdatedEntryAtTime(BubbleEntry entry, long postTime) {
+ setCurrentTime(postTime);
+ sendUpdatedEntryAtTime(entry, postTime, true /* isTextChanged */);
+ }
+
+ private void sendUpdatedEntryAtTime(BubbleEntry entry, long postTime, long currentTime) {
+ setCurrentTime(currentTime);
sendUpdatedEntryAtTime(entry, postTime, true /* isTextChanged */);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java
index 2b9bdce45a6c..335222e98c6c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java
@@ -59,19 +59,21 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC
private int mStackOffset;
private PointF mExpansionPoint;
private BubblePositioner mPositioner;
- private BubbleStackView.StackViewState mStackViewState;
+ private BubbleStackView.StackViewState mStackViewState = new BubbleStackView.StackViewState();
@SuppressLint("VisibleForTests")
@Before
public void setUp() throws Exception {
super.setUp();
- BubbleStackView stackView = mock(BubbleStackView.class);
- when(stackView.getState()).thenReturn(getStackViewState());
mPositioner = new BubblePositioner(getContext(), mock(WindowManager.class));
mPositioner.updateInternal(Configuration.ORIENTATION_PORTRAIT,
Insets.of(0, 0, 0, 0),
new Rect(0, 0, mDisplayWidth, mDisplayHeight));
+
+ BubbleStackView stackView = mock(BubbleStackView.class);
+ when(stackView.getState()).thenReturn(getStackViewState());
+
mExpandedController = new ExpandedAnimationController(mPositioner,
mOnBubbleAnimatedOutAction,
stackView);
@@ -135,6 +137,12 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC
testBubblesInCorrectExpandedPositions();
}
+ @Test
+ public void testDragBubbleOutDoesntNPE() throws InterruptedException {
+ mExpandedController.onGestureFinished();
+ mExpandedController.dragBubbleOut(mViews.get(0), 1, 1);
+ }
+
/** Expand the stack and wait for animations to finish. */
private void expand() throws InterruptedException {
mExpandedController.expandFromStack(mock(Runnable.class));
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index 453050fcfab4..83d5f04b7cdb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -24,6 +24,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -101,14 +102,21 @@ public class SplitLayoutTests extends ShellTestCase {
@Test
public void testSetDividePosition() {
- mSplitLayout.setDividePosition(anyInt());
+ mSplitLayout.setDividePosition(100, false /* applyLayoutChange */);
+ assertThat(mSplitLayout.getDividePosition()).isEqualTo(100);
+ verify(mSplitLayoutHandler, never()).onLayoutSizeChanged(any(SplitLayout.class));
+
+ mSplitLayout.setDividePosition(200, true /* applyLayoutChange */);
+ assertThat(mSplitLayout.getDividePosition()).isEqualTo(200);
verify(mSplitLayoutHandler).onLayoutSizeChanged(any(SplitLayout.class));
}
@Test
public void testSetDivideRatio() {
+ mSplitLayout.setDividePosition(200, false /* applyLayoutChange */);
mSplitLayout.setDivideRatio(0.5f);
- verify(mSplitLayoutHandler).onLayoutSizeChanged(any(SplitLayout.class));
+ assertThat(mSplitLayout.getDividePosition()).isEqualTo(
+ mSplitLayout.mDividerSnapAlgorithm.getMiddleTarget().position);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index f622edb7f134..4352fd3d2c27 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -16,6 +16,10 @@
package com.android.wm.shell.compatui;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -29,6 +33,9 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.TaskInfo;
+import android.app.TaskInfo.CameraCompatControlState;
import android.content.Context;
import android.content.res.Configuration;
import android.testing.AndroidTestingRunner;
@@ -90,8 +97,8 @@ public class CompatUIControllerTest extends ShellTestCase {
mController = new CompatUIController(mContext, mMockDisplayController,
mMockDisplayInsetsController, mMockImeController, mMockSyncQueue, mMockExecutor) {
@Override
- CompatUIWindowManager createLayout(Context context, int displayId, int taskId,
- Configuration taskConfig, ShellTaskOrganizer.TaskListener taskListener) {
+ CompatUIWindowManager createLayout(Context context, TaskInfo taskInfo,
+ ShellTaskOrganizer.TaskListener taskListener) {
return mMockLayout;
}
};
@@ -106,23 +113,59 @@ public class CompatUIControllerTest extends ShellTestCase {
@Test
public void testOnCompatInfoChanged() {
- final Configuration taskConfig = new Configuration();
+ TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN);
// Verify that the restart button is added with non-null size compat info.
- mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+ mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
- verify(mController).createLayout(any(), eq(DISPLAY_ID), eq(TASK_ID), eq(taskConfig),
- eq(mMockTaskListener));
+ verify(mController).createLayout(any(), eq(taskInfo), eq(mMockTaskListener));
// Verify that the restart button is updated with non-null new size compat info.
- final Configuration newTaskConfig = new Configuration();
- mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, newTaskConfig, mMockTaskListener);
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN),
+ mMockTaskListener);
+
+ verify(mMockLayout).updateCompatInfo(new Configuration(), mMockTaskListener,
+ true /* show */, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+
+ // Verify that the restart button is updated with new camera state.
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED),
+ mMockTaskListener);
+
+ verify(mMockLayout).updateCompatInfo(new Configuration(), mMockTaskListener,
+ true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED),
+ mMockTaskListener);
+
+ verify(mMockLayout).updateCompatInfo(new Configuration(), mMockTaskListener,
+ true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ // Verify that compat controls are removed with null compat info.
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ false /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN),
+ null /* taskListener */);
- verify(mMockLayout).updateCompatInfo(taskConfig, mMockTaskListener,
- true /* show */);
+ verify(mMockLayout).release();
+
+ clearInvocations(mMockLayout);
+ clearInvocations(mController);
+ // Verify that compat controls are removed with dismissed camera state.
+ taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
+
+ verify(mController).createLayout(any(), eq(taskInfo), eq(mMockTaskListener));
- // Verify that the restart button is removed with null size compat info.
- mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, null, mMockTaskListener);
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ false /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_DISMISSED),
+ null /* taskListener */);
verify(mMockLayout).release();
}
@@ -139,8 +182,8 @@ public class CompatUIControllerTest extends ShellTestCase {
@Test
public void testOnDisplayRemoved() {
mController.onDisplayAdded(DISPLAY_ID);
- final Configuration taskConfig = new Configuration();
- mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN),
mMockTaskListener);
mController.onDisplayRemoved(DISPLAY_ID + 1);
@@ -157,16 +200,14 @@ public class CompatUIControllerTest extends ShellTestCase {
@Test
public void testOnDisplayConfigurationChanged() {
- final Configuration taskConfig = new Configuration();
- mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
- mMockTaskListener);
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
- final Configuration newTaskConfig = new Configuration();
- mController.onDisplayConfigurationChanged(DISPLAY_ID + 1, newTaskConfig);
+ mController.onDisplayConfigurationChanged(DISPLAY_ID + 1, new Configuration());
verify(mMockLayout, never()).updateDisplayLayout(any());
- mController.onDisplayConfigurationChanged(DISPLAY_ID, newTaskConfig);
+ mController.onDisplayConfigurationChanged(DISPLAY_ID, new Configuration());
verify(mMockLayout).updateDisplayLayout(mMockDisplayLayout);
}
@@ -174,9 +215,8 @@ public class CompatUIControllerTest extends ShellTestCase {
@Test
public void testInsetsChanged() {
mController.onDisplayAdded(DISPLAY_ID);
- final Configuration taskConfig = new Configuration();
- mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
- mMockTaskListener);
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
InsetsState insetsState = new InsetsState();
InsetsSource insetsSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR);
insetsSource.setFrame(0, 0, 1000, 1000);
@@ -196,8 +236,8 @@ public class CompatUIControllerTest extends ShellTestCase {
@Test
public void testChangeButtonVisibilityOnImeShowHide() {
- final Configuration taskConfig = new Configuration();
- mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
// Verify that the restart button is hidden after IME is showing.
mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);
@@ -205,10 +245,11 @@ public class CompatUIControllerTest extends ShellTestCase {
verify(mMockLayout).updateVisibility(false);
// Verify button remains hidden while IME is showing.
- mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
- verify(mMockLayout).updateCompatInfo(taskConfig, mMockTaskListener,
- false /* show */);
+ verify(mMockLayout).updateCompatInfo(new Configuration(), mMockTaskListener,
+ false /* show */, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
// Verify button is shown after IME is hidden.
mController.onImeVisibilityChanged(DISPLAY_ID, false /* isShowing */);
@@ -218,8 +259,8 @@ public class CompatUIControllerTest extends ShellTestCase {
@Test
public void testChangeButtonVisibilityOnKeyguardOccludedChanged() {
- final Configuration taskConfig = new Configuration();
- mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
// Verify that the restart button is hidden after keyguard becomes occluded.
mController.onKeyguardOccludedChanged(true);
@@ -227,10 +268,11 @@ public class CompatUIControllerTest extends ShellTestCase {
verify(mMockLayout).updateVisibility(false);
// Verify button remains hidden while keyguard is occluded.
- mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
- verify(mMockLayout).updateCompatInfo(taskConfig, mMockTaskListener,
- false /* show */);
+ verify(mMockLayout).updateCompatInfo(new Configuration(), mMockTaskListener,
+ false /* show */, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
// Verify button is shown after keyguard becomes not occluded.
mController.onKeyguardOccludedChanged(false);
@@ -240,8 +282,8 @@ public class CompatUIControllerTest extends ShellTestCase {
@Test
public void testButtonRemainsHiddenOnKeyguardOccludedFalseWhenImeIsShowing() {
- final Configuration taskConfig = new Configuration();
- mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);
mController.onKeyguardOccludedChanged(true);
@@ -263,8 +305,8 @@ public class CompatUIControllerTest extends ShellTestCase {
@Test
public void testButtonRemainsHiddenOnImeHideWhenKeyguardIsOccluded() {
- final Configuration taskConfig = new Configuration();
- mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
+ mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);
mController.onKeyguardOccludedChanged(true);
@@ -283,4 +325,14 @@ public class CompatUIControllerTest extends ShellTestCase {
verify(mMockLayout).updateVisibility(true);
}
+
+ private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat,
+ @CameraCompatControlState int cameraCompatControlState) {
+ RunningTaskInfo taskInfo = new RunningTaskInfo();
+ taskInfo.taskId = taskId;
+ taskInfo.displayId = displayId;
+ taskInfo.topActivityInSizeCompat = hasSizeCompat;
+ taskInfo.cameraCompatControlState = cameraCompatControlState;
+ return taskInfo;
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
index 2c3987bc358d..353d8fe8bc52 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
@@ -16,6 +16,11 @@
package com.android.wm.shell.compatui;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
+
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static org.mockito.Mockito.doNothing;
@@ -69,7 +74,7 @@ public class CompatUILayoutTest extends ShellTestCase {
mWindowManager = new CompatUIWindowManager(mContext, new Configuration(),
mSyncTransactionQueue, mCallback, TASK_ID, mTaskListener, new DisplayLayout(),
- false /* hasShownHint */);
+ false /* hasShownSizeCompatHint */, false /* hasShownCameraCompatHint */);
mCompatUILayout = (CompatUILayout)
LayoutInflater.from(mContext).inflate(R.layout.compat_ui_layout, null);
@@ -78,6 +83,7 @@ public class CompatUILayoutTest extends ShellTestCase {
spyOn(mWindowManager);
spyOn(mCompatUILayout);
doReturn(mViewHost).when(mWindowManager).createSurfaceViewHost();
+ doReturn(mCompatUILayout).when(mWindowManager).inflateCompatUILayout();
}
@Test
@@ -86,7 +92,6 @@ public class CompatUILayoutTest extends ShellTestCase {
button.performClick();
verify(mWindowManager).onRestartButtonClicked();
- doReturn(mCompatUILayout).when(mWindowManager).inflateCompatUILayout();
verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID);
}
@@ -102,10 +107,92 @@ public class CompatUILayoutTest extends ShellTestCase {
@Test
public void testOnClickForSizeCompatHint() {
- mWindowManager.createLayout(true /* show */);
+ mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN);
final LinearLayout sizeCompatHint = mCompatUILayout.findViewById(R.id.size_compat_hint);
sizeCompatHint.performClick();
verify(mCompatUILayout).setSizeCompatHintVisibility(/* show= */ false);
}
+
+ @Test
+ public void testUpdateCameraTreatmentButton_treatmentAppliedByDefault() {
+ mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+ final ImageButton button =
+ mCompatUILayout.findViewById(R.id.camera_compat_treatment_button);
+ button.performClick();
+
+ verify(mWindowManager).onCameraTreatmentButtonClicked();
+ verify(mCallback).onCameraControlStateUpdated(
+ TASK_ID, CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ button.performClick();
+
+ verify(mCallback).onCameraControlStateUpdated(
+ TASK_ID, CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+ }
+
+ @Test
+ public void testUpdateCameraTreatmentButton_treatmentSuggestedByDefault() {
+ mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ final ImageButton button =
+ mCompatUILayout.findViewById(R.id.camera_compat_treatment_button);
+ button.performClick();
+
+ verify(mWindowManager).onCameraTreatmentButtonClicked();
+ verify(mCallback).onCameraControlStateUpdated(
+ TASK_ID, CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+
+ button.performClick();
+
+ verify(mCallback).onCameraControlStateUpdated(
+ TASK_ID, CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ }
+
+ @Test
+ public void testOnCameraDismissButtonClicked() {
+ mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ final ImageButton button =
+ mCompatUILayout.findViewById(R.id.camera_compat_dismiss_button);
+ button.performClick();
+
+ verify(mWindowManager).onCameraDismissButtonClicked();
+ verify(mCallback).onCameraControlStateUpdated(
+ TASK_ID, CAMERA_COMPAT_CONTROL_DISMISSED);
+ verify(mCompatUILayout).setCameraControlVisibility(/* show */ false);
+ }
+
+ @Test
+ public void testOnLongClickForCameraTreatementButton() {
+ doNothing().when(mWindowManager).onCameraButtonLongClicked();
+
+ final ImageButton button =
+ mCompatUILayout.findViewById(R.id.camera_compat_treatment_button);
+ button.performLongClick();
+
+ verify(mWindowManager).onCameraButtonLongClicked();
+ }
+
+ @Test
+ public void testOnLongClickForCameraDismissButton() {
+ doNothing().when(mWindowManager).onCameraButtonLongClicked();
+
+ final ImageButton button = mCompatUILayout.findViewById(R.id.camera_compat_dismiss_button);
+ button.performLongClick();
+
+ verify(mWindowManager).onCameraButtonLongClicked();
+ }
+
+ @Test
+ public void testOnClickForCameraCompatHint() {
+ mWindowManager.createLayout(true /* show */, false /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ final LinearLayout hint = mCompatUILayout.findViewById(R.id.camera_compat_hint);
+ hint.performClick();
+
+ verify(mCompatUILayout).setCameraCompatHintVisibility(/* show= */ false);
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
index d5dcf2e11a46..11c797363819 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
@@ -16,6 +16,10 @@
package com.android.wm.shell.compatui;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -23,6 +27,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -81,7 +86,7 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
mWindowManager = new CompatUIWindowManager(mContext, new Configuration(),
mSyncTransactionQueue, mCallback, TASK_ID, mTaskListener, new DisplayLayout(),
- false /* hasShownHint */);
+ false /* hasShownSizeCompatHint */, false /* hasShownSizeCompatHint */);
spyOn(mWindowManager);
doReturn(mCompatUILayout).when(mWindowManager).inflateCompatUILayout();
@@ -91,31 +96,35 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
@Test
public void testCreateSizeCompatButton() {
// Not create layout if show is false.
- mWindowManager.createLayout(false /* show */);
+ mWindowManager.createLayout(false /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN);
verify(mWindowManager, never()).inflateCompatUILayout();
// Not create hint popup.
- mWindowManager.mShouldShowHint = false;
- mWindowManager.createLayout(true /* show */);
+ mWindowManager.mShouldShowSizeCompatHint = false;
+ mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN);
verify(mWindowManager).inflateCompatUILayout();
- verify(mCompatUILayout).setSizeCompatHintVisibility(false /* show */);
+ verify(mCompatUILayout, never()).setSizeCompatHintVisibility(true /* show */);
// Create hint popup.
mWindowManager.release();
- mWindowManager.mShouldShowHint = true;
- mWindowManager.createLayout(true /* show */);
+ mWindowManager.mShouldShowSizeCompatHint = true;
+ mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN);
verify(mWindowManager, times(2)).inflateCompatUILayout();
assertNotNull(mCompatUILayout);
verify(mCompatUILayout).setSizeCompatHintVisibility(true /* show */);
- assertFalse(mWindowManager.mShouldShowHint);
+ assertFalse(mWindowManager.mShouldShowSizeCompatHint);
}
@Test
public void testRelease() {
- mWindowManager.createLayout(true /* show */);
+ mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN);
verify(mWindowManager).inflateCompatUILayout();
@@ -126,32 +135,60 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
@Test
public void testUpdateCompatInfo() {
- mWindowManager.createLayout(true /* show */);
+ mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN);
// No diff
clearInvocations(mWindowManager);
- mWindowManager.updateCompatInfo(mTaskConfig, mTaskListener, true /* show */);
+ mWindowManager.updateCompatInfo(mTaskConfig, mTaskListener, true /* show */,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
verify(mWindowManager, never()).updateSurfacePosition();
verify(mWindowManager, never()).release();
- verify(mWindowManager, never()).createLayout(anyBoolean());
+ verify(mWindowManager, never()).createLayout(anyBoolean(), anyBoolean(), anyInt());
// Change task listener, recreate button.
clearInvocations(mWindowManager);
final ShellTaskOrganizer.TaskListener newTaskListener = mock(
ShellTaskOrganizer.TaskListener.class);
mWindowManager.updateCompatInfo(mTaskConfig, newTaskListener,
- true /* show */);
+ true /* show */, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
verify(mWindowManager).release();
- verify(mWindowManager).createLayout(anyBoolean());
+ verify(mWindowManager).createLayout(anyBoolean(), anyBoolean(), anyInt());
+
+ // Change Camera Compat state, show a control.
+ mWindowManager.updateCompatInfo(mTaskConfig, newTaskListener, true /* show */,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+
+ verify(mCompatUILayout).setCameraControlVisibility(/* show */ true);
+ verify(mCompatUILayout).updateCameraTreatmentButton(
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+
+ clearInvocations(mWindowManager);
+ clearInvocations(mCompatUILayout);
+ // Change Camera Compat state, update a control.
+ mWindowManager.updateCompatInfo(mTaskConfig, newTaskListener, true /* show */,
+ true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ verify(mCompatUILayout).setCameraControlVisibility(/* show */ true);
+ verify(mCompatUILayout).updateCameraTreatmentButton(
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ clearInvocations(mWindowManager);
+ clearInvocations(mCompatUILayout);
+ // Change Camera Compat state to hidden, hide a control.
+ mWindowManager.updateCompatInfo(mTaskConfig, newTaskListener,
+ true /* show */, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+
+ verify(mCompatUILayout).setCameraControlVisibility(/* show */ false);
// Change task bounds, update position.
clearInvocations(mWindowManager);
final Configuration newTaskConfiguration = new Configuration();
newTaskConfiguration.windowConfiguration.setBounds(new Rect(0, 1000, 0, 2000));
mWindowManager.updateCompatInfo(newTaskConfiguration, newTaskListener,
- true /* show */);
+ true /* show */, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
verify(mWindowManager).updateSurfacePosition();
}
@@ -201,23 +238,25 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
public void testUpdateVisibility() {
// Create button if it is not created.
mWindowManager.mCompatUILayout = null;
+ mWindowManager.mHasSizeCompat = true;
mWindowManager.updateVisibility(true /* show */);
- verify(mWindowManager).createLayout(true /* show */);
+ verify(mWindowManager).createLayout(true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN);
// Hide button.
clearInvocations(mWindowManager);
doReturn(View.VISIBLE).when(mCompatUILayout).getVisibility();
mWindowManager.updateVisibility(false /* show */);
- verify(mWindowManager, never()).createLayout(anyBoolean());
+ verify(mWindowManager, never()).createLayout(anyBoolean(), anyBoolean(), anyInt());
verify(mCompatUILayout).setVisibility(View.GONE);
// Show button.
doReturn(View.GONE).when(mCompatUILayout).getVisibility();
mWindowManager.updateVisibility(true /* show */);
- verify(mWindowManager, never()).createLayout(anyBoolean());
+ verify(mWindowManager, never()).createLayout(anyBoolean(), anyBoolean(), anyInt());
verify(mCompatUILayout).setVisibility(View.VISIBLE);
}
@@ -230,6 +269,37 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
}
@Test
+ public void testOnCameraDismissButtonClicked() {
+ mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ clearInvocations(mCompatUILayout);
+ mWindowManager.onCameraDismissButtonClicked();
+
+ verify(mCallback).onCameraControlStateUpdated(TASK_ID, CAMERA_COMPAT_CONTROL_DISMISSED);
+ verify(mCompatUILayout).setCameraControlVisibility(/* show= */ false);
+ }
+
+ @Test
+ public void testOnCameraTreatmentButtonClicked() {
+ mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ clearInvocations(mCompatUILayout);
+ mWindowManager.onCameraTreatmentButtonClicked();
+
+ verify(mCallback).onCameraControlStateUpdated(
+ TASK_ID, CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+ verify(mCompatUILayout).updateCameraTreatmentButton(
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+
+ mWindowManager.onCameraTreatmentButtonClicked();
+
+ verify(mCallback).onCameraControlStateUpdated(
+ TASK_ID, CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ verify(mCompatUILayout).updateCameraTreatmentButton(
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ }
+
+ @Test
public void testOnRestartButtonClicked() {
mWindowManager.onRestartButtonClicked();
@@ -239,15 +309,60 @@ public class CompatUIWindowManagerTest extends ShellTestCase {
@Test
public void testOnRestartButtonLongClicked_showHint() {
// Not create hint popup.
- mWindowManager.mShouldShowHint = false;
- mWindowManager.createLayout(true /* show */);
+ mWindowManager.mShouldShowSizeCompatHint = false;
+ mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN);
verify(mWindowManager).inflateCompatUILayout();
- verify(mCompatUILayout).setSizeCompatHintVisibility(false /* show */);
+ verify(mCompatUILayout, never()).setSizeCompatHintVisibility(true /* show */);
mWindowManager.onRestartButtonLongClicked();
verify(mCompatUILayout).setSizeCompatHintVisibility(true /* show */);
}
+ @Test
+ public void testOnCamerControlLongClicked_showHint() {
+ // Not create hint popup.
+ mWindowManager.mShouldShowCameraCompatHint = false;
+ mWindowManager.createLayout(true /* show */, false /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ verify(mWindowManager).inflateCompatUILayout();
+ verify(mCompatUILayout, never()).setCameraCompatHintVisibility(true /* show */);
+
+ mWindowManager.onCameraButtonLongClicked();
+
+ verify(mCompatUILayout).setCameraCompatHintVisibility(true /* show */);
+ }
+
+ @Test
+ public void testCreateCameraCompatControl() {
+ // Not create layout if show is false.
+ mWindowManager.createLayout(false /* show */, false /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ verify(mWindowManager, never()).inflateCompatUILayout();
+
+ // Not create hint popup.
+ mWindowManager.mShouldShowCameraCompatHint = false;
+ mWindowManager.createLayout(true /* show */, false /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ verify(mWindowManager).inflateCompatUILayout();
+ verify(mCompatUILayout, never()).setCameraCompatHintVisibility(true /* show */);
+ verify(mCompatUILayout).setCameraControlVisibility(true /* show */);
+
+ // Create hint popup.
+ mWindowManager.release();
+ mWindowManager.mShouldShowCameraCompatHint = true;
+ mWindowManager.createLayout(true /* show */, false /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ verify(mWindowManager, times(2)).inflateCompatUILayout();
+ assertNotNull(mCompatUILayout);
+ verify(mCompatUILayout, times(2)).setCameraControlVisibility(true /* show */);
+ assertFalse(mWindowManager.mShouldShowCameraCompatHint);
+ }
+
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
index a6215d3347a8..8e30f65cee78 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
@@ -188,7 +188,7 @@ public class PipBoundsStateTest extends ShellTestCase {
final Rect newBounds = new Rect(50, 50, 100, 75);
mPipBoundsState.setBounds(currentBounds);
- mPipBoundsState.setPipExclusionBoundsChangeCallback(callback);
+ mPipBoundsState.addPipExclusionBoundsChangeCallback(callback);
// Setting the listener immediately calls back with the current bounds.
verify(callback).accept(currentBounds);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index 1eae625233a0..be1ef091ce67 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -17,6 +17,7 @@
package com.android.wm.shell.splitscreen;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
@@ -24,7 +25,11 @@ import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
import static com.android.wm.shell.splitscreen.SplitTestUtils.createMockSurface;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
@@ -66,7 +71,6 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.split.SplitLayout;
-import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.transition.Transitions;
import org.junit.Before;
@@ -133,6 +137,40 @@ public class SplitTransitionTests extends ShellTestCase {
}
@Test
+ public void testLaunchToSide() {
+ ActivityManager.RunningTaskInfo newTask = new TestRunningTaskInfoBuilder()
+ .setParentTaskId(mSideStage.mRootTaskInfo.taskId).build();
+ ActivityManager.RunningTaskInfo reparentTask = new TestRunningTaskInfoBuilder()
+ .setParentTaskId(mMainStage.mRootTaskInfo.taskId).build();
+
+ // Create a request to start a new task in side stage
+ TransitionRequestInfo request = new TransitionRequestInfo(TRANSIT_TO_FRONT, newTask, null);
+ IBinder transition = mock(IBinder.class);
+ WindowContainerTransaction result =
+ mStageCoordinator.handleRequest(transition, request);
+
+ // it should handle the transition to enter split screen.
+ assertNotNull(result);
+ assertTrue(containsSplitEnter(result));
+
+ // simulate the transition
+ TransitionInfo.Change openChange = createChange(TRANSIT_OPEN, newTask);
+ TransitionInfo.Change reparentChange = createChange(TRANSIT_CHANGE, reparentTask);
+
+ TransitionInfo info = new TransitionInfo(TRANSIT_TO_FRONT, 0);
+ info.addChange(openChange);
+ info.addChange(reparentChange);
+ mSideStage.onTaskAppeared(newTask, createMockSurface());
+ mMainStage.onTaskAppeared(reparentTask, createMockSurface());
+ boolean accepted = mStageCoordinator.startAnimation(transition, info,
+ mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
+ mock(Transitions.TransitionFinishCallback.class));
+ assertTrue(accepted);
+ assertTrue(mStageCoordinator.isSplitScreenVisible());
+ }
+
+ @Test
public void testLaunchPair() {
TransitionInfo info = createEnterPairInfo();
@@ -215,7 +253,9 @@ public class SplitTransitionTests extends ShellTestCase {
enterSplit();
ActivityManager.RunningTaskInfo homeTask = new TestRunningTaskInfoBuilder()
- .setActivityType(ACTIVITY_TYPE_HOME).build();
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+ .setActivityType(ACTIVITY_TYPE_HOME)
+ .build();
// Create a request to bring home forward
TransitionRequestInfo request = new TransitionRequestInfo(TRANSIT_TO_FRONT, homeTask, null);
@@ -246,6 +286,64 @@ public class SplitTransitionTests extends ShellTestCase {
}
@Test
+ public void testDismissFromBeingOccluded() {
+ enterSplit();
+
+ ActivityManager.RunningTaskInfo normalTask = new TestRunningTaskInfoBuilder()
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+ .build();
+
+ // Create a request to bring a normal task forward
+ TransitionRequestInfo request =
+ new TransitionRequestInfo(TRANSIT_TO_FRONT, normalTask, null);
+ IBinder transition = mock(IBinder.class);
+ WindowContainerTransaction result = mStageCoordinator.handleRequest(transition, request);
+
+ assertTrue(containsSplitExit(result));
+
+ // make sure we haven't made any local changes yet (need to wait until transition is ready)
+ assertTrue(mStageCoordinator.isSplitScreenVisible());
+
+ // simulate the transition
+ TransitionInfo.Change normalChange = createChange(TRANSIT_TO_FRONT, normalTask);
+ TransitionInfo.Change mainChange = createChange(TRANSIT_TO_BACK, mMainChild);
+ TransitionInfo.Change sideChange = createChange(TRANSIT_TO_BACK, mSideChild);
+
+ TransitionInfo info = new TransitionInfo(TRANSIT_TO_FRONT, 0);
+ info.addChange(normalChange);
+ info.addChange(mainChange);
+ info.addChange(sideChange);
+ mMainStage.onTaskVanished(mMainChild);
+ mSideStage.onTaskVanished(mSideChild);
+ mStageCoordinator.startAnimation(transition, info,
+ mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
+ mock(Transitions.TransitionFinishCallback.class));
+ assertFalse(mStageCoordinator.isSplitScreenVisible());
+ }
+
+ @Test
+ public void testDismissFromMultiWindowSupport() {
+ enterSplit();
+
+ // simulate the transition
+ TransitionInfo.Change mainChange = createChange(TRANSIT_TO_BACK, mMainChild);
+ TransitionInfo.Change sideChange = createChange(TRANSIT_TO_BACK, mSideChild);
+ TransitionInfo info = new TransitionInfo(TRANSIT_TO_BACK, 0);
+ info.addChange(mainChange);
+ info.addChange(sideChange);
+ IBinder transition = mSplitScreenTransitions.startDismissTransition(null,
+ new WindowContainerTransaction(), mStageCoordinator,
+ EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW, STAGE_TYPE_SIDE);
+ boolean accepted = mStageCoordinator.startAnimation(transition, info,
+ mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
+ mock(Transitions.TransitionFinishCallback.class));
+ assertTrue(accepted);
+ assertFalse(mStageCoordinator.isSplitScreenVisible());
+ }
+
+ @Test
public void testDismissSnap() {
enterSplit();
@@ -256,8 +354,9 @@ public class SplitTransitionTests extends ShellTestCase {
TransitionInfo info = new TransitionInfo(TRANSIT_TO_BACK, 0);
info.addChange(mainChange);
info.addChange(sideChange);
- IBinder transition = mStageCoordinator.onSnappedToDismissTransition(
- false /* mainStageToTop */);
+ IBinder transition = mSplitScreenTransitions.startDismissTransition(null,
+ new WindowContainerTransaction(), mStageCoordinator, EXIT_REASON_DRAG_DIVIDER,
+ STAGE_TYPE_SIDE);
mMainStage.onTaskVanished(mMainChild);
mSideStage.onTaskVanished(mSideChild);
boolean accepted = mStageCoordinator.startAnimation(transition, info,
@@ -324,6 +423,22 @@ public class SplitTransitionTests extends ShellTestCase {
true /* includingTopTask */);
}
+ private boolean containsSplitEnter(@NonNull WindowContainerTransaction wct) {
+ boolean movedMainToFront = false;
+ boolean movedSideToFront = false;
+ for (int i = 0; i < wct.getHierarchyOps().size(); ++i) {
+ WindowContainerTransaction.HierarchyOp op = wct.getHierarchyOps().get(i);
+ if (op.getType() == HIERARCHY_OP_TYPE_REORDER) {
+ if (op.getContainer() == mMainStage.mRootTaskInfo.token.asBinder()) {
+ movedMainToFront = true;
+ } else if (op.getContainer() == mSideStage.mRootTaskInfo.token.asBinder()) {
+ movedSideToFront = true;
+ }
+ }
+ }
+ return movedMainToFront && movedSideToFront;
+ }
+
private boolean containsSplitExit(@NonNull WindowContainerTransaction wct) {
// reparenting of child tasks to null constitutes exiting split.
boolean reparentedMain = false;
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 22904a0528d5..136fc6ca4e2a 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -902,6 +902,27 @@ std::string AssetManager2::GetLastResourceResolution() const {
return log_stream.str();
}
+base::expected<uint32_t, NullOrIOError> AssetManager2::GetParentThemeResourceId(uint32_t resid)
+const {
+ auto entry = FindEntry(resid, 0u /* density_override */,
+ false /* stop_at_first_match */,
+ false /* ignore_configuration */);
+ if (!entry.has_value()) {
+ return base::unexpected(entry.error());
+ }
+
+ auto entry_map = std::get_if<incfs::verified_map_ptr<ResTable_map_entry>>(&entry->entry);
+ if (entry_map == nullptr) {
+ // Not a bag, nothing to do.
+ return base::unexpected(std::nullopt);
+ }
+
+ auto map = *entry_map;
+ const uint32_t parent_resid = dtohl(map->parent.ident);
+
+ return parent_resid;
+}
+
base::expected<AssetManager2::ResourceName, NullOrIOError> AssetManager2::GetResourceName(
uint32_t resid) const {
auto result = FindEntry(resid, 0u /* density_override */, true /* stop_at_first_match */,
diff --git a/libs/androidfw/TEST_MAPPING b/libs/androidfw/TEST_MAPPING
index 9ebc9969a730..8abe79d01642 100644
--- a/libs/androidfw/TEST_MAPPING
+++ b/libs/androidfw/TEST_MAPPING
@@ -2,6 +2,9 @@
"presubmit": [
{
"name": "CtsResourcesLoaderTests"
+ },
+ {
+ "name": "libandroidfw_tests"
}
]
}
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index a3b42df12da9..1bde792da2ba 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -192,6 +192,12 @@ class AssetManager2 {
std::unique_ptr<Asset> OpenNonAsset(const std::string& filename, ApkAssetsCookie cookie,
Asset::AccessMode mode) const;
+ // Returns the resource id of parent style of the specified theme.
+ //
+ // Returns a null error if the name is missing/corrupt, or an I/O error if reading resource data
+ // failed.
+ base::expected<uint32_t, NullOrIOError> GetParentThemeResourceId(uint32_t resid) const;
+
// Returns the resource name of the specified resource ID.
//
// Utf8 strings are preferred, and only if they are unavailable are the Utf16 variants populated.
diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp
index 1b3892032e79..c4366f756a21 100644
--- a/libs/hwui/jni/Shader.cpp
+++ b/libs/hwui/jni/Shader.cpp
@@ -273,21 +273,99 @@ static inline int ThrowIAEFmt(JNIEnv* env, const char* fmt, ...) {
return ret;
}
-static void RuntimeShader_updateUniforms(JNIEnv* env, jobject, jlong shaderBuilder,
- jstring jUniformName, jfloatArray jvalues) {
+static bool isIntUniformType(const SkRuntimeEffect::Uniform::Type& type) {
+ switch (type) {
+ case SkRuntimeEffect::Uniform::Type::kFloat:
+ case SkRuntimeEffect::Uniform::Type::kFloat2:
+ case SkRuntimeEffect::Uniform::Type::kFloat3:
+ case SkRuntimeEffect::Uniform::Type::kFloat4:
+ case SkRuntimeEffect::Uniform::Type::kFloat2x2:
+ case SkRuntimeEffect::Uniform::Type::kFloat3x3:
+ case SkRuntimeEffect::Uniform::Type::kFloat4x4:
+ return false;
+ case SkRuntimeEffect::Uniform::Type::kInt:
+ case SkRuntimeEffect::Uniform::Type::kInt2:
+ case SkRuntimeEffect::Uniform::Type::kInt3:
+ case SkRuntimeEffect::Uniform::Type::kInt4:
+ return true;
+ }
+}
+
+static void UpdateFloatUniforms(JNIEnv* env, SkRuntimeShaderBuilder* builder,
+ const char* uniformName, const float values[], int count,
+ bool isColor) {
+ SkRuntimeShaderBuilder::BuilderUniform uniform = builder->uniform(uniformName);
+ if (uniform.fVar == nullptr) {
+ ThrowIAEFmt(env, "unable to find uniform named %s", uniformName);
+ } else if (isColor != ((uniform.fVar->flags & SkRuntimeEffect::Uniform::kColor_Flag) != 0)) {
+ if (isColor) {
+ jniThrowExceptionFmt(
+ env, "java/lang/IllegalArgumentException",
+ "attempting to set a color uniform using the non-color specific APIs: %s %x",
+ uniformName, uniform.fVar->flags);
+ } else {
+ ThrowIAEFmt(env,
+ "attempting to set a non-color uniform using the setColorUniform APIs: %s",
+ uniformName);
+ }
+ } else if (isIntUniformType(uniform.fVar->type)) {
+ ThrowIAEFmt(env, "attempting to set a int uniform using the setUniform APIs: %s",
+ uniformName);
+ } else if (!uniform.set<float>(values, count)) {
+ ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]",
+ uniform.fVar->sizeInBytes(), sizeof(float) * count);
+ }
+}
+
+static void RuntimeShader_updateFloatUniforms(JNIEnv* env, jobject, jlong shaderBuilder,
+ jstring jUniformName, jfloat value1, jfloat value2,
+ jfloat value3, jfloat value4, jint count) {
+ SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
+ ScopedUtfChars name(env, jUniformName);
+ const float values[4] = {value1, value2, value3, value4};
+ UpdateFloatUniforms(env, builder, name.c_str(), values, count, false);
+}
+
+static void RuntimeShader_updateFloatArrayUniforms(JNIEnv* env, jobject, jlong shaderBuilder,
+ jstring jUniformName, jfloatArray jvalues,
+ jboolean isColor) {
SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
ScopedUtfChars name(env, jUniformName);
AutoJavaFloatArray autoValues(env, jvalues, 0, kRO_JNIAccess);
+ UpdateFloatUniforms(env, builder, name.c_str(), autoValues.ptr(), autoValues.length(), isColor);
+}
- SkRuntimeShaderBuilder::BuilderUniform uniform = builder->uniform(name.c_str());
+static void UpdateIntUniforms(JNIEnv* env, SkRuntimeShaderBuilder* builder, const char* uniformName,
+ const int values[], int count) {
+ SkRuntimeShaderBuilder::BuilderUniform uniform = builder->uniform(uniformName);
if (uniform.fVar == nullptr) {
- ThrowIAEFmt(env, "unable to find uniform named %s", name.c_str());
- } else if (!uniform.set<float>(autoValues.ptr(), autoValues.length())) {
+ ThrowIAEFmt(env, "unable to find uniform named %s", uniformName);
+ } else if (!isIntUniformType(uniform.fVar->type)) {
+ ThrowIAEFmt(env, "attempting to set a non-int uniform using the setIntUniform APIs: %s",
+ uniformName);
+ } else if (!uniform.set<int>(values, count)) {
ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]",
- uniform.fVar->sizeInBytes(), sizeof(float) * autoValues.length());
+ uniform.fVar->sizeInBytes(), sizeof(float) * count);
}
}
+static void RuntimeShader_updateIntUniforms(JNIEnv* env, jobject, jlong shaderBuilder,
+ jstring jUniformName, jint value1, jint value2,
+ jint value3, jint value4, jint count) {
+ SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
+ ScopedUtfChars name(env, jUniformName);
+ const int values[4] = {value1, value2, value3, value4};
+ UpdateIntUniforms(env, builder, name.c_str(), values, count);
+}
+
+static void RuntimeShader_updateIntArrayUniforms(JNIEnv* env, jobject, jlong shaderBuilder,
+ jstring jUniformName, jintArray jvalues) {
+ SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
+ ScopedUtfChars name(env, jUniformName);
+ AutoJavaIntArray autoValues(env, jvalues, 0);
+ UpdateIntUniforms(env, builder, name.c_str(), autoValues.ptr(), autoValues.length());
+}
+
static void RuntimeShader_updateShader(JNIEnv* env, jobject, jlong shaderBuilder,
jstring jUniformName, jlong shaderHandle) {
SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
@@ -338,7 +416,14 @@ static const JNINativeMethod gRuntimeShaderMethods[] = {
{"nativeGetFinalizer", "()J", (void*)RuntimeShader_getNativeFinalizer},
{"nativeCreateShader", "(JJZ)J", (void*)RuntimeShader_create},
{"nativeCreateBuilder", "(Ljava/lang/String;)J", (void*)RuntimeShader_createShaderBuilder},
- {"nativeUpdateUniforms", "(JLjava/lang/String;[F)V", (void*)RuntimeShader_updateUniforms},
+ {"nativeUpdateUniforms", "(JLjava/lang/String;[FZ)V",
+ (void*)RuntimeShader_updateFloatArrayUniforms},
+ {"nativeUpdateUniforms", "(JLjava/lang/String;FFFFI)V",
+ (void*)RuntimeShader_updateFloatUniforms},
+ {"nativeUpdateUniforms", "(JLjava/lang/String;[I)V",
+ (void*)RuntimeShader_updateIntArrayUniforms},
+ {"nativeUpdateUniforms", "(JLjava/lang/String;IIIII)V",
+ (void*)RuntimeShader_updateIntUniforms},
{"nativeUpdateShader", "(JLjava/lang/String;J)V", (void*)RuntimeShader_updateShader},
};
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 38b57153b74a..bdad7727071f 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -526,8 +526,9 @@ nsecs_t CanvasContext::draw() {
if (vsyncId != UiFrameInfoBuilder::INVALID_VSYNC_ID) {
const auto inputEventId =
static_cast<int32_t>(mCurrentFrameInfo->get(FrameInfoIndex::InputEventId));
- native_window_set_frame_timeline_info(mNativeSurface->getNativeWindow(), vsyncId,
- inputEventId);
+ native_window_set_frame_timeline_info(
+ mNativeSurface->getNativeWindow(), vsyncId, inputEventId,
+ mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime));
}
}
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 2f8ddeecced0..02257db9df6a 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -262,8 +262,6 @@ EGLConfig EglManager::loadA8Config(EGLDisplay display, EglManager::SwapBehavior
0,
EGL_DEPTH_SIZE,
0,
- EGL_STENCIL_SIZE,
- STENCIL_BUFFER_SIZE,
EGL_SURFACE_TYPE,
EGL_WINDOW_BIT | eglSwapBehavior,
EGL_NONE};
@@ -354,10 +352,6 @@ void EglManager::loadConfigs() {
ALOGW("Failed to initialize 101010-2 format, error = %s",
eglErrorString());
}
- mEglConfigA8 = loadA8Config(mEglDisplay, mSwapBehavior);
- if (mEglConfigA8 == EGL_NO_CONFIG_KHR) {
- ALOGE("Failed to initialize A8 format, error = %s", eglErrorString());
- }
}
void EglManager::createContext() {
@@ -431,9 +425,13 @@ Result<EGLSurface, EGLint> EglManager::createSurface(EGLNativeWindowType window,
EGLConfig config = mEglConfig;
if (colorMode == ColorMode::A8) {
// A8 doesn't use a color space
+ if (!mEglConfigA8) {
+ mEglConfigA8 = loadA8Config(mEglDisplay, mSwapBehavior);
+ LOG_ALWAYS_FATAL_IF(!mEglConfigA8,
+ "Requested ColorMode::A8, but EGL lacks support! error = %s",
+ eglErrorString());
+ }
config = mEglConfigA8;
-
- LOG_ALWAYS_FATAL_IF(!mEglConfigA8, "Requested ColorMode::A8, but EGL lacks support!");
} else {
if (!mHasWideColorGamutSupport) {
colorMode = ColorMode::Default;
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index ecdd4b616e0f..2c94820d50c1 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -1860,7 +1860,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 a07a64acb6e6..b744017027b7 100644
--- a/location/java/android/location/GnssMeasurementsEvent.java
+++ b/location/java/android/location/GnssMeasurementsEvent.java
@@ -156,7 +156,7 @@ public final class GnssMeasurementsEvent implements Parcelable {
public GnssMeasurementsEvent createFromParcel(Parcel in) {
ClassLoader classLoader = getClass().getClassLoader();
- GnssClock clock = in.readParcelable(classLoader);
+ GnssClock clock = in.readParcelable(classLoader, android.location.GnssClock.class);
int measurementsLength = in.readInt();
GnssMeasurement[] measurementsArray = new GnssMeasurement[measurementsLength];
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/AudioPortDeviceExt.aidl b/media/aidl/android/media/audio/common/AudioPortDeviceExt.aidl
index 940566c158b9..63136d7a3423 100644
--- a/media/aidl/android/media/audio/common/AudioPortDeviceExt.aidl
+++ b/media/aidl/android/media/audio/common/AudioPortDeviceExt.aidl
@@ -29,10 +29,23 @@ import android.media.audio.common.AudioFormatDescription;
parcelable AudioPortDeviceExt {
/** Audio device specification. */
AudioDevice device;
+ /** Bitmask indexed by 'FLAG_INDEX_' constants. */
+ int flags;
/**
* List of supported encoded formats. Specified for ports that perform
* hardware-accelerated decoding or transcoding, or connected to external
* hardware.
*/
AudioFormatDescription[] encodedFormats;
+
+ /**
+ * A default device port is fallback used when the preference for the device
+ * to use has not been specified (AudioDeviceType.type == {IN|OUT}_DEFAULT),
+ * or the specified device does not satisfy routing criteria based on audio
+ * stream attributes and use cases. The device port for which the ID is
+ * returned must be associated with a permanently attached device
+ * (AudioDeviceDescription.connection == ''). There can be no more than one
+ * default device port in a HAL module in each I/O direction.
+ */
+ const int FLAG_INDEX_DEFAULT_DEVICE = 0;
}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPortDeviceExt.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPortDeviceExt.aidl
index 2e8124a2fa24..37d7041731bf 100644
--- a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPortDeviceExt.aidl
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPortDeviceExt.aidl
@@ -36,5 +36,7 @@ package android.media.audio.common;
@JavaDerive(equals=true, toString=true) @VintfStability
parcelable AudioPortDeviceExt {
android.media.audio.common.AudioDevice device;
+ int flags;
android.media.audio.common.AudioFormatDescription[] encodedFormats;
+ const int FLAG_INDEX_DEFAULT_DEVICE = 0;
}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 147e73599cad..7f6fb90297b3 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -460,4 +460,8 @@ interface IAudioService {
boolean register);
void setTestDeviceConnectionState(in AudioDeviceAttributes device, boolean connected);
+
+ List<AudioFocusInfo> getFocusStack();
+
+ boolean sendFocusLoss(in AudioFocusInfo focusLoser, in IAudioPolicyCallback apcb);
}
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/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/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java
index 0f08d79bf3bb..3ba1d1f0eca2 100644
--- a/media/java/android/media/audiopolicy/AudioPolicy.java
+++ b/media/java/android/media/audiopolicy/AudioPolicy.java
@@ -19,6 +19,7 @@ package android.media.audiopolicy;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.UserIdInt;
@@ -230,7 +231,7 @@ public class AudioPolicy {
* If set to {@code true}, it is mandatory to set an
* {@link AudioPolicy.AudioPolicyFocusListener} in order to successfully build
* an {@code AudioPolicy} instance.
- * @param enforce true if the policy will govern audio focus decisions.
+ * @param isFocusPolicy true if the policy will govern audio focus decisions.
* @return the same Builder instance.
*/
@NonNull
@@ -723,6 +724,45 @@ public class AudioPolicy {
}
/**
+ * Returns the list of entries in the focus stack.
+ * The list is ordered with increasing rank of focus ownership, where the last entry is at the
+ * top of the focus stack and is the current focus owner.
+ * @return the ordered list of focus owners
+ * @see AudioManager#registerAudioPolicy(AudioPolicy)
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public @NonNull List<AudioFocusInfo> getFocusStack() {
+ try {
+ return getService().getFocusStack();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Send AUDIOFOCUS_LOSS to a specific stack entry, causing it to be notified of the focus
+ * loss, and for it to exit the focus stack (its focus listener will not be invoked after that).
+ * This operation is only valid for a registered policy (with
+ * {@link AudioManager#registerAudioPolicy(AudioPolicy)}) that is also set as the policy focus
+ * listener (with {@link Builder#setAudioPolicyFocusListener(AudioPolicyFocusListener)}.
+ * @param focusLoser the stack entry that is exiting the stack through a focus loss
+ * @return false if the focusLoser wasn't found in the stack, true otherwise
+ * @throws IllegalStateException if used on an unregistered policy, or a registered policy
+ * with no {@link AudioPolicyFocusListener} set
+ * @see AudioManager#registerAudioPolicy(AudioPolicy)
+ * @see Builder#setAudioPolicyStatusListener(AudioPolicyStatusListener)
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public boolean sendFocusLoss(@NonNull AudioFocusInfo focusLoser) throws IllegalStateException {
+ Objects.requireNonNull(focusLoser);
+ try {
+ return getService().sendFocusLoss(focusLoser, cb());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Create an {@link AudioRecord} instance that is associated with the given {@link AudioMix}.
* Audio buffers recorded through the created instance will contain the mix of the audio
* streams that fed the given mixer.
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/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/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/TvContract.java b/media/java/android/media/tv/TvContract.java
index a0f6fb9577c3..9147c123c6f3 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -2720,6 +2720,42 @@ public final class TvContract {
*/
public static final String COLUMN_GLOBAL_CONTENT_ID = "global_content_id";
+ /**
+ * The flag indicating whether this TV program is scrambled or not.
+ *
+ * <p>Use the same coding for scrambled in the underlying broadcast standard
+ * if {@code free_ca_mode} in EIT is defined there (e.g. ETSI EN 300 468).
+ *
+ * <p>Type: INTEGER (boolean)
+ */
+ public static final String COLUMN_SCRAMBLED = "scrambled";
+
+ /**
+ * The comma-separated series IDs of this TV program for episodic TV shows.
+ *
+ * <p>This is used to indicate the series IDs.
+ * Programs in the same series share a series ID.
+ * Use this instead of {@link #COLUMN_SERIES_ID} if more than one series IDs
+ * are assigned to the TV program.
+ *
+ * <p>Can be empty.
+ *
+ * <p>Type: TEXT
+ */
+ public static final String COLUMN_MULTI_SERIES_ID = "multi_series_id";
+
+ /**
+ * The internal ID used by individual TV input services.
+ *
+ * <p>This is internal to the provider that inserted it, and should not be decoded by other
+ * apps.
+ *
+ * <p>Can be empty.
+ *
+ * <p>Type: TEXT
+ */
+ public static final String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
+
private Programs() {}
/** Canonical genres for TV programs. */
@@ -3052,6 +3088,32 @@ public final class TvContract {
public static final String COLUMN_RECORDING_EXPIRE_TIME_UTC_MILLIS =
"recording_expire_time_utc_millis";
+ /**
+ * The comma-separated series IDs of this TV program for episodic TV shows.
+ *
+ * <p>This is used to indicate the series IDs.
+ * Programs in the same series share a series ID.
+ * Use this instead of {@link #COLUMN_SERIES_ID} if more than one series IDs
+ * are assigned to the TV program.
+ *
+ * <p>Can be empty.
+ *
+ * <p>Type: TEXT
+ */
+ public static final String COLUMN_MULTI_SERIES_ID = "multi_series_id";
+
+ /**
+ * The internal ID used by individual TV input services.
+ *
+ * <p>This is internal to the provider that inserted it, and should not be decoded by other
+ * apps.
+ *
+ * <p>Can be empty.
+ *
+ * <p>Type: TEXT
+ */
+ public static final String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
+
private RecordedPrograms() {}
}
diff --git a/media/java/android/media/tv/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/interactive/ITvIAppClient.aidl b/media/java/android/media/tv/interactive/ITvIAppClient.aidl
index 30ef50342c11..a5f23315956d 100644
--- a/media/java/android/media/tv/interactive/ITvIAppClient.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppClient.aidl
@@ -15,9 +15,9 @@
*/
package android.media.tv.interactive;
-import android.media.tv.BroadcastInfoRequest;
import android.media.tv.BroadcastInfoRequest;
+import android.os.Bundle;
import android.view.InputChannel;
/**
@@ -31,4 +31,5 @@ oneway interface ITvIAppClient {
void onLayoutSurface(int left, int top, int right, int bottom, int seq);
void onBroadcastInfoRequest(in BroadcastInfoRequest request, int seq);
void onSessionStateChanged(int state, int seq);
-} \ No newline at end of file
+ void onCommandRequest(in String cmdType, in Bundle parameters, int seq);
+}
diff --git a/media/java/android/media/tv/interactive/ITvIAppManager.aidl b/media/java/android/media/tv/interactive/ITvIAppManager.aidl
index cd87a09b91e0..40d8034888d9 100644
--- a/media/java/android/media/tv/interactive/ITvIAppManager.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppManager.aidl
@@ -22,6 +22,7 @@ import android.media.tv.interactive.ITvIAppClient;
import android.media.tv.interactive.ITvIAppManagerCallback;
import android.media.tv.interactive.TvIAppInfo;
import android.net.Uri;
+import android.os.Bundle;
import android.view.Surface;
/**
@@ -31,6 +32,8 @@ import android.view.Surface;
interface ITvIAppManager {
List<TvIAppInfo> getTvIAppServiceList(int userId);
void prepare(String tiasId, int type, int userId);
+ void notifyAppLinkInfo(String tiasId, in Bundle info, int userId);
+ void sendAppLinkCommand(String tiasId, in Bundle command, int userId);
void startIApp(in IBinder sessionToken, int userId);
void createSession(
in ITvIAppClient client, in String iAppServiceId, int type, int seq, int userId);
diff --git a/media/java/android/media/tv/interactive/ITvIAppService.aidl b/media/java/android/media/tv/interactive/ITvIAppService.aidl
index af15dd8f5a3f..8acb75f6b8b9 100644
--- a/media/java/android/media/tv/interactive/ITvIAppService.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppService.aidl
@@ -18,6 +18,7 @@ package android.media.tv.interactive;
import android.media.tv.interactive.ITvIAppServiceCallback;
import android.media.tv.interactive.ITvIAppSessionCallback;
+import android.os.Bundle;
import android.view.InputChannel;
/**
@@ -31,4 +32,6 @@ oneway interface ITvIAppService {
void createSession(in InputChannel channel, in ITvIAppSessionCallback callback,
in String iAppServiceId, int type);
void prepare(int type);
+ void notifyAppLinkInfo(in Bundle info);
+ void sendAppLinkCommand(in Bundle command);
} \ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl b/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl
index ff8af88fa5fa..66f5fc1d54aa 100644
--- a/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl
@@ -19,6 +19,7 @@ package android.media.tv.interactive;
import android.media.tv.BroadcastInfoRequest;
import android.media.tv.interactive.ITvIAppSession;
import android.media.tv.BroadcastInfoRequest;
+import android.os.Bundle;
/**
* Helper interface for ITvIAppSession to allow TvIAppService to notify the system service when
@@ -30,4 +31,5 @@ oneway interface ITvIAppSessionCallback {
void onLayoutSurface(int left, int top, int right, int bottom);
void onBroadcastInfoRequest(in BroadcastInfoRequest request);
void onSessionStateChanged(int state);
-} \ No newline at end of file
+ void onCommandRequest(in String cmdType, in Bundle parameters);
+}
diff --git a/media/java/android/media/tv/interactive/TvIAppManager.java b/media/java/android/media/tv/interactive/TvIAppManager.java
index 2272084a4017..9390d8db08fb 100644
--- a/media/java/android/media/tv/interactive/TvIAppManager.java
+++ b/media/java/android/media/tv/interactive/TvIAppManager.java
@@ -26,6 +26,7 @@ import android.media.tv.BroadcastInfoRequest;
import android.media.tv.BroadcastInfoResponse;
import android.media.tv.TvInputManager;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -87,6 +88,80 @@ public final class TvIAppManager {
*/
public static final int TV_IAPP_RTE_STATE_ERROR = 4;
+ /**
+ * Key for package name in app link.
+ * <p>Type: String
+ *
+ * @see #notifyAppLinkInfo(String, Bundle)
+ * @see #sendAppLinkCommand(String, Bundle)
+ * @hide
+ */
+ public static final String KEY_PACKAGE_NAME = "package_name";
+
+ /**
+ * Key for class name in app link.
+ * <p>Type: String
+ *
+ * @see #notifyAppLinkInfo(String, Bundle)
+ * @see #sendAppLinkCommand(String, Bundle)
+ * @hide
+ */
+ public static final String KEY_CLASS_NAME = "class_name";
+
+ /**
+ * Key for URI scheme in app link.
+ * <p>Type: String
+ *
+ * @see #notifyAppLinkInfo(String, Bundle)
+ * @hide
+ */
+ public static final String KEY_URI_SCHEME = "uri_scheme";
+
+ /**
+ * Key for URI host in app link.
+ * <p>Type: String
+ *
+ * @see #notifyAppLinkInfo(String, Bundle)
+ * @hide
+ */
+ public static final String KEY_URI_HOST = "uri_host";
+
+ /**
+ * Key for URI prefix in app link.
+ * <p>Type: String
+ *
+ * @see #notifyAppLinkInfo(String, Bundle)
+ * @hide
+ */
+ public static final String KEY_URI_PREFIX = "uri_prefix";
+
+ /**
+ * Key for command type in app link command.
+ * <p>Type: String
+ *
+ * @see #sendAppLinkCommand(String, Bundle)
+ * @hide
+ */
+ public static final String KEY_COMMAND_TYPE = "command_type";
+
+ /**
+ * Key for service ID in app link command.
+ * <p>Type: String
+ *
+ * @see #sendAppLinkCommand(String, Bundle)
+ * @hide
+ */
+ public static final String KEY_SERVICE_ID = "service_id";
+
+ /**
+ * Key for back URI in app link command.
+ * <p>Type: String
+ *
+ * @see #sendAppLinkCommand(String, Bundle)
+ * @hide
+ */
+ public static final String KEY_BACK_URI = "back_uri";
+
private final ITvIAppManager mService;
private final int mUserId;
@@ -169,6 +244,19 @@ public final class TvIAppManager {
}
@Override
+ public void onCommandRequest(@TvIAppService.IAppServiceCommandType String cmdType,
+ Bundle parameters, int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ record.postCommandRequest(cmdType, parameters);
+ }
+ }
+
+ @Override
public void onSessionStateChanged(int state, int seq) {
synchronized (mSessionCallbackRecordMap) {
SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
@@ -418,6 +506,30 @@ public final class TvIAppManager {
}
/**
+ * Notifies app link info.
+ * @hide
+ */
+ public void notifyAppLinkInfo(String tvIAppServiceId, Bundle appLinkInfo) {
+ try {
+ mService.notifyAppLinkInfo(tvIAppServiceId, appLinkInfo, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Sends app link command.
+ * @hide
+ */
+ public void sendAppLinkCommand(String tvIAppServiceId, Bundle command) {
+ try {
+ mService.sendAppLinkCommand(tvIAppServiceId, command, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Registers a {@link TvIAppManager.TvIAppCallback}.
*
* @param callback A callback used to monitor status of the TV IApp services.
@@ -947,6 +1059,16 @@ public final class TvIAppManager {
});
}
+ void postCommandRequest(final @TvIAppService.IAppServiceCommandType String cmdType,
+ final Bundle parameters) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onCommandRequest(mSession, cmdType, parameters);
+ }
+ });
+ }
+
void postSessionStateChanged(int state) {
mHandler.post(new Runnable() {
@Override
@@ -994,6 +1116,17 @@ public final class TvIAppManager {
}
/**
+ * This is called when {@link TvIAppService.Session#requestCommand} is called.
+ *
+ * @param session A {@link TvIAppManager.Session} associated with this callback.
+ * @param cmdType type of the command.
+ * @param parameters parameters of the command.
+ */
+ public void onCommandRequest(Session session,
+ @TvIAppService.IAppServiceCommandType String cmdType, Bundle parameters) {
+ }
+
+ /**
* This is called when {@link TvIAppService.Session#notifySessionStateChanged} is called.
*
* @param session A {@link TvIAppManager.Session} associated with this callback.
diff --git a/media/java/android/media/tv/interactive/TvIAppService.java b/media/java/android/media/tv/interactive/TvIAppService.java
index f93b59784c7e..6475f903f257 100644
--- a/media/java/android/media/tv/interactive/TvIAppService.java
+++ b/media/java/android/media/tv/interactive/TvIAppService.java
@@ -19,6 +19,7 @@ package android.media.tv.interactive;
import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.StringDef;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.Service;
@@ -30,6 +31,7 @@ import android.media.tv.BroadcastInfoRequest;
import android.media.tv.BroadcastInfoResponse;
import android.net.Uri;
import android.os.AsyncTask;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -52,6 +54,8 @@ import android.widget.FrameLayout;
import com.android.internal.os.SomeArgs;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
@@ -82,6 +86,28 @@ public abstract class TvIAppService extends Service {
*/
public static final String SERVICE_META_DATA = "android.media.tv.interactive.app";
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef(prefix = "IAPP_SERVICE_COMMAND_TYPE_", value = {
+ IAPP_SERVICE_COMMAND_TYPE_TUNE,
+ IAPP_SERVICE_COMMAND_TYPE_TUNENEXT,
+ IAPP_SERVICE_COMMAND_TYPE_TUNEPREV,
+ IAPP_SERVICE_COMMAND_TYPE_STOP,
+ IAPP_SERVICE_COMMAND_TYPE_SETSTREAMVOLUME
+ })
+ public @interface IAppServiceCommandType {}
+
+ /** @hide */
+ public static final String IAPP_SERVICE_COMMAND_TYPE_TUNE = "Tune";
+ /** @hide */
+ public static final String IAPP_SERVICE_COMMAND_TYPE_TUNENEXT = "TuneNext";
+ /** @hide */
+ public static final String IAPP_SERVICE_COMMAND_TYPE_TUNEPREV = "TunePrevious";
+ /** @hide */
+ public static final String IAPP_SERVICE_COMMAND_TYPE_STOP = "Stop";
+ /** @hide */
+ public static final String IAPP_SERVICE_COMMAND_TYPE_SETSTREAMVOLUME = "setStreamVolume";
+
private final Handler mServiceHandler = new ServiceHandler();
private final RemoteCallbackList<ITvIAppServiceCallback> mCallbacks =
new RemoteCallbackList<>();
@@ -123,6 +149,16 @@ public abstract class TvIAppService extends Service {
public void prepare(int type) {
onPrepare(type);
}
+
+ @Override
+ public void notifyAppLinkInfo(Bundle appLinkInfo) {
+ onAppLinkInfo(appLinkInfo);
+ }
+
+ @Override
+ public void sendAppLinkCommand(Bundle command) {
+ onAppLinkCommand(command);
+ }
};
return tvIAppServiceBinder;
}
@@ -135,6 +171,22 @@ public abstract class TvIAppService extends Service {
// TODO: make it abstract when unhide
}
+ /**
+ * Registers App link info.
+ * @hide
+ */
+ public void onAppLinkInfo(Bundle appLinkInfo) {
+ // TODO: make it abstract when unhide
+ }
+
+ /**
+ * Sends App link info.
+ * @hide
+ */
+ public void onAppLinkCommand(Bundle command) {
+ // TODO: make it abstract when unhide
+ }
+
/**
* Returns a concrete implementation of {@link Session}.
@@ -416,6 +468,31 @@ public abstract class TvIAppService extends Service {
});
}
+ /**
+ * requests a specific command to be processed by the related TV input.
+ * @param cmdType type of the specific command
+ * @param parameters parameters of the specific command
+ */
+ public void requestCommand(@IAppServiceCommandType String cmdType, Bundle parameters) {
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) {
+ Log.d(TAG, "requestCommand (cmdType=" + cmdType + ", parameters="
+ + parameters.toString() + ")");
+ }
+ if (mSessionCallback != null) {
+ mSessionCallback.onCommandRequest(cmdType, parameters);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "error in requestCommand", e);
+ }
+ }
+ });
+ }
+
void startIApp() {
onStartIApp();
}
diff --git a/media/java/android/media/tv/interactive/TvIAppView.java b/media/java/android/media/tv/interactive/TvIAppView.java
index 803198162d30..efbe9e3bcf4b 100644
--- a/media/java/android/media/tv/interactive/TvIAppView.java
+++ b/media/java/android/media/tv/interactive/TvIAppView.java
@@ -26,6 +26,7 @@ import android.media.tv.TvInputManager;
import android.media.tv.TvView;
import android.media.tv.interactive.TvIAppManager.Session;
import android.media.tv.interactive.TvIAppManager.SessionCallback;
+import android.os.Bundle;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
@@ -53,6 +54,7 @@ public class TvIAppView extends ViewGroup {
private final Handler mHandler = new Handler();
private Session mSession;
private MySessionCallback mSessionCallback;
+ private TvIAppCallback mCallback;
private SurfaceView mSurfaceView;
private Surface mSurface;
@@ -123,6 +125,16 @@ public class TvIAppView extends ViewGroup {
mTvIAppManager = (TvIAppManager) getContext().getSystemService("tv_interactive_app");
}
+ /**
+ * Sets the callback to be invoked when an event is dispatched to this TvIAppView.
+ *
+ * @param callback The callback to receive events. A value of {@code null} removes the existing
+ * callback.
+ */
+ public void setCallback(@Nullable TvIAppCallback callback) {
+ mCallback = callback;
+ }
+
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
@@ -322,6 +334,23 @@ public class TvIAppView extends ViewGroup {
return UNSET_TVVIEW_SUCCESS;
}
+ /**
+ * Callback used to receive various status updates on the {@link TvIAppView}.
+ */
+ public abstract static class TvIAppCallback {
+
+ /**
+ * This is called when a command is requested to be processed by the related TV input.
+ *
+ * @param iAppServiceId The ID of the TV interactive app service bound to this view.
+ * @param cmdType type of the command
+ * @param parameters parameters of the command
+ */
+ public void onCommandRequest(String iAppServiceId,
+ @TvIAppService.IAppServiceCommandType String cmdType, Bundle parameters) {
+ }
+ }
+
private class MySessionCallback extends SessionCallback {
final String mIAppServiceId;
int mType;
@@ -395,5 +424,21 @@ public class TvIAppView extends ViewGroup {
mUseRequestedSurfaceLayout = true;
requestLayout();
}
+
+ @Override
+ public void onCommandRequest(Session session,
+ @TvIAppService.IAppServiceCommandType String cmdType, Bundle parameters) {
+ if (DEBUG) {
+ Log.d(TAG, "onCommandRequest (cmdType=" + cmdType + ", parameters="
+ + parameters.toString() + ")");
+ }
+ if (this != mSessionCallback) {
+ Log.w(TAG, "onCommandRequest - session not created");
+ return;
+ }
+ if (mCallback != null) {
+ mCallback.onCommandRequest(mIAppServiceId, cmdType, parameters);
+ }
+ }
}
}
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 94de7fa17ae7..255b391b2259 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -285,7 +285,7 @@ public class Tuner implements AutoCloseable {
@Nullable
private FrontendInfo mFrontendInfo;
private Integer mFrontendHandle;
- private Boolean mIsSharedFrontend = false;
+ private Tuner mFeOwnerTuner = null;
private int mFrontendType = FrontendSettings.TYPE_UNDEFINED;
private int mUserId;
private Lnb mLnb;
@@ -442,11 +442,10 @@ public class Tuner implements AutoCloseable {
mFrontendLock.lock();
try {
mTunerResourceManager.shareFrontend(mClientId, tuner.mClientId);
- synchronized (mIsSharedFrontend) {
- mFrontendHandle = tuner.mFrontendHandle;
- mFrontend = tuner.mFrontend;
- mIsSharedFrontend = true;
- }
+ mFeOwnerTuner = tuner;
+ mFeOwnerTuner.registerFrontendCallbackListener(this);
+ mFrontendHandle = mFeOwnerTuner.mFrontendHandle;
+ mFrontend = mFeOwnerTuner.mFrontend;
nativeShareFrontend(mFrontend.mId);
} finally {
releaseTRMSLock();
@@ -513,6 +512,27 @@ public class Tuner implements AutoCloseable {
private long mNativeContext; // used by native jMediaTuner
/**
+ * Registers a tuner as a listener for frontend callbacks.
+ */
+ private void registerFrontendCallbackListener(Tuner tuner) {
+ nativeRegisterFeCbListener(tuner.getNativeContext());
+ }
+
+ /**
+ * Unregisters a tuner as a listener for frontend callbacks.
+ */
+ private void unregisterFrontendCallbackListener(Tuner tuner) {
+ nativeUnregisterFeCbListener(tuner.getNativeContext());
+ }
+
+ /**
+ * Returns the pointer to the associated JTuner.
+ */
+ long getNativeContext() {
+ return mNativeContext;
+ }
+
+ /**
* Releases the Tuner instance.
*/
@Override
@@ -526,19 +546,21 @@ public class Tuner implements AutoCloseable {
}
}
- private void releaseAll() {
+ private void releaseFrontend() {
mFrontendLock.lock();
try {
if (mFrontendHandle != null) {
- synchronized (mIsSharedFrontend) {
- if (!mIsSharedFrontend) {
- int res = nativeCloseFrontend(mFrontendHandle);
- if (res != Tuner.RESULT_SUCCESS) {
- TunerUtils.throwExceptionForResult(res, "failed to close frontend");
- }
- mTunerResourceManager.releaseFrontend(mFrontendHandle, mClientId);
+ if (mFeOwnerTuner != null) {
+ // unregister self from the Frontend callback
+ mFeOwnerTuner.unregisterFrontendCallbackListener(this);
+ mFeOwnerTuner = null;
+ } else {
+ // close resource as owner
+ int res = nativeCloseFrontend(mFrontendHandle);
+ if (res != Tuner.RESULT_SUCCESS) {
+ TunerUtils.throwExceptionForResult(res, "failed to close frontend");
}
- mIsSharedFrontend = false;
+ mTunerResourceManager.releaseFrontend(mFrontendHandle, mClientId);
}
FrameworkStatsLog
.write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId,
@@ -549,9 +571,14 @@ public class Tuner implements AutoCloseable {
} finally {
mFrontendLock.unlock();
}
+ }
+
+ private void releaseAll() {
+ releaseFrontend();
mLnbLock.lock();
try {
+ // mLnb will be non-null only for owner tuner
if (mLnb != null) {
mLnb.close();
}
@@ -641,6 +668,8 @@ public class Tuner implements AutoCloseable {
*/
private native Frontend nativeOpenFrontendByHandle(int handle);
private native int nativeShareFrontend(int id);
+ private native void nativeRegisterFeCbListener(long nativeContext);
+ private native void nativeUnregisterFeCbListener(long nativeContext);
@Result
private native int nativeTune(int type, FrontendSettings settings);
private native int nativeStopTune();
@@ -1276,7 +1305,7 @@ public class Tuner implements AutoCloseable {
}
private void onFrontendEvent(int eventType) {
- Log.d(TAG, "Got event from tuning. Event type: " + eventType);
+ Log.d(TAG, "Got event from tuning. Event type: " + eventType + " for " + this);
synchronized (mOnTuneEventLock) {
if (mOnTuneEventExecutor != null && mOnTuneEventListener != null) {
mOnTuneEventExecutor.execute(() -> {
diff --git a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
index cfd85834048c..6f3ab032a75c 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
@@ -98,6 +98,7 @@ public class DvrPlayback implements AutoCloseable {
private native void nativeSetFileDescriptor(int fd);
private native long nativeRead(long size);
private native long nativeRead(byte[] bytes, long offset, long size);
+ private native long nativeSeek(long pos);
private DvrPlayback() {
mUserId = Process.myUid();
@@ -243,7 +244,7 @@ public class DvrPlayback implements AutoCloseable {
*
* @param fd the file descriptor to read data.
* @see #read(long)
- * @see #read(byte[], long, long)
+ * @see #seek(long)
*/
public void setFileDescriptor(@NonNull ParcelFileDescriptor fd) {
nativeSetFileDescriptor(fd.getFd());
@@ -261,19 +262,30 @@ public class DvrPlayback implements AutoCloseable {
}
/**
- * Reads data from the buffer for DVR playback and copies to the given byte array.
+ * Reads data from the buffer for DVR playback.
*
- * @param bytes the byte array to store the data.
- * @param offset the index of the first byte in {@code bytes} to copy to.
+ * @param buffer the byte array where DVR reads data from.
+ * @param offset the index of the first byte in {@code buffer} to read.
* @param size the maximum number of bytes to read.
* @return the number of bytes read.
*/
@BytesLong
- public long read(@NonNull byte[] bytes, @BytesLong long offset, @BytesLong long size) {
- if (size + offset > bytes.length) {
+ public long read(@NonNull byte[] buffer, @BytesLong long offset, @BytesLong long size) {
+ if (size + offset > buffer.length) {
throw new ArrayIndexOutOfBoundsException(
- "Array length=" + bytes.length + ", offset=" + offset + ", size=" + size);
+ "Array length=" + buffer.length + ", offset=" + offset + ", size=" + size);
}
- return nativeRead(bytes, offset, size);
+ return nativeRead(buffer, offset, size);
+ }
+
+ /**
+ * Sets the file pointer offset of the file descriptor.
+ *
+ * @param pos the offset position, measured in bytes from the beginning of the file.
+ * @return the new offset position.
+ */
+ @BytesLong
+ public long seek(@BytesLong long pos) {
+ return nativeSeek(pos);
}
}
diff --git a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
index 212a71343a49..e72026aab992 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
@@ -216,7 +216,6 @@ public class DvrRecorder implements AutoCloseable {
*
* @param fd the file descriptor to write data.
* @see #write(long)
- * @see #write(byte[], long, long)
*/
public void setFileDescriptor(@NonNull ParcelFileDescriptor fd) {
nativeSetFileDescriptor(fd.getFd());
@@ -236,17 +235,17 @@ public class DvrRecorder implements AutoCloseable {
/**
* Writes recording data to buffer.
*
- * @param bytes the byte array stores the data to be written to DVR.
- * @param offset the index of the first byte in {@code bytes} to be written to DVR.
+ * @param buffer the byte array stores the data from DVR.
+ * @param offset the index of the first byte in {@code buffer} to write the data from DVR.
* @param size the maximum number of bytes to write.
* @return the number of bytes written.
*/
@BytesLong
- public long write(@NonNull byte[] bytes, @BytesLong long offset, @BytesLong long size) {
- if (size + offset > bytes.length) {
+ public long write(@NonNull byte[] buffer, @BytesLong long offset, @BytesLong long size) {
+ if (size + offset > buffer.length) {
throw new ArrayIndexOutOfBoundsException(
- "Array length=" + bytes.length + ", offset=" + offset + ", size=" + size);
+ "Array length=" + buffer.length + ", offset=" + offset + ", size=" + size);
}
- return nativeWrite(bytes, offset, size);
+ return nativeWrite(buffer, offset, size);
}
}
diff --git a/media/java/android/media/tv/tuner/filter/DownloadEvent.java b/media/java/android/media/tv/tuner/filter/DownloadEvent.java
index 394211be646b..25989db2fb97 100644
--- a/media/java/android/media/tv/tuner/filter/DownloadEvent.java
+++ b/media/java/android/media/tv/tuner/filter/DownloadEvent.java
@@ -27,15 +27,17 @@ import android.annotation.SystemApi;
@SystemApi
public class DownloadEvent extends FilterEvent {
private final int mItemId;
+ private final int mDownloadId;
private final int mMpuSequenceNumber;
private final int mItemFragmentIndex;
private final int mLastItemFragmentIndex;
private final int mDataLength;
// This constructor is used by JNI code only
- private DownloadEvent(int itemId, int mpuSequenceNumber, int itemFragmentIndex,
+ private DownloadEvent(int itemId, int downloadId, int mpuSequenceNumber, int itemFragmentIndex,
int lastItemFragmentIndex, int dataLength) {
mItemId = itemId;
+ mDownloadId = downloadId;
mMpuSequenceNumber = mpuSequenceNumber;
mItemFragmentIndex = itemFragmentIndex;
mLastItemFragmentIndex = lastItemFragmentIndex;
@@ -50,6 +52,15 @@ public class DownloadEvent extends FilterEvent {
}
/**
+ * Gets download ID.
+ *
+ * <p>This query is only supported in Tuner 2.0 or higher version. Unsupported version will
+ * return {@code -1}.
+ * Use {@link TunerVersionChecker#getTunerVersion()} to get the version information.
+ */
+ public int getDownloadId() { return mDownloadId; }
+
+ /**
* Gets MPU sequence number of filtered data.
*/
@IntRange(from = 0)
@@ -80,4 +91,3 @@ public class DownloadEvent extends FilterEvent {
return mDataLength;
}
}
-
diff --git a/media/java/android/media/tv/tuner/filter/DownloadSettings.java b/media/java/android/media/tv/tuner/filter/DownloadSettings.java
index 7ba923e23c70..e2cfd7c173a2 100644
--- a/media/java/android/media/tv/tuner/filter/DownloadSettings.java
+++ b/media/java/android/media/tv/tuner/filter/DownloadSettings.java
@@ -19,6 +19,7 @@ package android.media.tv.tuner.filter;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.media.tv.tuner.TunerUtils;
+import android.media.tv.tuner.TunerVersionChecker;
/**
* Filter Settings for a Download.
@@ -27,10 +28,12 @@ import android.media.tv.tuner.TunerUtils;
*/
@SystemApi
public class DownloadSettings extends Settings {
+ private final boolean mUseDownloadId;
private final int mDownloadId;
- private DownloadSettings(int mainType, int downloadId) {
+ private DownloadSettings(int mainType, boolean useDownloadId, int downloadId) {
super(TunerUtils.getFilterSubtype(mainType, Filter.SUBTYPE_DOWNLOAD));
+ mUseDownloadId = useDownloadId;
mDownloadId = downloadId;
}
@@ -42,6 +45,15 @@ public class DownloadSettings extends Settings {
}
/**
+ * Gets whether download ID is used.
+ *
+ * <p>This query is only supported in Tuner 2.0 or higher version. Unsupported version will
+ * return {@code false}.
+ * Use {@link TunerVersionChecker#getTunerVersion()} to get the version information.
+ */
+ public boolean useDownloadId() { return mUseDownloadId; }
+
+ /**
* Creates a builder for {@link DownloadSettings}.
*
* @param mainType the filter main type.
@@ -56,6 +68,7 @@ public class DownloadSettings extends Settings {
*/
public static class Builder {
private final int mMainType;
+ private boolean mUseDownloadId = false;
private int mDownloadId;
private Builder(int mainType) {
@@ -63,6 +76,24 @@ public class DownloadSettings extends Settings {
}
/**
+ * Sets whether download ID is used or not.
+ *
+ * <p>This configuration is only supported in Tuner 2.0 or higher version. Unsupported
+ * version will cause no-op. Use {@link TunerVersionChecker#getTunerVersion()} to get the
+ * version information.
+ *
+ * <p>Default value is {@code false}.
+ */
+ @NonNull
+ public Builder setUseDownloadId(boolean useDownloadId) {
+ if (TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_2_0, "setUseDownloadId")) {
+ mUseDownloadId = useDownloadId;
+ }
+ return this;
+ }
+
+ /**
* Sets download ID.
*/
@NonNull
@@ -76,7 +107,7 @@ public class DownloadSettings extends Settings {
*/
@NonNull
public DownloadSettings build() {
- return new DownloadSettings(mMainType, mDownloadId);
+ return new DownloadSettings(mMainType, mUseDownloadId, mDownloadId);
}
}
}
diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
index f0f576e90d91..5d71a13bf701 100644
--- a/media/java/android/media/tv/tuner/filter/Filter.java
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -159,7 +159,8 @@ public class Filter implements AutoCloseable {
public @interface Status {}
/**
- * The status of a filter that the data in the filter buffer is ready to be read.
+ * The status of a filter that the data in the filter buffer is ready to be read. It can also be
+ * used to know the STC (System Time Clock) ready status if it's PCR filter.
*/
public static final int STATUS_DATA_READY = DemuxFilterStatus.DATA_READY;
/**
diff --git a/media/java/android/media/tv/tuner/filter/MediaEvent.java b/media/java/android/media/tv/tuner/filter/MediaEvent.java
index dbd85e9997d8..79d4062cfbbf 100644
--- a/media/java/android/media/tv/tuner/filter/MediaEvent.java
+++ b/media/java/android/media/tv/tuner/filter/MediaEvent.java
@@ -40,6 +40,8 @@ public class MediaEvent extends FilterEvent {
private final int mStreamId;
private final boolean mIsPtsPresent;
private final long mPts;
+ private final boolean mIsDtsPresent;
+ private final long mDts;
private final long mDataLength;
private final long mOffset;
private LinearBlock mLinearBlock;
@@ -50,12 +52,14 @@ public class MediaEvent extends FilterEvent {
private final AudioDescriptor mExtraMetaData;
// This constructor is used by JNI code only
- private MediaEvent(int streamId, boolean isPtsPresent, long pts, long dataLength, long offset,
- LinearBlock buffer, boolean isSecureMemory, long dataId, int mpuSequenceNumber,
- boolean isPrivateData, AudioDescriptor extraMetaData) {
+ private MediaEvent(int streamId, boolean isPtsPresent, long pts, boolean isDtsPresent, long dts,
+ long dataLength, long offset, LinearBlock buffer, boolean isSecureMemory, long dataId,
+ int mpuSequenceNumber, boolean isPrivateData, AudioDescriptor extraMetaData) {
mStreamId = streamId;
mIsPtsPresent = isPtsPresent;
mPts = pts;
+ mIsDtsPresent = isDtsPresent;
+ mDts = dts;
mDataLength = dataLength;
mOffset = offset;
mLinearBlock = buffer;
@@ -90,6 +94,26 @@ public class MediaEvent extends FilterEvent {
}
/**
+ * Returns whether DTS (Decode Time Stamp) is present.
+ *
+ * <p>This query is only supported in Tuner 2.0 or higher version. Unsupported version will
+ * return {@code false}.
+ * Use {@link TunerVersionChecker#getTunerVersion()} to get the version information.
+ *
+ * @return {@code true} if DTS is present in PES header; {@code false} otherwise.
+ */
+ public boolean isDtsPresent() { return mIsDtsPresent; }
+
+ /**
+ * Gets DTS (Decode Time Stamp) for audio or video frame.
+ *
+ * * <p>This query is only supported in Tuner 2.0 or higher version. Unsupported version will
+ * return {@code -1}.
+ * Use {@link TunerVersionChecker#getTunerVersion()} to get the version information.
+ */
+ public long getDts() { return mDts; }
+
+ /**
* Gets data size in bytes of audio or video frame.
*/
@BytesLong
diff --git a/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java b/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
index 58a81d99ff99..c8bb178253af 100644
--- a/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
+++ b/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
@@ -55,7 +55,7 @@ public class MmtpRecordEvent extends FilterEvent {
}
/**
- * Gets data size in bytes of filtered data.
+ * Gets the record data offset from the beginning of the record buffer.
*/
@BytesLong
public long getDataLength() {
diff --git a/media/java/android/media/tv/tuner/filter/SectionEvent.java b/media/java/android/media/tv/tuner/filter/SectionEvent.java
index ff1249296efd..182bb9463bd6 100644
--- a/media/java/android/media/tv/tuner/filter/SectionEvent.java
+++ b/media/java/android/media/tv/tuner/filter/SectionEvent.java
@@ -28,10 +28,10 @@ public class SectionEvent extends FilterEvent {
private final int mTableId;
private final int mVersion;
private final int mSectionNum;
- private final int mDataLength;
+ private final long mDataLength;
// This constructor is used by JNI code only
- private SectionEvent(int tableId, int version, int sectionNum, int dataLength) {
+ private SectionEvent(int tableId, int version, int sectionNum, long dataLength) {
mTableId = tableId;
mVersion = version;
mSectionNum = sectionNum;
@@ -61,8 +61,13 @@ public class SectionEvent extends FilterEvent {
/**
* Gets data size in bytes of filtered data.
+ *
+ * @deprecated Use {@link #getDataLengthLong()}
*/
+ @Deprecated
public int getDataLength() {
- return mDataLength;
+ return (int) getDataLengthLong();
}
+
+ public long getDataLengthLong() { return mDataLength; }
}
diff --git a/media/java/android/media/tv/tuner/filter/SectionSettings.java b/media/java/android/media/tv/tuner/filter/SectionSettings.java
index 58e22c907639..94fda30fecb1 100644
--- a/media/java/android/media/tv/tuner/filter/SectionSettings.java
+++ b/media/java/android/media/tv/tuner/filter/SectionSettings.java
@@ -45,8 +45,19 @@ public abstract class SectionSettings extends Settings {
public boolean isCrcEnabled() {
return mCrcEnabled;
}
+
/**
- * Returns whether the filter repeats the data with the same version.
+ * Returns whether the filter repeats the data.
+ *
+ * If {@code false}, for {@link SectionSettingsWithTableInfo}, HAL filters out all sections
+ * based on {@link SectionSettingsWithTableInfo} TableId and Version, and stops filtering data.
+ * For {@link SectionSettingsWithSectionBits}, HAL filters out the first section which matches
+ * the {@link SectionSettingsWithSectionBits} configuration, and stops filtering data.
+ *
+ * If {@code true}, for {@link SectionSettingsWithTableInfo}, HAL filters out all sections based
+ * on {@link SectionSettingsWithTableInfo} TableId and Version, and repeats. For
+ * {@link SectionSettingsWithSectionBits}, HAL filters out sections which match the
+ * {@link SectionSettingsWithSectionBits} configuration, and repeats.
*/
public boolean isRepeat() {
return mIsRepeat;
@@ -83,8 +94,20 @@ public abstract class SectionSettings extends Settings {
mCrcEnabled = crcEnabled;
return self();
}
+
/**
- * Sets whether the filter repeats the data with the same version.
+ * Sets whether the filter repeats the data.
+ *
+ * If {@code false}, for {@link SectionSettingsWithTableInfo}, HAL filters out all sections
+ * based on {@link SectionSettingsWithTableInfo} TableId and Version, and stops filtering
+ * data. For {@link SectionSettingsWithSectionBits}, HAL filters out the first section which
+ * matches the {@link SectionSettingsWithSectionBits} configuration, and stops filtering
+ * data.
+ *
+ * If {@code true}, for {@link SectionSettingsWithTableInfo}, HAL filters out all sections
+ * based on {@link SectionSettingsWithTableInfo} TableId and Version, and repeats. For
+ * {@link SectionSettingsWithSectionBits}, HAL filters out sections which match the
+ * {@link SectionSettingsWithSectionBits} configuration, and repeats.
*/
@NonNull
public T setRepeat(boolean isRepeat) {
diff --git a/media/java/android/media/tv/tuner/filter/TsRecordEvent.java b/media/java/android/media/tv/tuner/filter/TsRecordEvent.java
index bf2c000cb6e8..12ea22246356 100644
--- a/media/java/android/media/tv/tuner/filter/TsRecordEvent.java
+++ b/media/java/android/media/tv/tuner/filter/TsRecordEvent.java
@@ -71,7 +71,7 @@ public class TsRecordEvent extends FilterEvent {
}
/**
- * Gets data size in bytes of filtered data.
+ * Gets the record data offset from the beginning of the record buffer.
*/
@BytesLong
public long getDataLength() {
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
index 31f1a63e12ba..0527a1a5642e 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
@@ -19,10 +19,10 @@ package android.media.tv.tuner.frontend;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.media.tv.tuner.Lnb;
import android.media.tv.tuner.TunerVersionChecker;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -53,7 +53,7 @@ public class FrontendStatus {
FRONTEND_STATUS_TYPE_MODULATIONS_EXT, FRONTEND_STATUS_TYPE_ROLL_OFF,
FRONTEND_STATUS_TYPE_IS_MISO_ENABLED, FRONTEND_STATUS_TYPE_IS_LINEAR,
FRONTEND_STATUS_TYPE_IS_SHORT_FRAMES_ENABLED, FRONTEND_STATUS_TYPE_ISDBT_MODE,
- FRONTEND_STATUS_TYPE_ISDBT_PARTIAL_RECEPTION_FLAG})
+ FRONTEND_STATUS_TYPE_ISDBT_PARTIAL_RECEPTION_FLAG, FRONTEND_STATUS_TYPE_STREAM_ID_LIST})
@Retention(RetentionPolicy.SOURCE)
public @interface FrontendStatusType {}
@@ -254,6 +254,12 @@ public class FrontendStatus {
public static final int FRONTEND_STATUS_TYPE_ISDBT_PARTIAL_RECEPTION_FLAG =
android.hardware.tv.tuner.FrontendStatusType.ISDBT_PARTIAL_RECEPTION_FLAG;
+ /**
+ * Stream ID list included in a transponder.
+ */
+ public static final int FRONTEND_STATUS_TYPE_STREAM_ID_LIST =
+ android.hardware.tv.tuner.FrontendStatusType.STREAM_ID_LIST;
+
/** @hide */
@IntDef(value = {
AtscFrontendSettings.MODULATION_UNDEFINED,
@@ -493,6 +499,7 @@ public class FrontendStatus {
private Boolean mIsShortFrames;
private Integer mIsdbtMode;
private Integer mIsdbtPartialReceptionFlag;
+ private int[] mStreamIds;
// Constructed and fields set by JNI code.
private FrontendStatus() {
@@ -638,6 +645,8 @@ public class FrontendStatus {
}
/**
* Gets the current Automatic Gain Control value which is normalized from 0 to 255.
+ *
+ * Larger AGC values indicate it is applying more gain.
*/
public int getAgc() {
if (mAgc == null) {
@@ -656,6 +665,10 @@ public class FrontendStatus {
}
/**
* Gets the current Error information by layer.
+ *
+ * The order of the vectors is in ascending order of the required CNR (Contrast-to-noise ratio).
+ * The most robust layer is the first. For example, in ISDB-T, vec[0] is the information of
+ * layer A. vec[1] is the information of layer B.
*/
@NonNull
public boolean[] getLayerErrors() {
@@ -729,6 +742,10 @@ public class FrontendStatus {
*
* <p>This query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ *
+ * The order of the vectors is in ascending order of the required CNR (Contrast-to-noise ratio).
+ * The most robust layer is the first. For example, in ISDB-T, vec[0] is the information of
+ * layer A. vec[1] is the information of layer B.
*/
@NonNull
public int[] getBers() {
@@ -745,6 +762,10 @@ public class FrontendStatus {
*
* <p>This query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ *
+ * The order of the vectors is in ascending order of the required CNR (Contrast-to-noise ratio).
+ * The most robust layer is the first. For example, in ISDB-T, vec[0] is the information of
+ * layer A. vec[1] is the information of layer B.
*/
@NonNull
@FrontendSettings.InnerFec
@@ -842,6 +863,10 @@ public class FrontendStatus {
*
* <p>This query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ *
+ * The order of the vectors is in ascending order of the required CNR (Contrast-to-noise ratio).
+ * The most robust layer is the first. For example, in ISDB-T, vec[0] is the information of
+ * layer A. vec[1] is the information of layer B.
*/
@NonNull
@FrontendInterleaveMode
@@ -860,6 +885,10 @@ public class FrontendStatus {
*
* <p>This query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ *
+ * The order of the vectors is in ascending order of the required CNR (Contrast-to-noise ratio).
+ * The most robust layer is the first. For example, in ISDB-T, vec[0] is the information of
+ * layer A. vec[1] is the information of layer B.
*/
@NonNull
@IntRange(from = 0, to = 0xff)
@@ -893,6 +922,10 @@ public class FrontendStatus {
*
* <p>This query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ *
+ * The order of the vectors is in ascending order of the required CNR (Contrast-to-noise ratio).
+ * The most robust layer is the first. For example, in ISDB-T, vec[0] is the information of
+ * layer A. vec[1] is the information of layer B.
*/
@NonNull
@FrontendModulation
@@ -1001,6 +1034,24 @@ public class FrontendStatus {
}
/**
+ * Gets stream id list included in a transponder.
+ *
+ * <p>This query is only supported by Tuner HAL 2.0 or higher. Unsupported version or if HAL
+ * doesn't return stream id list status will throw IllegalStateException. Use
+ * {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ */
+ @SuppressLint("ArrayReturn")
+ @NonNull
+ public int[] getStreamIdList() {
+ TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_2_0, "stream id list status");
+ if (mStreamIds == null) {
+ throw new IllegalStateException("stream id list status is empty");
+ }
+ return mStreamIds;
+ }
+
+ /**
* Information of each tuning Physical Layer Pipes.
*/
public static class Atsc3PlpTuningInfo {
diff --git a/media/java/android/media/tv/tuner/frontend/ScanCallback.java b/media/java/android/media/tv/tuner/frontend/ScanCallback.java
index f61bd525beb4..cb35edb15c62 100644
--- a/media/java/android/media/tv/tuner/frontend/ScanCallback.java
+++ b/media/java/android/media/tv/tuner/frontend/ScanCallback.java
@@ -28,7 +28,12 @@ import android.annotation.SystemApi;
@SystemApi
public interface ScanCallback {
- /** Scan locked the signal. */
+ /**
+ * Scan locked the signal.
+ *
+ * It can also be notified after signal is locked if the signal attributes transmission
+ * parameter of the signal is changed (e.g., Modulation).
+ */
void onLocked();
/** Scan stopped. */
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index ded9652622a7..e91e2381bd42 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -588,13 +588,13 @@ void FilterClientCallbackImpl::getSectionEvent(jobjectArray &arr, const int size
const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/SectionEvent");
- jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IIII)V");
+ jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IIIJ)V");
const DemuxFilterSectionEvent &sectionEvent = event.get<DemuxFilterEvent::Tag::section>();
jint tableId = sectionEvent.tableId;
jint version = sectionEvent.version;
jint sectionNum = sectionEvent.sectionNum;
- jint dataLength = sectionEvent.dataLength;
+ jlong dataLength = sectionEvent.dataLength;
jobject obj = env->NewObject(eventClazz, eventInit, tableId, version, sectionNum, dataLength);
env->SetObjectArrayElement(arr, size, obj);
@@ -604,10 +604,9 @@ void FilterClientCallbackImpl::getMediaEvent(jobjectArray &arr, const int size,
const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/MediaEvent");
- jmethodID eventInit = env->GetMethodID(eventClazz,
- "<init>",
- "(IZJJJLandroid/media/MediaCodec$LinearBlock;"
- "ZJIZLandroid/media/tv/tuner/filter/AudioDescriptor;)V");
+ jmethodID eventInit = env->GetMethodID(eventClazz, "<init>",
+ "(IZJZJJJLandroid/media/MediaCodec$LinearBlock;"
+ "ZJIZLandroid/media/tv/tuner/filter/AudioDescriptor;)V");
jfieldID eventContext = env->GetFieldID(eventClazz, "mNativeContext", "J");
const DemuxFilterMediaEvent &mediaEvent = event.get<DemuxFilterEvent::Tag::media>();
@@ -633,15 +632,17 @@ void FilterClientCallbackImpl::getMediaEvent(jobjectArray &arr, const int size,
jint streamId = mediaEvent.streamId;
jboolean isPtsPresent = mediaEvent.isPtsPresent;
jlong pts = mediaEvent.pts;
+ jboolean isDtsPresent = mediaEvent.isDtsPresent;
+ jlong dts = mediaEvent.dts;
jlong offset = mediaEvent.offset;
jboolean isSecureMemory = mediaEvent.isSecureMemory;
jlong avDataId = mediaEvent.avDataId;
jint mpuSequenceNumber = mediaEvent.mpuSequenceNumber;
jboolean isPesPrivateData = mediaEvent.isPesPrivateData;
- jobject obj = env->NewObject(eventClazz, eventInit, streamId, isPtsPresent, pts, dataLength,
- offset, nullptr, isSecureMemory, avDataId, mpuSequenceNumber,
- isPesPrivateData, audioDescriptor);
+ jobject obj = env->NewObject(eventClazz, eventInit, streamId, isPtsPresent, pts, isDtsPresent,
+ dts, dataLength, offset, nullptr, isSecureMemory, avDataId,
+ mpuSequenceNumber, isPesPrivateData, audioDescriptor);
uint64_t avSharedMemSize = mFilterClient->getAvSharedHandleInfo().size;
if (mediaEvent.avMemory.fds.size() > 0 || mediaEvent.avDataId != 0 ||
@@ -733,16 +734,17 @@ void FilterClientCallbackImpl::getDownloadEvent(jobjectArray &arr, const int siz
const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/DownloadEvent");
- jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IIIII)V");
+ jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IIIIII)V");
const DemuxFilterDownloadEvent &downloadEvent = event.get<DemuxFilterEvent::Tag::download>();
jint itemId = downloadEvent.itemId;
+ jint downloadId = downloadEvent.downloadId;
jint mpuSequenceNumber = downloadEvent.mpuSequenceNumber;
jint itemFragmentIndex = downloadEvent.itemFragmentIndex;
jint lastItemFragmentIndex = downloadEvent.lastItemFragmentIndex;
jint dataLength = downloadEvent.dataLength;
- jobject obj = env->NewObject(eventClazz, eventInit, itemId, mpuSequenceNumber,
+ jobject obj = env->NewObject(eventClazz, eventInit, itemId, downloadId, mpuSequenceNumber,
itemFragmentIndex, lastItemFragmentIndex, dataLength);
env->SetObjectArrayElement(arr, size, obj);
}
@@ -951,20 +953,45 @@ FilterClientCallbackImpl::~FilterClientCallbackImpl() {
}
/////////////// FrontendClientCallbackImpl ///////////////////////
-FrontendClientCallbackImpl::FrontendClientCallbackImpl(jweak tunerObj) : mObject(tunerObj) {}
+FrontendClientCallbackImpl::FrontendClientCallbackImpl(JTuner* jtuner, jweak listener) {
+ ALOGV("FrontendClientCallbackImpl() with listener:%p", listener);
+ addCallbackListener(jtuner, listener);
+}
+
+void FrontendClientCallbackImpl::addCallbackListener(JTuner* jtuner, jweak listener) {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ jweak listenerRef = env->NewWeakGlobalRef(listener);
+ ALOGV("addCallbackListener() with listener:%p and ref:%p @%p", listener, listenerRef, this);
+ std::scoped_lock<std::mutex> lock(mMutex);
+ mListenersMap[jtuner] = listenerRef;
+}
+
+void FrontendClientCallbackImpl::removeCallbackListener(JTuner* listener) {
+ ALOGV("removeCallbackListener for listener:%p", listener);
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ std::scoped_lock<std::mutex> lock(mMutex);
+ if (mListenersMap.find(listener) != mListenersMap.end() && mListenersMap[listener]) {
+ env->DeleteWeakGlobalRef(mListenersMap[listener]);
+ mListenersMap.erase(listener);
+ }
+}
void FrontendClientCallbackImpl::onEvent(FrontendEventType frontendEventType) {
ALOGV("FrontendClientCallbackImpl::onEvent, type=%d", frontendEventType);
JNIEnv *env = AndroidRuntime::getJNIEnv();
- jobject frontend(env->NewLocalRef(mObject));
- if (!env->IsSameObject(frontend, nullptr)) {
- env->CallVoidMethod(
- frontend,
- gFields.onFrontendEventID,
- (jint)frontendEventType);
- } else {
- ALOGE("FrontendClientCallbackImpl::onEvent:"
- "Frontend object has been freed. Ignoring callback.");
+ std::scoped_lock<std::mutex> lock(mMutex);
+ for (const auto& mapEntry : mListenersMap) {
+ ALOGV("JTuner:%p, jweak:%p", mapEntry.first, mapEntry.second);
+ jobject frontend(env->NewLocalRef(mapEntry.second));
+ if (!env->IsSameObject(frontend, nullptr)) {
+ env->CallVoidMethod(
+ frontend,
+ gFields.onFrontendEventID,
+ (jint)frontendEventType);
+ } else {
+ ALOGW("FrontendClientCallbackImpl::onEvent:"
+ "Frontend object has been freed. Ignoring callback.");
+ }
}
}
@@ -973,12 +1000,25 @@ void FrontendClientCallbackImpl::onScanMessage(
ALOGV("FrontendClientCallbackImpl::onScanMessage, type=%d", type);
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass clazz = env->FindClass("android/media/tv/tuner/Tuner");
- jobject frontend(env->NewLocalRef(mObject));
- if (env->IsSameObject(frontend, nullptr)) {
- ALOGE("FrontendClientCallbackImpl::onScanMessage:"
- "Frontend object has been freed. Ignoring callback.");
- return;
+
+ std::scoped_lock<std::mutex> lock(mMutex);
+ for (const auto& mapEntry : mListenersMap) {
+ jobject frontend(env->NewLocalRef(mapEntry.second));
+ if (env->IsSameObject(frontend, nullptr)) {
+ ALOGE("FrontendClientCallbackImpl::onScanMessage:"
+ "Tuner object has been freed. Ignoring callback.");
+ continue;
+ }
+ executeOnScanMessage(env, clazz, frontend, type, message);
}
+}
+
+void FrontendClientCallbackImpl::executeOnScanMessage(
+ JNIEnv *env, const jclass& clazz, const jobject& frontend,
+ FrontendScanMessageType type,
+ const FrontendScanMessage& message) {
+ ALOGV("FrontendClientCallbackImpl::executeOnScanMessage, type=%d", type);
+
switch(type) {
case FrontendScanMessageType::LOCKED: {
if (message.get<FrontendScanMessage::Tag::isLocked>()) {
@@ -1157,11 +1197,14 @@ void FrontendClientCallbackImpl::onScanMessage(
}
FrontendClientCallbackImpl::~FrontendClientCallbackImpl() {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
- if (mObject != nullptr) {
- env->DeleteWeakGlobalRef(mObject);
- mObject = nullptr;
+ JNIEnv *env = android::AndroidRuntime::getJNIEnv();
+ ALOGV("~FrontendClientCallbackImpl()");
+ std::scoped_lock<std::mutex> lock(mMutex);
+ for (const auto& mapEntry : mListenersMap) {
+ ALOGV("deleteRef :%p at @ %p", mapEntry.second, this);
+ env->DeleteWeakGlobalRef(mapEntry.second);
}
+ mListenersMap.clear();
}
/////////////// Tuner ///////////////////////
@@ -1180,6 +1223,10 @@ JTuner::JTuner(JNIEnv *env, jobject thiz) : mClass(nullptr) {
mSharedFeId = (int)Constant::INVALID_FRONTEND_ID;
}
+jweak JTuner::getObject() {
+ return mObject;
+}
+
JTuner::~JTuner() {
if (mFeClient != nullptr) {
mFeClient->close();
@@ -1192,6 +1239,7 @@ JTuner::~JTuner() {
env->DeleteWeakGlobalRef(mObject);
env->DeleteGlobalRef(mClass);
mFeClient = nullptr;
+ mFeClientCb = nullptr;
mDemuxClient = nullptr;
mClass = nullptr;
mObject = nullptr;
@@ -1247,9 +1295,8 @@ jobject JTuner::openFrontendByHandle(int feHandle) {
return nullptr;
}
- sp<FrontendClientCallbackImpl> feClientCb =
- new FrontendClientCallbackImpl(env->NewWeakGlobalRef(mObject));
- mFeClient->setCallback(feClientCb);
+ mFeClientCb = new FrontendClientCallbackImpl(this, mObject);
+ mFeClient->setCallback(mFeClientCb);
// TODO: add more fields to frontend
return env->NewObject(
env->FindClass("android/media/tv/tuner/Tuner$Frontend"),
@@ -1269,6 +1316,18 @@ int JTuner::shareFrontend(int feId) {
return (int)Result::SUCCESS;
}
+void JTuner::registerFeCbListener(JTuner* jtuner) {
+ if (mFeClientCb != nullptr && jtuner != nullptr) {
+ mFeClientCb->addCallbackListener(jtuner, jtuner->getObject());
+ }
+}
+
+void JTuner::unregisterFeCbListener(JTuner* jtuner) {
+ if (mFeClientCb != nullptr && jtuner != nullptr) {
+ mFeClientCb->removeCallbackListener(jtuner);
+ }
+}
+
jobject JTuner::getAnalogFrontendCaps(JNIEnv *env, FrontendCapabilities &caps) {
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/AnalogFrontendCapabilities");
jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(II)V");
@@ -2450,7 +2509,14 @@ jobject JTuner::getFrontendStatus(jintArray types) {
env->SetObjectField(statusObj, field, newIntegerObj);
break;
}
- default: {
+ case FrontendStatus::Tag::streamIdList: {
+ jfieldID field = env->GetFieldID(clazz, "mStreamIds", "[I");
+ std::vector<int32_t> ids = s.get<FrontendStatus::Tag::streamIdList>();
+
+ jintArray valObj = env->NewIntArray(v.size());
+ env->SetIntArrayRegion(valObj, 0, v.size(), reinterpret_cast<jint *>(&ids[0]));
+
+ env->SetObjectField(statusObj, field, valObj);
break;
}
}
@@ -3188,6 +3254,20 @@ static int android_media_tv_Tuner_share_frontend(
return tuner->shareFrontend(id);
}
+static void android_media_tv_Tuner_register_fe_cb_listener(
+ JNIEnv *env, jobject thiz, jlong shareeJTuner) {
+ sp<JTuner> tuner = getTuner(env, thiz);
+ JTuner *jtuner = (JTuner *)shareeJTuner;
+ tuner->registerFeCbListener(jtuner);
+}
+
+static void android_media_tv_Tuner_unregister_fe_cb_listener(
+ JNIEnv *env, jobject thiz, jlong shareeJTuner) {
+ sp<JTuner> tuner = getTuner(env, thiz);
+ JTuner *jtuner = (JTuner *)shareeJTuner;
+ tuner->unregisterFeCbListener(jtuner);
+}
+
static int android_media_tv_Tuner_tune(JNIEnv *env, jobject thiz, jint type, jobject settings) {
sp<JTuner> tuner = getTuner(env, thiz);
FrontendSettings setting = getFrontendSettings(env, type, settings);
@@ -3469,10 +3549,13 @@ static DemuxFilterRecordSettings getFilterRecordSettings(JNIEnv *env, const jobj
static DemuxFilterDownloadSettings getFilterDownloadSettings(JNIEnv *env, const jobject& settings) {
jclass clazz = env->FindClass("android/media/tv/tuner/filter/DownloadSettings");
+ bool useDownloadId =
+ env->GetBooleanField(settings, env->GetFieldID(clazz, "mUseDownloadId", "Z"));
int32_t downloadId = env->GetIntField(settings, env->GetFieldID(clazz, "mDownloadId", "I"));
- DemuxFilterDownloadSettings filterDownloadSettings {
- .downloadId = downloadId,
+ DemuxFilterDownloadSettings filterDownloadSettings{
+ .useDownloadId = useDownloadId,
+ .downloadId = downloadId,
};
return filterDownloadSettings;
}
@@ -4253,6 +4336,17 @@ static jlong android_media_tv_Tuner_read_dvr(JNIEnv *env, jobject dvr, jlong siz
return (jlong)dvrClient->readFromFile(size);
}
+static jlong android_media_tv_Tuner_seek_dvr(JNIEnv *env, jobject dvr, jlong pos) {
+ sp<DvrClient> dvrClient = getDvrClient(env, dvr);
+ if (dvrClient == nullptr) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Failed to seek dvr: dvr client not found");
+ return -1;
+ }
+
+ return (jlong)dvrClient->seekFile(pos);
+}
+
static jlong android_media_tv_Tuner_read_dvr_from_array(
JNIEnv* env, jobject dvr, jbyteArray buffer, jlong offset, jlong size) {
sp<DvrClient> dvrClient = getDvrClient(env, dvr);
@@ -4361,6 +4455,10 @@ static const JNINativeMethod gTunerMethods[] = {
(void *)android_media_tv_Tuner_open_frontend_by_handle },
{ "nativeShareFrontend", "(I)I",
(void *)android_media_tv_Tuner_share_frontend },
+ { "nativeRegisterFeCbListener", "(J)V",
+ (void*)android_media_tv_Tuner_register_fe_cb_listener },
+ { "nativeUnregisterFeCbListener", "(J)V",
+ (void*)android_media_tv_Tuner_unregister_fe_cb_listener },
{ "nativeTune", "(ILandroid/media/tv/tuner/frontend/FrontendSettings;)I",
(void *)android_media_tv_Tuner_tune },
{ "nativeStopTune", "()I", (void *)android_media_tv_Tuner_stop_tune },
@@ -4403,38 +4501,37 @@ static const JNINativeMethod gTunerMethods[] = {
{ "nativeClose", "()I", (void *)android_media_tv_Tuner_close_tuner },
{ "nativeCloseFrontend", "(I)I", (void *)android_media_tv_Tuner_close_frontend },
{ "nativeCloseDemux", "(I)I", (void *)android_media_tv_Tuner_close_demux },
- {"nativeOpenSharedFilter",
+ { "nativeOpenSharedFilter",
"(Ljava/lang/String;)Landroid/media/tv/tuner/filter/SharedFilter;",
(void *)android_media_tv_Tuner_open_shared_filter},
};
static const JNINativeMethod gFilterMethods[] = {
{ "nativeConfigureFilter", "(IILandroid/media/tv/tuner/filter/FilterConfiguration;)I",
- (void *)android_media_tv_Tuner_configure_filter },
- { "nativeGetId", "()I", (void *)android_media_tv_Tuner_get_filter_id },
- { "nativeGetId64Bit", "()J",
- (void *)android_media_tv_Tuner_get_filter_64bit_id },
+ (void *)android_media_tv_Tuner_configure_filter},
+ { "nativeGetId", "()I", (void *)android_media_tv_Tuner_get_filter_id},
+ { "nativeGetId64Bit", "()J", (void *)android_media_tv_Tuner_get_filter_64bit_id},
{ "nativeConfigureMonitorEvent", "(I)I",
- (void *)android_media_tv_Tuner_configure_monitor_event },
+ (void *)android_media_tv_Tuner_configure_monitor_event},
{ "nativeSetDataSource", "(Landroid/media/tv/tuner/filter/Filter;)I",
- (void *)android_media_tv_Tuner_set_filter_data_source },
- { "nativeStartFilter", "()I", (void *)android_media_tv_Tuner_start_filter },
- { "nativeStopFilter", "()I", (void *)android_media_tv_Tuner_stop_filter },
- { "nativeFlushFilter", "()I", (void *)android_media_tv_Tuner_flush_filter },
- { "nativeRead", "([BJJ)I", (void *)android_media_tv_Tuner_read_filter_fmq },
- { "nativeClose", "()I", (void *)android_media_tv_Tuner_close_filter },
- {"nativeAcquireSharedFilterToken", "()Ljava/lang/String;",
+ (void *)android_media_tv_Tuner_set_filter_data_source},
+ { "nativeStartFilter", "()I", (void *)android_media_tv_Tuner_start_filter},
+ { "nativeStopFilter", "()I", (void *)android_media_tv_Tuner_stop_filter},
+ { "nativeFlushFilter", "()I", (void *)android_media_tv_Tuner_flush_filter},
+ { "nativeRead", "([BJJ)I", (void *)android_media_tv_Tuner_read_filter_fmq},
+ { "nativeClose", "()I", (void *)android_media_tv_Tuner_close_filter},
+ { "nativeAcquireSharedFilterToken", "()Ljava/lang/String;",
(void *)android_media_tv_Tuner_acquire_shared_filter_token},
- {"nativeFreeSharedFilterToken", "(Ljava/lang/String;)V",
+ { "nativeFreeSharedFilterToken", "(Ljava/lang/String;)V",
(void *)android_media_tv_Tuner_free_shared_filter_token},
};
static const JNINativeMethod gSharedFilterMethods[] = {
- {"nativeStartSharedFilter", "()I", (void *)android_media_tv_Tuner_start_filter},
- {"nativeStopSharedFilter", "()I", (void *)android_media_tv_Tuner_stop_filter},
- {"nativeFlushSharedFilter", "()I", (void *)android_media_tv_Tuner_flush_filter},
- {"nativeSharedRead", "([BJJ)I", (void *)android_media_tv_Tuner_read_filter_fmq},
- {"nativeSharedClose", "()I", (void *)android_media_tv_Tuner_close_filter},
+ { "nativeStartSharedFilter", "()I", (void *)android_media_tv_Tuner_start_filter},
+ { "nativeStopSharedFilter", "()I", (void *)android_media_tv_Tuner_stop_filter},
+ { "nativeFlushSharedFilter", "()I", (void *)android_media_tv_Tuner_flush_filter},
+ { "nativeSharedRead", "([BJJ)I", (void *)android_media_tv_Tuner_read_filter_fmq},
+ { "nativeSharedClose", "()I", (void *)android_media_tv_Tuner_close_filter},
};
static const JNINativeMethod gTimeFilterMethods[] = {
@@ -4474,18 +4571,19 @@ static const JNINativeMethod gDvrRecorderMethods[] = {
static const JNINativeMethod gDvrPlaybackMethods[] = {
{ "nativeAttachFilter", "(Landroid/media/tv/tuner/filter/Filter;)I",
- (void *)android_media_tv_Tuner_attach_filter },
+ (void *)android_media_tv_Tuner_attach_filter},
{ "nativeDetachFilter", "(Landroid/media/tv/tuner/filter/Filter;)I",
- (void *)android_media_tv_Tuner_detach_filter },
+ (void *)android_media_tv_Tuner_detach_filter},
{ "nativeConfigureDvr", "(Landroid/media/tv/tuner/dvr/DvrSettings;)I",
- (void *)android_media_tv_Tuner_configure_dvr },
- { "nativeStartDvr", "()I", (void *)android_media_tv_Tuner_start_dvr },
- { "nativeStopDvr", "()I", (void *)android_media_tv_Tuner_stop_dvr },
- { "nativeFlushDvr", "()I", (void *)android_media_tv_Tuner_flush_dvr },
- { "nativeClose", "()I", (void *)android_media_tv_Tuner_close_dvr },
- { "nativeSetFileDescriptor", "(I)V", (void *)android_media_tv_Tuner_dvr_set_fd },
- { "nativeRead", "(J)J", (void *)android_media_tv_Tuner_read_dvr },
- { "nativeRead", "([BJJ)J", (void *)android_media_tv_Tuner_read_dvr_from_array },
+ (void *)android_media_tv_Tuner_configure_dvr},
+ { "nativeStartDvr", "()I", (void *)android_media_tv_Tuner_start_dvr},
+ { "nativeStopDvr", "()I", (void *)android_media_tv_Tuner_stop_dvr},
+ { "nativeFlushDvr", "()I", (void *)android_media_tv_Tuner_flush_dvr},
+ { "nativeClose", "()I", (void *)android_media_tv_Tuner_close_dvr},
+ { "nativeSetFileDescriptor", "(I)V", (void *)android_media_tv_Tuner_dvr_set_fd},
+ { "nativeRead", "(J)J", (void *)android_media_tv_Tuner_read_dvr},
+ { "nativeRead", "([BJJ)J", (void *)android_media_tv_Tuner_read_dvr_from_array},
+ { "nativeSeek", "(J)J", (void *)android_media_tv_Tuner_seek_dvr},
};
static const JNINativeMethod gLnbMethods[] = {
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index 31d24ee13cf7..06e249212444 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -149,14 +149,21 @@ private:
void getRestartEvent(jobjectArray& arr, const int size, const DemuxFilterEvent& event);
};
+struct JTuner;
struct FrontendClientCallbackImpl : public FrontendClientCallback {
- FrontendClientCallbackImpl(jweak tunerObj);
+ FrontendClientCallbackImpl(JTuner*, jweak);
~FrontendClientCallbackImpl();
virtual void onEvent(FrontendEventType frontendEventType);
virtual void onScanMessage(
FrontendScanMessageType type, const FrontendScanMessage& message);
- jweak mObject;
+ void executeOnScanMessage(JNIEnv *env, const jclass& clazz, const jobject& frontend,
+ FrontendScanMessageType type,
+ const FrontendScanMessage& message);
+ void addCallbackListener(JTuner*, jweak obj);
+ void removeCallbackListener(JTuner* jtuner);
+ std::unordered_map<JTuner*, jweak> mListenersMap;
+ std::mutex mMutex;
};
struct JTuner : public RefBase {
@@ -171,6 +178,8 @@ struct JTuner : public RefBase {
jobject getFrontendIds();
jobject openFrontendByHandle(int feHandle);
int shareFrontend(int feId);
+ void registerFeCbListener(JTuner* jtuner);
+ void unregisterFeCbListener(JTuner* jtuner);
jint closeFrontendById(int id);
jobject getFrontendInfo(int id);
int tune(const FrontendSettings& settings);
@@ -192,6 +201,8 @@ struct JTuner : public RefBase {
jint closeFrontend();
jint closeDemux();
+ jweak getObject();
+
protected:
virtual ~JTuner();
@@ -200,6 +211,7 @@ private:
jweak mObject;
static sp<TunerClient> mTunerClient;
sp<FrontendClient> mFeClient;
+ sp<FrontendClientCallbackImpl> mFeClientCb;
int mFeId;
int mSharedFeId;
sp<LnbClient> mLnbClient;
diff --git a/media/jni/soundpool/Stream.cpp b/media/jni/soundpool/Stream.cpp
index bbbef3853b23..773cdc982af7 100644
--- a/media/jni/soundpool/Stream.cpp
+++ b/media/jni/soundpool/Stream.cpp
@@ -228,10 +228,9 @@ Stream* Stream::getPairStream() const
return mStreamManager->getPairStream(this);
}
-Stream* Stream::playPairStream() {
+Stream* Stream::playPairStream(std::vector<std::any>& garbage) {
Stream* pairStream = getPairStream();
LOG_ALWAYS_FATAL_IF(pairStream == nullptr, "No pair stream!");
- sp<AudioTrack> releaseTracks[2];
{
ALOGV("%s: track streamID: %d", __func__, (int)getStreamID());
// TODO: Do we really want to force a simultaneous synchronization between
@@ -260,7 +259,7 @@ Stream* Stream::playPairStream() {
const int pairState = pairStream->mState;
pairStream->play_l(pairStream->mSound, pairStream->mStreamID,
pairStream->mLeftVolume, pairStream->mRightVolume, pairStream->mPriority,
- pairStream->mLoop, pairStream->mRate, releaseTracks);
+ pairStream->mLoop, pairStream->mRate, garbage);
if (pairStream->mState == IDLE) {
return nullptr; // AudioTrack error
}
@@ -269,17 +268,16 @@ Stream* Stream::playPairStream() {
pairStream->mAudioTrack->pause();
}
}
- // release tracks outside of Stream lock
return pairStream;
}
void Stream::play_l(const std::shared_ptr<Sound>& sound, int32_t nextStreamID,
float leftVolume, float rightVolume, int32_t priority, int32_t loop, float rate,
- sp<AudioTrack> releaseTracks[2])
+ std::vector<std::any>& garbage)
{
- // These tracks are released without the lock.
- sp<AudioTrack> &oldTrack = releaseTracks[0];
- sp<AudioTrack> &newTrack = releaseTracks[1];
+ // oldTrack and newTrack are placeholders to be released by garbage without the lock.
+ sp<AudioTrack> oldTrack;
+ sp<AudioTrack> newTrack;
status_t status = NO_ERROR;
{
@@ -383,8 +381,12 @@ exit:
mState = IDLE;
mSoundID = 0;
mSound.reset();
- mAudioTrack.clear(); // actual release from releaseTracks[]
+ mAudioTrack.clear(); // actual release from garbage
}
+
+ // move tracks to garbage to be released later outside of lock.
+ if (newTrack) garbage.emplace_back(std::move(newTrack));
+ if (oldTrack) garbage.emplace_back(std::move(oldTrack));
}
/* static */
diff --git a/media/jni/soundpool/Stream.h b/media/jni/soundpool/Stream.h
index d4e5c9fe7f8a..aa0eef5bc66e 100644
--- a/media/jni/soundpool/Stream.h
+++ b/media/jni/soundpool/Stream.h
@@ -18,6 +18,7 @@
#include "Sound.h"
+#include <any>
#include <android-base/thread_annotations.h>
#include <audio_utils/clock.h>
#include <media/AudioTrack.h>
@@ -90,8 +91,9 @@ public:
void mute(bool muting);
void dump() const NO_THREAD_SAFETY_ANALYSIS; // disable for ALOGV (see func for details).
- // returns the pair stream if successful, nullptr otherwise
- Stream* playPairStream();
+ // returns the pair stream if successful, nullptr otherwise.
+ // garbage is used to release tracks and data outside of any lock.
+ Stream* playPairStream(std::vector<std::any>& garbage);
// These parameters are explicitly checked in the SoundPool class
// so never deviate from the Java API specified values.
@@ -123,9 +125,10 @@ public:
Stream* getPairStream() const;
private:
+ // garbage is used to release tracks and data outside of any lock.
void play_l(const std::shared_ptr<Sound>& sound, int streamID,
float leftVolume, float rightVolume, int priority, int loop, float rate,
- sp<AudioTrack> releaseTracks[2]) REQUIRES(mLock);
+ std::vector<std::any>& garbage) REQUIRES(mLock);
void stop_l() REQUIRES(mLock);
void setVolume_l(float leftVolume, float rightVolume) REQUIRES(mLock);
diff --git a/media/jni/soundpool/StreamManager.cpp b/media/jni/soundpool/StreamManager.cpp
index 309d71cfd062..7f987e31d1d8 100644
--- a/media/jni/soundpool/StreamManager.cpp
+++ b/media/jni/soundpool/StreamManager.cpp
@@ -157,6 +157,7 @@ int32_t StreamManager::queueForPlay(const std::shared_ptr<Sound> &sound,
__func__, sound.get(), soundID, leftVolume, rightVolume, priority, loop, rate);
bool launchThread = false;
int32_t streamID = 0;
+ std::vector<std::any> garbage;
{ // for lock
std::unique_lock lock(mStreamManagerLock);
@@ -243,7 +244,7 @@ int32_t StreamManager::queueForPlay(const std::shared_ptr<Sound> &sound,
removeFromQueues_l(newStream);
mProcessingStreams.emplace(newStream);
lock.unlock();
- if (Stream* nextStream = newStream->playPairStream()) {
+ if (Stream* nextStream = newStream->playPairStream(garbage)) {
lock.lock();
ALOGV("%s: starting streamID:%d", __func__, nextStream->getStreamID());
addToActiveQueue_l(nextStream);
@@ -266,6 +267,7 @@ int32_t StreamManager::queueForPlay(const std::shared_ptr<Sound> &sound,
ALOGV_IF(id != 0, "%s: launched thread %d", __func__, id);
}
ALOGV("%s: returning %d", __func__, streamID);
+ // garbage is cleared here outside mStreamManagerLock.
return streamID;
}
@@ -359,6 +361,7 @@ void StreamManager::run(int32_t id)
{
ALOGV("%s(%d) entering", __func__, id);
int64_t waitTimeNs = 0; // on thread start, mRestartStreams can be non-empty.
+ std::vector<std::any> garbage; // used for garbage collection
std::unique_lock lock(mStreamManagerLock);
while (!mQuit) {
if (waitTimeNs > 0) {
@@ -388,7 +391,7 @@ void StreamManager::run(int32_t id)
if (!mLockStreamManagerStop) lock.unlock();
stream->stop();
ALOGV("%s(%d) stopping streamID:%d", __func__, id, stream->getStreamID());
- if (Stream* nextStream = stream->playPairStream()) {
+ if (Stream* nextStream = stream->playPairStream(garbage)) {
ALOGV("%s(%d) starting streamID:%d", __func__, id, nextStream->getStreamID());
if (!mLockStreamManagerStop) lock.lock();
if (nextStream->getStopTimeNs() > 0) {
@@ -405,6 +408,12 @@ void StreamManager::run(int32_t id)
}
mProcessingStreams.erase(stream);
sanityCheckQueue_l();
+ if (!garbage.empty()) {
+ lock.unlock();
+ // garbage audio tracks (etc) are cleared here outside mStreamManagerLock.
+ garbage.clear();
+ lock.lock();
+ }
}
}
ALOGV("%s(%d) exiting", __func__, id);
diff --git a/media/jni/tuner/DvrClient.cpp b/media/jni/tuner/DvrClient.cpp
index 30278382c8ec..05683b661716 100644
--- a/media/jni/tuner/DvrClient.cpp
+++ b/media/jni/tuner/DvrClient.cpp
@@ -22,6 +22,8 @@
#include <aidl/android/hardware/tv/tuner/DemuxQueueNotifyBits.h>
#include <android-base/logging.h>
#include <inttypes.h>
+#include <sys/types.h>
+#include <unistd.h>
#include <utils/Log.h>
#include "ClientHelper.h"
@@ -200,6 +202,14 @@ int64_t DvrClient::writeToBuffer(int8_t* buffer, int64_t size) {
return size;
}
+int64_t DvrClient::seekFile(int64_t pos) {
+ if (mFd < 0) {
+ ALOGE("Failed to seekFile. File is not configured");
+ return -1;
+ }
+ return lseek64(mFd, pos, SEEK_SET);
+}
+
Result DvrClient::configure(DvrSettings settings) {
if (mTunerDvr != nullptr) {
Status s = mTunerDvr->configure(settings);
diff --git a/media/jni/tuner/DvrClient.h b/media/jni/tuner/DvrClient.h
index 9080c7288f76..61c0325813a4 100644
--- a/media/jni/tuner/DvrClient.h
+++ b/media/jni/tuner/DvrClient.h
@@ -82,6 +82,11 @@ public:
int64_t writeToFile(int64_t size);
/**
+ * Seeks the Dvr file descriptor from the beginning of the file.
+ */
+ int64_t seekFile(int64_t pos);
+
+ /**
* Write data to the given buffer with given size. Return the actual write size.
*/
int64_t writeToBuffer(int8_t* buffer, int64_t size);
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
index 6dc05ad010e6..a2eae2c9579a 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
@@ -303,6 +303,11 @@ public class CameraBinderTest extends AndroidTestCase {
public void onCameraClosed(String cameraId) {
Log.v(TAG, String.format("Camera %s is closed", cameraId));
}
+ @Override
+ public void onTorchStrengthLevelChanged(String cameraId, int torchStrength) {
+ Log.v(TAG, String.format("Camera " + cameraId + " torch strength level changed to "
+ + torchStrength ));
+ }
}
/**
diff --git a/native/android/input.cpp b/native/android/input.cpp
index 4de2c23e62b8..c06c81ed03ec 100644
--- a/native/android/input.cpp
+++ b/native/android/input.cpp
@@ -330,6 +330,6 @@ void AInputQueue_finishEvent(AInputQueue* queue, AInputEvent* event, int handled
iq->finishEvent(e, handled != 0);
}
-AInputQueue* AInputQueue_fromJava(jobject inputQueue) {
- return android::android_view_InputQueue_getNativePtr(inputQueue);
+AInputQueue* AInputQueue_fromJava(JNIEnv* env, jobject inputQueue) {
+ return android::android_view_InputQueue_getNativePtr(env, inputQueue);
}
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index f00eef2f1b00..3c1aa4409254 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -263,6 +263,7 @@ LIBANDROID {
ASurfaceTransaction_setEnableBackPressure; # introduced=31
ASurfaceTransaction_setFrameRate; # introduced=30
ASurfaceTransaction_setFrameRateWithChangeStrategy; # introduced=31
+ ASurfaceTransaction_setFrameTimeline; # introduced=Tiramisu
ASurfaceTransaction_setGeometry; # introduced=29
ASurfaceTransaction_setHdrMetadata_cta861_3; # introduced=29
ASurfaceTransaction_setHdrMetadata_smpte2086; # introduced=29
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index ceba4d60bed0..5b19102334d9 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -364,7 +364,7 @@ void ASurfaceTransaction_setBuffer(ASurfaceTransaction* aSurfaceTransaction,
sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
- sp<GraphicBuffer> graphic_buffer(reinterpret_cast<GraphicBuffer*>(buffer));
+ sp<GraphicBuffer> graphic_buffer(GraphicBuffer::fromAHardwareBuffer(buffer));
std::optional<sp<Fence>> fence = std::nullopt;
if (acquire_fence_fd != -1) {
@@ -659,3 +659,11 @@ void ASurfaceTransaction_setOnCommit(ASurfaceTransaction* aSurfaceTransaction, v
transaction->addTransactionCommittedCallback(callback, context);
}
+
+void ASurfaceTransaction_setFrameTimeline(ASurfaceTransaction* aSurfaceTransaction,
+ int64_t vsyncId) {
+ CHECK_NOT_NULL(aSurfaceTransaction);
+ // TODO(b/210043506): Get start time from platform.
+ ASurfaceTransaction_to_Transaction(aSurfaceTransaction)
+ ->setFrameTimelineInfo({.vsyncId = vsyncId, .startTimeNanos = 0});
+}
diff --git a/packages/CarrierDefaultApp/AndroidManifest.xml b/packages/CarrierDefaultApp/AndroidManifest.xml
index 3e65df23fb2b..632dfb397d51 100644
--- a/packages/CarrierDefaultApp/AndroidManifest.xml
+++ b/packages/CarrierDefaultApp/AndroidManifest.xml
@@ -27,6 +27,7 @@
<uses-permission android:name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS" />
<uses-permission android:name="android.permission.NETWORK_BYPASS_PRIVATE_DNS" />
<uses-permission android:name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" />
+ <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application
android:label="@string/app_name"
diff --git a/packages/CompanionDeviceManager/AndroidManifest.xml b/packages/CompanionDeviceManager/AndroidManifest.xml
index c5926a5c75cc..06f2d9d0f0c8 100644
--- a/packages/CompanionDeviceManager/AndroidManifest.xml
+++ b/packages/CompanionDeviceManager/AndroidManifest.xml
@@ -19,10 +19,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.companiondevicemanager">
- <permission
- android:name="com.android.companiondevicemanager.permission.BIND"
- android:protectionLevel="signature" />
-
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE"/>
@@ -43,23 +39,17 @@
android:forceQueryable="true"
android:supportsRtl="true">
- <service
- android:name=".CompanionDeviceDiscoveryService"
- android:permission="android.permission.BIND_COMPANION_DEVICE_MANAGER_SERVICE"
- android:exported="true">
- </service>
-
<activity
android:name=".CompanionDeviceActivity"
- android:theme="@style/ChooserActivity"
+ android:exported="true"
+ android:launchMode="singleInstance"
+ android:excludeFromRecents="true"
android:permission="android.permission.BIND_COMPANION_DEVICE_MANAGER_SERVICE"
- android:exported="true">
- <!--TODO include url scheme filter similar to PrintSpooler -->
- <intent-filter>
- <action android:name="android.companiondevice.START_DISCOVERY" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </activity>
+ android:theme="@style/ChooserActivity"/>
+
+ <service
+ android:name=".CompanionDeviceDiscoveryService"
+ android:exported="false" />
</application>
diff --git a/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml b/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
new file mode 100644
index 000000000000..c87bac67fdff
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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: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">
+
+ <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"/>
+ <!-- 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" />
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1">
+
+ <ListView
+ android:id="@+id/device_list"
+ style="@android:style/Widget.Material.ListView"
+ android:layout_width="match_parent"
+ android:layout_height="200dp" />
+
+ </RelativeLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="end">
+
+ <Button
+ android:id="@+id/button_cancel"
+ 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/button_allow"
+ 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/layout/buttons.xml b/packages/CompanionDeviceManager/res/layout/buttons.xml
deleted file mode 100644
index a80720c40737..000000000000
--- a/packages/CompanionDeviceManager/res/layout/buttons.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/buttons"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:layout_alignParentBottom="true"
- android:layout_alignParentEnd="true"
- android:gravity="end"
->
- <Button
- android:id="@+id/button_cancel"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/consent_no"
- android:textColor="?android:attr/textColorSecondary"
- style="@android:style/Widget.Material.Button.Borderless.Colored"
- />
- <Button
- android:id="@+id/button_pair"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/consent_yes"
- style="@android:style/Widget.Material.Button.Borderless.Colored"
- />
-</LinearLayout> \ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/layout/device_chooser.xml b/packages/CompanionDeviceManager/res/layout/device_chooser.xml
deleted file mode 100644
index 273347af6119..000000000000
--- a/packages/CompanionDeviceManager/res/layout/device_chooser.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<RelativeLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/container"
- android:layout_height="400dp"
- style="@style/ContainerLayout"
- >
-
- <include layout="@layout/title" />
-
- <include layout="@layout/profile_summary" />
-
- <ListView
- android:id="@+id/device_list"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_below="@+id/profile_summary"
- android:layout_above="@+id/buttons"
- style="@android:style/Widget.Material.ListView"
- />
-
- <include layout="@layout/buttons" />
-
-</RelativeLayout> \ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/layout/device_confirmation.xml b/packages/CompanionDeviceManager/res/layout/device_confirmation.xml
deleted file mode 100644
index 1336e79a855b..000000000000
--- a/packages/CompanionDeviceManager/res/layout/device_confirmation.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/container"
- android:layout_height="wrap_content"
- style="@style/ContainerLayout"
- >
-
- <include layout="@layout/title" />
-
- <include layout="@layout/profile_summary" />
-
- <include layout="@layout/buttons" />
-
-</LinearLayout> \ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/layout/title.xml b/packages/CompanionDeviceManager/res/layout/title.xml
deleted file mode 100644
index 9a5036622468..000000000000
--- a/packages/CompanionDeviceManager/res/layout/title.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-
-<TextView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- style="@*android:style/TextAppearance.Widget.Toolbar.Title"
-/> \ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/values-af/strings.xml b/packages/CompanionDeviceManager/res/values-af/strings.xml
index 3faed5525946..aec8f89f20e7 100644
--- a/packages/CompanionDeviceManager/res/values-af/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-af/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Metgeseltoestel-bestuurder"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Laat &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; toe om jou &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; te bestuur"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"horlosie"</string>
<string name="chooser_title" msgid="2262294130493605839">"Kies \'n <xliff:g id="PROFILE_NAME">%1$s</xliff:g> om deur &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; bestuur te word"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"toestel"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"horlosie"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Laat &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; toe om jou &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; te bestuur"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Hierdie program is nodig om jou <xliff:g id="PROFILE_NAME">%1$s</xliff:g> te bestuur. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Laat toe"</string>
<string name="consent_no" msgid="2640796915611404382">"Moenie toelaat nie"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-am/strings.xml b/packages/CompanionDeviceManager/res/values-am/strings.xml
index 99466d7d2e1a..235912410580 100644
--- a/packages/CompanionDeviceManager/res/values-am/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-am/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"አጃቢ የመሣሪያ አስተዳዳሪ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; የእርስዎን <xliff:g id="DEVICE_NAME">%2$s</xliff:g> - &lt;strong&gt;&lt;/strong&gt; እንዲያስተዳደር ይፍቀዱ"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"ሰዓት"</string>
<string name="chooser_title" msgid="2262294130493605839">"በ&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; የሚተዳደር <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ይምረጡ"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"መሣሪያ"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"ሰዓት"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; የእርስዎን <xliff:g id="DEVICE_NAME">%2$s</xliff:g> - &lt;strong&gt;&lt;/strong&gt; እንዲያስተዳደር ይፍቀዱ"</string>
- <string name="profile_summary" msgid="2059360676631420073">"የእርስዎን <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ለማስተዳደር ይህ መተግበሪያ ያስፈልጋል <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"ፍቀድ"</string>
<string name="consent_no" msgid="2640796915611404382">"አትፍቀድ"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ar/strings.xml b/packages/CompanionDeviceManager/res/values-ar/strings.xml
index c3f1e73d3ad4..5fc3a4fc8633 100644
--- a/packages/CompanionDeviceManager/res/values-ar/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ar/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"تطبيق \"مدير الجهاز المصاحب\""</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"‏السماح للتطبيق &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; بإدارة &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"ساعة"</string>
<string name="chooser_title" msgid="2262294130493605839">"‏اختَر <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ليديره تطبيق &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"جهاز"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"ساعة"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"‏السماح للتطبيق &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; بإدارة &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"هذا التطبيق مطلوب لإدارة <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"السماح"</string>
<string name="consent_no" msgid="2640796915611404382">"عدم السماح"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-as/strings.xml b/packages/CompanionDeviceManager/res/values-as/strings.xml
index 2a2bc25c7881..743d725621e5 100644
--- a/packages/CompanionDeviceManager/res/values-as/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-as/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"কম্পেনিয়ন ডিভাইচ মেনেজাৰ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ক আপোনাৰ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; পৰিচালনা কৰিবলৈ দিয়ক"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"ঘড়ী"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;এ পৰিচালনা কৰিব লগা এটা <xliff:g id="PROFILE_NAME">%1$s</xliff:g> বাছনি কৰক"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"ডিভাইচ"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"ঘড়ী"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ক আপোনাৰ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; পৰিচালনা কৰিবলৈ দিয়ক"</string>
- <string name="profile_summary" msgid="2059360676631420073">"আপোনাৰ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> পৰিচালনা কৰিবলৈ এই এপ্‌টোৰ আৱশ্যক। <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"অনুমতি দিয়ক"</string>
<string name="consent_no" msgid="2640796915611404382">"অনুমতি নিদিব"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-az/strings.xml b/packages/CompanionDeviceManager/res/values-az/strings.xml
index 2ec13c587166..ca32052d4758 100644
--- a/packages/CompanionDeviceManager/res/values-az/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-az/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Kompanyon Cihaz Meneceri"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tətbiqinə &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; cihazınızı idarə etməsinə icazə verin"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"izləyin"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; tərəfindən idarə ediləcək <xliff:g id="PROFILE_NAME">%1$s</xliff:g> seçin"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"cihaz"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"izləyin"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tətbiqinə &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; cihazınızı idarə etməsinə icazə verin"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Bu tətbiq <xliff:g id="PROFILE_NAME">%1$s</xliff:g> profilinizi idarə etmək üçün lazımdır. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"İcazə verin"</string>
<string name="consent_no" msgid="2640796915611404382">"İcazə verməyin"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
index d687b05896c6..d919e6734030 100644
--- a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Menadžer pridruženog uređaja"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Dozvolite aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja uređajem &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"sat"</string>
<string name="chooser_title" msgid="2262294130493605839">"Odaberite profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kojim će upravljati aplikacija &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"sat"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Dozvolite aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja uređajem &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Ova aplikacija je potrebna za upravljanje profilom <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Dozvoli"</string>
<string name="consent_no" msgid="2640796915611404382">"Ne dozvoli"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-be/strings.xml b/packages/CompanionDeviceManager/res/values-be/strings.xml
index 2236052f5545..919d7290866f 100644
--- a/packages/CompanionDeviceManager/res/values-be/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-be/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Менеджар спадарожнай прылады"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Дазвольце праграме &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; кіраваць прыладай &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"гадзіннік"</string>
<string name="chooser_title" msgid="2262294130493605839">"Выберыце прыладу (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), якой будзе кіраваць праграма &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"прылада"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"гадзіннік"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Дазвольце праграме &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; кіраваць прыладай &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Гэта праграма неабходная для кіравання профілем \"<xliff:g id="PROFILE_NAME">%1$s</xliff:g>\". <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Дазволіць"</string>
<string name="consent_no" msgid="2640796915611404382">"Не дазваляць"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-bg/strings.xml b/packages/CompanionDeviceManager/res/values-bg/strings.xml
index 996ca9062f80..1e2aa4e8214c 100644
--- a/packages/CompanionDeviceManager/res/values-bg/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bg/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Разрешаване на &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да управлява устройството ви &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"часовник"</string>
<string name="chooser_title" msgid="2262294130493605839">"Изберете устройство (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), което да се управлява от &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"устройство"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"часовник"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Разрешаване на &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да управлява устройството ви &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Това приложение е необходимо за управление на <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Разрешаване"</string>
<string name="consent_no" msgid="2640796915611404382">"Забраняване"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-bn/strings.xml b/packages/CompanionDeviceManager/res/values-bn/strings.xml
index 16d25ce57870..3b537b6e48bf 100644
--- a/packages/CompanionDeviceManager/res/values-bn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bn/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"আপনার &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ম্যানেজ করার জন্য &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; -কে অনুমতি দিন"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"ঘড়ি"</string>
<string name="chooser_title" msgid="2262294130493605839">"<xliff:g id="PROFILE_NAME">%1$s</xliff:g> বেছে নিন যেটি &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ম্যানেজ করবে"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"ডিভাইস"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"ঘড়ি"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"আপনার &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ম্যানেজ করার জন্য &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; -কে অনুমতি দিন"</string>
- <string name="profile_summary" msgid="2059360676631420073">"আপনার <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ম্যানেজ করতে এই অ্যাপটি প্রয়োজন। <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"অনুমতি দিন"</string>
<string name="consent_no" msgid="2640796915611404382">"অনুমতি দেবেন না"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-bs/strings.xml b/packages/CompanionDeviceManager/res/values-bs/strings.xml
index 10f753c1cf45..b010626ab395 100644
--- a/packages/CompanionDeviceManager/res/values-bs/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bs/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Prateći upravitelj uređaja"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Dozvolite aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja uređajem &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"sat"</string>
<string name="chooser_title" msgid="2262294130493605839">"Odaberite uređaj <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kojim će upravljati aplikacija &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"sat"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Dozvolite aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja uređajem &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Ova aplikacija je potrebna za upravljanje profilom: <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Dozvoli"</string>
<string name="consent_no" msgid="2640796915611404382">"Nemoj dozvoliti"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ca/strings.xml b/packages/CompanionDeviceManager/res/values-ca/strings.xml
index e55a033cf800..efd801e59bde 100644
--- a/packages/CompanionDeviceManager/res/values-ca/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ca/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Gestor de dispositius complementaris"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Permet que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gestioni el teu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"rellotge"</string>
<string name="chooser_title" msgid="2262294130493605839">"Tria un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> perquè el gestioni &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositiu"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"rellotge"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Permet que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gestioni el teu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Aquesta aplicació es necessita per gestionar el teu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Permet"</string>
<string name="consent_no" msgid="2640796915611404382">"No permetis"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-cs/strings.xml b/packages/CompanionDeviceManager/res/values-cs/strings.xml
index 48fbda10cfed..bd57213ca02e 100644
--- a/packages/CompanionDeviceManager/res/values-cs/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-cs/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Správce doprovodných zařízení"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Povolit aplikaci &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; spravovat &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"hodinky"</string>
<string name="chooser_title" msgid="2262294130493605839">"Vyberte zařízení <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, které chcete spravovat pomocí aplikace &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"zařízení"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"hodinky"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Povolit aplikaci &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; spravovat &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Tato aplikace je nutná pro správu profilu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Povolit"</string>
<string name="consent_no" msgid="2640796915611404382">"Nepovolovat"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-da/strings.xml b/packages/CompanionDeviceManager/res/values-da/strings.xml
index 446c301747df..742845310dea 100644
--- a/packages/CompanionDeviceManager/res/values-da/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-da/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Medfølgende enhedsadministrator"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Tillad at &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; kan administrere: &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"ur"</string>
<string name="chooser_title" msgid="2262294130493605839">"Vælg den enhed (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), som skal administreres af &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"enhed"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"ur"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Tillad at &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; kan administrere: &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Du skal bruge denne app for at administrere dit <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Tillad"</string>
<string name="consent_no" msgid="2640796915611404382">"Tillad ikke"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-de/strings.xml b/packages/CompanionDeviceManager/res/values-de/strings.xml
index 33d831d41aec..4c431401c490 100644
--- a/packages/CompanionDeviceManager/res/values-de/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-de/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Begleitgerät-Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; erlauben, dein Gerät &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; zu verwalten"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"Smartwatch"</string>
<string name="chooser_title" msgid="2262294130493605839">"Gerät (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) auswählen, das von &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; verwaltet werden soll"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"Gerät"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"Smartwatch"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; erlauben, dein Gerät &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; zu verwalten"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Diese App wird zur Verwaltung des Profils „<xliff:g id="PROFILE_NAME">%1$s</xliff:g>“ benötigt. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Zulassen"</string>
<string name="consent_no" msgid="2640796915611404382">"Nicht zulassen"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-el/strings.xml b/packages/CompanionDeviceManager/res/values-el/strings.xml
index 7a78c0669825..07a4fda587a8 100644
--- a/packages/CompanionDeviceManager/res/values-el/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-el/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Διαχείριση συνοδευτικής εφαρμογής"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Επιτρέψτε στην εφαρμογή &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; να διαχειρίζεται τη συσκευή &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"ρολόι"</string>
<string name="chooser_title" msgid="2262294130493605839">"Επιλέξτε ένα προφίλ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> για διαχείριση από την εφαρμογή &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"συσκευή"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"ρολόι"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Επιτρέψτε στην εφαρμογή &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; να διαχειρίζεται τη συσκευή &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Αυτή η εφαρμογή είναι απαραίτητη για τη διαχείριση του προφίλ σας <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Να επιτρέπεται"</string>
<string name="consent_no" msgid="2640796915611404382">"Να μην επιτρέπεται"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
index a6ebe658d622..1756d22c129f 100644
--- a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
@@ -17,11 +17,19 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
<string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="summary_watch" product="default" msgid="7113724443198337683">"<xliff:g id="APP_NAME">%1$s</xliff:g> will be allowed to interact with your notifications and access your Phone, SMS, Contacts and Calendar permissions."</string>
+ <string name="summary_watch" product="tablet" msgid="7113724443198337683">"<xliff:g id="APP_NAME">%1$s</xliff:g> will be allowed to interact with your notifications and access your Phone, SMS, Contacts and Calendar permissions."</string>
+ <string name="title_app_streaming" msgid="4459136600249308574">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to stream applications?"</string>
+ <string name="summary_app_streaming" product="default" msgid="6105916810614498138">"Let &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; provide &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; remote access to access to applications installed on this phone when connected."</string>
+ <string name="summary_app_streaming" product="tablet" msgid="2996373715966272792">"Let &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; provide &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; remote access to access to applications installed on this tablet when connected."</string>
+ <string name="summary_app_streaming" product="device" msgid="7614171699434639963">"Let &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; provide &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; remote access to access to applications installed on this device when connected."</string>
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"This app is needed to manage your <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Allow"</string>
<string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml
index a6ebe658d622..1756d22c129f 100644
--- a/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml
@@ -17,11 +17,19 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
<string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="summary_watch" product="default" msgid="7113724443198337683">"<xliff:g id="APP_NAME">%1$s</xliff:g> will be allowed to interact with your notifications and access your Phone, SMS, Contacts and Calendar permissions."</string>
+ <string name="summary_watch" product="tablet" msgid="7113724443198337683">"<xliff:g id="APP_NAME">%1$s</xliff:g> will be allowed to interact with your notifications and access your Phone, SMS, Contacts and Calendar permissions."</string>
+ <string name="title_app_streaming" msgid="4459136600249308574">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to stream applications?"</string>
+ <string name="summary_app_streaming" product="default" msgid="6105916810614498138">"Let &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; provide &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; remote access to access to applications installed on this phone when connected."</string>
+ <string name="summary_app_streaming" product="tablet" msgid="2996373715966272792">"Let &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; provide &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; remote access to access to applications installed on this tablet when connected."</string>
+ <string name="summary_app_streaming" product="device" msgid="7614171699434639963">"Let &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; provide &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; remote access to access to applications installed on this device when connected."</string>
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"This app is needed to manage your <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Allow"</string>
<string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
index a6ebe658d622..1756d22c129f 100644
--- a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
@@ -17,11 +17,19 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
<string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="summary_watch" product="default" msgid="7113724443198337683">"<xliff:g id="APP_NAME">%1$s</xliff:g> will be allowed to interact with your notifications and access your Phone, SMS, Contacts and Calendar permissions."</string>
+ <string name="summary_watch" product="tablet" msgid="7113724443198337683">"<xliff:g id="APP_NAME">%1$s</xliff:g> will be allowed to interact with your notifications and access your Phone, SMS, Contacts and Calendar permissions."</string>
+ <string name="title_app_streaming" msgid="4459136600249308574">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to stream applications?"</string>
+ <string name="summary_app_streaming" product="default" msgid="6105916810614498138">"Let &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; provide &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; remote access to access to applications installed on this phone when connected."</string>
+ <string name="summary_app_streaming" product="tablet" msgid="2996373715966272792">"Let &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; provide &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; remote access to access to applications installed on this tablet when connected."</string>
+ <string name="summary_app_streaming" product="device" msgid="7614171699434639963">"Let &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; provide &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; remote access to access to applications installed on this device when connected."</string>
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"This app is needed to manage your <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Allow"</string>
<string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
index a6ebe658d622..1756d22c129f 100644
--- a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
@@ -17,11 +17,19 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
<string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="summary_watch" product="default" msgid="7113724443198337683">"<xliff:g id="APP_NAME">%1$s</xliff:g> will be allowed to interact with your notifications and access your Phone, SMS, Contacts and Calendar permissions."</string>
+ <string name="summary_watch" product="tablet" msgid="7113724443198337683">"<xliff:g id="APP_NAME">%1$s</xliff:g> will be allowed to interact with your notifications and access your Phone, SMS, Contacts and Calendar permissions."</string>
+ <string name="title_app_streaming" msgid="4459136600249308574">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to stream applications?"</string>
+ <string name="summary_app_streaming" product="default" msgid="6105916810614498138">"Let &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; provide &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; remote access to access to applications installed on this phone when connected."</string>
+ <string name="summary_app_streaming" product="tablet" msgid="2996373715966272792">"Let &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; provide &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; remote access to access to applications installed on this tablet when connected."</string>
+ <string name="summary_app_streaming" product="device" msgid="7614171699434639963">"Let &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; provide &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; remote access to access to applications installed on this device when connected."</string>
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"This app is needed to manage your <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Allow"</string>
<string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml b/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml
index 6cc56a4f44e7..efda04ec0d3a 100644
--- a/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml
@@ -17,11 +17,19 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‎‎‏‎‏‏‎‏‏‎‏‏‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‎‎‎‏‎‏‎‏‎‎‏‏‏‏‏‏‎‎‎‏‏‏‎‎‏‎‏‎Companion Device Manager‎‏‎‎‏‎"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‏‏‎‎‎‎‎‏‎‎‎‎‏‎‎‏‏‎‏‏‎‏‏‏‏‎‎‏‏‏‎‎‏‏‎‎‎‏‎‎‏‎‏‏‎‎‏‏‏‎‎‎‎‎Allow &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt; to manage your &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt;‎‏‎‎‏‎"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‎‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‎‎‎‏‎‏‎‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎watch‎‏‎‎‏‎"</string>
<string name="chooser_title" msgid="2262294130493605839">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‎‎‏‎‎‎‏‎‎‎‎‏‏‎‏‎‎‎‏‎‎‏‏‎‎‎‎‏‎‏‏‏‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎Choose a ‎‏‎‎‏‏‎<xliff:g id="PROFILE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ to be managed by &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt;‎‏‎‎‏‎"</string>
+ <string name="summary_watch" product="default" msgid="7113724443198337683">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‏‏‏‎‎‏‎‎‎‎‎‏‏‎‏‎‏‏‎‎‎‎‏‏‏‎‎‎‏‎‏‏‎‏‏‎‎‏‏‎‏‏‎‎‏‎‏‎‎‏‎‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ will be allowed to interact with your notifications and access your Phone, SMS, Contacts and Calendar permissions.‎‏‎‎‏‎"</string>
+ <string name="summary_watch" product="tablet" msgid="7113724443198337683">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‏‏‏‎‎‏‎‎‎‎‎‏‏‎‏‎‏‏‎‎‎‎‏‏‏‎‎‎‏‎‏‏‎‏‏‎‎‏‏‎‏‏‎‎‏‎‏‎‎‏‎‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ will be allowed to interact with your notifications and access your Phone, SMS, Contacts and Calendar permissions.‎‏‎‎‏‎"</string>
+ <string name="title_app_streaming" msgid="4459136600249308574">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‏‎‎‎‏‎‎‎‎‎‏‎‎‏‎‎‎‏‏‏‎‎‏‏‎‎‎‏‏‎‏‏‎‎‏‏‎‏‏‎‎‏‎‏‎‏‏‎‎‏‏‏‏‎‎Allow &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt; to stream applications?‎‏‎‎‏‎"</string>
+ <string name="summary_app_streaming" product="default" msgid="6105916810614498138">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‏‏‏‏‎‎‏‎‎‏‎‎‏‎‏‏‏‏‏‏‎‏‎‎‎‏‏‏‎‎‏‏‎‎‎‎‏‏‎‏‏‎‎‎‏‏‎‏‎‏‏‎‏‎‎Let &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt; to provide &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt; remote access to access to applications installed on this phone when connected.‎‏‎‎‏‎"</string>
+ <string name="summary_app_streaming" product="tablet" msgid="2996373715966272792">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‏‎‎‏‎‏‎‏‎‏‎‎‎‎‏‎‎‎‎‎‎‏‎‎‏‏‏‎‏‎‏‎‎‏‏‏‏‏‎‎‏‎‏‎‎‏‎‏‎‎‎‏‏‎‎‎‎Let &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt; to provide &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt; remote access to access to applications installed on this tablet when connected.‎‏‎‎‏‎"</string>
+ <string name="summary_app_streaming" product="device" msgid="7614171699434639963">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‎‏‎‏‎‏‏‏‏‏‎‎‎‏‏‎‏‎‎‎‏‏‎‏‎‏‎‏‏‏‎‏‎‎‎‏‎‎‏‎‏‎‎‏‎‎‏‎‏‏‎‏‏‎Let &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt; to provide &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt; remote access to access to applications installed on this device when connected.‎‏‎‎‏‎"</string>
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‏‎‎‏‏‏‎‏‏‏‏‏‎‎‏‎‎‏‎‎‏‏‏‏‎‎‎‏‏‏‎‏‏‏‎‎‎‎‎‎‎‎‏‏‏‎‏‏‎‏‏‎‎‎device‎‏‎‎‏‎"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‎‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‎‎‎‏‎‏‎‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎watch‎‏‎‎‏‎"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‏‏‎‎‎‎‎‏‎‎‎‎‏‎‎‏‏‎‏‏‎‏‏‏‏‎‎‏‏‏‎‎‏‏‎‎‎‏‎‎‏‎‏‏‎‎‏‏‏‎‎‎‎‎Allow &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt; to manage your &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt;‎‏‎‎‏‎"</string>
- <string name="profile_summary" msgid="2059360676631420073">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‏‎‎‏‎‏‎‎‎‏‎‏‎‎‎‏‏‎‎‏‏‏‏‏‏‏‎‏‏‎‏‏‎‎‎‎‎‏‏‎‎‎‎‏‏‏‎‎‏‎‏‎‏‎‎‏‎This app is needed to manage your ‎‏‎‎‏‏‎<xliff:g id="PROFILE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎. ‎‏‎‎‏‏‎<xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‎‏‏‎‏‏‎‎‏‎‎‏‎‏‏‏‏‎‎‏‏‏‎‎‏‏‏‏‎‎‏‎‏‏‎‎‏‎‏‏‎‎‎‎‎‎‏‏‏‏‎‎‎‎Allow‎‏‎‎‏‎"</string>
<string name="consent_no" msgid="2640796915611404382">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‎‏‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‏‏‎‎‏‏‏‏‎‏‏‏‏‎‏‏‏‎‎‎‎‏‎‎‎‎‏‎‏‏‏‏‎‎Don’t allow‎‏‎‎‏‎"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
index dc1315916159..9bbc1b8a8831 100644
--- a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Administrador de dispositivo complementario"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Permite que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; administre tu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"reloj"</string>
<string name="chooser_title" msgid="2262294130493605839">"Elige un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para que &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; lo administre"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"reloj"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Permite que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; administre tu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Esta app es necesaria para administrar tu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
<string name="consent_no" msgid="2640796915611404382">"No permitir"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-es/strings.xml b/packages/CompanionDeviceManager/res/values-es/strings.xml
index 7e37c1d33522..daece565a14e 100644
--- a/packages/CompanionDeviceManager/res/values-es/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-es/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Gestor de dispositivos complementario"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gestione tu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"reloj"</string>
<string name="chooser_title" msgid="2262294130493605839">"Elige un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para gestionarlo con &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"reloj"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gestione tu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Se necesita esta aplicación para gestionar tu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
<string name="consent_no" msgid="2640796915611404382">"No permitir"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-et/strings.xml b/packages/CompanionDeviceManager/res/values-et/strings.xml
index 399556de789b..ffaa0c0709fb 100644
--- a/packages/CompanionDeviceManager/res/values-et/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-et/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Kaasseadme haldur"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Lubage rakendusel &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; hallata teie seadet &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"käekell"</string>
<string name="chooser_title" msgid="2262294130493605839">"Valige seade <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, mida haldab rakendus &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"seade"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"käekell"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Lubage rakendusel &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; hallata teie seadet &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Seda rakendust on vaja teie profiili <xliff:g id="PROFILE_NAME">%1$s</xliff:g> haldamiseks. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Luba"</string>
<string name="consent_no" msgid="2640796915611404382">"Ära luba"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-eu/strings.xml b/packages/CompanionDeviceManager/res/values-eu/strings.xml
index 764505e52959..5bf6677de7eb 100644
--- a/packages/CompanionDeviceManager/res/values-eu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-eu/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Gailu osagarriaren kudeatzailea"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Eman &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; kudeatzeko baimena &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aplikazioari"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"erlojua"</string>
<string name="chooser_title" msgid="2262294130493605839">"Aukeratu &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; aplikazioak kudeatu beharreko <xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"gailua"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"erlojua"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Eman &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; kudeatzeko baimena &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aplikazioari"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Aplikazioa <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kudeatzeko beharrezkoa da. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Eman baimena"</string>
<string name="consent_no" msgid="2640796915611404382">"Ez eman baimenik"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-fa/strings.xml b/packages/CompanionDeviceManager/res/values-fa/strings.xml
index 07d04aa1ecc9..1ede28cebfc6 100644
--- a/packages/CompanionDeviceManager/res/values-fa/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fa/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"مدیر دستگاه مرتبط"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"‏مجاز کردن &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; برای مدیریت کردن &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"ساعت"</string>
<string name="chooser_title" msgid="2262294130493605839">"‏انتخاب <xliff:g id="PROFILE_NAME">%1$s</xliff:g> برای مدیریت کردن با &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>‏&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"دستگاه"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"ساعت"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"‏مجاز کردن &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; برای مدیریت کردن &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"این برنامه برای مدیریت <xliff:g id="PROFILE_NAME">%1$s</xliff:g> شما لازم است. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"مجاز بودن"</string>
<string name="consent_no" msgid="2640796915611404382">"مجاز نبودن"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-fi/strings.xml b/packages/CompanionDeviceManager/res/values-fi/strings.xml
index 528d16c2e425..ac948dfdc059 100644
--- a/packages/CompanionDeviceManager/res/values-fi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fi/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Salli, että &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; voi hallinnoida tätä laitettasi: &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"kello"</string>
<string name="chooser_title" msgid="2262294130493605839">"Valitse <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, jota &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; hallinnoi"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"laite"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"kello"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Salli, että &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; voi hallinnoida tätä laitettasi: &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Profiilin (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) ylläpitoon tarvitaan tätä sovellusta. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Salli"</string>
<string name="consent_no" msgid="2640796915611404382">"Älä salli"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
index 1dcd3375c1cf..19f97f0c5887 100644
--- a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Gestionnaire d\'appareil compagnon"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Autoriser &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; à gérer votre &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"montre"</string>
<string name="chooser_title" msgid="2262294130493605839">"Choisissez un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> qui sera géré par &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"appareil"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"montre"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Autoriser &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; à gérer votre &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Cette application est nécessaire pour gérer votre <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Autoriser"</string>
<string name="consent_no" msgid="2640796915611404382">"Ne pas autoriser"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-fr/strings.xml b/packages/CompanionDeviceManager/res/values-fr/strings.xml
index ba2fc8ea9424..8a7ae1ad807f 100644
--- a/packages/CompanionDeviceManager/res/values-fr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fr/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Gestionnaire d\'appareils associés"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Autoriser &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; à gérer votre &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"montre"</string>
<string name="chooser_title" msgid="2262294130493605839">"Sélectionner le/la <xliff:g id="PROFILE_NAME">%1$s</xliff:g> qui sera géré(e) par &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"appareil"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"montre"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Autoriser &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; à gérer votre &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Cette appli est nécessaire pour gérer votre <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Autoriser"</string>
<string name="consent_no" msgid="2640796915611404382">"Ne pas autoriser"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-gl/strings.xml b/packages/CompanionDeviceManager/res/values-gl/strings.xml
index 5f9a8d786739..052c2073068e 100644
--- a/packages/CompanionDeviceManager/res/values-gl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-gl/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Xestor de dispositivos complementarios"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; xestione o teu dispositivo (&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;)"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"reloxo"</string>
<string name="chooser_title" msgid="2262294130493605839">"Escolle un perfil (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) para que o xestione a aplicación &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"reloxo"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; xestione o teu dispositivo (&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;)"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Esta aplicación é necesaria para xestionar o teu perfil (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>). <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
<string name="consent_no" msgid="2640796915611404382">"Non permitir"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-gu/strings.xml b/packages/CompanionDeviceManager/res/values-gu/strings.xml
index 71cf012f24b3..279de1622612 100644
--- a/packages/CompanionDeviceManager/res/values-gu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-gu/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"કમ્પેનિયન ડિવાઇસ મેનેજર"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"તમારા &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;ને મેનેજ કરવા માટે &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ને મંજૂર કરો"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"સ્માર્ટવૉચ"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; દ્વારા મેનેજ કરવા માટે કોઈ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> પસંદ કરો"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"ડિવાઇસ"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"સ્માર્ટવૉચ"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"તમારા &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;ને મેનેજ કરવા માટે &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ને મંજૂર કરો"</string>
- <string name="profile_summary" msgid="2059360676631420073">"તમારી <xliff:g id="PROFILE_NAME">%1$s</xliff:g> મેનેજ કરવા માટે આ ઍપ જરૂરી છે. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"મંજૂરી આપો"</string>
<string name="consent_no" msgid="2640796915611404382">"મંજૂરી આપશો નહીં"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-hi/strings.xml b/packages/CompanionDeviceManager/res/values-hi/strings.xml
index d4dd1cb4f1b1..77048290af65 100644
--- a/packages/CompanionDeviceManager/res/values-hi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hi/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"सहयोगी डिवाइस मैनेजर"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; को, अपनी &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; मैनेज करने की अनुमति दें"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"स्मार्टवॉच"</string>
<string name="chooser_title" msgid="2262294130493605839">"कोई <xliff:g id="PROFILE_NAME">%1$s</xliff:g> चुनें, ताकि उसे &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; की मदद से प्रबंधित किया जा सके"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"डिवाइस"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"स्मार्टवॉच"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; को, अपनी &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; मैनेज करने की अनुमति दें"</string>
- <string name="profile_summary" msgid="2059360676631420073">"<xliff:g id="PROFILE_NAME">%1$s</xliff:g> को मैनेज करने के लिए, यह ऐप्लिकेशन ज़रूरी है. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"अनुमति दें"</string>
<string name="consent_no" msgid="2640796915611404382">"अनुमति न दें"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-hr/strings.xml b/packages/CompanionDeviceManager/res/values-hr/strings.xml
index 87c5ae2670e9..e7db2badbdc1 100644
--- a/packages/CompanionDeviceManager/res/values-hr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hr/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Dopustite aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja vašim &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"satom"</string>
<string name="chooser_title" msgid="2262294130493605839">"Odaberite profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kojim će upravljati aplikacija &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"satom"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Dopustite aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja vašim &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Ta je aplikacija potrebna za upravljanje vašim profilom <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Dopusti"</string>
<string name="consent_no" msgid="2640796915611404382">"Nemoj dopustiti"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-hu/strings.xml b/packages/CompanionDeviceManager/res/values-hu/strings.xml
index c7ceb384ed16..56f02a5808ab 100644
--- a/packages/CompanionDeviceManager/res/values-hu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hu/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Társeszközök kezelője"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"A(z) &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; engedélyezése a(z) &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; kezelésére"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"óra"</string>
<string name="chooser_title" msgid="2262294130493605839">"A(z) &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; alkalmazással kezelni kívánt <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kiválasztása"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"eszköz"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"óra"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"A(z) &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; engedélyezése a(z) &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; kezelésére"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Szükség van erre az alkalmazásra a következő kezeléséhez: <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Engedélyezés"</string>
<string name="consent_no" msgid="2640796915611404382">"Tiltás"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-hy/strings.xml b/packages/CompanionDeviceManager/res/values-hy/strings.xml
index 26f7990d7b66..cf22fbcf12a2 100644
--- a/packages/CompanionDeviceManager/res/values-hy/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hy/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Թույլատրեք &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; հավելվածին կառավարել ձեր &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; սարքը"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"ժամացույց"</string>
<string name="chooser_title" msgid="2262294130493605839">"Ընտրեք <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ը, որը պետք է կառավարվի &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; հավելվածի կողմից"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"սարք"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"ժամացույց"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Թույլատրեք &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; հավելվածին կառավարել ձեր &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; սարքը"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Այս հավելվածն անհրաժեշտ է <xliff:g id="PROFILE_NAME">%1$s</xliff:g> սարքը կամ պրոֆիլը կառավարելու համար։ <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Թույլատրել"</string>
<string name="consent_no" msgid="2640796915611404382">"Չթույլատրել"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-in/strings.xml b/packages/CompanionDeviceManager/res/values-in/strings.xml
index b0618d409527..41f1d096a01a 100644
--- a/packages/CompanionDeviceManager/res/values-in/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-in/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Pengelola Perangkat Pendamping"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Izinkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; untuk mengelola &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"smartwatch"</string>
<string name="chooser_title" msgid="2262294130493605839">"Pilih <xliff:g id="PROFILE_NAME">%1$s</xliff:g> untuk dikelola oleh &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"perangkat"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"smartwatch"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Izinkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; untuk mengelola &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Aplikasi ini diperlukan untuk mengelola <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Izinkan"</string>
<string name="consent_no" msgid="2640796915611404382">"Jangan izinkan"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-is/strings.xml b/packages/CompanionDeviceManager/res/values-is/strings.xml
index b7d7c6abf749..5376912c6479 100644
--- a/packages/CompanionDeviceManager/res/values-is/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-is/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Stjórnun fylgdartækja"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Veita &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; stjórn á: &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"úr"</string>
<string name="chooser_title" msgid="2262294130493605839">"Velja <xliff:g id="PROFILE_NAME">%1$s</xliff:g> sem &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; á að stjórna"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"tæki"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"úr"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Veita &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; stjórn á: &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Þetta forrit er nauðsynlegt til að hafa umsjón með <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Leyfa"</string>
<string name="consent_no" msgid="2640796915611404382">"Ekki leyfa"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-it/strings.xml b/packages/CompanionDeviceManager/res/values-it/strings.xml
index ce003e7d7645..af9e8ca38a10 100644
--- a/packages/CompanionDeviceManager/res/values-it/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-it/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Gestione dispositivi companion"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Consenti all\'app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; di gestire &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"orologio"</string>
<string name="chooser_title" msgid="2262294130493605839">"Scegli un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> che sia gestito da &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"orologio"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Consenti all\'app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; di gestire &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Questa app è necessaria per gestire il tuo <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Consenti"</string>
<string name="consent_no" msgid="2640796915611404382">"Non consentire"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-iw/strings.xml b/packages/CompanionDeviceManager/res/values-iw/strings.xml
index 54c523c92f4b..68ca9d94e1fb 100644
--- a/packages/CompanionDeviceManager/res/values-iw/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-iw/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"ניהול מכשיר מותאם"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"‏אישור לאפליקציה &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; לנהל את &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"שעון"</string>
<string name="chooser_title" msgid="2262294130493605839">"‏בחירת <xliff:g id="PROFILE_NAME">%1$s</xliff:g> לניהול באמצעות &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"מכשיר"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"שעון"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"‏אישור לאפליקציה &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; לנהל את &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"האפליקציה הזו נחוצה כדי לנהל את ה<xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"יש אישור"</string>
<string name="consent_no" msgid="2640796915611404382">"אין אישור"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ja/strings.xml b/packages/CompanionDeviceManager/res/values-ja/strings.xml
index f92fafe54fde..c10a1e1f5226 100644
--- a/packages/CompanionDeviceManager/res/values-ja/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ja/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"コンパニオン デバイス マネージャ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; に &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; の管理を許可する"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"ウォッチ"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; の管理対象となる<xliff:g id="PROFILE_NAME">%1$s</xliff:g>の選択"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"デバイス"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"ウォッチ"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; に &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; の管理を許可する"</string>
- <string name="profile_summary" msgid="2059360676631420073">"このアプリは <xliff:g id="PROFILE_NAME">%1$s</xliff:g> の管理に必要です。<xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"許可"</string>
<string name="consent_no" msgid="2640796915611404382">"許可しない"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ka/strings.xml b/packages/CompanionDeviceManager/res/values-ka/strings.xml
index 34efdd2223ac..637248170fe7 100644
--- a/packages/CompanionDeviceManager/res/values-ka/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ka/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"კომპანიონი მოწყობილობების მენეჯერი"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"ნება დართეთ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>-ს&lt;/strong&gt;, რომ მართოს თქვენი &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"საათი"</string>
<string name="chooser_title" msgid="2262294130493605839">"აირჩიეთ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, რომელიც უნდა მართოს &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;-მა"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"მოწყობილობა"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"საათი"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"ნება დართეთ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>-ს&lt;/strong&gt;, რომ მართოს თქვენი &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"ეს აპი საჭიროა თქვენი <xliff:g id="PROFILE_NAME">%1$s</xliff:g>-ს სამართავად. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"დაშვება"</string>
<string name="consent_no" msgid="2640796915611404382">"არ დაიშვას"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-kk/strings.xml b/packages/CompanionDeviceManager/res/values-kk/strings.xml
index 3c7f697b984f..6ac9c04732a5 100644
--- a/packages/CompanionDeviceManager/res/values-kk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-kk/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; қолданбасына &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; құрылғысын басқаруға рұқсат беру"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"сағат"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; арқылы басқарылатын <xliff:g id="PROFILE_NAME">%1$s</xliff:g> құрылғысын таңдаңыз"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"құрылғы"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"сағат"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; қолданбасына &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; құрылғысын басқаруға рұқсат беру"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Бұл қолданба <xliff:g id="PROFILE_NAME">%1$s</xliff:g> профиліңізді басқару үшін қажет. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Рұқсат беру"</string>
<string name="consent_no" msgid="2640796915611404382">"Рұқсат бермеу"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-km/strings.xml b/packages/CompanionDeviceManager/res/values-km/strings.xml
index 74ccd84956ea..db2634f9ec79 100644
--- a/packages/CompanionDeviceManager/res/values-km/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-km/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"កម្មវិធី​គ្រប់​គ្រង​ឧបករណ៍ដៃគូ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"អនុញ្ញាតឱ្យ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; គ្រប់គ្រង &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; របស់អ្នក"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"នាឡិកា"</string>
<string name="chooser_title" msgid="2262294130493605839">"ជ្រើសរើស <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ដើម្បីឱ្យស្ថិតក្រោម​ការគ្រប់គ្រងរបស់ &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"ឧបករណ៍"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"នាឡិកា"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"អនុញ្ញាតឱ្យ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; គ្រប់គ្រង &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; របស់អ្នក"</string>
- <string name="profile_summary" msgid="2059360676631420073">"ត្រូវការកម្មវិធីនេះ ដើម្បីគ្រប់គ្រង <xliff:g id="PROFILE_NAME">%1$s</xliff:g> របស់អ្នក។ <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"អនុញ្ញាត"</string>
<string name="consent_no" msgid="2640796915611404382">"កុំអនុញ្ញាត"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-kn/strings.xml b/packages/CompanionDeviceManager/res/values-kn/strings.xml
index 2a82d1fd6bac..e6413dac75d2 100644
--- a/packages/CompanionDeviceManager/res/values-kn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-kn/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"ಕಂಪ್ಯಾನಿಯನ್ ಸಾಧನ ನಿರ್ವಾಹಕರು"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"ನಿಮ್ಮ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ಅನ್ನು ನಿರ್ವಹಿಸಲು &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ಅನ್ನು ಅನುಮತಿಸಿ"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"ವೀಕ್ಷಿಸಿ"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ಮೂಲಕ ನಿರ್ವಹಿಸಬೇಕಾದ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ಅನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"ಸಾಧನ"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"ವೀಕ್ಷಿಸಿ"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"ನಿಮ್ಮ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ಅನ್ನು ನಿರ್ವಹಿಸಲು &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ಅನ್ನು ಅನುಮತಿಸಿ"</string>
- <string name="profile_summary" msgid="2059360676631420073">"ನಿಮ್ಮ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. ಅನ್ನು ನಿರ್ವಹಿಸಲು ಈ ಆ್ಯಪ್‌ನ ಅಗತ್ಯವಿದೆ. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"ಅನುಮತಿಸಿ"</string>
<string name="consent_no" msgid="2640796915611404382">"ಅನುಮತಿಸಬೇಡಿ"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ko/strings.xml b/packages/CompanionDeviceManager/res/values-ko/strings.xml
index 6ca01bff5364..02459d51cfbc 100644
--- a/packages/CompanionDeviceManager/res/values-ko/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ko/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"부속 기기 관리자"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;에서 &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; 기기를 관리하도록 허용"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"시계"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;에서 관리할 <xliff:g id="PROFILE_NAME">%1$s</xliff:g>을(를) 선택"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"기기"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"시계"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;에서 &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; 기기를 관리하도록 허용"</string>
- <string name="profile_summary" msgid="2059360676631420073">"이 앱은 <xliff:g id="PROFILE_NAME">%1$s</xliff:g> 프로필을 관리하는 데 필요합니다. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"허용"</string>
<string name="consent_no" msgid="2640796915611404382">"허용 안함"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ky/strings.xml b/packages/CompanionDeviceManager/res/values-ky/strings.xml
index 18d38a832e0b..ea4230add677 100644
--- a/packages/CompanionDeviceManager/res/values-ky/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ky/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; колдонмосуна &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; түзмөгүңүздү башкарууга уруксат бериңиз"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"саат"</string>
<string name="chooser_title" msgid="2262294130493605839">"<xliff:g id="PROFILE_NAME">%1$s</xliff:g> &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; тарабынан башкарылсын"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"түзмөк"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"саат"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; колдонмосуна &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; түзмөгүңүздү башкарууга уруксат бериңиз"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Бул колдонмо <xliff:g id="PROFILE_NAME">%1$s</xliff:g> профилиңизди башкаруу үчүн керек. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Уруксат берүү"</string>
<string name="consent_no" msgid="2640796915611404382">"Уруксат берилбесин"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-lo/strings.xml b/packages/CompanionDeviceManager/res/values-lo/strings.xml
index a1eb71342446..b6c6289cd029 100644
--- a/packages/CompanionDeviceManager/res/values-lo/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lo/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"ຕົວຈັດການອຸປະກອນປະກອບ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"ອະນຸຍາດໃຫ້ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ຈັດການ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ຂອງທ່ານໄດ້"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"ໂມງ"</string>
<string name="chooser_title" msgid="2262294130493605839">"ເລືອກ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ເພື່ອໃຫ້ຖືກຈັດການໂດຍ &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"ອຸປະກອນ"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"ໂມງ"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"ອະນຸຍາດໃຫ້ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ຈັດການ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ຂອງທ່ານໄດ້"</string>
- <string name="profile_summary" msgid="2059360676631420073">"ຕ້ອງໃຊ້ແອັບນີ້ເພື່ອຈັດການ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ຂອງທ່ານ. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"ອະນຸຍາດ"</string>
<string name="consent_no" msgid="2640796915611404382">"ບໍ່ອະນຸຍາດ"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-lt/strings.xml b/packages/CompanionDeviceManager/res/values-lt/strings.xml
index 65f371d3d312..e5ff48025338 100644
--- a/packages/CompanionDeviceManager/res/values-lt/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lt/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Leisti &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tvarkyti &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"laikrodį"</string>
<string name="chooser_title" msgid="2262294130493605839">"Jūsų <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, kurį valdys &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; (pasirinkite)"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"įrenginys"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"laikrodį"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Leisti &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tvarkyti &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Ši programa reikalinga norint tvarkyti jūsų <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Leisti"</string>
<string name="consent_no" msgid="2640796915611404382">"Neleisti"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-lv/strings.xml b/packages/CompanionDeviceManager/res/values-lv/strings.xml
index b18bfe4a4243..d5212401e27e 100644
--- a/packages/CompanionDeviceManager/res/values-lv/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lv/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Palīgierīču pārzinis"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Atļauja lietotnei &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pārvaldīt ierīci &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"pulkstenis"</string>
<string name="chooser_title" msgid="2262294130493605839">"Profila (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) izvēle, ko pārvaldīt lietotnē &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"ierīce"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"pulkstenis"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Atļauja lietotnei &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pārvaldīt ierīci &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Šī lietotne ir nepieciešama jūsu profila (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) pārvaldībai. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Atļaut"</string>
<string name="consent_no" msgid="2640796915611404382">"Neatļaut"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-mk/strings.xml b/packages/CompanionDeviceManager/res/values-mk/strings.xml
index 9d745c413b39..e6131e607f3b 100644
--- a/packages/CompanionDeviceManager/res/values-mk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mk/strings.xml
@@ -17,11 +17,19 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Дозволете &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да управува со вашиот &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"часовник"</string>
<string name="chooser_title" msgid="2262294130493605839">"Изберете <xliff:g id="PROFILE_NAME">%1$s</xliff:g> со којшто ќе управува &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="summary_watch" product="default" msgid="7113724443198337683">"<xliff:g id="APP_NAME">%1$s</xliff:g> ќе може да остварува интеракција со известувањата и да пристапува до дозволите за телефонот, SMS, контактите и календарот."</string>
+ <string name="summary_watch" product="tablet" msgid="7113724443198337683">"<xliff:g id="APP_NAME">%1$s</xliff:g> ќе може да остварува интеракција со известувањата и да пристапува до дозволите за телефонот, SMS, контактите и календарот."</string>
+ <string name="title_app_streaming" msgid="4459136600249308574">"Да се дозволи &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да стримува апликации?"</string>
+ <string name="summary_app_streaming" product="default" msgid="6105916810614498138">"Дозволете &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да обезбеди далечински пристап на &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; за да пристапува до апликации инсталирани на телефонов кога ќе се поврзе."</string>
+ <string name="summary_app_streaming" product="tablet" msgid="2996373715966272792">"Дозволете &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да обезбеди далечински пристап на &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; за да пристапува до апликации инсталирани на таблетов кога ќе се поврзе."</string>
+ <string name="summary_app_streaming" product="device" msgid="7614171699434639963">"Дозволете &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да обезбеди далечински пристап на &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; за да пристапува до апликации инсталирани на уредов кога ќе се поврзе."</string>
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"уред"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"часовник"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Дозволете &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да управува со вашиот &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Апликацијава е потребна за управување со вашиот <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Дозволи"</string>
<string name="consent_no" msgid="2640796915611404382">"Не дозволувај"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ml/strings.xml b/packages/CompanionDeviceManager/res/values-ml/strings.xml
index 28c88da9e4af..639909a3f260 100644
--- a/packages/CompanionDeviceManager/res/values-ml/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ml/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"കമ്പാനിയൻ ഉപകരണ മാനേജർ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"നിങ്ങളുടെ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; മാനേജ് ചെയ്യാൻ, &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; എന്നതിനെ അനുവദിക്കുക"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"വാച്ച്"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ഉപയോഗിച്ച് മാനേജ് ചെയ്യുന്നതിന് ഒരു <xliff:g id="PROFILE_NAME">%1$s</xliff:g> തിരഞ്ഞെടുക്കുക"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"ഉപകരണം"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"വാച്ച്"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"നിങ്ങളുടെ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; മാനേജ് ചെയ്യാൻ, &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; എന്നതിനെ അനുവദിക്കുക"</string>
- <string name="profile_summary" msgid="2059360676631420073">"നിങ്ങളുടെ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> മാനേജ് ചെയ്യാൻ ഈ ആപ്പ് ആവശ്യമാണ്. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"അനുവദിക്കുക"</string>
<string name="consent_no" msgid="2640796915611404382">"അനുവദിക്കരുത്"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-mn/strings.xml b/packages/CompanionDeviceManager/res/values-mn/strings.xml
index 11e61d910ee4..adbe62d041c5 100644
--- a/packages/CompanionDeviceManager/res/values-mn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mn/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;-г удирдахын тулд &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;-г зөвшөөрнө үү"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"цаг"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;-н удирдах<xliff:g id="PROFILE_NAME">%1$s</xliff:g>-г сонгоно уу"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"төхөөрөмж"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"цаг"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;-г удирдахын тулд &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;-г зөвшөөрнө үү"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Энэ апп таны <xliff:g id="PROFILE_NAME">%1$s</xliff:g>-г удирдахад шаардлагатай. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Зөвшөөрөх"</string>
<string name="consent_no" msgid="2640796915611404382">"Бүү зөвшөөр"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-mr/strings.xml b/packages/CompanionDeviceManager/res/values-mr/strings.xml
index c73ed283c768..fce058399702 100644
--- a/packages/CompanionDeviceManager/res/values-mr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mr/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"सहयोगी डिव्हाइस व्यवस्थापक"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"तुमचे &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; व्यवस्थापित करण्यासाठी &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ला अनुमती द्या"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"वॉच"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; द्वारे व्यवस्थापित करण्यासाठी <xliff:g id="PROFILE_NAME">%1$s</xliff:g> निवडा"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"डिव्हाइस"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"वॉच"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"तुमचे &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; व्यवस्थापित करण्यासाठी &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ला अनुमती द्या"</string>
- <string name="profile_summary" msgid="2059360676631420073">"तुमची <xliff:g id="PROFILE_NAME">%1$s</xliff:g> प्रोफाइल व्यवस्थापित करण्यासाठी हे ॲप आवश्यक आहे. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"अनुमती द्या"</string>
<string name="consent_no" msgid="2640796915611404382">"अनुमती देऊ नका"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ms/strings.xml b/packages/CompanionDeviceManager/res/values-ms/strings.xml
index d2aebb45f49e..5c4ec781132f 100644
--- a/packages/CompanionDeviceManager/res/values-ms/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ms/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Pengurus Peranti Rakan"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Benarkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; untuk mengurus &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; anda"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"jam tangan"</string>
<string name="chooser_title" msgid="2262294130493605839">"Pilih <xliff:g id="PROFILE_NAME">%1$s</xliff:g> untuk diurus oleh &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"peranti"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"jam tangan"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Benarkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; untuk mengurus &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; anda"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Apl ini diperlukan untuk mengurus <xliff:g id="PROFILE_NAME">%1$s</xliff:g> anda. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Benarkan"</string>
<string name="consent_no" msgid="2640796915611404382">"Jangan benarkan"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-my/strings.xml b/packages/CompanionDeviceManager/res/values-my/strings.xml
index 45c9d8b240fa..f3e572e0e542 100644
--- a/packages/CompanionDeviceManager/res/values-my/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-my/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"တွဲဖက်ကိရိယာ မန်နေဂျာ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"သင်၏ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ကို စီမံခန့်ခွဲရန် &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ကို ခွင့်ပြုပါ"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"နာရီ"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; က စီမံခန့်ခွဲရန် <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ကို ရွေးချယ်ပါ"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"စက်"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"နာရီ"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"သင်၏ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ကို စီမံခန့်ခွဲရန် &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ကို ခွင့်ပြုပါ"</string>
- <string name="profile_summary" msgid="2059360676631420073">"သင်၏ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ကို စီမံခန့်ခွဲရန် ဤအက်ပ်ကိုလိုအပ်သည်။ <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"ခွင့်ပြုရန်"</string>
<string name="consent_no" msgid="2640796915611404382">"ခွင့်မပြုပါ"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-nb/strings.xml b/packages/CompanionDeviceManager/res/values-nb/strings.xml
index af1ffe94cd36..d3eb7e595c8d 100644
--- a/packages/CompanionDeviceManager/res/values-nb/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-nb/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Tillat at &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; administrerer &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"klokke"</string>
<string name="chooser_title" msgid="2262294130493605839">"Velg <xliff:g id="PROFILE_NAME">%1$s</xliff:g> som skal administreres av &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"enhet"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"klokke"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Tillat at &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; administrerer &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Denne appen kreves for å administrere <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Tillat"</string>
<string name="consent_no" msgid="2640796915611404382">"Ikke tillat"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ne/strings.xml b/packages/CompanionDeviceManager/res/values-ne/strings.xml
index b29f94ce1b05..9bcf69b07fcc 100644
--- a/packages/CompanionDeviceManager/res/values-ne/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ne/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"सहयोगी डिभाइसको प्रबन्धक"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"आफ्नो &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; लाई &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; व्यवस्थापन गर्ने अनुमति दिनुहोस्"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"घडी"</string>
<string name="chooser_title" msgid="2262294130493605839">"आफूले &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; प्रयोग गरी व्यवस्थापन गर्न चाहेको <xliff:g id="PROFILE_NAME">%1$s</xliff:g> चयन गर्नुहोस्"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"यन्त्र"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"घडी"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"आफ्नो &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; लाई &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; व्यवस्थापन गर्ने अनुमति दिनुहोस्"</string>
- <string name="profile_summary" msgid="2059360676631420073">"तपाईंको <xliff:g id="PROFILE_NAME">%1$s</xliff:g> व्यवस्थापन गर्न यो एपलाई अनुमति दिनु पर्ने हुन्छ। <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"अनुमति दिनुहोस्"</string>
<string name="consent_no" msgid="2640796915611404382">"अनुमति नदिनुहोस्"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-nl/strings.xml b/packages/CompanionDeviceManager/res/values-nl/strings.xml
index a56fb9a62ce7..9ee09db08385 100644
--- a/packages/CompanionDeviceManager/res/values-nl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-nl/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; toestaan je &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; te beheren"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"horloge"</string>
<string name="chooser_title" msgid="2262294130493605839">"Een <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kiezen om te beheren met &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"apparaat"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"horloge"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; toestaan je &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; te beheren"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Deze app is vereist om je <xliff:g id="PROFILE_NAME">%1$s</xliff:g> te beheren. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Toestaan"</string>
<string name="consent_no" msgid="2640796915611404382">"Niet toestaan"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-or/strings.xml b/packages/CompanionDeviceManager/res/values-or/strings.xml
index 8e43213a9e43..e08ec28eeab5 100644
--- a/packages/CompanionDeviceManager/res/values-or/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-or/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"ସହଯୋଗୀ ଡିଭାଇସ୍ ପରିଚାଳକ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"ଆପଣଙ୍କ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;କୁ ପରିଚାଳନା କରିବା ପାଇଁ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;କୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"ୱାଚ୍"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ଦ୍ୱାରା ପରିଚାଳିତ ହେବା ପାଇଁ ଏକ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>କୁ ବାଛନ୍ତୁ"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"ଡିଭାଇସ୍"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"ୱାଚ୍"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"ଆପଣଙ୍କ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;କୁ ପରିଚାଳନା କରିବା ପାଇଁ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;କୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
- <string name="profile_summary" msgid="2059360676631420073">"ଆପଣଙ୍କ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>କୁ ପରିଚାଳନା କରିବା ପାଇଁ ଏହି ଆପ୍ ଆବଶ୍ୟକ। <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"ଅନୁମତି ଦିଅନ୍ତୁ"</string>
<string name="consent_no" msgid="2640796915611404382">"ଅନୁମତି ଦିଅନ୍ତୁ ନାହିଁ"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-pa/strings.xml b/packages/CompanionDeviceManager/res/values-pa/strings.xml
index 54f4f8c42302..e317a464f72a 100644
--- a/packages/CompanionDeviceManager/res/values-pa/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pa/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"ਸੰਬੰਧੀ ਡੀਵਾਈਸ ਪ੍ਰਬੰਧਕ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ਨੂੰ ਤੁਹਾਡੇ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"ਸਮਾਰਟ-ਵਾਚ"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ਵੱਲੋਂ ਪ੍ਰਬੰਧਿਤ ਕੀਤੇ ਜਾਣ ਲਈ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ਚੁਣੋ"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"ਡੀਵਾਈਸ"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"ਸਮਾਰਟ-ਵਾਚ"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ਨੂੰ ਤੁਹਾਡੇ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ"</string>
- <string name="profile_summary" msgid="2059360676631420073">"ਤੁਹਾਡੇ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਲਈ ਇਹ ਐਪ ਲੋੜੀਂਦੀ ਹੈ। <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"ਇਜਾਜ਼ਤ ਦਿਓ"</string>
<string name="consent_no" msgid="2640796915611404382">"ਇਜਾਜ਼ਤ ਨਾ ਦਿਓ"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-pl/strings.xml b/packages/CompanionDeviceManager/res/values-pl/strings.xml
index a989baa4e36c..6cb7cc68d72e 100644
--- a/packages/CompanionDeviceManager/res/values-pl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pl/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Menedżer urządzeń towarzyszących"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Zezwól na zarządzanie urządzeniem &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; przez aplikację &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"zegarek"</string>
<string name="chooser_title" msgid="2262294130493605839">"Wybierz profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, którym ma zarządzać aplikacja &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"urządzenie"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"zegarek"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Zezwól na zarządzanie urządzeniem &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; przez aplikację &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Ta aplikacja jest niezbędna do zarządzania profilem <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Zezwól"</string>
<string name="consent_no" msgid="2640796915611404382">"Nie zezwalaj"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
index d2724c0a8127..4306286937d7 100644
--- a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Gerenciador de dispositivos complementar"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Permitir que o app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gerencie seu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"relógio"</string>
<string name="chooser_title" msgid="2262294130493605839">"Escolha um <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para ser gerenciado pelo app &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"relógio"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Permitir que o app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gerencie seu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Esse app é necessário para gerenciar seu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
<string name="consent_no" msgid="2640796915611404382">"Não permitir"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
index 2f5a53b2e2a7..8f05d49c7cbb 100644
--- a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Gestor de dispositivos associados"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Permita que a app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; faça a gestão do seu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"relógio"</string>
<string name="chooser_title" msgid="2262294130493605839">"Escolha um <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para ser gerido pela app &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"relógio"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Permita que a app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; faça a gestão do seu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Esta app é necessária para gerir o seu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
<string name="consent_no" msgid="2640796915611404382">"Não permitir"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-pt/strings.xml b/packages/CompanionDeviceManager/res/values-pt/strings.xml
index d2724c0a8127..4306286937d7 100644
--- a/packages/CompanionDeviceManager/res/values-pt/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Gerenciador de dispositivos complementar"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Permitir que o app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gerencie seu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"relógio"</string>
<string name="chooser_title" msgid="2262294130493605839">"Escolha um <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para ser gerenciado pelo app &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"relógio"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Permitir que o app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gerencie seu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Esse app é necessário para gerenciar seu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
<string name="consent_no" msgid="2640796915611404382">"Não permitir"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ro/strings.xml b/packages/CompanionDeviceManager/res/values-ro/strings.xml
index 4df74de2a441..43a4de7b1cb5 100644
--- a/packages/CompanionDeviceManager/res/values-ro/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ro/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Manager de dispozitiv Companion"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Permiteți ca &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; să vă gestioneze dispozitivul &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"ceas"</string>
<string name="chooser_title" msgid="2262294130493605839">"Alegeți un profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> pe care să îl gestioneze &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispozitiv"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"ceas"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Permiteți ca &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; să vă gestioneze dispozitivul &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Această aplicație este necesară pentru a gestiona <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Permiteți"</string>
<string name="consent_no" msgid="2640796915611404382">"Nu permiteți"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ru/strings.xml b/packages/CompanionDeviceManager/res/values-ru/strings.xml
index ea372d51658b..6d5c0de9e012 100644
--- a/packages/CompanionDeviceManager/res/values-ru/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ru/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Управление подключенными устройствами"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Разрешите приложению &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; управлять этим устройством: &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"часы"</string>
<string name="chooser_title" msgid="2262294130493605839">"Выберите устройство (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), которым будет управлять приложение &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"устройство"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"часы"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Разрешите приложению &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; управлять этим устройством: &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Это приложение необходимо для управления вашим профилем \"<xliff:g id="PROFILE_NAME">%1$s</xliff:g>\". <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Разрешить"</string>
<string name="consent_no" msgid="2640796915611404382">"Запретить"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-si/strings.xml b/packages/CompanionDeviceManager/res/values-si/strings.xml
index a5c2c8842830..b4e28d8cdb65 100644
--- a/packages/CompanionDeviceManager/res/values-si/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-si/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"සහායක උපාංග කළමනාකරු"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; හට ඔබගේ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; කළමනාකරණය කිරීමට ඉඩ දෙන්න"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"ඔරලෝසුව"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; මගින් කළමනාකරණය කරනු ලැබීමට <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ක් තෝරන්න"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"උපාංගය"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"ඔරලෝසුව"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; හට ඔබගේ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; කළමනාකරණය කිරීමට ඉඩ දෙන්න"</string>
- <string name="profile_summary" msgid="2059360676631420073">"මෙම යෙදුමට ඔබගේ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> කළමනාකරණය කිරීමට අවශ්‍යයි. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"ඉඩ දෙන්න"</string>
<string name="consent_no" msgid="2640796915611404382">"ඉඩ නොදෙන්න"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sk/strings.xml b/packages/CompanionDeviceManager/res/values-sk/strings.xml
index a9bf77f809e5..4f86f08e83f9 100644
--- a/packages/CompanionDeviceManager/res/values-sk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sk/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Správca sprievodných zariadení"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Povoliť aplikácii &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; spravovať zariadenie &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"hodinky"</string>
<string name="chooser_title" msgid="2262294130493605839">"Vyberte profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, ktorý bude spravovať aplikácia &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"zariadenie"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"hodinky"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Povoliť aplikácii &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; spravovať zariadenie &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Táto aplikácia sa vyžaduje na správu profilu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Povoliť"</string>
<string name="consent_no" msgid="2640796915611404382">"Nepovoliť"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sl/strings.xml b/packages/CompanionDeviceManager/res/values-sl/strings.xml
index 4eb8f5029fcd..a54af21d3586 100644
--- a/packages/CompanionDeviceManager/res/values-sl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sl/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Upravitelj spremljevalnih naprav"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; dovolite upravljanje naprave &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"ura"</string>
<string name="chooser_title" msgid="2262294130493605839">"Izbira naprave <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, ki jo bo upravljala aplikacija &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"naprava"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"ura"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; dovolite upravljanje naprave &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Ta aplikacija je potrebna za upravljanje profila »<xliff:g id="PROFILE_NAME">%1$s</xliff:g>«. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Dovoli"</string>
<string name="consent_no" msgid="2640796915611404382">"Ne dovoli"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sq/strings.xml b/packages/CompanionDeviceManager/res/values-sq/strings.xml
index 34357b478a17..d3f97df1bff0 100644
--- a/packages/CompanionDeviceManager/res/values-sq/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sq/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Menaxheri i pajisjes shoqëruese"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Lejo që &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; të menaxhojë pajisjen tënde &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"ora inteligjente"</string>
<string name="chooser_title" msgid="2262294130493605839">"Zgjidh një profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> që do të menaxhohet nga &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"pajisja"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"ora inteligjente"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Lejo që &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; të menaxhojë pajisjen tënde &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Ky aplikacion nevojitet për të menaxhuar profilin tënd <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Lejo"</string>
<string name="consent_no" msgid="2640796915611404382">"Mos lejo"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sr/strings.xml b/packages/CompanionDeviceManager/res/values-sr/strings.xml
index 37af18534712..db8f291d6874 100644
--- a/packages/CompanionDeviceManager/res/values-sr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sr/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Менаџер придруженог уређаја"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Дозволите апликацији &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да управља уређајем &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"сат"</string>
<string name="chooser_title" msgid="2262294130493605839">"Одаберите профил <xliff:g id="PROFILE_NAME">%1$s</xliff:g> којим ће управљати апликација &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"уређај"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"сат"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Дозволите апликацији &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да управља уређајем &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Ова апликација је потребна за управљање профилом <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Дозволи"</string>
<string name="consent_no" msgid="2640796915611404382">"Не дозволи"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sv/strings.xml b/packages/CompanionDeviceManager/res/values-sv/strings.xml
index f78fadf8d864..733b2f799d29 100644
--- a/packages/CompanionDeviceManager/res/values-sv/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sv/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Tillåt att &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; hanterar din &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"klocka"</string>
<string name="chooser_title" msgid="2262294130493605839">"Välj en <xliff:g id="PROFILE_NAME">%1$s</xliff:g> för hantering av &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"enhet"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"klocka"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Tillåt att &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; hanterar din &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Appen behövs för att hantera <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Tillåt"</string>
<string name="consent_no" msgid="2640796915611404382">"Tillåt inte"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sw/strings.xml b/packages/CompanionDeviceManager/res/values-sw/strings.xml
index 495c44111290..02db2569f9eb 100644
--- a/packages/CompanionDeviceManager/res/values-sw/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sw/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Kidhibiti cha Vifaa Visaidizi"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Ruhusu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; idhibiti &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; yako"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"saa"</string>
<string name="chooser_title" msgid="2262294130493605839">"Chagua <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ili idhibitiwe na &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"kifaa"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"saa"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Ruhusu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; idhibiti &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; yako"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Programu hii inahitajika ili udhibiti wasifu wako wa <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Ruhusu"</string>
<string name="consent_no" msgid="2640796915611404382">"Usiruhusu"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ta/strings.xml b/packages/CompanionDeviceManager/res/values-ta/strings.xml
index 20845bd69562..3e998c8a1983 100644
--- a/packages/CompanionDeviceManager/res/values-ta/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ta/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"கம்பேனியன் சாதன நிர்வாகி"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"உங்கள் &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ஐ நிர்வகிக்க &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ஆப்ஸை அனுமதியுங்கள்"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"வாட்ச்"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ஆப்ஸ் நிர்வகிக்கக்கூடிய <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ஐத் தேர்ந்தெடுங்கள்"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"சாதனம்"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"வாட்ச்"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"உங்கள் &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ஐ நிர்வகிக்க &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ஆப்ஸை அனுமதியுங்கள்"</string>
- <string name="profile_summary" msgid="2059360676631420073">"உங்கள் <xliff:g id="PROFILE_NAME">%1$s</xliff:g> சுயவிவரத்தை நிர்வகிக்க இந்த ஆப்ஸ் தேவைப்படுகிறது. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"அனுமதி"</string>
<string name="consent_no" msgid="2640796915611404382">"அனுமதிக்க வேண்டாம்"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-te/strings.xml b/packages/CompanionDeviceManager/res/values-te/strings.xml
index c855cf2a8501..8c5312695c89 100644
--- a/packages/CompanionDeviceManager/res/values-te/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-te/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"సహచర పరికర మేనేజర్"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"మీ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;ను మేనేజ్ చేయడానికి &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ను అనుమతించండి;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"వాచ్"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ద్వారా మేనేజ్ చేయబడటానికి ఒక <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ను ఎంచుకోండి"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"పరికరం"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"వాచ్"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"మీ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;ను మేనేజ్ చేయడానికి &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ను అనుమతించండి;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"మీ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ను మేనేజ్ చేయడానికి ఈ యాప్ అవసరం. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"అనుమతించు"</string>
<string name="consent_no" msgid="2640796915611404382">"అనుమతించవద్దు"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-th/strings.xml b/packages/CompanionDeviceManager/res/values-th/strings.xml
index d78ada26cd8a..504731e9cb05 100644
--- a/packages/CompanionDeviceManager/res/values-th/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-th/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"อนุญาตให้ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; จัดการ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ของคุณ"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"นาฬิกา"</string>
<string name="chooser_title" msgid="2262294130493605839">"เลือก<xliff:g id="PROFILE_NAME">%1$s</xliff:g>ที่จะให้มีการจัดการโดย &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"อุปกรณ์"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"นาฬิกา"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"อนุญาตให้ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; จัดการ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ของคุณ"</string>
- <string name="profile_summary" msgid="2059360676631420073">"ต้องใช้แอปนี้ในการจัดการ<xliff:g id="PROFILE_NAME">%1$s</xliff:g>ของคุณ <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"อนุญาต"</string>
<string name="consent_no" msgid="2640796915611404382">"ไม่อนุญาต"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-tl/strings.xml b/packages/CompanionDeviceManager/res/values-tl/strings.xml
index 03165b43ab0f..30d5e5719f1d 100644
--- a/packages/CompanionDeviceManager/res/values-tl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-tl/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Kasamang Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Payagan ang &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; na pamahalaan ang iyong &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"relo"</string>
<string name="chooser_title" msgid="2262294130493605839">"Pumili ng <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para pamahalaan ng &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"relo"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Payagan ang &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; na pamahalaan ang iyong &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Kinakailangan ang app na ito para pamahalaan ang iyong <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Payagan"</string>
<string name="consent_no" msgid="2640796915611404382">"Huwag payagan"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-tr/strings.xml b/packages/CompanionDeviceManager/res/values-tr/strings.xml
index b2c1cf2fa832..1b1d71d36fb7 100644
--- a/packages/CompanionDeviceManager/res/values-tr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-tr/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; uygulaması &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; cihazınızı yönetebilsin mi?"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"saat"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; tarafından yönetilecek bir <xliff:g id="PROFILE_NAME">%1$s</xliff:g> seçin"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"cihaz"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"saat"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; uygulaması &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; cihazınızı yönetebilsin mi?"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Bu uygulama, <xliff:g id="PROFILE_NAME">%1$s</xliff:g> profilinizin yönetilmesi için gereklidir. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"İzin ver"</string>
<string name="consent_no" msgid="2640796915611404382">"İzin verme"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-uk/strings.xml b/packages/CompanionDeviceManager/res/values-uk/strings.xml
index 61b78e9a0b18..149841a84ead 100644
--- a/packages/CompanionDeviceManager/res/values-uk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-uk/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Диспетчер супутніх пристроїв"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Дозволити додатку &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; керувати вашим пристроєм &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"годинник"</string>
<string name="chooser_title" msgid="2262294130493605839">"Виберіть <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, яким керуватиме додаток &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"пристрій"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"годинник"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Дозволити додатку &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; керувати вашим пристроєм &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Цей додаток потрібен, щоб керувати профілем \"<xliff:g id="PROFILE_NAME">%1$s</xliff:g>\". <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Дозволити"</string>
<string name="consent_no" msgid="2640796915611404382">"Не дозволяти"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ur/strings.xml b/packages/CompanionDeviceManager/res/values-ur/strings.xml
index ee7992109a18..b467550324dc 100644
--- a/packages/CompanionDeviceManager/res/values-ur/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ur/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"ساتھی آلہ مینیجر"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"‏اپنے &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; کا نظم کرنے کے لیے ‎&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; کو اجازت دیں"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"دیکھیں"</string>
<string name="chooser_title" msgid="2262294130493605839">"‏&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; کے ذریعے نظم کئے جانے کیلئے <xliff:g id="PROFILE_NAME">%1$s</xliff:g> کو منتخب کریں"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"آلہ"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"دیکھیں"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"‏اپنے &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; کا نظم کرنے کے لیے ‎&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; کو اجازت دیں"</string>
- <string name="profile_summary" msgid="2059360676631420073">"اس ایپ کو آپ کے <xliff:g id="PROFILE_NAME">%1$s</xliff:g> کا نظم کرنے کی ضرورت ہے۔ <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"اجازت دیں"</string>
<string name="consent_no" msgid="2640796915611404382">"اجازت نہ دیں"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-uz/strings.xml b/packages/CompanionDeviceManager/res/values-uz/strings.xml
index 7221b6d24893..8505ca93c442 100644
--- a/packages/CompanionDeviceManager/res/values-uz/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-uz/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; qurilmasini boshqarish uchun &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ilovasiga ruxsat bering"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"soat"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; boshqaradigan <xliff:g id="PROFILE_NAME">%1$s</xliff:g> qurilmasini tanlang"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"qurilma"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"soat"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; qurilmasini boshqarish uchun &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ilovasiga ruxsat bering"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Bu ilova <xliff:g id="PROFILE_NAME">%1$s</xliff:g> profilini boshqarish uchun kerak. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Ruxsat"</string>
<string name="consent_no" msgid="2640796915611404382">"Ruxsat berilmasin"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-vi/strings.xml b/packages/CompanionDeviceManager/res/values-vi/strings.xml
index 2819e1df75ba..5dec271a9f71 100644
--- a/packages/CompanionDeviceManager/res/values-vi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-vi/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Cho phép &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; quản lý &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; của bạn"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"đồng hồ"</string>
<string name="chooser_title" msgid="2262294130493605839">"Chọn một <xliff:g id="PROFILE_NAME">%1$s</xliff:g> sẽ do &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; quản lý"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"thiết bị"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"đồng hồ"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Cho phép &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; quản lý &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; của bạn"</string>
- <string name="profile_summary" msgid="2059360676631420073">"Cần có ứng dụng này để quản lý <xliff:g id="PROFILE_NAME">%1$s</xliff:g> của bạn. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Cho phép"</string>
<string name="consent_no" msgid="2640796915611404382">"Không cho phép"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
index 1440c401673e..f41406790299 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"配套设备管理器"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"允许&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;管理您的&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"手表"</string>
<string name="chooser_title" msgid="2262294130493605839">"选择要由&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;管理的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"设备"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"手表"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"允许&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;管理您的&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"需要使用此应用,才能管理您的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>。<xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"允许"</string>
<string name="consent_no" msgid="2640796915611404382">"不允许"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
index e3f1eb1249f1..37f4bd9aa099 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"隨附裝置管理工具"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"允許 &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; 管理您的&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"手錶"</string>
<string name="chooser_title" msgid="2262294130493605839">"選擇由 &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; 管理的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"裝置"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"手錶"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"允許 &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; 管理您的&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"必須使用此應用程式,才能管理<xliff:g id="PROFILE_NAME">%1$s</xliff:g>。<xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"允許"</string>
<string name="consent_no" msgid="2640796915611404382">"不允許"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
index 9f4041dd0aff..76c837927c57 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"隨附裝置管理員"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;管理你的「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"手錶"</string>
<string name="chooser_title" msgid="2262294130493605839">"選擇要讓「<xliff:g id="APP_NAME">%2$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;管理的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"裝置"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"手錶"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;管理你的「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2059360676631420073">"需使用這個應用程式,才能管理「<xliff:g id="PROFILE_NAME">%1$s</xliff:g>」。<xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"允許"</string>
<string name="consent_no" msgid="2640796915611404382">"不允許"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-zu/strings.xml b/packages/CompanionDeviceManager/res/values-zu/strings.xml
index dc933ae21599..fdfda007fea4 100644
--- a/packages/CompanionDeviceManager/res/values-zu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zu/strings.xml
@@ -17,11 +17,25 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"Isiphathi sedivayisi esihambisanayo"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Vumela i-&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ukuthi iphathe i-&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; yakho"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"buka"</string>
<string name="chooser_title" msgid="2262294130493605839">"Khetha i-<xliff:g id="PROFILE_NAME">%1$s</xliff:g> ezophathwa yi-&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for summary_watch (7113724443198337683) -->
+ <skip />
+ <!-- no translation found for title_app_streaming (4459136600249308574) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (6105916810614498138) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (2996373715966272792) -->
+ <skip />
+ <!-- no translation found for summary_app_streaming (7614171699434639963) -->
+ <skip />
+ <string name="title_automotive_projection" msgid="3296005598978412847"></string>
+ <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="profile_name_generic" msgid="6851028682723034988">"idivayisi"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"buka"</string>
- <string name="confirmation_title" msgid="8455544820286920304">"Vumela i-&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ukuthi iphathe i-&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; yakho"</string>
- <string name="profile_summary" msgid="2059360676631420073">"I-app iyadingeka ukuphatha i-<xliff:g id="PROFILE_NAME">%1$s</xliff:g> yakho. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Vumela"</string>
<string name="consent_no" msgid="2640796915611404382">"Ungavumeli"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values/dimens.xml b/packages/CompanionDeviceManager/res/values/dimens.xml
deleted file mode 100644
index da7b0d1447c1..000000000000
--- a/packages/CompanionDeviceManager/res/values/dimens.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-
- <!-- Padding applied on most UI elements -->
- <dimen name="padding">12dp</dimen>
-
-</resources> \ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml
index 44748e9cc692..cb8b616ec009 100644
--- a/packages/CompanionDeviceManager/res/values/strings.xml
+++ b/packages/CompanionDeviceManager/res/values/strings.xml
@@ -19,25 +19,58 @@
<!-- Title of the CompanionDeviceManager application. [CHAR LIMIT=50] -->
<string name="app_label">Companion Device Manager</string>
- <!-- Title of the device selection dialog. -->
- <string name="chooser_title">Choose a <xliff:g id="profile_name" example="watch">%1$s</xliff:g> to be managed by &lt;strong&gt;<xliff:g id="app_name" example="Android Wear">%2$s</xliff:g>&lt;/strong&gt;</string>
+ <!-- Title of the device association confirmation dialog. -->
+ <string name="confirmation_title">Allow &lt;strong&gt;<xliff:g id="app_name" example="Android Wear">%1$s</xliff:g>&lt;/strong&gt; to manage your &lt;strong&gt;<xliff:g id="device_name" example="ASUS ZenWatch 2">%2$s</xliff:g>&lt;/strong&gt;</string>
- <!-- The generic placeholder for a device type when nothing specific is known about it [CHAR LIMIT=30] -->
- <string name="profile_name_generic">device</string>
+ <!-- ================= DEVICE_PROFILE_WATCH and null profile ================= -->
<!-- The name of the "watch" device type [CHAR LIMIT=30] -->
<string name="profile_name_watch">watch</string>
- <!-- Title of the device association confirmation dialog. -->
- <string name="confirmation_title">Allow &lt;strong&gt;<xliff:g id="app_name" example="Android Wear">%1$s</xliff:g>&lt;/strong&gt; to manage your &lt;strong&gt;<xliff:g id="device_name" example="ASUS ZenWatch 2">%2$s</xliff:g>&lt;/strong&gt;</string>
+ <!-- Title of the device selection dialog. -->
+ <string name="chooser_title">Choose a <xliff:g id="profile_name" example="watch">%1$s</xliff:g> to be managed by &lt;strong&gt;<xliff:g id="app_name" example="Android Wear">%2$s</xliff:g>&lt;/strong&gt;</string>
+
+ <!-- Description of the privileges the application will get if associated with the companion device of WATCH profile (type) [CHAR LIMIT=NONE] -->
+ <string name="summary_watch" product="default"><xliff:g id="app_name" example="Wear">%1$s</xliff:g> will be allowed to interact with your notifications and access your Phone, SMS, Contacts and Calendar permissions.</string>
+
+ <!-- Description of the privileges the application will get if associated with the companion device of WATCH profile (type) [CHAR LIMIT=NONE] -->
+ <string name="summary_watch" product="tablet"><xliff:g id="app_name" example="Wear">%1$s</xliff:g> will be allowed to interact with your notifications and access your Phone, SMS, Contacts and Calendar permissions.</string>
+
+ <!-- ================= DEVICE_PROFILE_APP_STREAMING ================= -->
- <!-- Text of the device profile permissions explanation in the association dialog. -->
- <string name="profile_summary">This app is needed to manage your <xliff:g id="profile_name" example="watch">%1$s</xliff:g>. <xliff:g id="privileges_discplaimer" example="Android Wear will get access to your Notifications, Calendar and Contacts.">%2$s</xliff:g></string>
+ <!-- Confirmation for associating an application with a companion device of APP_STREAMING profile (type) [CHAR LIMIT=NONE] -->
+ <string name="title_app_streaming">Allow &lt;strong&gt;<xliff:g id="app_name" example="Exo">%1$s</xliff:g>&lt;/strong&gt; to stream applications?</string>
+
+ <!-- Description of the privileges the application will get if associated with the companion device of APP_STREAMING profile (type) [CHAR LIMIT=NONE] -->
+ <string name="summary_app_streaming" product="default">Let &lt;strong&gt;<xliff:g id="app_name" example="Exo">%1$s</xliff:g>&lt;/strong&gt; to provide &lt;strong&gt;<xliff:g id="device_name" example="Pixelbook Go">%2$s</xliff:g>&lt;/strong&gt; remote access to access to applications installed on this phone when connected.</string>
+
+ <!-- Description of the privileges the application will get if associated with the companion device of APP_STREAMING profile (type) [CHAR LIMIT=NONE] -->
+ <string name="summary_app_streaming" product="tablet">Let &lt;strong&gt;<xliff:g id="app_name" example="Exo">%1$s</xliff:g>&lt;/strong&gt; to provide &lt;strong&gt;<xliff:g id="device_name" example="Pixelbook Go">%2$s</xliff:g>&lt;/strong&gt; remote access to access to applications installed on this tablet when connected.</string>
+
+ <!-- Description of the privileges the application will get if associated with the companion device of APP_STREAMING profile (type) [CHAR LIMIT=NONE] -->
+ <string name="summary_app_streaming" product="device">Let &lt;strong&gt;<xliff:g id="app_name" example="Exo">%1$s</xliff:g>&lt;/strong&gt; to provide &lt;strong&gt;<xliff:g id="device_name" example="Pixelbook Go">%2$s</xliff:g>&lt;/strong&gt; remote access to access to applications installed on this device when connected.</string>
+
+ <!-- ================= DEVICE_PROFILE_AUTOMOTIVE_PROJECTION ================= -->
+
+ <!-- Confirmation for associating an application with a companion device of AUTOMOTIVE_PROJECTION profile (type) [CHAR LIMIT=NONE] -->
+ <string name="title_automotive_projection"></string>
+
+ <!-- Description of the privileges the application will get if associated with the companion device of AUTOMOTIVE_PROJECTION profile (type) [CHAR LIMIT=NONE] -->
+ <string name="summary_automotive_projection"></string>
+
+ <!-- ================= null profile ================= -->
+
+ <!-- A noun for a companion device with unspecified profile (type) [CHAR LIMIT=30] -->
+ <string name="profile_name_generic">device</string>
+
+ <!-- Description of the privileges the application will get if associated with the companion device of unspecified profile (type) [CHAR LIMIT=NONE] -->
+ <string name="summary_generic"></string>
+
+ <!-- ================= Buttons ================= -->
<!-- Positive button for the device-app association consent dialog [CHAR LIMIT=30] -->
<string name="consent_yes">Allow</string>
<!-- Negative button for the device-app association consent dialog [CHAR LIMIT=30] -->
<string name="consent_no">Don\u2019t allow</string>
-
</resources>
diff --git a/packages/CompanionDeviceManager/res/values/styles.xml b/packages/CompanionDeviceManager/res/values/styles.xml
deleted file mode 100644
index 9dced47bb36f..000000000000
--- a/packages/CompanionDeviceManager/res/values/styles.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<resources>
- <style name="ContainerLayout">
- <item name="android:orientation">vertical</item>
- <item name="android:layout_width">match_parent</item>
- <item name="android:elevation">16dp</item>
- <item name="android:background">@drawable/dialog_background</item>
- <item name="android:paddingTop">18dip</item>
- <item name="android:paddingStart">20dip</item>
- <item name="android:paddingEnd">16dip</item>
- <item name="android:paddingBottom">16dip</item>
- <item name="android:layout_gravity">center</item>
- </style>
-</resources> \ No newline at end of file
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
index a5168ccd977c..cc887c34414e 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
@@ -16,327 +16,356 @@
package com.android.companiondevicemanager;
-import static android.companion.BluetoothDeviceFilterUtils.getDeviceMacAddress;
-import static android.text.TextUtils.emptyIfNull;
-import static android.text.TextUtils.isEmpty;
-import static android.text.TextUtils.withoutPrefix;
+import static android.companion.AssociationRequest.DEVICE_PROFILE_APP_STREAMING;
+import static android.companion.AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION;
+import static android.companion.AssociationRequest.DEVICE_PROFILE_WATCH;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+import static com.android.companiondevicemanager.Utils.getApplicationLabel;
+import static com.android.companiondevicemanager.Utils.getHtmlFromResources;
+import static com.android.companiondevicemanager.Utils.prepareResultReceiverForIpc;
+
import static java.util.Objects.requireNonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
+import android.companion.AssociationInfo;
import android.companion.AssociationRequest;
import android.companion.CompanionDeviceManager;
+import android.companion.IAssociationRequestCallback;
import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.database.DataSetObserver;
-import android.graphics.Color;
-import android.graphics.drawable.Drawable;
+import android.net.MacAddress;
import android.os.Bundle;
-import android.text.Html;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.text.Spanned;
import android.util.Log;
-import android.util.SparseArray;
-import android.util.TypedValue;
-import android.view.Gravity;
import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
+import android.widget.Button;
import android.widget.ListView;
-import android.widget.ProgressBar;
import android.widget.TextView;
-import com.android.companiondevicemanager.CompanionDeviceDiscoveryService.DeviceFilterPair;
-import com.android.internal.util.Preconditions;
-
public class CompanionDeviceActivity extends Activity {
-
private static final boolean DEBUG = false;
- private static final String LOG_TAG = CompanionDeviceActivity.class.getSimpleName();
+ private static final String TAG = CompanionDeviceActivity.class.getSimpleName();
+
+ // Keep the following constants in sync with
+ // frameworks/base/services/companion/java/
+ // com/android/server/companion/AssociationRequestsProcessor.java
+
+ // AssociationRequestsProcessor <-> UI
+ private static final String EXTRA_APPLICATION_CALLBACK = "application_callback";
+ private static final String EXTRA_ASSOCIATION_REQUEST = "association_request";
+ private static final String EXTRA_RESULT_RECEIVER = "result_receiver";
+
+ // AssociationRequestsProcessor -> UI
+ private static final int RESULT_CODE_ASSOCIATION_CREATED = 0;
+ private static final String EXTRA_ASSOCIATION = "association";
- static CompanionDeviceActivity sInstance;
+ // UI -> AssociationRequestsProcessor
+ private static final int RESULT_CODE_ASSOCIATION_APPROVED = 0;
+ private static final String EXTRA_MAC_ADDRESS = "mac_address";
- View mLoadingIndicator = null;
- ListView mDeviceListView;
- private View mPairButton;
- private View mCancelButton;
+ private AssociationRequest mRequest;
+ private IAssociationRequestCallback mAppCallback;
+ private ResultReceiver mCdmServiceReceiver;
- DevicesAdapter mDevicesAdapter;
+ // Always present widgets.
+ private TextView mTitle;
+ private TextView mSummary;
+
+ // Progress indicator is only shown while we are looking for the first suitable device for a
+ // "regular" (ie. not self-managed) association.
+ private View mProgressIndicator;
+
+ // Present for self-managed association requests and "single-device" regular association
+ // regular.
+ private Button mButtonAllow;
+
+ // The list is only shown for multiple-device regular association request, after at least one
+ // matching device is found.
+ private @Nullable ListView mListView;
+ private @Nullable DeviceListAdapter mAdapter;
+
+ // The flag used to prevent double taps, that may lead to sending several requests for creating
+ // an association to CDM.
+ private boolean mAssociationApproved;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+ }
- Log.i(LOG_TAG, "Starting UI for " + getService().mRequest);
+ @Override
+ protected void onStart() {
+ super.onStart();
+ if (DEBUG) Log.d(TAG, "onStart()");
+
+ final Intent intent = getIntent();
+ mRequest = intent.getParcelableExtra(EXTRA_ASSOCIATION_REQUEST);
+ mAppCallback = IAssociationRequestCallback.Stub.asInterface(
+ intent.getExtras().getBinder(EXTRA_APPLICATION_CALLBACK));
+ mCdmServiceReceiver = intent.getParcelableExtra(EXTRA_RESULT_RECEIVER);
+
+ requireNonNull(mRequest);
+ requireNonNull(mAppCallback);
+ requireNonNull(mCdmServiceReceiver);
+
+ // Start discovery services if needed.
+ if (!mRequest.isSelfManaged()) {
+ CompanionDeviceDiscoveryService.startForRequest(this, mRequest);
+ }
+ // Init UI.
+ initUI();
+ }
- if (getService().mDevicesFound.isEmpty()) {
- Log.e(LOG_TAG, "About to show UI, but no devices to show");
+ @Override
+ protected void onStop() {
+ super.onStop();
+ if (DEBUG) Log.d(TAG, "onStop(), finishing=" + isFinishing());
+
+ // TODO: handle config changes without cancelling.
+ if (!isFinishing()) {
+ cancel(); // will finish()
}
- getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
- sInstance = this;
- getService().mActivity = this;
-
- String deviceProfile = getRequest().getDeviceProfile();
- String profilePrivacyDisclaimer = emptyIfNull(getRequest()
- .getDeviceProfilePrivilegesDescription())
- .replace("APP_NAME", getCallingAppName());
- boolean useDeviceProfile = deviceProfile != null && !isEmpty(profilePrivacyDisclaimer);
- String profileName = useDeviceProfile
- ? getDeviceProfileName(deviceProfile)
- : getString(R.string.profile_name_generic);
-
- if (getRequest().isSingleDevice()) {
- setContentView(R.layout.device_confirmation);
- final DeviceFilterPair selectedDevice = getService().mDevicesFound.get(0);
- setTitle(Html.fromHtml(getString(
- R.string.confirmation_title,
- Html.escapeHtml(getCallingAppName()),
- Html.escapeHtml(selectedDevice.getDisplayName())), 0));
-
- mPairButton = findViewById(R.id.button_pair);
- mPairButton.setOnClickListener(v -> onDeviceConfirmed(getService().mSelectedDevice));
- getService().mSelectedDevice = selectedDevice;
- onSelectionUpdate();
- if (getRequest().isSkipPrompt()) {
- onDeviceConfirmed(selectedDevice);
- }
- } else {
- setContentView(R.layout.device_chooser);
- mPairButton = findViewById(R.id.button_pair);
- mPairButton.setVisibility(View.GONE);
- setTitle(Html.fromHtml(getString(R.string.chooser_title,
- Html.escapeHtml(profileName),
- Html.escapeHtml(getCallingAppName())), 0));
- mDeviceListView = findViewById(R.id.device_list);
- mDevicesAdapter = new DevicesAdapter();
- mDeviceListView.setAdapter(mDevicesAdapter);
- mDeviceListView.setOnItemClickListener((adapterView, view, pos, l) -> {
- getService().mSelectedDevice =
- (DeviceFilterPair) adapterView.getItemAtPosition(pos);
- mDevicesAdapter.notifyDataSetChanged();
- });
- mDevicesAdapter.registerDataSetObserver(new DataSetObserver() {
- @Override
- public void onChanged() {
- onSelectionUpdate();
- }
- });
- mDeviceListView.addFooterView(mLoadingIndicator = getProgressBar(), null, false);
+ // mAdapter may be observing - need to remove it.
+ CompanionDeviceDiscoveryService.SCAN_RESULTS_OBSERVABLE.deleteObservers();
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ // Handle another incoming request (while we are not done with the original - mRequest -
+ // yet).
+ final AssociationRequest request = requireNonNull(
+ intent.getParcelableExtra(EXTRA_ASSOCIATION_REQUEST));
+ if (DEBUG) Log.d(TAG, "onNewIntent(), request=" + request);
+
+ // We can only "process" one request at a time.
+ final IAssociationRequestCallback appCallback = IAssociationRequestCallback.Stub
+ .asInterface(intent.getExtras().getBinder(EXTRA_APPLICATION_CALLBACK));
+ try {
+ requireNonNull(appCallback).onFailure("Busy.");
+ } catch (RemoteException ignore) {
}
+ }
+
+ private void initUI() {
+ if (DEBUG) Log.d(TAG, "initUI(), request=" + mRequest);
+
+ setContentView(R.layout.activity_confirmation);
+
+ mTitle = findViewById(R.id.title);
+ mSummary = findViewById(R.id.summary);
+
+ mListView = findViewById(R.id.device_list);
+ mListView.setOnItemClickListener((av, iv, position, id) -> onListItemClick(position));
+
+ mButtonAllow = findViewById(R.id.button_allow);
+ mButtonAllow.setOnClickListener(this::onAllowButtonClick);
- TextView profileSummary = findViewById(R.id.profile_summary);
+ findViewById(R.id.button_cancel).setOnClickListener(v -> cancel());
- if (useDeviceProfile) {
- profileSummary.setVisibility(View.VISIBLE);
- String deviceRef = getRequest().isSingleDevice()
- ? getService().mDevicesFound.get(0).getDisplayName()
- : profileName;
- profileSummary.setText(getString(R.string.profile_summary,
- deviceRef,
- profilePrivacyDisclaimer));
+ final CharSequence appLabel = getApplicationLabel(this, mRequest.getPackageName());
+ if (mRequest.isSelfManaged()) {
+ initUiForSelfManagedAssociation(appLabel);
+ } else if (mRequest.isSingleDevice()) {
+ initUiForSingleDevice(appLabel);
} else {
- profileSummary.setVisibility(View.GONE);
+ initUiForMultipleDevices(appLabel);
}
+ }
+
+ private void onAssociationCreated(@NonNull AssociationInfo association) {
+ if (DEBUG) Log.i(TAG, "onAssociationCreated(), association=" + association);
- mCancelButton = findViewById(R.id.button_cancel);
- mCancelButton.setOnClickListener(v -> cancel());
+ // Don't need to notify the app, CdmService has already done that. Just finish.
+ setResultAndFinish(association);
}
- static void notifyDevicesChanged() {
- if (sInstance != null && sInstance.mDevicesAdapter != null && !sInstance.isFinishing()) {
- sInstance.mDevicesAdapter.notifyDataSetChanged();
+ private void cancel() {
+ if (DEBUG) Log.i(TAG, "cancel()");
+
+ // Stop discovery service if it was used.
+ if (!mRequest.isSelfManaged()) {
+ CompanionDeviceDiscoveryService.stop(this);
}
- }
- private AssociationRequest getRequest() {
- return getService().mRequest;
+ // First send callback to the app directly...
+ try {
+ mAppCallback.onFailure("Cancelled.");
+ } catch (RemoteException ignore) {
+ }
+
+ // ... then set result and finish ("sending" onActivityResult()).
+ setResultAndFinish(null);
}
- private String getDeviceProfileName(@Nullable String deviceProfile) {
- if (deviceProfile == null) {
- return getString(R.string.profile_name_generic);
- }
- switch (deviceProfile) {
- case AssociationRequest.DEVICE_PROFILE_WATCH: {
- return getString(R.string.profile_name_watch);
- }
- default: {
- Log.w(LOG_TAG,
- "No localized profile name found for device profile: " + deviceProfile);
- return withoutPrefix("android.app.role.COMPANION_DEVICE_", deviceProfile)
- .toLowerCase()
- .replace('_', ' ');
+ private void setResultAndFinish(@Nullable AssociationInfo association) {
+ if (DEBUG) Log.i(TAG, "setResultAndFinish(), association=" + association);
+
+ final Intent data = new Intent();
+ if (association != null) {
+ data.putExtra(CompanionDeviceManager.EXTRA_ASSOCIATION, association);
+ if (!association.isSelfManaged()) {
+ data.putExtra(CompanionDeviceManager.EXTRA_DEVICE,
+ association.getDeviceMacAddressAsString());
}
}
- }
+ setResult(association != null ? RESULT_OK : RESULT_CANCELED, data);
- private void cancel() {
- Log.i(LOG_TAG, "cancel()");
- getService().onCancel();
- setResult(RESULT_CANCELED);
finish();
}
- @Override
- protected void onStop() {
- super.onStop();
- if (!isFinishing() && !isChangingConfigurations()) {
- Log.i(LOG_TAG, "onStop() - cancelling");
- cancel();
- }
- }
+ private void initUiForSelfManagedAssociation(CharSequence appLabel) {
+ if (DEBUG) Log.i(TAG, "initUiFor_SelfManaged_Association()");
- @Override
- protected void onDestroy() {
- super.onDestroy();
- getService().mActivity = null;
- if (sInstance == this) {
- sInstance = null;
+ final CharSequence deviceName = mRequest.getDisplayName(); // "<device>";
+ final String deviceProfile = mRequest.getDeviceProfile(); // DEVICE_PROFILE_APP_STREAMING;
+
+ final Spanned title;
+ final Spanned summary;
+ switch (deviceProfile) {
+ case DEVICE_PROFILE_APP_STREAMING:
+ title = getHtmlFromResources(this, R.string.title_app_streaming, appLabel);
+ summary = getHtmlFromResources(
+ this, R.string.summary_app_streaming, appLabel, deviceName);
+ break;
+
+ case DEVICE_PROFILE_AUTOMOTIVE_PROJECTION:
+ title = getHtmlFromResources(this, R.string.title_automotive_projection, appLabel);
+ summary = getHtmlFromResources(
+ this, R.string.summary_automotive_projection, appLabel, deviceName);
+ break;
+
+ default:
+ throw new RuntimeException("Unsupported profile " + deviceProfile);
}
+ mTitle.setText(title);
+ mSummary.setText(summary);
+
+ mListView.setVisibility(View.GONE);
}
- private CharSequence getCallingAppName() {
- try {
- final PackageManager packageManager = getPackageManager();
- String callingPackage = Preconditions.checkStringNotEmpty(
- getCallingPackage(),
- "This activity must be called for result");
- return packageManager.getApplicationLabel(
- packageManager.getApplicationInfo(callingPackage, 0));
- } catch (PackageManager.NameNotFoundException e) {
- throw new RuntimeException(e);
+ private void initUiForSingleDevice(CharSequence appLabel) {
+ if (DEBUG) Log.i(TAG, "initUiFor_SingleDevice()");
+
+ // TODO: use real name
+ final String deviceName = "<device>";
+ final String deviceProfile = mRequest.getDeviceProfile();
+
+ final Spanned title = getHtmlFromResources(
+ this, R.string.confirmation_title, appLabel, deviceName);
+ final Spanned summary;
+ if (deviceProfile == null) {
+ summary = getHtmlFromResources(this, R.string.summary_generic);
+ } else if (deviceProfile.equals(DEVICE_PROFILE_WATCH)) {
+ summary = getHtmlFromResources(this, R.string.summary_watch, appLabel, deviceName);
+ } else {
+ throw new RuntimeException("Unsupported profile " + deviceProfile);
}
- }
- @Override
- public String getCallingPackage() {
- return requireNonNull(getRequest().getCallingPackage());
- }
+ mTitle.setText(title);
+ mSummary.setText(summary);
- @Override
- public void setTitle(CharSequence title) {
- final TextView titleView = findViewById(R.id.title);
- final int padding = getPadding(getResources());
- titleView.setPadding(padding, padding, padding, padding);
- titleView.setText(title);
+ mListView.setVisibility(View.GONE);
}
- private ProgressBar getProgressBar() {
- final ProgressBar progressBar = new ProgressBar(this);
- progressBar.setForegroundGravity(Gravity.CENTER_HORIZONTAL);
- final int padding = getPadding(getResources());
- progressBar.setPadding(padding, padding, padding, padding);
- return progressBar;
- }
+ private void initUiForMultipleDevices(CharSequence appLabel) {
+ if (DEBUG) Log.i(TAG, "initUiFor_MultipleDevices()");
- static int getPadding(Resources r) {
- return r.getDimensionPixelSize(R.dimen.padding);
- }
+ final String deviceProfile = mRequest.getDeviceProfile();
- private void onSelectionUpdate() {
- DeviceFilterPair selectedDevice = getService().mSelectedDevice;
- if (mPairButton.getVisibility() != View.VISIBLE && selectedDevice != null) {
- onDeviceConfirmed(selectedDevice);
+ final String profileName;
+ final Spanned summary;
+ if (deviceProfile == null) {
+ profileName = getString(R.string.profile_name_generic);
+ summary = getHtmlFromResources(this, R.string.summary_generic);
+ } else if (deviceProfile.equals(DEVICE_PROFILE_WATCH)) {
+ profileName = getString(R.string.profile_name_watch);
+ summary = getHtmlFromResources(this, R.string.summary_watch, appLabel);
} else {
- mPairButton.setEnabled(selectedDevice != null);
+ throw new RuntimeException("Unsupported profile " + deviceProfile);
}
- }
+ final Spanned title = getHtmlFromResources(
+ this, R.string.chooser_title, profileName, appLabel);
- private CompanionDeviceDiscoveryService getService() {
- return CompanionDeviceDiscoveryService.sInstance;
- }
+ mTitle.setText(title);
+ mSummary.setText(summary);
- protected void onDeviceConfirmed(DeviceFilterPair selectedDevice) {
- Log.i(LOG_TAG, "onDeviceConfirmed(selectedDevice = " + selectedDevice + ")");
- getService().onDeviceSelected(
- getCallingPackage(), getDeviceMacAddress(selectedDevice.device));
- }
+ mAdapter = new DeviceListAdapter(this);
+ CompanionDeviceDiscoveryService.SCAN_RESULTS_OBSERVABLE.addObserver(mAdapter);
+ // TODO: hide the list and show a spinner until a first device matching device is found.
+ mListView.setAdapter(mAdapter);
- void setResultAndFinish() {
- Log.i(LOG_TAG, "setResultAndFinish(selectedDevice = "
- + getService().mSelectedDevice.device + ")");
- setResult(RESULT_OK,
- new Intent().putExtra(
- CompanionDeviceManager.EXTRA_DEVICE, getService().mSelectedDevice.device));
- finish();
+ // "Remove" consent button: users would need to click on the list item.
+ mButtonAllow.setVisibility(View.GONE);
}
- class DevicesAdapter extends BaseAdapter {
- private final Drawable mBluetoothIcon = icon(android.R.drawable.stat_sys_data_bluetooth);
- private final Drawable mWifiIcon = icon(com.android.internal.R.drawable.ic_wifi_signal_3);
+ private void onListItemClick(int position) {
+ if (DEBUG) Log.d(TAG, "onListItemClick() " + position);
- private SparseArray<Integer> mColors = new SparseArray();
+ final DeviceFilterPair<?> selectedDevice = mAdapter.getItem(position);
+ final MacAddress macAddress = selectedDevice.getMacAddress();
+ onAssociationApproved(macAddress);
+ }
- private Drawable icon(int drawableRes) {
- Drawable icon = getResources().getDrawable(drawableRes, null);
- icon.setTint(Color.DKGRAY);
- return icon;
- }
+ private void onAllowButtonClick(View v) {
+ if (DEBUG) Log.d(TAG, "onAllowButtonClick()");
- @Override
- public View getView(
- int position,
- @Nullable View convertView,
- @NonNull ViewGroup parent) {
- TextView view = convertView instanceof TextView
- ? (TextView) convertView
- : newView();
- bind(view, getItem(position));
- return view;
- }
+ // Disable the button, to prevent more clicks.
+ v.setEnabled(false);
- private void bind(TextView textView, DeviceFilterPair device) {
- textView.setText(device.getDisplayName());
- textView.setBackgroundColor(
- device.equals(getService().mSelectedDevice)
- ? getColor(android.R.attr.colorControlHighlight)
- : Color.TRANSPARENT);
- textView.setCompoundDrawablesWithIntrinsicBounds(
- device.device instanceof android.net.wifi.ScanResult
- ? mWifiIcon
- : mBluetoothIcon,
- null, null, null);
- textView.getCompoundDrawables()[0].setTint(getColor(android.R.attr.colorForeground));
+ final MacAddress macAddress;
+ if (mRequest.isSelfManaged()) {
+ macAddress = null;
+ } else {
+ // TODO: implement.
+ throw new UnsupportedOperationException(
+ "isSingleDevice() requests are not supported yet.");
}
+ onAssociationApproved(macAddress);
+ }
- private TextView newView() {
- final TextView textView = new TextView(CompanionDeviceActivity.this);
- textView.setTextColor(getColor(android.R.attr.colorForeground));
- final int padding = CompanionDeviceActivity.getPadding(getResources());
- textView.setPadding(padding, padding, padding, padding);
- textView.setCompoundDrawablePadding(padding);
- return textView;
- }
+ private void onAssociationApproved(@Nullable MacAddress macAddress) {
+ if (mAssociationApproved) return;
+ mAssociationApproved = true;
- private int getColor(int colorAttr) {
- if (mColors.contains(colorAttr)) {
- return mColors.get(colorAttr);
- }
- TypedValue typedValue = new TypedValue();
- TypedArray a = obtainStyledAttributes(typedValue.data, new int[] { colorAttr });
- int result = a.getColor(0, 0);
- a.recycle();
- mColors.put(colorAttr, result);
- return result;
- }
+ if (DEBUG) Log.i(TAG, "onAssociationApproved() macAddress=" + macAddress);
- @Override
- public int getCount() {
- return getService().mDevicesFound.size();
+ if (!mRequest.isSelfManaged()) {
+ requireNonNull(macAddress);
+ CompanionDeviceDiscoveryService.stop(this);
}
- @Override
- public DeviceFilterPair getItem(int position) {
- return getService().mDevicesFound.get(position);
+ final Bundle data = new Bundle();
+ data.putParcelable(EXTRA_ASSOCIATION_REQUEST, mRequest);
+ data.putBinder(EXTRA_APPLICATION_CALLBACK, mAppCallback.asBinder());
+ if (macAddress != null) {
+ data.putParcelable(EXTRA_MAC_ADDRESS, macAddress);
}
- @Override
- public long getItemId(int position) {
- return position;
- }
+ data.putParcelable(EXTRA_RESULT_RECEIVER,
+ prepareResultReceiverForIpc(mOnAssociationCreatedReceiver));
+
+ mCdmServiceReceiver.send(RESULT_CODE_ASSOCIATION_APPROVED, data);
}
+
+ private final ResultReceiver mOnAssociationCreatedReceiver =
+ new ResultReceiver(Handler.getMain()) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle data) {
+ if (resultCode != RESULT_CODE_ASSOCIATION_CREATED) {
+ throw new RuntimeException("Unknown result code: " + resultCode);
+ }
+
+ final AssociationInfo association = data.getParcelable(EXTRA_ASSOCIATION);
+ requireNonNull(association);
+
+ onAssociationCreated(association);
+ }
+ };
}
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java
index 126b823ab271..a4ff1dc00fda 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java
@@ -16,18 +16,17 @@
package com.android.companiondevicemanager;
-import static android.companion.BluetoothDeviceFilterUtils.getDeviceDisplayNameInternal;
-import static android.companion.BluetoothDeviceFilterUtils.getDeviceMacAddress;
-
+import static com.android.companiondevicemanager.Utils.runOnMainThread;
import static com.android.internal.util.ArrayUtils.isEmpty;
-import static com.android.internal.util.CollectionUtils.emptyIfNull;
-import static com.android.internal.util.CollectionUtils.size;
-import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+import static com.android.internal.util.CollectionUtils.filter;
+import static com.android.internal.util.CollectionUtils.find;
+import static com.android.internal.util.CollectionUtils.map;
+
+import static java.util.Objects.requireNonNull;
import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.PendingIntent;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
@@ -42,8 +41,6 @@ import android.companion.AssociationRequest;
import android.companion.BluetoothDeviceFilter;
import android.companion.BluetoothLeDeviceFilter;
import android.companion.DeviceFilter;
-import android.companion.IAssociationRequestCallback;
-import android.companion.ICompanionDeviceDiscoveryService;
import android.companion.WifiDeviceFilter;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -53,417 +50,411 @@ import android.net.wifi.WifiManager;
import android.os.Handler;
import android.os.IBinder;
import android.os.Parcelable;
-import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;
-import com.android.internal.infra.AndroidFuture;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.CollectionUtils;
-import com.android.internal.util.Preconditions;
-
import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
+import java.util.Observable;
public class CompanionDeviceDiscoveryService extends Service {
-
private static final boolean DEBUG = false;
- private static final String LOG_TAG = CompanionDeviceDiscoveryService.class.getSimpleName();
+ private static final String TAG = CompanionDeviceDiscoveryService.class.getSimpleName();
- private static final long SCAN_TIMEOUT = 20000;
+ private static final String ACTION_START_DISCOVERY =
+ "com.android.companiondevicemanager.action.START_DISCOVERY";
+ private static final String ACTION_STOP_DISCOVERY =
+ "com.android.companiondevicemanager.action.ACTION_STOP_DISCOVERY";
+ private static final String EXTRA_ASSOCIATION_REQUEST = "association_request";
- static CompanionDeviceDiscoveryService sInstance;
+ private static final long SCAN_TIMEOUT = 20_000L; // 20 seconds
- private BluetoothManager mBluetoothManager;
- private BluetoothAdapter mBluetoothAdapter;
+ // TODO: replace with LiveData-s?
+ static final Observable TIMEOUT_OBSERVABLE = new MyObservable();
+ static final Observable SCAN_RESULTS_OBSERVABLE = new MyObservable();
+
+ private static CompanionDeviceDiscoveryService sInstance;
+
+ private BluetoothManager mBtManager;
+ private BluetoothAdapter mBtAdapter;
private WifiManager mWifiManager;
- @Nullable private BluetoothLeScanner mBLEScanner;
- private ScanSettings mDefaultScanSettings = new ScanSettings.Builder()
- .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
- .build();
-
- private List<DeviceFilter<?>> mFilters;
- private List<BluetoothLeDeviceFilter> mBLEFilters;
- private List<BluetoothDeviceFilter> mBluetoothFilters;
- private List<WifiDeviceFilter> mWifiFilters;
- private List<ScanFilter> mBLEScanFilters;
-
- AssociationRequest mRequest;
- List<DeviceFilterPair> mDevicesFound;
- DeviceFilterPair mSelectedDevice;
- IAssociationRequestCallback mApplicationCallback;
-
- AndroidFuture<String> mServiceCallback;
- boolean mIsScanning = false;
- @Nullable
- CompanionDeviceActivity mActivity = null;
+ private BluetoothLeScanner mBleScanner;
- private final ICompanionDeviceDiscoveryService mBinder =
- new ICompanionDeviceDiscoveryService.Stub() {
- @Override
- public void startDiscovery(AssociationRequest request,
- String callingPackage,
- IAssociationRequestCallback appCallback,
- AndroidFuture<String> serviceCallback) {
- Log.i(LOG_TAG,
- "startDiscovery() called with: filter = [" + request
- + "], appCallback = [" + appCallback + "]"
- + "], serviceCallback = [" + serviceCallback + "]");
- mApplicationCallback = appCallback;
- mServiceCallback = serviceCallback;
- Handler.getMain().sendMessage(obtainMessage(
- CompanionDeviceDiscoveryService::startDiscovery,
- CompanionDeviceDiscoveryService.this, request));
- }
+ private ScanCallback mBleScanCallback;
+ private BluetoothBroadcastReceiver mBtReceiver;
+ private WifiBroadcastReceiver mWifiReceiver;
- @Override
- public void onAssociationCreated() {
- Handler.getMain().post(CompanionDeviceDiscoveryService.this::onAssociationCreated);
- }
- };
+ private boolean mDiscoveryStarted = false;
+ private boolean mDiscoveryStopped = false;
+ private final List<DeviceFilterPair<?>> mDevicesFound = new ArrayList<>();
- private ScanCallback mBLEScanCallback;
- private BluetoothBroadcastReceiver mBluetoothBroadcastReceiver;
- private WifiBroadcastReceiver mWifiBroadcastReceiver;
+ private final Runnable mTimeoutRunnable = this::timeout;
- @Override
- public IBinder onBind(Intent intent) {
- Log.i(LOG_TAG, "onBind(" + intent + ")");
- return mBinder.asBinder();
+ static void startForRequest(
+ @NonNull Context context, @NonNull AssociationRequest associationRequest) {
+ requireNonNull(associationRequest);
+ final Intent intent = new Intent(context, CompanionDeviceDiscoveryService.class);
+ intent.setAction(ACTION_START_DISCOVERY);
+ intent.putExtra(EXTRA_ASSOCIATION_REQUEST, associationRequest);
+ context.startService(intent);
+ }
+
+ static void stop(@NonNull Context context) {
+ final Intent intent = new Intent(context, CompanionDeviceDiscoveryService.class);
+ intent.setAction(ACTION_STOP_DISCOVERY);
+ context.startService(intent);
+ }
+
+ @MainThread
+ static @NonNull List<DeviceFilterPair<?>> getScanResults() {
+ return sInstance != null ? new ArrayList<>(sInstance.mDevicesFound)
+ : Collections.emptyList();
}
@Override
public void onCreate() {
super.onCreate();
+ if (DEBUG) Log.d(TAG, "onCreate()");
- Log.i(LOG_TAG, "onCreate()");
+ sInstance = this;
- mBluetoothManager = getSystemService(BluetoothManager.class);
- mBluetoothAdapter = mBluetoothManager.getAdapter();
- mBLEScanner = mBluetoothAdapter.getBluetoothLeScanner();
+ mBtManager = getSystemService(BluetoothManager.class);
+ mBtAdapter = mBtManager.getAdapter();
+ mBleScanner = mBtAdapter.getBluetoothLeScanner();
mWifiManager = getSystemService(WifiManager.class);
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ final String action = intent.getAction();
+ if (DEBUG) Log.d(TAG, "onStartCommand() action=" + action);
+
+ switch (action) {
+ case ACTION_START_DISCOVERY:
+ final AssociationRequest request =
+ intent.getParcelableExtra(EXTRA_ASSOCIATION_REQUEST);
+ startDiscovery(request);
+ break;
+
+ case ACTION_STOP_DISCOVERY:
+ stopDiscoveryAndFinish();
+ break;
+ }
+ return START_NOT_STICKY;
+ }
- mDevicesFound = new ArrayList<>();
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (DEBUG) Log.d(TAG, "onDestroy()");
- sInstance = this;
+ sInstance = null;
}
@MainThread
- private void startDiscovery(AssociationRequest request) {
- if (!request.equals(mRequest)) {
- mRequest = request;
-
- mFilters = request.getDeviceFilters();
- mWifiFilters = CollectionUtils.filter(mFilters, WifiDeviceFilter.class);
- mBluetoothFilters = CollectionUtils.filter(mFilters, BluetoothDeviceFilter.class);
- mBLEFilters = CollectionUtils.filter(mFilters, BluetoothLeDeviceFilter.class);
- mBLEScanFilters
- = CollectionUtils.map(mBLEFilters, BluetoothLeDeviceFilter::getScanFilter);
-
- reset();
- } else {
- Log.i(LOG_TAG, "startDiscovery: duplicate request: " + request);
- }
+ private void startDiscovery(@NonNull AssociationRequest request) {
+ if (DEBUG) Log.i(TAG, "startDiscovery() request=" + request);
+ requireNonNull(request);
+
+ if (mDiscoveryStarted) throw new RuntimeException("Discovery in progress.");
+ mDiscoveryStarted = true;
+
+ final List<DeviceFilter<?>> allFilters = request.getDeviceFilters();
+ final List<BluetoothDeviceFilter> btFilters =
+ filter(allFilters, BluetoothDeviceFilter.class);
+ final List<BluetoothLeDeviceFilter> bleFilters =
+ filter(allFilters, BluetoothLeDeviceFilter.class);
+ final List<WifiDeviceFilter> wifiFilters = filter(allFilters, WifiDeviceFilter.class);
+
+ checkBoundDevicesIfNeeded(request, btFilters);
+
+ // If no filters are specified: look for everything.
+ final boolean forceStartScanningAll = isEmpty(allFilters);
+ // Start BT scanning (if needed)
+ mBtReceiver = startBtScanningIfNeeded(btFilters, forceStartScanningAll);
+ // Start Wi-Fi scanning (if needed)
+ mWifiReceiver = startWifiScanningIfNeeded(wifiFilters, forceStartScanningAll);
+ // Start BLE scanning (if needed)
+ mBleScanCallback = startBleScanningIfNeeded(bleFilters, forceStartScanningAll);
+
+ // Schedule a time-out.
+ Handler.getMain().postDelayed(mTimeoutRunnable, SCAN_TIMEOUT);
+ }
- if (!ArrayUtils.isEmpty(mDevicesFound)) {
- onReadyToShowUI();
- }
+ @MainThread
+ private void stopDiscoveryAndFinish() {
+ if (DEBUG) Log.i(TAG, "stopDiscovery()");
- // If filtering to get single device by mac address, also search in the set of already
- // bonded devices to allow linking those directly
- String singleMacAddressFilter = null;
- if (mRequest.isSingleDevice()) {
- int numFilters = size(mBluetoothFilters);
- for (int i = 0; i < numFilters; i++) {
- BluetoothDeviceFilter filter = mBluetoothFilters.get(i);
- if (!TextUtils.isEmpty(filter.getAddress())) {
- singleMacAddressFilter = filter.getAddress();
- break;
- }
- }
- }
- if (singleMacAddressFilter != null) {
- for (BluetoothDevice dev : emptyIfNull(mBluetoothAdapter.getBondedDevices())) {
- onDeviceFound(DeviceFilterPair.findMatch(dev, mBluetoothFilters));
- }
- for (BluetoothDevice dev : emptyIfNull(
- mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT))) {
- onDeviceFound(DeviceFilterPair.findMatch(dev, mBluetoothFilters));
- }
- for (BluetoothDevice dev : emptyIfNull(
- mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT_SERVER))) {
- onDeviceFound(DeviceFilterPair.findMatch(dev, mBluetoothFilters));
- }
+ if (!mDiscoveryStarted) {
+ stopSelf();
+ return;
}
- if (shouldScan(mBluetoothFilters)) {
- final IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
+ if (mDiscoveryStopped) return;
+ mDiscoveryStopped = true;
- Log.i(LOG_TAG, "registerReceiver(BluetoothDevice.ACTION_FOUND)");
- mBluetoothBroadcastReceiver = new BluetoothBroadcastReceiver();
- registerReceiver(mBluetoothBroadcastReceiver, intentFilter);
- mBluetoothAdapter.startDiscovery();
+ // Stop BT discovery.
+ if (mBtReceiver != null) {
+ // Cancel discovery.
+ mBtAdapter.cancelDiscovery();
+ // Unregister receiver.
+ unregisterReceiver(mBtReceiver);
+ mBtReceiver = null;
}
- if (shouldScan(mBLEFilters) && mBLEScanner != null) {
- Log.i(LOG_TAG, "BLEScanner.startScan");
- mBLEScanCallback = new BLEScanCallback();
- mBLEScanner.startScan(mBLEScanFilters, mDefaultScanSettings, mBLEScanCallback);
+ // Stop Wi-Fi scanning.
+ if (mWifiReceiver != null) {
+ // TODO: need to stop scan?
+ // Unregister receiver.
+ unregisterReceiver(mWifiReceiver);
+ mWifiReceiver = null;
}
- if (shouldScan(mWifiFilters)) {
- Log.i(LOG_TAG, "registerReceiver(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)");
- mWifiBroadcastReceiver = new WifiBroadcastReceiver();
- registerReceiver(mWifiBroadcastReceiver,
- new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
- mWifiManager.startScan();
+ // Stop BLE scanning.
+ if (mBleScanCallback != null) {
+ mBleScanner.stopScan(mBleScanCallback);
}
- mIsScanning = true;
- Handler.getMain().sendMessageDelayed(
- obtainMessage(CompanionDeviceDiscoveryService::stopScan, this),
- SCAN_TIMEOUT);
- }
- @MainThread
- private void onAssociationCreated() {
- mActivity.setResultAndFinish();
- }
+ Handler.getMain().removeCallbacks(mTimeoutRunnable);
- private boolean shouldScan(List<? extends DeviceFilter> mediumSpecificFilters) {
- return !isEmpty(mediumSpecificFilters) || isEmpty(mFilters);
+ // "Finish".
+ stopSelf();
}
- @MainThread
- private void reset() {
- Log.i(LOG_TAG, "reset()");
- stopScan();
- mDevicesFound.clear();
- mSelectedDevice = null;
- CompanionDeviceActivity.notifyDevicesChanged();
- }
+ private void checkBoundDevicesIfNeeded(@NonNull AssociationRequest request,
+ @NonNull List<BluetoothDeviceFilter> btFilters) {
+ // If filtering to get single device by mac address, also search in the set of already
+ // bonded devices to allow linking those directly
+ if (btFilters.isEmpty() || !request.isSingleDevice()) return;
- @Override
- public boolean onUnbind(Intent intent) {
- Log.i(LOG_TAG, "onUnbind(intent = " + intent + ")");
- stopScan();
- return super.onUnbind(intent);
- }
+ final BluetoothDeviceFilter singleMacAddressFilter =
+ find(btFilters, filter -> !TextUtils.isEmpty(filter.getAddress()));
- private void stopScan() {
- Log.i(LOG_TAG, "stopScan()");
+ if (singleMacAddressFilter == null) return;
- if (!mIsScanning) return;
- mIsScanning = false;
+ findAndReportMatches(mBtAdapter.getBondedDevices(), btFilters);
+ findAndReportMatches(mBtManager.getConnectedDevices(BluetoothProfile.GATT), btFilters);
+ findAndReportMatches(
+ mBtManager.getConnectedDevices(BluetoothProfile.GATT_SERVER), btFilters);
+ }
- if (mActivity != null && mActivity.mDeviceListView != null) {
- mActivity.mDeviceListView.removeFooterView(mActivity.mLoadingIndicator);
- }
+ private void findAndReportMatches(@Nullable Collection<BluetoothDevice> devices,
+ @NonNull List<BluetoothDeviceFilter> filters) {
+ if (devices == null) return;
- mBluetoothAdapter.cancelDiscovery();
- if (mBluetoothBroadcastReceiver != null) {
- unregisterReceiver(mBluetoothBroadcastReceiver);
- mBluetoothBroadcastReceiver = null;
- }
- if (mBLEScanner != null) mBLEScanner.stopScan(mBLEScanCallback);
- if (mWifiBroadcastReceiver != null) {
- unregisterReceiver(mWifiBroadcastReceiver);
- mWifiBroadcastReceiver = null;
+ for (BluetoothDevice device : devices) {
+ final DeviceFilterPair<BluetoothDevice> match = findMatch(device, filters);
+ if (match != null) {
+ onDeviceFound(match);
+ }
}
}
- private void onDeviceFound(@Nullable DeviceFilterPair device) {
- if (device == null) return;
+ private BluetoothBroadcastReceiver startBtScanningIfNeeded(
+ List<BluetoothDeviceFilter> filters, boolean force) {
+ if (isEmpty(filters) && !force) return null;
+ if (DEBUG) Log.d(TAG, "registerReceiver(BluetoothDevice.ACTION_FOUND)");
- Handler.getMain().sendMessage(obtainMessage(
- CompanionDeviceDiscoveryService::onDeviceFoundMainThread, this, device));
- }
+ final BluetoothBroadcastReceiver receiver = new BluetoothBroadcastReceiver(filters);
- @MainThread
- void onDeviceFoundMainThread(@NonNull DeviceFilterPair device) {
- if (mDevicesFound.contains(device)) {
- Log.i(LOG_TAG, "Skipping device " + device + " - already among found devices");
- return;
- }
+ final IntentFilter intentFilter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
+ registerReceiver(receiver, intentFilter);
- Log.i(LOG_TAG, "Found device " + device);
+ mBtAdapter.startDiscovery();
- if (mDevicesFound.isEmpty()) {
- onReadyToShowUI();
- }
- mDevicesFound.add(device);
- CompanionDeviceActivity.notifyDevicesChanged();
+ return receiver;
}
- //TODO also, on timeout -> call onFailure
- private void onReadyToShowUI() {
- try {
- mApplicationCallback.onAssociationPending(PendingIntent.getActivity(
- this, 0,
- new Intent(this, CompanionDeviceActivity.class),
- PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_CANCEL_CURRENT
- | PendingIntent.FLAG_IMMUTABLE));
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
+ private WifiBroadcastReceiver startWifiScanningIfNeeded(
+ List<WifiDeviceFilter> filters, boolean force) {
+ if (isEmpty(filters) && !force) return null;
+ if (DEBUG) Log.d(TAG, "registerReceiver(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)");
- private void onDeviceLost(@Nullable DeviceFilterPair device) {
- Log.i(LOG_TAG, "Lost device " + device.getDisplayName());
- Handler.getMain().sendMessage(obtainMessage(
- CompanionDeviceDiscoveryService::onDeviceLostMainThread, this, device));
- }
+ final WifiBroadcastReceiver receiver = new WifiBroadcastReceiver(filters);
- @MainThread
- void onDeviceLostMainThread(@Nullable DeviceFilterPair device) {
- mDevicesFound.remove(device);
- CompanionDeviceActivity.notifyDevicesChanged();
- }
+ final IntentFilter intentFilter = new IntentFilter(
+ WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+ registerReceiver(receiver, intentFilter);
- void onDeviceSelected(String callingPackage, String deviceAddress) {
- if (callingPackage == null || deviceAddress == null) {
- return;
- }
- mServiceCallback.complete(deviceAddress);
- }
+ mWifiManager.startScan();
- void onCancel() {
- if (DEBUG) Log.i(LOG_TAG, "onCancel()");
- mActivity = null;
- mServiceCallback.cancel(true);
+ return receiver;
}
- /**
- * A pair of device and a filter that matched this device if any.
- *
- * @param <T> device type
- */
- static class DeviceFilterPair<T extends Parcelable> {
- public final T device;
- @Nullable
- public final DeviceFilter<T> filter;
-
- private DeviceFilterPair(T device, @Nullable DeviceFilter<T> filter) {
- this.device = device;
- this.filter = filter;
- }
+ private ScanCallback startBleScanningIfNeeded(
+ List<BluetoothLeDeviceFilter> filters, boolean force) {
+ if (isEmpty(filters) && !force) return null;
+ if (DEBUG) Log.d(TAG, "BLEScanner.startScan");
- /**
- * {@code (device, null)} if the filters list is empty or null
- * {@code null} if none of the provided filters match the device
- * {@code (device, filter)} where filter is among the list of filters and matches the device
- */
- @Nullable
- public static <T extends Parcelable> DeviceFilterPair<T> findMatch(
- T dev, @Nullable List<? extends DeviceFilter<T>> filters) {
- if (isEmpty(filters)) return new DeviceFilterPair<>(dev, null);
- final DeviceFilter<T> matchingFilter
- = CollectionUtils.find(filters, f -> f.matches(dev));
-
- DeviceFilterPair<T> result = matchingFilter != null
- ? new DeviceFilterPair<>(dev, matchingFilter)
- : null;
- if (DEBUG) Log.i(LOG_TAG, "findMatch(dev = " + dev + ", filters = " + filters +
- ") -> " + result);
- return result;
+ if (mBleScanner == null) {
+ Log.w(TAG, "BLE Scanner is not available.");
+ return null;
}
- public String getDisplayName() {
- if (filter == null) {
- Preconditions.checkNotNull(device);
- if (device instanceof BluetoothDevice) {
- return getDeviceDisplayNameInternal((BluetoothDevice) device);
- } else if (device instanceof android.net.wifi.ScanResult) {
- return getDeviceDisplayNameInternal((android.net.wifi.ScanResult) device);
- } else if (device instanceof ScanResult) {
- return getDeviceDisplayNameInternal(((ScanResult) device).getDevice());
- } else {
- throw new IllegalArgumentException("Unknown device type: " + device.getClass());
+ final BLEScanCallback callback = new BLEScanCallback(filters);
+
+ final List<ScanFilter> scanFilters = map(
+ filters, BluetoothLeDeviceFilter::getScanFilter);
+ final ScanSettings scanSettings = new ScanSettings.Builder()
+ .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
+ .build();
+ mBleScanner.startScan(scanFilters, scanSettings, callback);
+
+ return callback;
+ }
+
+ private void onDeviceFound(@NonNull DeviceFilterPair<?> device) {
+ runOnMainThread(() -> {
+ if (DEBUG) Log.v(TAG, "onDeviceFound() " + device);
+ if (mDevicesFound.contains(device)) {
+ // TODO: update the device instead of ignoring (new found device may contain
+ // additional/updated info, eg. name of the device).
+ if (DEBUG) {
+ Log.d(TAG, "onDeviceFound() " + device.toShortString()
+ + " - Already seen: ignore.");
}
+ return;
}
- return filter.getDeviceDisplayName(device);
- }
+ if (DEBUG) Log.i(TAG, "onDeviceFound() " + device.toShortString() + " - New device.");
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- DeviceFilterPair<?> that = (DeviceFilterPair<?>) o;
- return Objects.equals(getDeviceMacAddress(device), getDeviceMacAddress(that.device));
- }
+ // First: make change.
+ mDevicesFound.add(device);
+ // Then: notify observers.
+ SCAN_RESULTS_OBSERVABLE.notifyObservers();
+ });
+ }
- @Override
- public int hashCode() {
- return Objects.hash(getDeviceMacAddress(device));
- }
+ private void onDeviceLost(@Nullable DeviceFilterPair<?> device) {
+ runOnMainThread(() -> {
+ if (DEBUG) Log.i(TAG, "onDeviceLost(), device=" + device.toShortString());
- @Override
- public String toString() {
- return "DeviceFilterPair{"
- + "device=" + device + " " + getDisplayName()
- + ", filter=" + filter
- + '}';
- }
+ // First: make change.
+ mDevicesFound.remove(device);
+ // Then: notify observers.
+ SCAN_RESULTS_OBSERVABLE.notifyObservers();
+ });
+ }
+
+ private void timeout() {
+ if (DEBUG) Log.i(TAG, "timeout()");
+ stopDiscoveryAndFinish();
+ TIMEOUT_OBSERVABLE.notifyObservers();
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
}
private class BLEScanCallback extends ScanCallback {
+ final List<BluetoothLeDeviceFilter> mFilters;
- public BLEScanCallback() {
- if (DEBUG) Log.i(LOG_TAG, "new BLEScanCallback() -> " + this);
+ BLEScanCallback(List<BluetoothLeDeviceFilter> filters) {
+ mFilters = filters;
}
@Override
public void onScanResult(int callbackType, ScanResult result) {
if (DEBUG) {
- Log.i(LOG_TAG,
- "BLE.onScanResult(callbackType = " + callbackType + ", result = " + result
- + ")");
+ Log.v(TAG, "BLE.onScanResult() callback=" + callbackType + ", result=" + result);
}
- final DeviceFilterPair<ScanResult> deviceFilterPair
- = DeviceFilterPair.findMatch(result, mBLEFilters);
- if (deviceFilterPair == null) return;
+
+ final DeviceFilterPair<ScanResult> match = findMatch(result, mFilters);
+ if (match == null) return;
+
if (callbackType == ScanSettings.CALLBACK_TYPE_MATCH_LOST) {
- onDeviceLost(deviceFilterPair);
+ onDeviceLost(match);
} else {
- onDeviceFound(deviceFilterPair);
+ // TODO: check this logic.
+ onDeviceFound(match);
}
}
}
private class BluetoothBroadcastReceiver extends BroadcastReceiver {
+ final List<BluetoothDeviceFilter> mFilters;
+
+ BluetoothBroadcastReceiver(List<BluetoothDeviceFilter> filters) {
+ this.mFilters = filters;
+ }
+
@Override
public void onReceive(Context context, Intent intent) {
- if (DEBUG) {
- Log.i(LOG_TAG,
- "BL.onReceive(context = " + context + ", intent = " + intent + ")");
- }
+ final String action = intent.getAction();
final BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
- final DeviceFilterPair<BluetoothDevice> deviceFilterPair
- = DeviceFilterPair.findMatch(device, mBluetoothFilters);
- if (deviceFilterPair == null) return;
- if (intent.getAction().equals(BluetoothDevice.ACTION_FOUND)) {
- onDeviceFound(deviceFilterPair);
+
+ if (DEBUG) Log.v(TAG, action + ", device=" + device);
+
+ if (action == null) return;
+
+ final DeviceFilterPair<BluetoothDevice> match = findMatch(device, mFilters);
+ if (match == null) return;
+
+ if (action.equals(BluetoothDevice.ACTION_FOUND)) {
+ onDeviceFound(match);
} else {
- onDeviceLost(deviceFilterPair);
+ // TODO: check this logic.
+ onDeviceLost(match);
}
}
}
private class WifiBroadcastReceiver extends BroadcastReceiver {
+ final List<WifiDeviceFilter> mFilters;
+
+ private WifiBroadcastReceiver(List<WifiDeviceFilter> filters) {
+ this.mFilters = filters;
+ }
+
@Override
public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
- List<android.net.wifi.ScanResult> scanResults = mWifiManager.getScanResults();
+ if (!Objects.equals(intent.getAction(), WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
+ return;
+ }
- if (DEBUG) {
- Log.i(LOG_TAG, "Wifi scan results: " + TextUtils.join("\n", scanResults));
- }
+ final List<android.net.wifi.ScanResult> scanResults = mWifiManager.getScanResults();
+ if (DEBUG) {
+ Log.v(TAG, "WifiManager.SCAN_RESULTS_AVAILABLE_ACTION, results:\n "
+ + TextUtils.join("\n ", scanResults));
+ }
- for (int i = 0; i < scanResults.size(); i++) {
- onDeviceFound(DeviceFilterPair.findMatch(scanResults.get(i), mWifiFilters));
+ for (int i = 0; i < scanResults.size(); i++) {
+ final android.net.wifi.ScanResult scanResult = scanResults.get(i);
+ final DeviceFilterPair<?> match = findMatch(scanResult, mFilters);
+ if (match != null) {
+ onDeviceFound(match);
}
}
}
}
+
+ /**
+ * {@code (device, null)} if the filters list is empty or null
+ * {@code null} if none of the provided filters match the device
+ * {@code (device, filter)} where filter is among the list of filters and matches the device
+ */
+ @Nullable
+ public static <T extends Parcelable> DeviceFilterPair<T> findMatch(
+ T dev, @Nullable List<? extends DeviceFilter<T>> filters) {
+ if (isEmpty(filters)) return new DeviceFilterPair<>(dev, null);
+ final DeviceFilter<T> matchingFilter = find(filters, f -> f.matches(dev));
+
+ DeviceFilterPair<T> result = matchingFilter != null
+ ? new DeviceFilterPair<>(dev, matchingFilter) : null;
+ if (DEBUG) {
+ Log.v(TAG, "findMatch(dev=" + dev + ", filters=" + filters + ") -> " + result);
+ }
+ return result;
+ }
+
+ private static class MyObservable extends Observable {
+ @Override
+ public void notifyObservers() {
+ setChanged();
+ super.notifyObservers();
+ }
+ }
}
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceFilterPair.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceFilterPair.java
new file mode 100644
index 000000000000..faca1ae3f058
--- /dev/null
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceFilterPair.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.companion.BluetoothDeviceFilterUtils.getDeviceDisplayNameInternal;
+import static android.companion.BluetoothDeviceFilterUtils.getDeviceMacAddress;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.bluetooth.BluetoothDevice;
+import android.companion.DeviceFilter;
+import android.net.MacAddress;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * A pair of device and a filter that matched this device if any.
+ *
+ * @param <T> device type.
+ */
+class DeviceFilterPair<T extends Parcelable> {
+ private final T mDevice;
+ private final @Nullable DeviceFilter<T> mFilter;
+
+ DeviceFilterPair(T device, @Nullable DeviceFilter<T> filter) {
+ this.mDevice = device;
+ this.mFilter = filter;
+ }
+
+ T getDevice() {
+ return mDevice;
+ }
+
+ String getDisplayName() {
+ if (mFilter != null) mFilter.getDeviceDisplayName(mDevice);
+
+ if (mDevice instanceof BluetoothDevice) {
+ return getDeviceDisplayNameInternal((BluetoothDevice) mDevice);
+ } else if (mDevice instanceof android.bluetooth.le.ScanResult) {
+ final android.bluetooth.le.ScanResult bleScanResult =
+ (android.bluetooth.le.ScanResult) mDevice;
+ return getDeviceDisplayNameInternal(bleScanResult.getDevice());
+ } else if (mDevice instanceof android.net.wifi.ScanResult) {
+ final android.net.wifi.ScanResult wifiScanResult =
+ (android.net.wifi.ScanResult) mDevice;
+ return getDeviceDisplayNameInternal(wifiScanResult);
+ } else {
+ throw new IllegalArgumentException("Unknown device type: " + mDevice.getClass());
+ }
+ }
+
+ @NonNull MacAddress getMacAddress() {
+ return MacAddress.fromString(getDeviceMacAddress(getDevice()));
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ DeviceFilterPair<?> that = (DeviceFilterPair<?>) o;
+ return Objects.equals(getDeviceMacAddress(mDevice), getDeviceMacAddress(that.mDevice));
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getDeviceMacAddress(mDevice));
+ }
+
+ @Override
+ public String toString() {
+ return "DeviceFilterPair{"
+ + "device=" + mDevice + " " + getDisplayName()
+ + ", filter=" + mFilter
+ + '}';
+ }
+
+ @NonNull String toShortString() {
+ return '(' + getDeviceTypeAsString() + ") " + getMacAddress() + " '" + getDisplayName()
+ + '\'';
+ }
+
+ private @NonNull String getDeviceTypeAsString() {
+ if (mDevice instanceof BluetoothDevice) {
+ return "BT";
+ } else if (mDevice instanceof android.bluetooth.le.ScanResult) {
+ return "BLE";
+ } else if (mDevice instanceof android.net.wifi.ScanResult) {
+ return "Wi-Fi";
+ } else {
+ return "Unknown";
+ }
+ }
+}
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java
new file mode 100644
index 000000000000..cf2a2bfa468b
--- /dev/null
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 android.annotation.ColorInt;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.TextView;
+
+import java.util.List;
+import java.util.Observable;
+import java.util.Observer;
+
+/**
+ * Adapter for the list of "found" devices.
+ */
+class DeviceListAdapter extends BaseAdapter implements Observer {
+ private final Context mContext;
+ private final Resources mResources;
+
+ private final Drawable mBluetoothIcon;
+ private final Drawable mWifiIcon;
+
+ private final @ColorInt int mTextColor;
+
+ // List if pairs (display name, address)
+ private List<DeviceFilterPair<?>> mDevices;
+
+ DeviceListAdapter(Context context) {
+ mContext = context;
+ mResources = context.getResources();
+ mBluetoothIcon = getTintedIcon(mResources, android.R.drawable.stat_sys_data_bluetooth);
+ mWifiIcon = getTintedIcon(mResources, com.android.internal.R.drawable.ic_wifi_signal_3);
+ mTextColor = getColor(context, android.R.attr.colorForeground);
+ }
+
+ @Override
+ public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
+ final TextView view = convertView != null ? (TextView) convertView : newView();
+ bind(view, getItem(position));
+ return view;
+ }
+
+ private void bind(TextView textView, DeviceFilterPair<?> item) {
+ textView.setText(item.getDisplayName());
+ textView.setBackgroundColor(Color.TRANSPARENT);
+ /*
+ textView.setCompoundDrawablesWithIntrinsicBounds(
+ item.getDevice() instanceof android.net.wifi.ScanResult
+ ? mWifiIcon
+ : mBluetoothIcon,
+ null, null, null);
+ textView.getCompoundDrawables()[0].setTint(mTextColor);
+ */
+ }
+
+ private TextView newView() {
+ final TextView textView = new TextView(mContext);
+ textView.setTextColor(mTextColor);
+ final int padding = 24;
+ textView.setPadding(padding, padding, padding, padding);
+ //textView.setCompoundDrawablePadding(padding);
+ return textView;
+ }
+
+ @Override
+ public int getCount() {
+ return mDevices != null ? mDevices.size() : 0;
+ }
+
+ @Override
+ public DeviceFilterPair<?> getItem(int position) {
+ return mDevices.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public void update(Observable o, Object arg) {
+ mDevices = CompanionDeviceDiscoveryService.getScanResults();
+ notifyDataSetChanged();
+ }
+
+ private @ColorInt int getColor(Context context, int attr) {
+ final TypedArray a = context.obtainStyledAttributes(new TypedValue().data,
+ new int[] { attr });
+ final int color = a.getColor(0, 0);
+ a.recycle();
+ return color;
+ }
+
+ private static Drawable getTintedIcon(Resources resources, int drawableRes) {
+ Drawable icon = resources.getDrawable(drawableRes, null);
+ icon.setTint(Color.DKGRAY);
+ return icon;
+ }
+}
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java
new file mode 100644
index 000000000000..eab421e48446
--- /dev/null
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.companiondevicemanager;
+
+import android.annotation.NonNull;
+import android.annotation.StringRes;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Parcel;
+import android.os.ResultReceiver;
+import android.text.Html;
+import android.text.Spanned;
+
+/**
+ * Utilities.
+ */
+class Utils {
+
+ /**
+ * Convert an instance of a "locally-defined" ResultReceiver to an instance of
+ * {@link android.os.ResultReceiver} itself, which the receiving process will be able to
+ * unmarshall.
+ */
+ static <T extends ResultReceiver> ResultReceiver prepareResultReceiverForIpc(T resultReceiver) {
+ final Parcel parcel = Parcel.obtain();
+ resultReceiver.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ final ResultReceiver ipcFriendly = ResultReceiver.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
+
+ return ipcFriendly;
+ }
+
+ static @NonNull CharSequence getApplicationLabel(
+ @NonNull Context context, @NonNull String packageName) {
+ final PackageManager packageManager = context.getPackageManager();
+ final ApplicationInfo appInfo;
+ try {
+ appInfo = packageManager.getApplicationInfo(packageName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ return packageManager.getApplicationLabel(appInfo);
+ }
+
+ static Spanned getHtmlFromResources(
+ @NonNull Context context, @StringRes int resId, CharSequence... formatArgs) {
+ final String[] escapedArgs = new String[formatArgs.length];
+ for (int i = 0; i < escapedArgs.length; i++) {
+ escapedArgs[i] = Html.escapeHtml(formatArgs[i]);
+ }
+ final String plain = context.getString(resId, (Object[]) escapedArgs);
+ return Html.fromHtml(plain, 0);
+ }
+
+ static void runOnMainThread(Runnable runnable) {
+ if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
+ runnable.run();
+ } else {
+ Handler.getMain().post(runnable);
+ }
+ }
+
+ private Utils() {
+ }
+}
diff --git a/packages/ConnectivityT/OWNERS b/packages/ConnectivityT/OWNERS
index 4862377852ac..e267d19ad7e2 100644
--- a/packages/ConnectivityT/OWNERS
+++ b/packages/ConnectivityT/OWNERS
@@ -1 +1,2 @@
-file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking \ No newline at end of file
+file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking
+per-file **IpSec* = file:/services/core/java/com/android/server/vcn/OWNERS
diff --git a/packages/ConnectivityT/framework-t/Android.bp b/packages/ConnectivityT/framework-t/Android.bp
index 9c07f399f92e..0bda923f2389 100644
--- a/packages/ConnectivityT/framework-t/Android.bp
+++ b/packages/ConnectivityT/framework-t/Android.bp
@@ -100,15 +100,58 @@ filegroup {
],
}
+// IpSec related libraries.
+
+filegroup {
+ name: "framework-connectivity-ipsec-sources",
+ srcs: [
+ "src/android/net/IIpSecService.aidl",
+ "src/android/net/IpSec*.*",
+ ],
+ path: "src",
+ visibility: [
+ "//visibility:private",
+ ],
+}
+
+// Ethernet related libraries.
+
+filegroup {
+ name: "framework-connectivity-ethernet-sources",
+ srcs: [
+ "src/android/net/EthernetManager.java",
+ "src/android/net/EthernetNetworkSpecifier.java",
+ "src/android/net/IEthernetManager.aidl",
+ "src/android/net/IEthernetServiceListener.aidl",
+ "src/android/net/ITetheredInterfaceCallback.aidl",
+ ],
+ path: "src",
+ visibility: [
+ "//visibility:private",
+ ],
+}
+
// Connectivity-T common libraries.
filegroup {
+ name: "framework-connectivity-tiramisu-internal-sources",
+ srcs: [
+ "src/android/net/ConnectivityFrameworkInitializerTiramisu.java",
+ ],
+ path: "src",
+ visibility: [
+ "//visibility:private",
+ ],
+}
+
+filegroup {
name: "framework-connectivity-tiramisu-sources",
srcs: [
+ ":framework-connectivity-ethernet-sources",
+ ":framework-connectivity-ipsec-sources",
":framework-connectivity-netstats-sources",
":framework-connectivity-nsd-sources",
+ ":framework-connectivity-tiramisu-internal-sources",
],
- visibility: [
- "//frameworks/base",
- ],
-} \ No newline at end of file
+ visibility: ["//frameworks/base"],
+}
diff --git a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
index 8a6c85d54896..ca8330921620 100644
--- a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
@@ -45,8 +45,6 @@ import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.ServiceManager.ServiceNotFoundException;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.DataUnit;
@@ -135,15 +133,6 @@ public class NetworkStatsManager {
private int mFlags;
- /**
- * {@hide}
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public NetworkStatsManager(Context context) throws ServiceNotFoundException {
- this(context, INetworkStatsService.Stub.asInterface(
- ServiceManager.getServiceOrThrow(Context.NETWORK_STATS_SERVICE)));
- }
-
/** @hide */
@VisibleForTesting
public NetworkStatsManager(Context context, INetworkStatsService service) {
diff --git a/packages/ConnectivityT/framework-t/src/android/net/ConnectivityFrameworkInitializerTiramisu.java b/packages/ConnectivityT/framework-t/src/android/net/ConnectivityFrameworkInitializerTiramisu.java
new file mode 100644
index 000000000000..630f902ecfd7
--- /dev/null
+++ b/packages/ConnectivityT/framework-t/src/android/net/ConnectivityFrameworkInitializerTiramisu.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.SystemApi;
+import android.app.SystemServiceRegistry;
+import android.content.Context;
+import android.net.nsd.INsdManager;
+import android.net.nsd.NsdManager;
+
+/**
+ * Class for performing registration for Connectivity services which are exposed via updatable APIs
+ * since Android T.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class ConnectivityFrameworkInitializerTiramisu {
+ private ConnectivityFrameworkInitializerTiramisu() {}
+
+ /**
+ * Called by {@link SystemServiceRegistry}'s static initializer and registers nsd services to
+ * {@link Context}, so that {@link Context#getSystemService} can return them.
+ *
+ * @throws IllegalStateException if this is called anywhere besides
+ * {@link SystemServiceRegistry}.
+ */
+ public static void registerServiceWrappers() {
+ SystemServiceRegistry.registerContextAwareService(
+ Context.NSD_SERVICE,
+ NsdManager.class,
+ (context, serviceBinder) -> {
+ INsdManager service = INsdManager.Stub.asInterface(serviceBinder);
+ return new NsdManager(context, service);
+ }
+ );
+ }
+}
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/core/java/android/net/EthernetManager.java b/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java
index 7cd63ef9cc5a..7cd63ef9cc5a 100644
--- a/core/java/android/net/EthernetManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java
diff --git a/core/java/android/net/EthernetNetworkSpecifier.java b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkSpecifier.java
index 62c576144221..62c576144221 100644
--- a/core/java/android/net/EthernetNetworkSpecifier.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkSpecifier.java
diff --git a/core/java/android/net/IEthernetManager.aidl b/packages/ConnectivityT/framework-t/src/android/net/IEthernetManager.aidl
index e058e5a70c71..e058e5a70c71 100644
--- a/core/java/android/net/IEthernetManager.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/IEthernetManager.aidl
diff --git a/core/java/android/net/IEthernetServiceListener.aidl b/packages/ConnectivityT/framework-t/src/android/net/IEthernetServiceListener.aidl
index 782fa19d9df7..782fa19d9df7 100644
--- a/core/java/android/net/IEthernetServiceListener.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/IEthernetServiceListener.aidl
diff --git a/core/java/android/net/IIpSecService.aidl b/packages/ConnectivityT/framework-t/src/android/net/IIpSecService.aidl
index 933256a3b475..933256a3b475 100644
--- a/core/java/android/net/IIpSecService.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/IIpSecService.aidl
diff --git a/core/java/android/net/ITetheredInterfaceCallback.aidl b/packages/ConnectivityT/framework-t/src/android/net/ITetheredInterfaceCallback.aidl
index 14aa0237f24a..14aa0237f24a 100644
--- a/core/java/android/net/ITetheredInterfaceCallback.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/ITetheredInterfaceCallback.aidl
diff --git a/core/java/android/net/IpSecAlgorithm.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecAlgorithm.java
index 86052484eaf6..a84e7a9c6344 100644
--- a/core/java/android/net/IpSecAlgorithm.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/IpSecAlgorithm.java
@@ -23,7 +23,6 @@ import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.HexDump;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -297,7 +296,7 @@ public final class IpSecAlgorithm implements Parcelable {
return mTruncLenBits;
}
- /* Parcelable Implementation */
+ /** Parcelable Implementation */
public int describeContents() {
return 0;
}
@@ -469,20 +468,12 @@ public final class IpSecAlgorithm implements Parcelable {
}
}
- // Because encryption keys are sensitive and userdebug builds are used by large user pools
- // such as beta testers, we only allow sensitive info such as keys on eng builds.
- private static boolean isUnsafeBuild() {
- return Build.IS_DEBUGGABLE && Build.IS_ENG;
- }
-
@Override
@NonNull
public String toString() {
return new StringBuilder()
.append("{mName=")
.append(mName)
- .append(", mKey=")
- .append(isUnsafeBuild() ? HexDump.toHexString(mKey) : "<hidden>")
.append(", mTruncLenBits=")
.append(mTruncLenBits)
.append("}")
diff --git a/core/java/android/net/IpSecConfig.aidl b/packages/ConnectivityT/framework-t/src/android/net/IpSecConfig.aidl
index eaefca74d3a0..eaefca74d3a0 100644
--- a/core/java/android/net/IpSecConfig.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/IpSecConfig.aidl
diff --git a/core/java/android/net/IpSecConfig.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecConfig.java
index 575c5ed968f8..03bb187f119f 100644
--- a/core/java/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/core/java/android/net/IpSecManager.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java
index c10680761ff1..837629911ccd 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java
@@ -99,9 +99,9 @@ public final class IpSecManager {
/** @hide */
public interface Status {
- public static final int OK = 0;
- public static final int RESOURCE_UNAVAILABLE = 1;
- public static final int SPI_UNAVAILABLE = 2;
+ int OK = 0;
+ int RESOURCE_UNAVAILABLE = 1;
+ int SPI_UNAVAILABLE = 2;
}
/** @hide */
@@ -276,7 +276,7 @@ public final class IpSecManager {
* @param destinationAddress the destination address for traffic bearing the requested SPI.
* For inbound traffic, the destination should be an address currently assigned on-device.
* @return the reserved SecurityParameterIndex
- * @throws {@link #ResourceUnavailableException} indicating that too many SPIs are
+ * @throws ResourceUnavailableException indicating that too many SPIs are
* currently allocated for this user
*/
@NonNull
@@ -307,9 +307,9 @@ public final class IpSecManager {
* @param requestedSpi the requested SPI. The range 1-255 is reserved and may not be used. See
* RFC 4303 Section 2.1.
* @return the reserved SecurityParameterIndex
- * @throws {@link #ResourceUnavailableException} indicating that too many SPIs are
+ * @throws ResourceUnavailableException indicating that too many SPIs are
* currently allocated for this user
- * @throws {@link #SpiUnavailableException} indicating that the requested SPI could not be
+ * @throws SpiUnavailableException indicating that the requested SPI could not be
* reserved
*/
@NonNull
diff --git a/core/java/android/net/IpSecSpiResponse.aidl b/packages/ConnectivityT/framework-t/src/android/net/IpSecSpiResponse.aidl
index 6484a0013c53..6484a0013c53 100644
--- a/core/java/android/net/IpSecSpiResponse.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/IpSecSpiResponse.aidl
diff --git a/core/java/android/net/IpSecSpiResponse.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecSpiResponse.java
index f99e570fb761..f99e570fb761 100644
--- a/core/java/android/net/IpSecSpiResponse.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/IpSecSpiResponse.java
diff --git a/core/java/android/net/IpSecTransform.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecTransform.java
index b48c1fdaf1b2..b48c1fdaf1b2 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/IpSecTransform.java
diff --git a/core/java/android/net/IpSecTransformResponse.aidl b/packages/ConnectivityT/framework-t/src/android/net/IpSecTransformResponse.aidl
index 546230d5b888..546230d5b888 100644
--- a/core/java/android/net/IpSecTransformResponse.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/IpSecTransformResponse.aidl
diff --git a/core/java/android/net/IpSecTransformResponse.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecTransformResponse.java
index a38488954fc0..363f3165ee65 100644
--- a/core/java/android/net/IpSecTransformResponse.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/IpSecTransformResponse.java
@@ -60,7 +60,8 @@ public final class IpSecTransformResponse implements Parcelable {
resourceId = in.readInt();
}
- public static final @android.annotation.NonNull Parcelable.Creator<IpSecTransformResponse> CREATOR =
+ @android.annotation.NonNull
+ public static final Parcelable.Creator<IpSecTransformResponse> CREATOR =
new Parcelable.Creator<IpSecTransformResponse>() {
public IpSecTransformResponse createFromParcel(Parcel in) {
return new IpSecTransformResponse(in);
diff --git a/core/java/android/net/IpSecTunnelInterfaceResponse.aidl b/packages/ConnectivityT/framework-t/src/android/net/IpSecTunnelInterfaceResponse.aidl
index 7239221415ce..7239221415ce 100644
--- a/core/java/android/net/IpSecTunnelInterfaceResponse.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/IpSecTunnelInterfaceResponse.aidl
diff --git a/core/java/android/net/IpSecTunnelInterfaceResponse.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecTunnelInterfaceResponse.java
index e3411e003d6b..127e30a69394 100644
--- a/core/java/android/net/IpSecTunnelInterfaceResponse.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/IpSecTunnelInterfaceResponse.java
@@ -65,7 +65,8 @@ public final class IpSecTunnelInterfaceResponse implements Parcelable {
interfaceName = in.readString();
}
- public static final @android.annotation.NonNull Parcelable.Creator<IpSecTunnelInterfaceResponse> CREATOR =
+ @android.annotation.NonNull
+ public static final Parcelable.Creator<IpSecTunnelInterfaceResponse> CREATOR =
new Parcelable.Creator<IpSecTunnelInterfaceResponse>() {
public IpSecTunnelInterfaceResponse createFromParcel(Parcel in) {
return new IpSecTunnelInterfaceResponse(in);
diff --git a/core/java/android/net/IpSecUdpEncapResponse.aidl b/packages/ConnectivityT/framework-t/src/android/net/IpSecUdpEncapResponse.aidl
index 5e451f3651f1..5e451f3651f1 100644
--- a/core/java/android/net/IpSecUdpEncapResponse.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/IpSecUdpEncapResponse.aidl
diff --git a/core/java/android/net/IpSecUdpEncapResponse.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecUdpEncapResponse.java
index 4e7ba9b515d0..390af8236696 100644
--- a/core/java/android/net/IpSecUdpEncapResponse.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/IpSecUdpEncapResponse.java
@@ -18,6 +18,7 @@ package android.net;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
+
import java.io.FileDescriptor;
import java.io.IOException;
@@ -80,10 +81,11 @@ 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);
}
- public static final @android.annotation.NonNull Parcelable.Creator<IpSecUdpEncapResponse> CREATOR =
+ @android.annotation.NonNull
+ public static final Parcelable.Creator<IpSecUdpEncapResponse> CREATOR =
new Parcelable.Creator<IpSecUdpEncapResponse>() {
public IpSecUdpEncapResponse createFromParcel(Parcel in) {
return new IpSecUdpEncapResponse(in);
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/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/framework-t/src/android/net/nsd/NsdManager.java b/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdManager.java
index 6c597e26e042..0f21e55b9f27 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdManager.java
@@ -16,10 +16,6 @@
package android.net.nsd;
-import static com.android.internal.util.Preconditions.checkArgument;
-import static com.android.internal.util.Preconditions.checkNotNull;
-import static com.android.internal.util.Preconditions.checkStringNotEmpty;
-
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemService;
@@ -32,11 +28,13 @@ import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Protocol;
+
+import java.util.Objects;
/**
* The Network Service Discovery Manager class provides the API to discover services
@@ -175,65 +173,63 @@ public final class NsdManager {
*/
public static final int NSD_STATE_ENABLED = 2;
- private static final int BASE = Protocol.BASE_NSD_MANAGER;
-
/** @hide */
- public static final int DISCOVER_SERVICES = BASE + 1;
+ public static final int DISCOVER_SERVICES = 1;
/** @hide */
- public static final int DISCOVER_SERVICES_STARTED = BASE + 2;
+ public static final int DISCOVER_SERVICES_STARTED = 2;
/** @hide */
- public static final int DISCOVER_SERVICES_FAILED = BASE + 3;
+ public static final int DISCOVER_SERVICES_FAILED = 3;
/** @hide */
- public static final int SERVICE_FOUND = BASE + 4;
+ public static final int SERVICE_FOUND = 4;
/** @hide */
- public static final int SERVICE_LOST = BASE + 5;
+ public static final int SERVICE_LOST = 5;
/** @hide */
- public static final int STOP_DISCOVERY = BASE + 6;
+ public static final int STOP_DISCOVERY = 6;
/** @hide */
- public static final int STOP_DISCOVERY_FAILED = BASE + 7;
+ public static final int STOP_DISCOVERY_FAILED = 7;
/** @hide */
- public static final int STOP_DISCOVERY_SUCCEEDED = BASE + 8;
+ public static final int STOP_DISCOVERY_SUCCEEDED = 8;
/** @hide */
- public static final int REGISTER_SERVICE = BASE + 9;
+ public static final int REGISTER_SERVICE = 9;
/** @hide */
- public static final int REGISTER_SERVICE_FAILED = BASE + 10;
+ public static final int REGISTER_SERVICE_FAILED = 10;
/** @hide */
- public static final int REGISTER_SERVICE_SUCCEEDED = BASE + 11;
+ public static final int REGISTER_SERVICE_SUCCEEDED = 11;
/** @hide */
- public static final int UNREGISTER_SERVICE = BASE + 12;
+ public static final int UNREGISTER_SERVICE = 12;
/** @hide */
- public static final int UNREGISTER_SERVICE_FAILED = BASE + 13;
+ public static final int UNREGISTER_SERVICE_FAILED = 13;
/** @hide */
- public static final int UNREGISTER_SERVICE_SUCCEEDED = BASE + 14;
+ public static final int UNREGISTER_SERVICE_SUCCEEDED = 14;
/** @hide */
- public static final int RESOLVE_SERVICE = BASE + 18;
+ public static final int RESOLVE_SERVICE = 15;
/** @hide */
- public static final int RESOLVE_SERVICE_FAILED = BASE + 19;
+ public static final int RESOLVE_SERVICE_FAILED = 16;
/** @hide */
- public static final int RESOLVE_SERVICE_SUCCEEDED = BASE + 20;
+ public static final int RESOLVE_SERVICE_SUCCEEDED = 17;
/** @hide */
- public static final int DAEMON_CLEANUP = BASE + 21;
+ public static final int DAEMON_CLEANUP = 18;
/** @hide */
- public static final int DAEMON_STARTUP = BASE + 22;
+ public static final int DAEMON_STARTUP = 19;
/** @hide */
- public static final int ENABLE = BASE + 24;
+ public static final int ENABLE = 20;
/** @hide */
- public static final int DISABLE = BASE + 25;
+ public static final int DISABLE = 21;
/** @hide */
- public static final int NATIVE_DAEMON_EVENT = BASE + 26;
+ public static final int NATIVE_DAEMON_EVENT = 22;
/** @hide */
- public static final int REGISTER_CLIENT = BASE + 27;
+ public static final int REGISTER_CLIENT = 23;
/** @hide */
- public static final int UNREGISTER_CLIENT = BASE + 28;
+ public static final int UNREGISTER_CLIENT = 24;
/** Dns based service discovery protocol */
public static final int PROTOCOL_DNS_SD = 0x0001;
@@ -550,7 +546,9 @@ public final class NsdManager {
final int key;
synchronized (mMapLock) {
int valueIndex = mListenerMap.indexOfValue(listener);
- checkArgument(valueIndex == -1, "listener already in use");
+ if (valueIndex != -1) {
+ throw new IllegalArgumentException("listener already in use");
+ }
key = nextListenerKey();
mListenerMap.put(key, listener);
mServiceMap.put(key, s);
@@ -569,7 +567,9 @@ public final class NsdManager {
checkListener(listener);
synchronized (mMapLock) {
int valueIndex = mListenerMap.indexOfValue(listener);
- checkArgument(valueIndex != -1, "listener not registered");
+ if (valueIndex == -1) {
+ throw new IllegalArgumentException("listener not registered");
+ }
return mListenerMap.keyAt(valueIndex);
}
}
@@ -598,7 +598,9 @@ public final class NsdManager {
*/
public void registerService(NsdServiceInfo serviceInfo, int protocolType,
RegistrationListener listener) {
- checkArgument(serviceInfo.getPort() > 0, "Invalid port number");
+ if (serviceInfo.getPort() <= 0) {
+ throw new IllegalArgumentException("Invalid port number");
+ }
checkServiceInfo(serviceInfo);
checkProtocol(protocolType);
int key = putListener(listener, serviceInfo);
@@ -660,7 +662,9 @@ public final class NsdManager {
* Cannot be null. Cannot be in use for an active service discovery.
*/
public void discoverServices(String serviceType, int protocolType, DiscoveryListener listener) {
- checkStringNotEmpty(serviceType, "Service type cannot be empty");
+ if (TextUtils.isEmpty(serviceType)) {
+ throw new IllegalArgumentException("Service type cannot be empty");
+ }
checkProtocol(protocolType);
NsdServiceInfo s = new NsdServiceInfo();
@@ -719,16 +723,22 @@ public final class NsdManager {
}
private static void checkListener(Object listener) {
- checkNotNull(listener, "listener cannot be null");
+ Objects.requireNonNull(listener, "listener cannot be null");
}
private static void checkProtocol(int protocolType) {
- checkArgument(protocolType == PROTOCOL_DNS_SD, "Unsupported protocol");
+ if (protocolType != PROTOCOL_DNS_SD) {
+ throw new IllegalArgumentException("Unsupported protocol");
+ }
}
private static void checkServiceInfo(NsdServiceInfo serviceInfo) {
- checkNotNull(serviceInfo, "NsdServiceInfo cannot be null");
- checkStringNotEmpty(serviceInfo.getServiceName(), "Service name cannot be empty");
- checkStringNotEmpty(serviceInfo.getServiceType(), "Service type cannot be empty");
+ Objects.requireNonNull(serviceInfo, "NsdServiceInfo cannot be null");
+ if (TextUtils.isEmpty(serviceInfo.getServiceName())) {
+ throw new IllegalArgumentException("Service name cannot be empty");
+ }
+ if (TextUtils.isEmpty(serviceInfo.getServiceType())) {
+ throw new IllegalArgumentException("Service type cannot be empty");
+ }
}
}
diff --git a/packages/ConnectivityT/service/Android.bp b/packages/ConnectivityT/service/Android.bp
index 6a64910609ff..97dfb64b33dd 100644
--- a/packages/ConnectivityT/service/Android.bp
+++ b/packages/ConnectivityT/service/Android.bp
@@ -48,16 +48,42 @@ filegroup {
],
}
+// IpSec related libraries.
+
+filegroup {
+ name: "services.connectivity-ipsec-sources",
+ srcs: [
+ "src/com/android/server/IpSecService.java",
+ ],
+ path: "src",
+ visibility: [
+ "//visibility:private",
+ ],
+}
+
+// Ethernet related libraries.
+
+filegroup {
+ name: "services.connectivity-ethernet-sources",
+ srcs: [
+ "src/com/android/server/net/IpConfigStore.java",
+ ],
+ path: "src",
+ visibility: [
+ "//frameworks/opt/net/ethernet",
+ ],
+}
+
// Connectivity-T common libraries.
filegroup {
name: "services.connectivity-tiramisu-sources",
srcs: [
+ ":services.connectivity-ethernet-sources",
+ ":services.connectivity-ipsec-sources",
":services.connectivity-netstats-sources",
":services.connectivity-nsd-sources",
],
path: "src",
- visibility: [
- "//frameworks/base/services/core",
- ],
-} \ No newline at end of file
+ visibility: ["//frameworks/base/services/core"],
+}
diff --git a/services/core/java/com/android/server/IpSecService.java b/packages/ConnectivityT/service/src/com/android/server/IpSecService.java
index aeb814327e66..d1e432e80f51 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/IpSecService.java
@@ -45,7 +45,6 @@ import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.TrafficStats;
-import android.net.util.NetdService;
import android.os.Binder;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
@@ -96,8 +95,6 @@ import java.util.Objects;
public class IpSecService extends IIpSecService.Stub {
private static final String TAG = "IpSecService";
private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
-
- private static final String NETD_SERVICE_NAME = "netd";
private static final int[] ADDRESS_FAMILIES =
new int[] {OsConstants.AF_INET, OsConstants.AF_INET6};
@@ -106,6 +103,8 @@ public class IpSecService extends IIpSecService.Stub {
@VisibleForTesting static final int MAX_PORT_BIND_ATTEMPTS = 10;
+ private final INetd mNetd;
+
static {
try {
INADDR_ANY = InetAddress.getByAddress(new byte[] {0, 0, 0, 0});
@@ -119,6 +118,7 @@ public class IpSecService extends IIpSecService.Stub {
/* Binder context for this service */
private final Context mContext;
+ private final Dependencies mDeps;
/**
* The next non-repeating global ID for tracking resources between users, this service, and
@@ -129,23 +129,24 @@ public class IpSecService extends IIpSecService.Stub {
@GuardedBy("IpSecService.this")
private int mNextResourceId = 1;
- interface IpSecServiceConfiguration {
- INetd getNetdInstance() throws RemoteException;
-
- static IpSecServiceConfiguration GETSRVINSTANCE =
- new IpSecServiceConfiguration() {
- @Override
- public INetd getNetdInstance() throws RemoteException {
- final INetd netd = NetdService.getInstance();
- if (netd == null) {
- throw new RemoteException("Failed to Get Netd Instance");
- }
- return netd;
- }
- };
+ /**
+ * Dependencies of IpSecService, for injection in tests.
+ */
+ @VisibleForTesting
+ public static class Dependencies {
+ /**
+ * Get a reference to INetd.
+ */
+ public INetd getNetdInstance(Context context) throws RemoteException {
+ final INetd netd = INetd.Stub.asInterface((IBinder)
+ context.getSystemService(Context.NETD_SERVICE));
+ if (netd == null) {
+ throw new RemoteException("Failed to Get Netd Instance");
+ }
+ return netd;
+ }
}
- private final IpSecServiceConfiguration mSrvConfig;
final UidFdTagger mUidFdTagger;
/**
@@ -491,8 +492,8 @@ public class IpSecService extends IIpSecService.Stub {
* <p>This class associates kernel resources with the UID that owns and controls them.
*/
private abstract class OwnedResourceRecord implements IResource {
- final int pid;
- final int uid;
+ final int mPid;
+ final int mUid;
protected final int mResourceId;
OwnedResourceRecord(int resourceId) {
@@ -501,8 +502,8 @@ public class IpSecService extends IIpSecService.Stub {
throw new IllegalArgumentException("Resource ID must not be INVALID_RESOURCE_ID");
}
mResourceId = resourceId;
- pid = Binder.getCallingPid();
- uid = Binder.getCallingUid();
+ mPid = Binder.getCallingPid();
+ mUid = Binder.getCallingUid();
getResourceTracker().take();
}
@@ -512,7 +513,7 @@ public class IpSecService extends IIpSecService.Stub {
/** Convenience method; retrieves the user resource record for the stored UID. */
protected UserRecord getUserRecord() {
- return mUserResourceTracker.getUserRecord(uid);
+ return mUserResourceTracker.getUserRecord(mUid);
}
@Override
@@ -527,9 +528,9 @@ public class IpSecService extends IIpSecService.Stub {
.append("{mResourceId=")
.append(mResourceId)
.append(", pid=")
- .append(pid)
+ .append(mPid)
.append(", uid=")
- .append(uid)
+ .append(mUid)
.append("}")
.toString();
}
@@ -545,7 +546,7 @@ public class IpSecService extends IIpSecService.Stub {
SparseArray<RefcountedResource<T>> mArray = new SparseArray<>();
private final String mTypeName;
- public RefcountedResourceArray(String typeName) {
+ RefcountedResourceArray(String typeName) {
this.mTypeName = typeName;
}
@@ -625,16 +626,14 @@ public class IpSecService extends IIpSecService.Stub {
public void freeUnderlyingResources() {
int spi = mSpi.getSpi();
try {
- mSrvConfig
- .getNetdInstance()
- .ipSecDeleteSecurityAssociation(
- uid,
- mConfig.getSourceAddress(),
- mConfig.getDestinationAddress(),
- spi,
- mConfig.getMarkValue(),
- mConfig.getMarkMask(),
- mConfig.getXfrmInterfaceId());
+ mNetd.ipSecDeleteSecurityAssociation(
+ mUid,
+ mConfig.getSourceAddress(),
+ mConfig.getDestinationAddress(),
+ spi,
+ mConfig.getMarkValue(),
+ mConfig.getMarkMask(),
+ mConfig.getXfrmInterfaceId());
} catch (RemoteException | ServiceSpecificException e) {
Log.e(TAG, "Failed to delete SA with ID: " + mResourceId, e);
}
@@ -681,7 +680,8 @@ public class IpSecService extends IIpSecService.Stub {
private boolean mOwnedByTransform = false;
- SpiRecord(int resourceId, String sourceAddress, String destinationAddress, int spi) {
+ SpiRecord(int resourceId, String sourceAddress,
+ String destinationAddress, int spi) {
super(resourceId);
mSourceAddress = sourceAddress;
mDestinationAddress = destinationAddress;
@@ -693,11 +693,9 @@ public class IpSecService extends IIpSecService.Stub {
public void freeUnderlyingResources() {
try {
if (!mOwnedByTransform) {
- mSrvConfig
- .getNetdInstance()
- .ipSecDeleteSecurityAssociation(
- uid, mSourceAddress, mDestinationAddress, mSpi, 0 /* mark */,
- 0 /* mask */, 0 /* if_id */);
+ mNetd.ipSecDeleteSecurityAssociation(
+ mUid, mSourceAddress, mDestinationAddress, mSpi, 0 /* mark */,
+ 0 /* mask */, 0 /* if_id */);
}
} catch (ServiceSpecificException | RemoteException e) {
Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId, e);
@@ -844,19 +842,18 @@ public class IpSecService extends IIpSecService.Stub {
// Teardown VTI
// Delete global policies
try {
- final INetd netd = mSrvConfig.getNetdInstance();
- netd.ipSecRemoveTunnelInterface(mInterfaceName);
+ mNetd.ipSecRemoveTunnelInterface(mInterfaceName);
for (int selAddrFamily : ADDRESS_FAMILIES) {
- netd.ipSecDeleteSecurityPolicy(
- uid,
+ mNetd.ipSecDeleteSecurityPolicy(
+ mUid,
selAddrFamily,
IpSecManager.DIRECTION_OUT,
mOkey,
0xffffffff,
mIfId);
- netd.ipSecDeleteSecurityPolicy(
- uid,
+ mNetd.ipSecDeleteSecurityPolicy(
+ mUid,
selAddrFamily,
IpSecManager.DIRECTION_IN,
mIkey,
@@ -1012,29 +1009,28 @@ public class IpSecService extends IIpSecService.Stub {
* @param context Binder context for this service
*/
private IpSecService(Context context) {
- this(context, IpSecServiceConfiguration.GETSRVINSTANCE);
+ this(context, new Dependencies());
}
static IpSecService create(Context context)
throws InterruptedException {
final IpSecService service = new IpSecService(context);
- service.connectNativeNetdService();
return service;
}
@NonNull
private AppOpsManager getAppOpsManager() {
AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
- if(appOps == null) throw new RuntimeException("System Server couldn't get AppOps");
+ if (appOps == null) throw new RuntimeException("System Server couldn't get AppOps");
return appOps;
}
/** @hide */
@VisibleForTesting
- public IpSecService(Context context, IpSecServiceConfiguration config) {
+ public IpSecService(Context context, Dependencies deps) {
this(
context,
- config,
+ deps,
(fd, uid) -> {
try {
TrafficStats.setThreadStatsUid(uid);
@@ -1047,13 +1043,18 @@ public class IpSecService extends IIpSecService.Stub {
/** @hide */
@VisibleForTesting
- public IpSecService(Context context, IpSecServiceConfiguration config,
- UidFdTagger uidFdTagger) {
+ public IpSecService(Context context, Dependencies deps, UidFdTagger uidFdTagger) {
mContext = context;
- mSrvConfig = config;
+ mDeps = Objects.requireNonNull(deps, "Missing dependencies.");
mUidFdTagger = uidFdTagger;
+ try {
+ mNetd = mDeps.getNetdInstance(mContext);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
+ /** Called by system server when system is ready. */
public void systemReady() {
if (isNetdAlive()) {
Log.d(TAG, "IpSecService is ready");
@@ -1062,25 +1063,12 @@ public class IpSecService extends IIpSecService.Stub {
}
}
- private void connectNativeNetdService() {
- // Avoid blocking the system server to do this
- new Thread() {
- @Override
- public void run() {
- synchronized (IpSecService.this) {
- NetdService.get(NETD_FETCH_TIMEOUT_MS);
- }
- }
- }.start();
- }
-
synchronized boolean isNetdAlive() {
try {
- final INetd netd = mSrvConfig.getNetdInstance();
- if (netd == null) {
+ if (mNetd == null) {
return false;
}
- return netd.isAlive();
+ return mNetd.isAlive();
} catch (RemoteException re) {
return false;
}
@@ -1141,15 +1129,13 @@ public class IpSecService extends IIpSecService.Stub {
IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
}
- spi =
- mSrvConfig
- .getNetdInstance()
- .ipSecAllocateSpi(callingUid, "", destinationAddress, requestedSpi);
+ spi = mNetd.ipSecAllocateSpi(callingUid, "", destinationAddress, requestedSpi);
Log.d(TAG, "Allocated SPI " + spi);
userRecord.mSpiRecords.put(
resourceId,
new RefcountedResource<SpiRecord>(
- new SpiRecord(resourceId, "", destinationAddress, spi), binder));
+ new SpiRecord(resourceId, "",
+ destinationAddress, spi), binder));
} catch (ServiceSpecificException e) {
if (e.errorCode == OsConstants.ENOENT) {
return new IpSecSpiResponse(
@@ -1229,7 +1215,7 @@ public class IpSecService extends IIpSecService.Stub {
* <p>Since the socket is created on behalf of an unprivileged application, all traffic
* should be accounted to the UID of the unprivileged application.
*/
- public void tag(FileDescriptor fd, int uid) throws IOException;
+ void tag(FileDescriptor fd, int uid) throws IOException;
}
/**
@@ -1266,8 +1252,7 @@ public class IpSecService extends IIpSecService.Stub {
OsConstants.UDP_ENCAP,
OsConstants.UDP_ENCAP_ESPINUDP);
- mSrvConfig.getNetdInstance().ipSecSetEncapSocketOwner(
- new ParcelFileDescriptor(sockFd), callingUid);
+ mNetd.ipSecSetEncapSocketOwner(new ParcelFileDescriptor(sockFd), callingUid);
if (port != 0) {
Log.v(TAG, "Binding to port " + port);
Os.bind(sockFd, INADDR_ANY, port);
@@ -1329,16 +1314,15 @@ public class IpSecService extends IIpSecService.Stub {
// Create VTI
// Add inbound/outbound global policies
// (use reqid = 0)
- final INetd netd = mSrvConfig.getNetdInstance();
- netd.ipSecAddTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey, resourceId);
+ mNetd.ipSecAddTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey, resourceId);
BinderUtils.withCleanCallingIdentity(() -> {
- NetdUtils.setInterfaceUp(netd, intfName);
+ NetdUtils.setInterfaceUp(mNetd, intfName);
});
for (int selAddrFamily : ADDRESS_FAMILIES) {
// Always send down correct local/remote addresses for template.
- netd.ipSecAddSecurityPolicy(
+ mNetd.ipSecAddSecurityPolicy(
callerUid,
selAddrFamily,
IpSecManager.DIRECTION_OUT,
@@ -1348,7 +1332,7 @@ public class IpSecService extends IIpSecService.Stub {
okey,
0xffffffff,
resourceId);
- netd.ipSecAddSecurityPolicy(
+ mNetd.ipSecAddSecurityPolicy(
callerUid,
selAddrFamily,
IpSecManager.DIRECTION_IN,
@@ -1368,7 +1352,7 @@ public class IpSecService extends IIpSecService.Stub {
//
// This is necessary only on the tunnel interface, and not any the interface to
// which traffic will be forwarded to.
- netd.ipSecAddSecurityPolicy(
+ mNetd.ipSecAddSecurityPolicy(
callerUid,
selAddrFamily,
IpSecManager.DIRECTION_FWD,
@@ -1425,12 +1409,10 @@ public class IpSecService extends IIpSecService.Stub {
try {
// We can assume general validity of the IP address, since we get them as a
// LinkAddress, which does some validation.
- mSrvConfig
- .getNetdInstance()
- .interfaceAddAddress(
- tunnelInterfaceInfo.mInterfaceName,
- localAddr.getAddress().getHostAddress(),
- localAddr.getPrefixLength());
+ mNetd.interfaceAddAddress(
+ tunnelInterfaceInfo.mInterfaceName,
+ localAddr.getAddress().getHostAddress(),
+ localAddr.getPrefixLength());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1454,9 +1436,7 @@ public class IpSecService extends IIpSecService.Stub {
try {
// We can assume general validity of the IP address, since we get them as a
// LinkAddress, which does some validation.
- mSrvConfig
- .getNetdInstance()
- .interfaceDelAddress(
+ mNetd.interfaceDelAddress(
tunnelInterfaceInfo.mInterfaceName,
localAddr.getAddress().getHostAddress(),
localAddr.getPrefixLength());
@@ -1669,30 +1649,28 @@ public class IpSecService extends IIpSecService.Stub {
cryptName = crypt.getName();
}
- mSrvConfig
- .getNetdInstance()
- .ipSecAddSecurityAssociation(
- Binder.getCallingUid(),
- c.getMode(),
- c.getSourceAddress(),
- c.getDestinationAddress(),
- (c.getNetwork() != null) ? c.getNetwork().getNetId() : 0,
- spiRecord.getSpi(),
- c.getMarkValue(),
- c.getMarkMask(),
- (auth != null) ? auth.getName() : "",
- (auth != null) ? auth.getKey() : new byte[] {},
- (auth != null) ? auth.getTruncationLengthBits() : 0,
- cryptName,
- (crypt != null) ? crypt.getKey() : new byte[] {},
- (crypt != null) ? crypt.getTruncationLengthBits() : 0,
- (authCrypt != null) ? authCrypt.getName() : "",
- (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
- (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
- encapType,
- encapLocalPort,
- encapRemotePort,
- c.getXfrmInterfaceId());
+ mNetd.ipSecAddSecurityAssociation(
+ Binder.getCallingUid(),
+ c.getMode(),
+ c.getSourceAddress(),
+ c.getDestinationAddress(),
+ (c.getNetwork() != null) ? c.getNetwork().getNetId() : 0,
+ spiRecord.getSpi(),
+ c.getMarkValue(),
+ c.getMarkMask(),
+ (auth != null) ? auth.getName() : "",
+ (auth != null) ? auth.getKey() : new byte[] {},
+ (auth != null) ? auth.getTruncationLengthBits() : 0,
+ cryptName,
+ (crypt != null) ? crypt.getKey() : new byte[] {},
+ (crypt != null) ? crypt.getTruncationLengthBits() : 0,
+ (authCrypt != null) ? authCrypt.getName() : "",
+ (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
+ (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
+ encapType,
+ encapLocalPort,
+ encapRemotePort,
+ c.getXfrmInterfaceId());
}
/**
@@ -1771,7 +1749,7 @@ public class IpSecService extends IIpSecService.Stub {
TransformRecord info = userRecord.mTransformRecords.getResourceOrThrow(resourceId);
// TODO: make this a function.
- if (info.pid != getCallingPid() || info.uid != callingUid) {
+ if (info.mPid != getCallingPid() || info.mUid != callingUid) {
throw new SecurityException("Only the owner of an IpSec Transform may apply it!");
}
@@ -1781,15 +1759,13 @@ public class IpSecService extends IIpSecService.Stub {
c.getMode() == IpSecTransform.MODE_TRANSPORT,
"Transform mode was not Transport mode; cannot be applied to a socket");
- mSrvConfig
- .getNetdInstance()
- .ipSecApplyTransportModeTransform(
- socket,
- callingUid,
- direction,
- c.getSourceAddress(),
- c.getDestinationAddress(),
- info.getSpiRecord().getSpi());
+ mNetd.ipSecApplyTransportModeTransform(
+ socket,
+ callingUid,
+ direction,
+ c.getSourceAddress(),
+ c.getDestinationAddress(),
+ info.getSpiRecord().getSpi());
}
/**
@@ -1801,9 +1777,7 @@ public class IpSecService extends IIpSecService.Stub {
@Override
public synchronized void removeTransportModeTransforms(ParcelFileDescriptor socket)
throws RemoteException {
- mSrvConfig
- .getNetdInstance()
- .ipSecRemoveTransportModeTransform(socket);
+ mNetd.ipSecRemoveTransportModeTransform(socket);
}
/**
@@ -1878,18 +1852,16 @@ public class IpSecService extends IIpSecService.Stub {
// Always update the policy with the relevant XFRM_IF_ID
for (int selAddrFamily : ADDRESS_FAMILIES) {
- mSrvConfig
- .getNetdInstance()
- .ipSecUpdateSecurityPolicy(
- callingUid,
- selAddrFamily,
- direction,
- transformInfo.getConfig().getSourceAddress(),
- transformInfo.getConfig().getDestinationAddress(),
- spi, // If outbound, also add SPI to the policy.
- mark, // Must always set policy mark; ikey/okey for VTIs
- 0xffffffff,
- c.getXfrmInterfaceId());
+ mNetd.ipSecUpdateSecurityPolicy(
+ callingUid,
+ selAddrFamily,
+ direction,
+ transformInfo.getConfig().getSourceAddress(),
+ transformInfo.getConfig().getDestinationAddress(),
+ spi, // If outbound, also add SPI to the policy.
+ mark, // Must always set policy mark; ikey/okey for VTIs
+ 0xffffffff,
+ c.getXfrmInterfaceId());
}
// Update SA with tunnel mark (ikey or okey based on direction)
diff --git a/services/core/java/com/android/server/net/IpConfigStore.java b/packages/ConnectivityT/service/src/com/android/server/net/IpConfigStore.java
index d17dbde496ce..3a9a54415537 100644
--- a/services/core/java/com/android/server/net/IpConfigStore.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/IpConfigStore.java
@@ -44,6 +44,9 @@ import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;
+/**
+ * This class provides an API to store and manage L3 network IP configuration.
+ */
public class IpConfigStore {
private static final String TAG = "IpConfigStore";
private static final boolean DBG = false;
@@ -78,6 +81,9 @@ public class IpConfigStore {
return writeConfig(out, configKey, config, IPCONFIG_FILE_VERSION);
}
+ /**
+ * Write the IP configuration with the given parameters to {@link DataOutputStream}.
+ */
@VisibleForTesting
public static boolean writeConfig(DataOutputStream out, String configKey,
IpConfiguration config, int version) throws IOException {
@@ -154,10 +160,10 @@ public class IpConfigStore {
break;
case UNASSIGNED:
/* Ignore */
- break;
- default:
- loge("Ignore invalid proxy settings while writing");
- break;
+ break;
+ default:
+ loge("Ignore invalid proxy settings while writing");
+ break;
}
if (written) {
@@ -177,7 +183,7 @@ public class IpConfigStore {
}
/**
- * @Deprecated use {@link #writeIpConfigurations(String, ArrayMap)} instead.
+ * @deprecated use {@link #writeIpConfigurations(String, ArrayMap)} instead.
* New method uses string as network identifier which could be interface name or MAC address or
* other token.
*/
@@ -186,22 +192,28 @@ public class IpConfigStore {
final SparseArray<IpConfiguration> networks) {
mWriter.write(filePath, out -> {
out.writeInt(IPCONFIG_FILE_VERSION);
- for(int i = 0; i < networks.size(); i++) {
+ for (int i = 0; i < networks.size(); i++) {
writeConfig(out, String.valueOf(networks.keyAt(i)), networks.valueAt(i));
}
});
}
+ /**
+ * Write the IP configuration associated to the target networks to the destination path.
+ */
public void writeIpConfigurations(String filePath,
ArrayMap<String, IpConfiguration> networks) {
mWriter.write(filePath, out -> {
out.writeInt(IPCONFIG_FILE_VERSION);
- for(int i = 0; i < networks.size(); i++) {
+ for (int i = 0; i < networks.size(); i++) {
writeConfig(out, networks.keyAt(i), networks.valueAt(i));
}
});
}
+ /**
+ * Read the IP configuration from the destination path to {@link BufferedInputStream}.
+ */
public static ArrayMap<String, IpConfiguration> readIpConfigurations(String filePath) {
BufferedInputStream bufferedInputStream;
try {
@@ -215,7 +227,7 @@ public class IpConfigStore {
return readIpConfigurations(bufferedInputStream);
}
- /** @Deprecated use {@link #readIpConfigurations(String)} */
+ /** @deprecated use {@link #readIpConfigurations(String)} */
@Deprecated
public static SparseArray<IpConfiguration> readIpAndProxyConfigurations(String filePath) {
BufferedInputStream bufferedInputStream;
@@ -230,7 +242,7 @@ public class IpConfigStore {
return readIpAndProxyConfigurations(bufferedInputStream);
}
- /** @Deprecated use {@link #readIpConfigurations(InputStream)} */
+ /** @deprecated use {@link #readIpConfigurations(InputStream)} */
@Deprecated
public static SparseArray<IpConfiguration> readIpAndProxyConfigurations(
InputStream inputStream) {
@@ -420,7 +432,7 @@ public class IpConfigStore {
if (in != null) {
try {
in.close();
- } catch (Exception e) {}
+ } catch (Exception e) { }
}
}
diff --git a/packages/SettingsLib/RestrictedLockUtils/Android.bp b/packages/SettingsLib/RestrictedLockUtils/Android.bp
index c0623edabefe..ef548b59323e 100644
--- a/packages/SettingsLib/RestrictedLockUtils/Android.bp
+++ b/packages/SettingsLib/RestrictedLockUtils/Android.bp
@@ -7,6 +7,12 @@ package {
default_applicable_licenses: ["frameworks_base_license"],
}
+filegroup {
+ name: "SettingsLibRestrictedLockUtilsSrc",
+ srcs: ["src/**/*.java"],
+ visibility: ["//frameworks/base/services/accessibility"],
+}
+
android_library {
name: "SettingsLibRestrictedLockUtils",
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index b429fe6d4939..3debd9a179da 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -87,7 +87,7 @@ public class CachedBluetoothDeviceManager {
}
// Check the member devices for the coordinated set if it exists
final Set<CachedBluetoothDevice> memberDevices = cachedDevice.getMemberDevice();
- if (memberDevices != null) {
+ if (!memberDevices.isEmpty()) {
for (CachedBluetoothDevice memberDevice : memberDevices) {
if (memberDevice.getDevice().equals(device)) {
return memberDevice;
@@ -141,7 +141,7 @@ public class CachedBluetoothDeviceManager {
final Set<CachedBluetoothDevice> memberDevices = device.getMemberDevice();
// TODO: check the CSIP group size instead of the real member device set size, and adjust
// the size restriction.
- if (memberDevices.size() == 1) {
+ if (!memberDevices.isEmpty()) {
for (CachedBluetoothDevice memberDevice : memberDevices) {
if (memberDevice.isConnected()) {
return memberDevice.getConnectionSummary();
@@ -166,7 +166,7 @@ public class CachedBluetoothDeviceManager {
if (!cachedDevice.getDevice().equals(device)) {
// Check the member devices of the coordinated set if it exists
Set<CachedBluetoothDevice> memberDevices = cachedDevice.getMemberDevice();
- if (memberDevices != null) {
+ if (!memberDevices.isEmpty()) {
for (CachedBluetoothDevice memberDevice : memberDevices) {
if (memberDevice.getDevice().equals(device)) {
return true;
@@ -230,9 +230,10 @@ public class CachedBluetoothDeviceManager {
private void clearNonBondedSubDevices() {
for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
- final Set<CachedBluetoothDevice> memberDevices = cachedDevice.getMemberDevice();
- if (memberDevices != null) {
- for (CachedBluetoothDevice memberDevice : memberDevices) {
+ Set<CachedBluetoothDevice> memberDevices = cachedDevice.getMemberDevice();
+ if (!memberDevices.isEmpty()) {
+ for (Object it : memberDevices.toArray()) {
+ CachedBluetoothDevice memberDevice = (CachedBluetoothDevice) it;
// Member device exists and it is not bonded
if (memberDevice.getDevice().getBondState() == BluetoothDevice.BOND_NONE) {
cachedDevice.removeMemberDevice(memberDevice);
@@ -257,7 +258,7 @@ public class CachedBluetoothDeviceManager {
CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
cachedDevice.setJustDiscovered(false);
final Set<CachedBluetoothDevice> memberDevices = cachedDevice.getMemberDevice();
- if (memberDevices != null) {
+ if (!memberDevices.isEmpty()) {
for (CachedBluetoothDevice memberDevice : memberDevices) {
memberDevice.setJustDiscovered(false);
}
@@ -277,7 +278,7 @@ public class CachedBluetoothDeviceManager {
for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
final Set<CachedBluetoothDevice> memberDevices = cachedDevice.getMemberDevice();
- if (memberDevices != null) {
+ if (!memberDevices.isEmpty()) {
for (CachedBluetoothDevice memberDevice : memberDevices) {
if (memberDevice.getBondState() != BluetoothDevice.BOND_BONDED) {
cachedDevice.removeMemberDevice(memberDevice);
@@ -315,7 +316,7 @@ public class CachedBluetoothDeviceManager {
public synchronized void onDeviceUnpaired(CachedBluetoothDevice device) {
CachedBluetoothDevice mainDevice = mCsipDeviceManager.findMainDevice(device);
final Set<CachedBluetoothDevice> memberDevices = device.getMemberDevice();
- if (memberDevices != null) {
+ if (!memberDevices.isEmpty()) {
// Main device is unpaired, to unpair the member device
for (CachedBluetoothDevice memberDevice : memberDevices) {
memberDevice.unpair();
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
index ed12e83c330c..cc56a212aea1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
@@ -193,7 +193,7 @@ public class CsipDeviceManager {
return true;
}
final Set<CachedBluetoothDevice> memberSet = cachedDevice.getMemberDevice();
- if (memberSet == null) {
+ if (memberSet.isEmpty()) {
break;
}
@@ -225,7 +225,7 @@ public class CsipDeviceManager {
for (CachedBluetoothDevice cachedDevice : mCachedDevices) {
if (isValidGroupId(cachedDevice.getGroupId())) {
Set<CachedBluetoothDevice> memberSet = cachedDevice.getMemberDevice();
- if (memberSet == null) {
+ if (memberSet.isEmpty()) {
continue;
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
index 2c4f57fa77be..d53a3e896868 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
@@ -26,7 +26,9 @@ import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothUuid;
import android.content.Context;
+import android.os.ParcelUuid;
import org.junit.Before;
import org.junit.Test;
@@ -37,6 +39,8 @@ import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
@RunWith(RobolectricTestRunner.class)
public class CachedBluetoothDeviceManagerTest {
@@ -51,6 +55,10 @@ public class CachedBluetoothDeviceManagerTest {
private final static String DEVICE_ADDRESS_3 = "AA:BB:CC:DD:EE:33";
private final static long HISYNCID1 = 10;
private final static long HISYNCID2 = 11;
+ private final static Map<Integer, ParcelUuid> CAP_GROUP1 =
+ Map.of(1, BluetoothUuid.CAP);
+ private final static Map<Integer, ParcelUuid> CAP_GROUP2 =
+ Map.of(2, BluetoothUuid.CAP);
private final BluetoothClass DEVICE_CLASS_1 =
new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES);
private final BluetoothClass DEVICE_CLASS_2 =
@@ -70,6 +78,8 @@ public class CachedBluetoothDeviceManagerTest {
@Mock
private HearingAidProfile mHearingAidProfile;
@Mock
+ private CsipSetCoordinatorProfile mCsipSetCoordinatorProfile;
+ @Mock
private BluetoothDevice mDevice1;
@Mock
private BluetoothDevice mDevice2;
@@ -105,8 +115,12 @@ public class CachedBluetoothDeviceManagerTest {
when(mA2dpProfile.isProfileReady()).thenReturn(true);
when(mPanProfile.isProfileReady()).thenReturn(true);
when(mHearingAidProfile.isProfileReady()).thenReturn(true);
+ when(mCsipSetCoordinatorProfile.isProfileReady())
+ .thenReturn(true);
doAnswer((invocation) -> mHearingAidProfile).
when(mLocalProfileManager).getHearingAidProfile();
+ doAnswer((invocation) -> mCsipSetCoordinatorProfile)
+ .when(mLocalProfileManager).getCsipSetCoordinatorProfile();
mCachedDeviceManager = new CachedBluetoothDeviceManager(mContext, mLocalBluetoothManager);
mCachedDevice1 = spy(new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice1));
mCachedDevice2 = spy(new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice2));
@@ -298,7 +312,7 @@ public class CachedBluetoothDeviceManagerTest {
* Test to verify OnDeviceUnpaired() for main hearing Aid device unpair.
*/
@Test
- public void onDeviceUnpaired_unpairMainDevice() {
+ public void onDeviceUnpaired_unpairHearingAidMainDevice() {
when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
@@ -398,4 +412,153 @@ public class CachedBluetoothDeviceManagerTest {
when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
assertThat(mCachedDeviceManager.onDeviceDisappeared(cachedDevice1)).isTrue();
}
+
+ /**
+ * Test to verify getMemberDevice(), new device has the same group id.
+ */
+ @Test
+ public void addDevice_sameGroupId_validMemberDevice() {
+ doAnswer((invocation) -> CAP_GROUP1).when(mCsipSetCoordinatorProfile)
+ .getGroupUuidMapByDevice(mDevice1);
+ doAnswer((invocation) -> CAP_GROUP1).when(mCsipSetCoordinatorProfile)
+ .getGroupUuidMapByDevice(mDevice2);
+ doAnswer((invocation) -> CAP_GROUP1).when(mCsipSetCoordinatorProfile)
+ .getGroupUuidMapByDevice(mDevice3);
+ CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
+ CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
+ CachedBluetoothDevice cachedDevice3 = mCachedDeviceManager.addDevice(mDevice3);
+
+ assertThat(cachedDevice1.getMemberDevice()).contains(cachedDevice2);
+ assertThat(cachedDevice1.getMemberDevice()).contains(cachedDevice3);
+ }
+
+ /**
+ * Test to verify getMemberDevice(), new device has the different group id.
+ */
+ @Test
+ public void addDevice_differentGroupId_validMemberDevice() {
+ doAnswer((invocation) -> CAP_GROUP1).when(mCsipSetCoordinatorProfile)
+ .getGroupUuidMapByDevice(mDevice1);
+ doAnswer((invocation) -> CAP_GROUP2).when(mCsipSetCoordinatorProfile)
+ .getGroupUuidMapByDevice(mDevice2);
+ CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
+ CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
+
+ assertThat(cachedDevice1.getMemberDevice()).isEmpty();
+ }
+
+ /**
+ * Test to verify addDevice(), new device has the same group id.
+ */
+ @Test
+ public void addDevice_sameGroupId_validCachedDevices_mainDevicesAdded_memberDevicesNotAdded() {
+ doAnswer((invocation) -> CAP_GROUP1).when(mCsipSetCoordinatorProfile)
+ .getGroupUuidMapByDevice(mDevice1);
+ doAnswer((invocation) -> CAP_GROUP1).when(mCsipSetCoordinatorProfile)
+ .getGroupUuidMapByDevice(mDevice2);
+ CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
+ CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
+
+ Collection<CachedBluetoothDevice> devices = mCachedDeviceManager.getCachedDevicesCopy();
+ assertThat(devices).contains(cachedDevice1);
+ assertThat(devices).doesNotContain(cachedDevice2);
+ }
+
+ /**
+ * Test to verify addDevice(), new device has the different group id.
+ */
+ @Test
+ public void addDevice_differentGroupId_validCachedDevices_bothAdded() {
+ doAnswer((invocation) -> CAP_GROUP1).when(mCsipSetCoordinatorProfile)
+ .getGroupUuidMapByDevice(mDevice1);
+ doAnswer((invocation) -> CAP_GROUP2).when(mCsipSetCoordinatorProfile)
+ .getGroupUuidMapByDevice(mDevice2);
+ CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
+ CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
+
+ Collection<CachedBluetoothDevice> devices = mCachedDeviceManager.getCachedDevicesCopy();
+ assertThat(devices).contains(cachedDevice1);
+ assertThat(devices).contains(cachedDevice2);
+ }
+
+ /**
+ * Test to verify clearNonBondedDevices() for csip set member device.
+ */
+ @Test
+ public void clearNonBondedDevices_nonBondedMemberDevice() {
+ when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
+ when(mDevice3.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
+ CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
+ CachedBluetoothDevice cachedDevice3 = mCachedDeviceManager.addDevice(mDevice3);
+ cachedDevice1.setMemberDevice(cachedDevice2);
+ cachedDevice1.setMemberDevice(cachedDevice3);
+
+ assertThat(cachedDevice1.getMemberDevice()).contains(cachedDevice2);
+ assertThat(cachedDevice1.getMemberDevice()).contains(cachedDevice3);
+ mCachedDeviceManager.clearNonBondedDevices();
+
+ assertThat(cachedDevice1.getMemberDevice().contains(cachedDevice2)).isFalse();
+ assertThat(cachedDevice1.getMemberDevice().contains(cachedDevice3)).isTrue();
+ }
+
+ /**
+ * Test to verify OnDeviceUnpaired() for csip device unpair.
+ */
+ @Test
+ public void onDeviceUnpaired_unpairCsipMainDevice() {
+ when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
+ CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
+ cachedDevice1.setGroupId(1);
+ cachedDevice2.setGroupId(1);
+ cachedDevice1.setMemberDevice(cachedDevice2);
+
+ // Call onDeviceUnpaired for the one in mCachedDevices.
+ mCachedDeviceManager.onDeviceUnpaired(cachedDevice1);
+ verify(mDevice2).removeBond();
+ }
+
+ /**
+ * Test to verify OnDeviceUnpaired() for csip device unpair.
+ */
+ @Test
+ public void onDeviceUnpaired_unpairCsipSubDevice() {
+ when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
+ CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
+ cachedDevice1.setGroupId(1);
+ cachedDevice2.setGroupId(1);
+ cachedDevice1.setMemberDevice(cachedDevice2);
+
+ // Call onDeviceUnpaired for the one in mCachedDevices.
+ mCachedDeviceManager.onDeviceUnpaired(cachedDevice2);
+ verify(mDevice1).removeBond();
+ }
+
+ /**
+ * Test to verify isSubDevice_validSubDevice().
+ */
+ @Test
+ public void isSubDevice_validMemberDevice() {
+ doReturn(CAP_GROUP1).when(mCsipSetCoordinatorProfile).getGroupUuidMapByDevice(mDevice1);
+ mCachedDeviceManager.addDevice(mDevice1);
+
+ // Both device are not sub device in default value.
+ assertThat(mCachedDeviceManager.isSubDevice(mDevice1)).isFalse();
+ assertThat(mCachedDeviceManager.isSubDevice(mDevice2)).isFalse();
+
+ // Add Device-2 as device with Device-1 with the same group id, and add Device-3 with
+ // the different group id.
+ doReturn(CAP_GROUP1).when(mCsipSetCoordinatorProfile).getGroupUuidMapByDevice(mDevice2);
+ doReturn(CAP_GROUP2).when(mCsipSetCoordinatorProfile).getGroupUuidMapByDevice(mDevice3);
+ mCachedDeviceManager.addDevice(mDevice2);
+ mCachedDeviceManager.addDevice(mDevice3);
+
+ // Verify Device-2 is sub device, but Device-1, and Device-3 is not.
+ assertThat(mCachedDeviceManager.isSubDevice(mDevice1)).isFalse();
+ assertThat(mCachedDeviceManager.isSubDevice(mDevice2)).isTrue();
+ assertThat(mCachedDeviceManager.isSubDevice(mDevice3)).isFalse();
+ }
}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index fb8ffbd476a6..0dfad172ec5b 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -396,6 +396,7 @@ public class SettingsBackupTest {
Settings.Global.OTA_DISABLE_AUTOMATIC_UPDATE,
Settings.Global.OVERLAY_DISPLAY_DEVICES,
Settings.Global.PAC_CHANGE_DELAY,
+ Settings.Global.PACKAGE_STREAMING_VERIFIER_TIMEOUT,
Settings.Global.PACKAGE_VERIFIER_DEFAULT_RESPONSE,
Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB,
Settings.Global.PACKAGE_VERIFIER_SETTING_VISIBLE,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 23c53a1b624b..a3f07d8fd1b4 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -165,6 +165,7 @@
<uses-permission android:name="com.android.permission.USE_INSTALLER_V2" />
<uses-permission android:name="android.permission.INSTALL_TEST_ONLY_PACKAGE" />
<uses-permission android:name="com.android.permission.USE_SYSTEM_DATA_LOADERS" />
+ <uses-permission android:name="android.permission.PACKAGE_VERIFICATION_AGENT" />
<uses-permission android:name="android.permission.MOVE_PACKAGE" />
<uses-permission android:name="android.permission.KEEP_UNINSTALLED_PACKAGES" />
<uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" />
@@ -206,6 +207,7 @@
<uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
<uses-permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS" />
<uses-permission android:name="android.permission.QUERY_ADMIN_POLICY" />
+ <uses-permission android:name="android.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES" />
<uses-permission android:name="android.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS" />
<uses-permission android:name="android.permission.CLEAR_FREEZE_PERIOD" />
<uses-permission android:name="android.permission.MODIFY_QUIET_MODE" />
@@ -239,6 +241,7 @@
<uses-permission android:name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND" />
<uses-permission android:name="android.permission.ACTIVITY_EMBEDDING" />
<uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
+ <uses-permission android:name="android.permission.MANAGE_CLOUDSEARCH" />
<uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
<uses-permission android:name="android.permission.MANAGE_AUTO_FILL" />
<uses-permission android:name="android.permission.MANAGE_CONTENT_CAPTURE" />
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 2f117fc72468..559c31a92004 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -234,9 +234,36 @@ android_library {
plugins: ["dagger2-compiler"],
}
+soong_config_module_type_import {
+ from: "frameworks/base/services/Android.bp",
+ module_types: ["system_optimized_java_defaults"],
+}
+
+system_optimized_java_defaults {
+ name: "SystemUI_app_defaults",
+ soong_config_variables: {
+ SYSTEM_OPTIMIZE_JAVA: {
+ optimize: {
+ enabled: true,
+ optimize: true,
+ shrink: true,
+ proguard_flags_files: ["proguard.flags"],
+ },
+ conditions_default: {
+ optimize: {
+ proguard_flags_files: ["proguard.flags"],
+ },
+ },
+ },
+ },
+}
+
android_app {
name: "SystemUI",
- defaults: ["platform_app_defaults"],
+ defaults: [
+ "platform_app_defaults",
+ "SystemUI_app_defaults",
+ ],
static_libs: [
"SystemUI-core",
],
@@ -247,10 +274,6 @@ android_app {
certificate: "platform",
privileged: true,
- optimize: {
- proguard_flags_files: ["proguard.flags"],
- },
-
kotlincflags: ["-Xjvm-default=enable"],
dxflags: ["--multi-dex"],
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index b27159571af7..092758ee34ed 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -25,6 +25,9 @@
},
{
"exclude-annotation": "android.platform.test.scenario.annotation.LargeScreenOnly"
+ },
+ {
+ "exclude-annotation": "android.platform.test.scenario.annotation.FoldableOnly"
}
]
},
@@ -100,6 +103,9 @@
},
{
"exclude-annotation": "android.platform.test.scenario.annotation.LargeScreenOnly"
+ },
+ {
+ "exclude-annotation": "android.platform.test.scenario.annotation.FoldableOnly"
}
]
}
@@ -119,6 +125,9 @@
},
{
"exclude-annotation": "android.platform.test.scenario.annotation.LargeScreenOnly"
+ },
+ {
+ "exclude-annotation": "android.platform.test.scenario.annotation.FoldableOnly"
}
]
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index 066e169dcde3..f7a7603944f6 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -299,6 +299,13 @@ private class AnimatedDialog(
fullscreenTransparentBackground.setOnClickListener { dialog.dismiss() }
dialogContentWithBackground.isClickable = true
+ // Make sure the transparent and dialog backgrounds are not focusable by accessibility
+ // features.
+ fullscreenTransparentBackground.importantForAccessibility =
+ View.IMPORTANT_FOR_ACCESSIBILITY_NO
+ dialogContentWithBackground.importantForAccessibility =
+ View.IMPORTANT_FOR_ACCESSIBILITY_NO
+
fullscreenTransparentBackground.addView(
dialogContentWithBackground,
FrameLayout.LayoutParams(
@@ -342,8 +349,10 @@ private class AnimatedDialog(
?.color
?.defaultColor ?: Color.BLACK
- // Make the background view invisible until we start the animation.
- dialogContentWithBackground.visibility = View.INVISIBLE
+ // Make the background view invisible until we start the animation. We use the transition
+ // visibility like GhostView does so that we don't mess up with the accessibility tree (see
+ // b/204944038#comment17).
+ dialogContentWithBackground.setTransitionVisibility(View.INVISIBLE)
// Make sure the dialog is visible instantly and does not do any window animation.
window.attributes.windowAnimations = R.style.Animation_LaunchAnimation
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
index 5fec4cccda6c..183584227087 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
@@ -190,7 +190,11 @@ open class GhostedViewLaunchAnimatorController(
// Making the ghost view invisible will make the ghosted view visible, so order is
// important here.
ghostView.visibility = View.INVISIBLE
- ghostedView.visibility = View.INVISIBLE
+
+ // Make the ghosted view invisible again. We use the transition visibility like
+ // GhostView does so that we don't mess up with the accessibility tree (see
+ // b/204944038#comment17).
+ ghostedView.setTransitionVisibility(View.INVISIBLE)
backgroundView.visibility = View.INVISIBLE
}
return
@@ -261,6 +265,10 @@ open class GhostedViewLaunchAnimatorController(
GhostView.removeGhost(ghostedView)
launchContainerOverlay.remove(backgroundView)
+
+ // Make sure that the view is considered VISIBLE by accessibility by first making it
+ // INVISIBLE then VISIBLE (see b/204944038#comment17 for more info).
+ ghostedView.visibility = View.INVISIBLE
ghostedView.visibility = View.VISIBLE
ghostedView.invalidate()
}
diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
index a16f5cd5d930..da9a92a1f6b4 100644
--- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
+++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -20,9 +20,11 @@ import android.app.PendingIntent;
import android.app.smartspace.SmartspaceAction;
import android.app.smartspace.SmartspaceTarget;
import android.app.smartspace.SmartspaceTargetEvent;
+import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Parcelable;
+import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -39,6 +41,7 @@ import java.util.List;
public interface BcSmartspaceDataPlugin extends Plugin {
String ACTION = "com.android.systemui.action.PLUGIN_BC_SMARTSPACE_DATA";
int VERSION = 1;
+ String TAG = "BcSmartspaceDataPlugin";
/** Register a listener to get Smartspace data. */
void registerListener(SmartspaceTargetListener listener);
@@ -124,10 +127,14 @@ public interface BcSmartspaceDataPlugin extends Plugin {
/** Interface for launching Intents, which can differ on the lockscreen */
interface IntentStarter {
default void startFromAction(SmartspaceAction action, View v, boolean showOnLockscreen) {
- if (action.getIntent() != null) {
- startIntent(v, action.getIntent(), showOnLockscreen);
- } else if (action.getPendingIntent() != null) {
- startPendingIntent(action.getPendingIntent(), showOnLockscreen);
+ try {
+ if (action.getIntent() != null) {
+ startIntent(v, action.getIntent(), showOnLockscreen);
+ } else if (action.getPendingIntent() != null) {
+ startPendingIntent(action.getPendingIntent(), showOnLockscreen);
+ }
+ } catch (ActivityNotFoundException e) {
+ Log.w(TAG, "Could not launch intent for action: " + action, e);
}
}
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index 3517ebae10bf..ce63b446efb7 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -33,10 +33,14 @@
*;
}
--keep class com.android.systemui.dagger.GlobalRootComponent { *; }
--keep class com.android.systemui.dagger.GlobalRootComponent$SysUIComponentImpl { *; }
--keep class com.android.systemui.dagger.Dagger** { *; }
--keep class com.android.systemui.tv.Dagger** { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.systemui.dagger.GlobalRootComponent { !synthetic *; }
+-keep,allowoptimization,allowaccessmodification class com.android.systemui.dagger.GlobalRootComponent$SysUIComponentImpl { !synthetic *; }
+-keep,allowoptimization,allowaccessmodification class com.android.systemui.dagger.Dagger** { !synthetic *; }
+-keep,allowoptimization,allowaccessmodification class com.android.systemui.tv.Dagger** { !synthetic *; }
+
+# Allows proguard to make private and protected methods and fields public as
+# part of optimization. This lets proguard inline trivial getter/setter methods.
+-allowaccessmodification
# Removes runtime checks added through Kotlin to JVM code genereration to
# avoid linear growth as more Kotlin code is converted / added to the codebase.
diff --git a/packages/SystemUI/res-keyguard/drawable/super_lock_icon.xml b/packages/SystemUI/res-keyguard/drawable/super_lock_icon.xml
index 67a70bb39964..b3987f1aeeda 100644
--- a/packages/SystemUI/res-keyguard/drawable/super_lock_icon.xml
+++ b/packages/SystemUI/res-keyguard/drawable/super_lock_icon.xml
@@ -94,4 +94,9 @@
android:fromId="@id/unlocked"
android:toId="@id/unlocked_aod"
android:drawable="@drawable/unlocked_ls_to_aod" />
+
+ <transition
+ android:fromId="@id/unlocked"
+ android:toId="@id/locked_aod"
+ android:drawable="@drawable/unlocked_to_aod_lock" />
</animated-selector>
diff --git a/packages/SystemUI/res-keyguard/drawable/unlocked_to_aod_lock.xml b/packages/SystemUI/res-keyguard/drawable/unlocked_to_aod_lock.xml
new file mode 100644
index 000000000000..b6d76e01ad95
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/unlocked_to_aod_lock.xml
@@ -0,0 +1,169 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<animated-vector xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android">
+ <aapt:attr name="android:drawable">
+ <vector android:height="65dp" android:width="46dp" android:viewportHeight="65" android:viewportWidth="46">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_2_G_T_1" android:translateX="22.75" android:translateY="22.25" android:scaleX="1.02" android:scaleY="1.02">
+ <group android:name="_R_G_L_2_G" android:translateX="-8.75" android:translateY="-8.75">
+ <path android:name="_R_G_L_2_G_D_0_P_0" android:strokeColor="#FF000000"
+ android:strokeLineJoin="round"
+ android:strokeWidth="2.5"
+ android:strokeAlpha="1"
+ android:pathData=" M27.19 14.81 C27.19,14.81 27.19,8.3 27.19,8.3 C27.19,4.92 24.44,2.88 21.19,2.75 C17.74,2.62 15,4.74 15,8.11 C15,8.11 15,15 15,15 " />
+ </group>
+ </group>
+ <group android:name="_R_G_L_1_G" android:translateX="23" android:translateY="32.125">
+ <path android:name="_R_G_L_1_G_D_0_P_0"
+ android:strokeColor="#FF000000"
+ android:strokeLineJoin="round"
+ android:strokeWidth="2"
+ android:strokeAlpha="1"
+ android:pathData=" M11.25 -0.64 C11.25,-0.64 11.25,13.64 11.25,13.64 C11.25,15.22 9.97,16.5 8.39,16.5 C8.39,16.5 -8.39,16.5 -8.39,16.5 C-9.97,16.5 -11.25,15.22 -11.25,13.64 C-11.25,13.64 -11.25,-0.64 -11.25,-0.64 C-11.25,-2.22 -9.97,-3.5 -8.39,-3.5 C-8.39,-3.5 8.39,-3.5 8.39,-3.5 C9.97,-3.5 11.25,-2.22 11.25,-0.64c " />
+ </group>
+ <group android:name="_R_G_L_0_G" android:translateX="23" android:translateY="32.125">
+ <path android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillColor="#FF000000"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M-0.09 8.63 C1.2,8.63 2.25,7.57 2.25,6.28 C2.25,4.99 1.2,3.94 -0.09,3.94 C-1.39,3.94 -2.44,4.99 -2.44,6.28 C-2.44,7.57 -1.39,8.63 -0.09,8.63c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_2_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="strokeWidth"
+ android:duration="333" android:startOffset="0" android:valueFrom="2.5" android:valueTo="2" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0.833,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData"
+ android:duration="83" android:startOffset="0" android:valueFrom="M27.19 14.81 C27.19,14.81 27.19,8.3 27.19,8.3 C27.19,4.92 24.44,2.88 21.19,2.75 C17.74,2.62 15,4.74 15,8.11 C15,8.11 15,15 15,15 " android:valueTo="M27.13 10.19 C27.13,10.19 27.13,3.67 27.13,3.67 C27.13,0.3 24.38,-1.75 21.13,-1.87 C17.68,-2.01 14.94,0.11 14.94,3.49 C14.94,3.49 15,15 15,15 " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.456,0 0.464,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="pathData"
+ android:duration="133" android:startOffset="83" android:valueFrom="M27.13 10.19 C27.13,10.19 27.13,3.67 27.13,3.67 C27.13,0.3 24.38,-1.75 21.13,-1.87 C17.68,-2.01 14.94,0.11 14.94,3.49 C14.94,3.49 15,15 15,15 " android:valueTo="M2.5 10.38 C2.5,10.38 2.5,3.99 2.5,3.99 C2.5,0.61 5.3,-2.12 8.75,-2.12 C12.2,-2.12 15,0.61 15,3.99 C15,3.99 15,15 15,15 " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.606,0 0.035,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="pathData"
+ android:duration="117" android:startOffset="217" android:valueFrom="M2.5 10.38 C2.5,10.38 2.5,3.99 2.5,3.99 C2.5,0.61 5.3,-2.12 8.75,-2.12 C12.2,-2.12 15,0.61 15,3.99 C15,3.99 15,15 15,15 " android:valueTo="M2.5 15 C2.5,15 2.5,8.61 2.5,8.61 C2.5,5.24 5.3,2.5 8.75,2.5 C12.2,2.5 15,5.24 15,8.61 C15,8.61 15,15 15,15 " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.511,0 0.409,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateX"
+ android:duration="333" android:startOffset="0" android:valueFrom="22.75" android:valueTo="23" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateY" android:duration="333" android:startOffset="0" android:valueFrom="22.25" android:valueTo="25.5" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="scaleX"
+ android:duration="333" android:startOffset="0" android:valueFrom="1.02" android:valueTo="0.72" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY"
+ android:duration="333" android:startOffset="0" android:valueFrom="1.02" android:valueTo="0.72" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="strokeWidth"
+ android:duration="333" android:startOffset="0" android:valueFrom="2" android:valueTo="1.5" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.38,0 0.274,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData"
+ android:duration="333" android:startOffset="0" android:valueFrom="M11.25 -0.64 C11.25,-0.64 11.25,13.64 11.25,13.64 C11.25,15.22 9.97,16.5 8.39,16.5 C8.39,16.5 -8.39,16.5 -8.39,16.5 C-9.97,16.5 -11.25,15.22 -11.25,13.64 C-11.25,13.64 -11.25,-0.64 -11.25,-0.64 C-11.25,-2.22 -9.97,-3.5 -8.39,-3.5 C-8.39,-3.5 8.39,-3.5 8.39,-3.5 C9.97,-3.5 11.25,-2.22 11.25,-0.64c " android:valueTo="M7.88 -0.62 C7.88,-0.62 7.88,9.38 7.88,9.38 C7.88,10.48 6.98,11.38 5.88,11.38 C5.88,11.38 -5.87,11.38 -5.87,11.38 C-6.98,11.38 -7.87,10.48 -7.87,9.38 C-7.87,9.38 -7.87,-0.62 -7.87,-0.62 C-7.87,-1.73 -6.98,-2.62 -5.87,-2.62 C-5.87,-2.62 5.88,-2.62 5.88,-2.62 C6.98,-2.62 7.88,-1.73 7.88,-0.62c " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.431,0 0.133,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData"
+ android:duration="333" android:startOffset="0" android:valueFrom="M-0.09 8.63 C1.2,8.63 2.25,7.57 2.25,6.28 C2.25,4.99 1.2,3.94 -0.09,3.94 C-1.39,3.94 -2.44,4.99 -2.44,6.28 C-2.44,7.57 -1.39,8.63 -0.09,8.63c " android:valueTo="M0 6.13 C0.97,6.13 1.75,5.34 1.75,4.38 C1.75,3.41 0.97,2.63 0,2.63 C-0.97,2.63 -1.75,3.41 -1.75,4.38 C-1.75,5.34 -0.97,6.13 0,6.13c " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.431,0 0.133,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateX"
+ android:duration="850" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector> \ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml
index a2b8bf6d3d4c..4f0925f3bfbb 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml
@@ -20,19 +20,18 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/keyguard_bouncer_user_switcher"
android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false"
android:orientation="vertical"
android:gravity="center"
- android:paddingTop="12dp"
android:importantForAccessibility="yes"> <!-- Needed because TYPE_WINDOW_STATE_CHANGED is sent
from this view when bouncer is shown -->
- <ImageView
- android:id="@+id/user_icon"
- android:layout_width="@dimen/keyguard_user_switcher_icon_size"
- android:layout_height="@dimen/keyguard_user_switcher_icon_size" />
+ <ImageView
+ android:id="@+id/user_icon"
+ android:layout_width="@dimen/keyguard_user_switcher_icon_size"
+ android:layout_height="@dimen/keyguard_user_switcher_icon_size" />
<!-- need to keep this outer view in order to have a correctly sized anchor
for the dropdown menu, as well as dropdown background in the right place -->
@@ -41,7 +40,7 @@
android:orientation="horizontal"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
- android:layout_marginTop="32dp"
+ android:layout_marginTop="30dp"
android:minHeight="48dp">
<TextView
style="@style/Keyguard.UserSwitcher.Spinner.Header"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_slice_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_slice_view.xml
index 1863d1112947..7c5dbc247428 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_slice_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_slice_view.xml
@@ -30,8 +30,6 @@
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingStart="44dp"
- android:paddingEnd="44dp"
android:visibility="gone"
android:textColor="?attr/wallpaperTextColor"
android:theme="@style/TextAppearance.Keyguard"
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 95330400145f..2819dc9c27b7 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -108,10 +108,10 @@
<dimen name="one_handed_bouncer_move_animation_translation">120dp</dimen>
- <dimen name="keyguard_user_switcher_header_text_size">24sp</dimen>
- <dimen name="keyguard_user_switcher_item_text_size">18sp</dimen>
- <dimen name="keyguard_user_switcher_width">300dp</dimen>
- <dimen name="keyguard_user_switcher_icon_size">250dp</dimen>
+ <dimen name="keyguard_user_switcher_header_text_size">32sp</dimen>
+ <dimen name="keyguard_user_switcher_item_text_size">32sp</dimen>
+ <dimen name="keyguard_user_switcher_width">320dp</dimen>
+ <dimen name="keyguard_user_switcher_icon_size">310dp</dimen>
<dimen name="keyguard_user_switcher_corner">32dp</dimen>
<dimen name="keyguard_user_switcher_popup_corner">24dp</dimen>
<dimen name="keyguard_user_switcher_item_padding_vertical">15dp</dimen>
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index a7b2b47a4cda..60f034ae7ee8 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -112,7 +112,7 @@
<style name="TextAppearance.Keyguard">
<item name="android:textSize">@dimen/widget_title_font_size</item>
<item name="android:lineHeight">@dimen/widget_title_line_height</item>
- <item name="android:gravity">center</item>
+ <item name="android:gravity">start</item>
<item name="android:ellipsize">end</item>
<item name="android:maxLines">2</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
@@ -131,6 +131,7 @@
<style name="TextAppearance.Keyguard.BottomArea">
<item name="android:textSize">14sp</item>
<item name="android:maxLines">1</item>
+ <item name="android:gravity">center</item>
<item name="android:textColor">?attr/wallpaperTextColor</item>
<item name="android:shadowColor">@color/keyguard_shadow_color</item>
<item name="android:shadowRadius">?attr/shadowRadius</item>
diff --git a/packages/SystemUI/res/drawable/ic_signal_wifi_off.xml b/packages/SystemUI/res/drawable/ic_signal_wifi_off.xml
new file mode 100644
index 000000000000..a93abb71c9d7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_signal_wifi_off.xml
@@ -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.
+ -->
+<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="M18.575,15.2L7.55,4.175q1.1,-0.325 2.212,-0.462 1.113,-0.138 2.238,-0.138 3.45,0 6.55,1.45Q21.65,6.475 24,9zM20.225,23.575l-4.75,-4.75 -3.475,4.1L0,9q0.675,-0.725 1.438,-1.4 0.762,-0.675 1.612,-1.225l-2.625,-2.6L2.1,2.1l19.8,19.8z"/>
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/combined_qs_header.xml b/packages/SystemUI/res/layout/combined_qs_header.xml
index 1f10e5dfeed8..405863dc9d92 100644
--- a/packages/SystemUI/res/layout/combined_qs_header.xml
+++ b/packages/SystemUI/res/layout/combined_qs_header.xml
@@ -101,4 +101,16 @@
app:layout_constraintBottom_toBottomOf="parent"
/>
+ <FrameLayout
+ android:id="@+id/privacy_container"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:gravity="center"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="@id/date"
+ app:layout_constraintBottom_toBottomOf="@id/date"
+ >
+ <include layout="@layout/ongoing_privacy_chip"/>
+ </FrameLayout>
+
</androidx.constraintlayout.motion.widget.MotionLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/dream_overlay_container.xml b/packages/SystemUI/res/layout/dream_overlay_container.xml
new file mode 100644
index 000000000000..b611ffa6d84e
--- /dev/null
+++ b/packages/SystemUI/res/layout/dream_overlay_container.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+-->
+<com.android.systemui.dreams.DreamOverlayContainerView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/dream_overlay_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/dream_overlay_content"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent" />
+
+ <com.android.systemui.dreams.DreamOverlayStatusBarView
+ android:id="@+id/dream_overlay_status_bar"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/dream_overlay_status_bar_height"
+ android:layout_marginEnd="@dimen/dream_overlay_status_bar_margin"
+ android:layout_marginStart="@dimen/dream_overlay_status_bar_margin"
+ app:layout_constraintTop_toTopOf="parent">
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/dream_overlay_system_status"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ app:layout_constraintEnd_toEndOf="parent">
+
+ <com.android.systemui.statusbar.AlphaOptimizedImageView
+ android:id="@+id/dream_overlay_wifi_status"
+ android:layout_width="@dimen/status_bar_wifi_signal_size"
+ android:layout_height="match_parent"
+ android:layout_marginEnd="@dimen/dream_overlay_status_icon_margin"
+ android:visibility="gone"
+ app:layout_constraintEnd_toStartOf="@id/dream_overlay_battery" />
+
+ <com.android.systemui.battery.BatteryMeterView
+ android:id="@+id/dream_overlay_battery"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ app:layout_constraintEnd_toEndOf="parent" />
+
+ </androidx.constraintlayout.widget.ConstraintLayout>
+ </com.android.systemui.dreams.DreamOverlayStatusBarView>
+</com.android.systemui.dreams.DreamOverlayContainerView> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
index 275e0a52ab45..baf533656822 100644
--- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -390,36 +390,34 @@
android:focusable="false">
<FrameLayout
- android:id="@+id/apm_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:clickable="true"
- android:focusable="true"
android:layout_gravity="start|center_vertical"
android:orientation="vertical">
<Button
+ android:id="@+id/apm_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/turn_off_airplane_mode"
android:ellipsize="end"
style="@style/Widget.Dialog.Button.BorderButton"
- android:clickable="false"/>
+ android:clickable="true"
+ android:focusable="true"/>
</FrameLayout>
<FrameLayout
- android:id="@+id/done_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
- android:layout_gravity="end|center_vertical"
- android:clickable="true"
- android:focusable="true">
+ android:layout_gravity="end|center_vertical">
<Button
+ android:id="@+id/done_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/inline_done_button"
style="@style/Widget.Dialog.Button"
- android:clickable="false"/>
+ android:clickable="true"
+ android:focusable="true"/>
</FrameLayout>
</FrameLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/values-h800dp/dimens.xml b/packages/SystemUI/res/values-h800dp/dimens.xml
index f057603e2cd0..1d6f279afc66 100644
--- a/packages/SystemUI/res/values-h800dp/dimens.xml
+++ b/packages/SystemUI/res/values-h800dp/dimens.xml
@@ -22,5 +22,5 @@
<dimen name="large_clock_text_size">200dp</dimen>
<!-- With the large clock, move up slightly from the center -->
- <dimen name="keyguard_large_clock_top_margin">-104dp</dimen>
+ <dimen name="keyguard_large_clock_top_margin">-112dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index c7350a1eee8b..843c69fb1f49 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -602,7 +602,7 @@
<!-- When large clock is showing, offset the smartspace by this amount -->
<dimen name="keyguard_smartspace_top_offset">12dp</dimen>
<!-- With the large clock, move up slightly from the center -->
- <dimen name="keyguard_large_clock_top_margin">-52dp</dimen>
+ <dimen name="keyguard_large_clock_top_margin">-60dp</dimen>
<!-- Default line spacing multiplier between hours and minutes of the keyguard clock -->
<item name="keyguard_clock_line_spacing_scale" type="dimen" format="float">.7</item>
@@ -1303,4 +1303,9 @@
<dimen name="keyguard_unfold_translation_x">16dp</dimen>
<dimen name="fgs_manager_min_width_minor">100%</dimen>
+
+ <!-- Dream overlay related dimensions -->
+ <dimen name="dream_overlay_status_bar_height">80dp</dimen>
+ <dimen name="dream_overlay_status_bar_margin">40dp</dimen>
+ <dimen name="dream_overlay_status_icon_margin">8dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/xml/combined_qs_header_scene.xml b/packages/SystemUI/res/xml/combined_qs_header_scene.xml
index d61e4a95bb68..91607d2f9bea 100644
--- a/packages/SystemUI/res/xml/combined_qs_header_scene.xml
+++ b/packages/SystemUI/res/xml/combined_qs_header_scene.xml
@@ -23,11 +23,22 @@
app:constraintSetEnd="@id/qs_header_constraint"
app:constraintSetStart="@id/qqs_header_constraint">
<KeyFrameSet>
+ <!-- These positions are to prevent visual movement of @id/date -->
<KeyPosition
app:keyPositionType="pathRelative"
app:percentX="0"
- app:framePosition="50"
+ app:framePosition="49"
app:motionTarget="@id/date" />
+ <KeyPosition
+ app:keyPositionType="pathRelative"
+ app:percentX="1"
+ app:framePosition="51"
+ app:motionTarget="@id/date" />
+ <KeyAttribute
+ app:motionTarget="@id/date"
+ app:framePosition="50"
+ android:alpha="0"
+ />
</KeyFrameSet>
</Transition>
diff --git a/packages/SystemUI/res/xml/qqs_header.xml b/packages/SystemUI/res/xml/qqs_header.xml
index 3d7b549fc54b..c5b4c5d776b9 100644
--- a/packages/SystemUI/res/xml/qqs_header.xml
+++ b/packages/SystemUI/res/xml/qqs_header.xml
@@ -49,6 +49,14 @@
</Constraint>
<Constraint
+ android:id="@+id/statusIcons">
+ </Constraint>
+
+ <Constraint
+ android:id="@+id/batteryRemainingIcon" >
+ </Constraint>
+
+ <Constraint
android:id="@+id/carrier_group">
<CustomAttribute
app:attributeName="alpha"
@@ -56,6 +64,15 @@
/>
</Constraint>
-
+ <Constraint
+ android:id="@+id/privacy_container">
+ <Layout
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="@id/date"
+ app:layout_constraintBottom_toBottomOf="@id/date"
+ />
+ </Constraint>
</ConstraintSet> \ No newline at end of file
diff --git a/packages/SystemUI/res/xml/qs_header.xml b/packages/SystemUI/res/xml/qs_header.xml
index 6a0ab866966c..8248fcdb50fb 100644
--- a/packages/SystemUI/res/xml/qs_header.xml
+++ b/packages/SystemUI/res/xml/qs_header.xml
@@ -58,5 +58,14 @@
/>
</Constraint>
-
+ <Constraint
+ android:id="@+id/privacy_container">
+ <Layout
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="@id/date"
+ app:layout_constraintBottom_toBottomOf="@id/date"
+ />
+ </Constraint>
</ConstraintSet> \ No newline at end of file
diff --git a/packages/SystemUI/res/xml/split_header.xml b/packages/SystemUI/res/xml/split_header.xml
index 44d42a05cd46..03401b3d51d1 100644
--- a/packages/SystemUI/res/xml/split_header.xml
+++ b/packages/SystemUI/res/xml/split_header.xml
@@ -53,5 +53,29 @@
/>
</Constraint>
+ <Constraint
+ android:id="@+id/batteryRemainingIcon">
+ <Layout
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ app:layout_constraintHeight_min="@dimen/split_shade_header_min_height"
+ app:layout_constraintStart_toEndOf="@id/statusIcons"
+ app:layout_constraintEnd_toStartOf="@id/privacy_container"
+ app:layout_constraintTop_toTopOf="@id/clock"
+ app:layout_constraintBottom_toBottomOf="parent"
+ />
+ </Constraint>
+
+ <Constraint
+ android:id="@+id/privacy_container">
+ <Layout
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="@id/date"
+ app:layout_constraintBottom_toBottomOf="@id/date"
+ app:layout_constraintStart_toEndOf="@id/batteryRemainingIcon"
+ />
+ </Constraint>
</ConstraintSet> \ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index be15c700818c..b3983d276dcb 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -155,5 +155,10 @@ interface ISystemUiProxy {
*/
void onImeSwitcherPressed() = 49;
- // Next id = 50
+ /**
+ * Notifies to toggle notification panel.
+ */
+ void toggleNotificationPanel() = 50;
+
+ // Next id = 51
}
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 78867f7220af..605d37628ec7 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
@@ -36,6 +36,7 @@ import android.os.Looper;
import android.os.RemoteException;
import android.provider.Settings;
import android.util.Log;
+import android.view.HapticFeedbackConstants;
import android.view.IRotationWatcher;
import android.view.MotionEvent;
import android.view.Surface;
@@ -453,6 +454,7 @@ public class RotationButtonController {
mUiEventLogger.log(RotationButtonEvent.ROTATION_SUGGESTION_ACCEPTED);
incrementNumAcceptedRotationSuggestionsIfNeeded();
setRotationLockedAtAngle(mLastRotationSuggestion);
+ v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
}
private boolean onRotateSuggestionHover(View v, MotionEvent event) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index 38eded878014..48fcbbda7e46 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -61,7 +61,7 @@ import java.util.function.Consumer;
public class ActivityManagerWrapper {
private static final String TAG = "ActivityManagerWrapper";
-
+ private static final int NUM_RECENT_ACTIVITIES_REQUEST = 3;
private static final ActivityManagerWrapper sInstance = new ActivityManagerWrapper();
// Should match the values in PhoneWindowManager
@@ -113,6 +113,22 @@ public class ActivityManagerWrapper {
}
/**
+ * We ask for {@link #NUM_RECENT_ACTIVITIES_REQUEST} activities because when in split screen,
+ * we'll get back 2 activities for each split app and one for launcher. Launcher might be more
+ * "recently" used than one of the split apps so if we only request 2 tasks, then we might miss
+ * out on one of the split apps
+ *
+ * @return an array of up to {@link #NUM_RECENT_ACTIVITIES_REQUEST} running tasks
+ * filtering only for tasks that can be visible in the recent tasks list.
+ */
+ public ActivityManager.RunningTaskInfo[] getRunningTasks(boolean filterOnlyVisibleRecents) {
+ // Note: The set of running tasks from the system is ordered by recency
+ List<ActivityManager.RunningTaskInfo> tasks =
+ mAtm.getTasks(NUM_RECENT_ACTIVITIES_REQUEST, filterOnlyVisibleRecents);
+ return tasks.toArray(new RunningTaskInfo[tasks.size()]);
+ }
+
+ /**
* @return a list of the recents tasks.
*/
public List<RecentTaskInfo> getRecentTasks(int numTasks, int userId) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
index 30db13611f4a..c1d9d0d0e142 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
@@ -65,6 +65,7 @@ public class RemoteAnimationTargetCompat {
public final Rect localBounds;
public final Rect sourceContainerBounds;
public final Rect screenSpaceBounds;
+ public final Rect startScreenSpaceBounds;
public final boolean isNotInRecents;
public final Rect contentInsets;
public final ActivityManager.RunningTaskInfo taskInfo;
@@ -88,6 +89,7 @@ public class RemoteAnimationTargetCompat {
localBounds = app.localBounds;
sourceContainerBounds = app.sourceContainerBounds;
screenSpaceBounds = app.screenSpaceBounds;
+ startScreenSpaceBounds = screenSpaceBounds;
prefixOrderIndex = app.prefixOrderIndex;
isNotInRecents = app.isNotInRecents;
contentInsets = app.contentInsets;
@@ -219,6 +221,8 @@ public class RemoteAnimationTargetCompat {
localBounds.offsetTo(change.getEndRelOffset().x, change.getEndRelOffset().y);
sourceContainerBounds = null;
screenSpaceBounds = new Rect(change.getEndAbsBounds());
+ startScreenSpaceBounds = new Rect(change.getStartAbsBounds());
+
prefixOrderIndex = order;
// TODO(shell-transitions): I guess we need to send content insets? evaluate how its used.
contentInsets = new Rect(0, 0, 0, 0);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
index ea93a3b9375e..ac946cacdc5b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
@@ -33,6 +33,12 @@ import com.android.systemui.unfold.updates.hinge.HingeSensorAngleProvider
import java.lang.IllegalStateException
import java.util.concurrent.Executor
+/**
+ * Factory for [UnfoldTransitionProgressProvider].
+ *
+ * This is needed as Launcher has to create the object manually.
+ * Sysui create it using dagger (see [UnfoldTransitionModule]).
+ */
fun createUnfoldTransitionProgressProvider(
context: Context,
config: UnfoldTransitionConfig,
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
index ac463ebc1c97..157191302010 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
@@ -168,6 +168,12 @@ public class AnimatableClockController extends ViewController<AnimatableClockVie
if (!mIsDozing) mView.animateAppearOnLockscreen();
}
+ /** Animate the clock appearance when a foldable device goes from fully-open/half-open state to
+ * fully folded state and it goes to sleep (always on display screen) */
+ public void animateFoldAppear() {
+ mView.animateFoldAppear();
+ }
+
/**
* Updates the time for the view.
*/
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
deleted file mode 100644
index 2a0c2855c3b2..000000000000
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
+++ /dev/null
@@ -1,315 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.keyguard;
-
-import android.annotation.FloatRange;
-import android.annotation.IntRange;
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.text.format.DateFormat;
-import android.util.AttributeSet;
-import android.widget.TextView;
-
-import com.android.systemui.R;
-
-import java.util.Calendar;
-import java.util.Locale;
-import java.util.TimeZone;
-
-import kotlin.Unit;
-
-/**
- * Displays the time with the hour positioned above the minutes. (ie: 09 above 30 is 9:30)
- * The time's text color is a gradient that changes its colors based on its controller.
- */
-public class AnimatableClockView extends TextView {
- private static final CharSequence DOUBLE_LINE_FORMAT_12_HOUR = "hh\nmm";
- private static final CharSequence DOUBLE_LINE_FORMAT_24_HOUR = "HH\nmm";
- private static final long DOZE_ANIM_DURATION = 300;
- private static final long APPEAR_ANIM_DURATION = 350;
- private static final long CHARGE_ANIM_DURATION_PHASE_0 = 500;
- private static final long CHARGE_ANIM_DURATION_PHASE_1 = 1000;
-
- private final Calendar mTime = Calendar.getInstance();
-
- private final int mDozingWeight;
- private final int mLockScreenWeight;
- private CharSequence mFormat;
- private CharSequence mDescFormat;
- private int mDozingColor;
- private int mLockScreenColor;
- private float mLineSpacingScale = 1f;
- private int mChargeAnimationDelay = 0;
-
- private TextAnimator mTextAnimator = null;
- private Runnable mOnTextAnimatorInitialized;
-
- private boolean mIsSingleLine;
-
- public AnimatableClockView(Context context) {
- this(context, null, 0, 0);
- }
-
- public AnimatableClockView(Context context, AttributeSet attrs) {
- this(context, attrs, 0, 0);
- }
-
- public AnimatableClockView(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public AnimatableClockView(Context context, AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- TypedArray ta = context.obtainStyledAttributes(
- attrs, R.styleable.AnimatableClockView, defStyleAttr, defStyleRes);
- try {
- mDozingWeight = ta.getInt(R.styleable.AnimatableClockView_dozeWeight, 100);
- mLockScreenWeight = ta.getInt(R.styleable.AnimatableClockView_lockScreenWeight, 300);
- mChargeAnimationDelay = ta.getInt(
- R.styleable.AnimatableClockView_chargeAnimationDelay, 200);
- } finally {
- ta.recycle();
- }
-
- ta = context.obtainStyledAttributes(
- attrs, android.R.styleable.TextView, defStyleAttr, defStyleRes);
- try {
- mIsSingleLine = ta.getBoolean(android.R.styleable.TextView_singleLine, false);
- } finally {
- ta.recycle();
- }
-
- refreshFormat();
- }
-
- @Override
- public void onAttachedToWindow() {
- super.onAttachedToWindow();
- refreshFormat();
- }
-
- @Override
- public void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- }
-
- int getDozingWeight() {
- if (useBoldedVersion()) {
- return mDozingWeight + 100;
- }
- return mDozingWeight;
- }
-
- int getLockScreenWeight() {
- if (useBoldedVersion()) {
- return mLockScreenWeight + 100;
- }
- return mLockScreenWeight;
- }
-
- /**
- * Whether to use a bolded version based on the user specified fontWeightAdjustment.
- */
- boolean useBoldedVersion() {
- // "Bold text" fontWeightAdjustment is 300.
- return getResources().getConfiguration().fontWeightAdjustment > 100;
- }
-
- void refreshTime() {
- mTime.setTimeInMillis(System.currentTimeMillis());
- setText(DateFormat.format(mFormat, mTime));
- setContentDescription(DateFormat.format(mDescFormat, mTime));
- }
-
- void onTimeZoneChanged(TimeZone timeZone) {
- mTime.setTimeZone(timeZone);
- refreshFormat();
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- if (mTextAnimator == null) {
- mTextAnimator = new TextAnimator(
- getLayout(),
- () -> {
- invalidate();
- return Unit.INSTANCE;
- });
- if (mOnTextAnimatorInitialized != null) {
- mOnTextAnimatorInitialized.run();
- mOnTextAnimatorInitialized = null;
- }
- } else {
- mTextAnimator.updateLayout(getLayout());
- }
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- mTextAnimator.draw(canvas);
- }
-
- void setLineSpacingScale(float scale) {
- mLineSpacingScale = scale;
- setLineSpacing(0, mLineSpacingScale);
- }
-
- void setColors(int dozingColor, int lockScreenColor) {
- mDozingColor = dozingColor;
- mLockScreenColor = lockScreenColor;
- }
-
- void animateAppearOnLockscreen() {
- if (mTextAnimator == null) {
- return;
- }
-
- setTextStyle(
- getDozingWeight(),
- -1 /* text size, no update */,
- mLockScreenColor,
- false /* animate */,
- 0 /* duration */,
- 0 /* delay */,
- null /* onAnimationEnd */);
-
- setTextStyle(
- getLockScreenWeight(),
- -1 /* text size, no update */,
- mLockScreenColor,
- true, /* animate */
- APPEAR_ANIM_DURATION,
- 0 /* delay */,
- null /* onAnimationEnd */);
- }
-
- void animateCharge(DozeStateGetter dozeStateGetter) {
- if (mTextAnimator == null || mTextAnimator.isRunning()) {
- // Skip charge animation if dozing animation is already playing.
- return;
- }
- Runnable startAnimPhase2 = () -> setTextStyle(
- dozeStateGetter.isDozing() ? getDozingWeight() : getLockScreenWeight() /* weight */,
- -1,
- null,
- true /* animate */,
- CHARGE_ANIM_DURATION_PHASE_1,
- 0 /* delay */,
- null /* onAnimationEnd */);
- setTextStyle(dozeStateGetter.isDozing()
- ? getLockScreenWeight()
- : getDozingWeight()/* weight */,
- -1,
- null,
- true /* animate */,
- CHARGE_ANIM_DURATION_PHASE_0,
- mChargeAnimationDelay,
- startAnimPhase2);
- }
-
- void animateDoze(boolean isDozing, boolean animate) {
- setTextStyle(isDozing ? getDozingWeight() : getLockScreenWeight() /* weight */,
- -1,
- isDozing ? mDozingColor : mLockScreenColor,
- animate,
- DOZE_ANIM_DURATION,
- 0 /* delay */,
- null /* onAnimationEnd */);
- }
-
- /**
- * Set text style with an optional animation.
- *
- * By passing -1 to weight, the view preserves its current weight.
- * By passing -1 to textSize, the view preserves its current text size.
- *
- * @param weight text weight.
- * @param textSize font size.
- * @param animate true to animate the text style change, otherwise false.
- */
- private void setTextStyle(
- @IntRange(from = 0, to = 1000) int weight,
- @FloatRange(from = 0) float textSize,
- Integer color,
- boolean animate,
- long duration,
- long delay,
- Runnable onAnimationEnd) {
- if (mTextAnimator != null) {
- mTextAnimator.setTextStyle(weight, textSize, color, animate, duration, null,
- delay, onAnimationEnd);
- } else {
- // when the text animator is set, update its start values
- mOnTextAnimatorInitialized =
- () -> mTextAnimator.setTextStyle(
- weight, textSize, color, false, duration, null,
- delay, onAnimationEnd);
- }
- }
-
- void refreshFormat() {
- Patterns.update(mContext);
-
- final boolean use24HourFormat = DateFormat.is24HourFormat(getContext());
- if (mIsSingleLine && use24HourFormat) {
- mFormat = Patterns.sClockView24;
- } else if (!mIsSingleLine && use24HourFormat) {
- mFormat = DOUBLE_LINE_FORMAT_24_HOUR;
- } else if (mIsSingleLine && !use24HourFormat) {
- mFormat = Patterns.sClockView12;
- } else {
- mFormat = DOUBLE_LINE_FORMAT_12_HOUR;
- }
-
- mDescFormat = use24HourFormat ? Patterns.sClockView24 : Patterns.sClockView12;
- refreshTime();
- }
-
- // DateFormat.getBestDateTimePattern is extremely expensive, and refresh is called often.
- // This is an optimization to ensure we only recompute the patterns when the inputs change.
- private static final class Patterns {
- static String sClockView12;
- static String sClockView24;
- static String sCacheKey;
-
- static void update(Context context) {
- final Locale locale = Locale.getDefault();
- final Resources res = context.getResources();
- final String clockView12Skel = res.getString(R.string.clock_12hr_format);
- final String clockView24Skel = res.getString(R.string.clock_24hr_format);
- final String key = locale.toString() + clockView12Skel + clockView24Skel;
- if (key.equals(sCacheKey)) return;
- sClockView12 = DateFormat.getBestDateTimePattern(locale, clockView12Skel);
-
- // CLDR insists on adding an AM/PM indicator even though it wasn't in the skeleton
- // format. The following code removes the AM/PM indicator if we didn't want it.
- if (!clockView12Skel.contains("a")) {
- sClockView12 = sClockView12.replaceAll("a", "").trim();
- }
- sClockView24 = DateFormat.getBestDateTimePattern(locale, clockView24Skel);
- sCacheKey = key;
- }
- }
-
- interface DozeStateGetter {
- boolean isDozing();
- }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.kt b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.kt
new file mode 100644
index 000000000000..357be2503a3a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.kt
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard
+
+import android.animation.TimeInterpolator
+import android.annotation.ColorInt
+import android.annotation.FloatRange
+import android.annotation.IntRange
+import android.annotation.SuppressLint
+import android.content.Context
+import android.graphics.Canvas
+import android.text.format.DateFormat
+import android.util.AttributeSet
+import android.widget.TextView
+import com.android.systemui.R
+import com.android.systemui.animation.Interpolators
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator
+import java.util.Calendar
+import java.util.Locale
+import java.util.TimeZone
+
+/**
+ * Displays the time with the hour positioned above the minutes. (ie: 09 above 30 is 9:30)
+ * The time's text color is a gradient that changes its colors based on its controller.
+ */
+class AnimatableClockView @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ defStyleRes: Int = 0
+) : TextView(context, attrs, defStyleAttr, defStyleRes) {
+
+ private val time = Calendar.getInstance()
+
+ private val dozingWeightInternal: Int
+ private val lockScreenWeightInternal: Int
+ private val isSingleLineInternal: Boolean
+
+ private var format: CharSequence? = null
+ private var descFormat: CharSequence? = null
+
+ @ColorInt
+ private var dozingColor = 0
+
+ @ColorInt
+ private var lockScreenColor = 0
+
+ private var lineSpacingScale = 1f
+ private val chargeAnimationDelay: Int
+ private var textAnimator: TextAnimator? = null
+ private var onTextAnimatorInitialized: Runnable? = null
+
+ val dozingWeight: Int
+ get() = if (useBoldedVersion()) dozingWeightInternal + 100 else dozingWeightInternal
+
+ val lockScreenWeight: Int
+ get() = if (useBoldedVersion()) lockScreenWeightInternal + 100 else lockScreenWeightInternal
+
+ init {
+ val animatableClockViewAttributes = context.obtainStyledAttributes(
+ attrs, R.styleable.AnimatableClockView, defStyleAttr, defStyleRes
+ )
+
+ try {
+ dozingWeightInternal = animatableClockViewAttributes.getInt(
+ R.styleable.AnimatableClockView_dozeWeight,
+ 100
+ )
+ lockScreenWeightInternal = animatableClockViewAttributes.getInt(
+ R.styleable.AnimatableClockView_lockScreenWeight,
+ 300
+ )
+ chargeAnimationDelay = animatableClockViewAttributes.getInt(
+ R.styleable.AnimatableClockView_chargeAnimationDelay, 200
+ )
+ } finally {
+ animatableClockViewAttributes.recycle()
+ }
+
+ val textViewAttributes = context.obtainStyledAttributes(
+ attrs, android.R.styleable.TextView,
+ defStyleAttr, defStyleRes
+ )
+
+ isSingleLineInternal =
+ try {
+ textViewAttributes.getBoolean(android.R.styleable.TextView_singleLine, false)
+ } finally {
+ textViewAttributes.recycle()
+ }
+
+ refreshFormat()
+ }
+
+ override fun onAttachedToWindow() {
+ super.onAttachedToWindow()
+ refreshFormat()
+ }
+
+ /**
+ * Whether to use a bolded version based on the user specified fontWeightAdjustment.
+ */
+ fun useBoldedVersion(): Boolean {
+ // "Bold text" fontWeightAdjustment is 300.
+ return resources.configuration.fontWeightAdjustment > 100
+ }
+
+ fun refreshTime() {
+ time.timeInMillis = System.currentTimeMillis()
+ text = DateFormat.format(format, time)
+ contentDescription = DateFormat.format(descFormat, time)
+ }
+
+ fun onTimeZoneChanged(timeZone: TimeZone?) {
+ time.timeZone = timeZone
+ refreshFormat()
+ }
+
+ @SuppressLint("DrawAllocation")
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+ val animator = textAnimator
+ if (animator == null) {
+ textAnimator = TextAnimator(layout) { invalidate() }
+ onTextAnimatorInitialized?.run()
+ onTextAnimatorInitialized = null
+ } else {
+ animator.updateLayout(layout)
+ }
+ }
+
+ override fun onDraw(canvas: Canvas) {
+ textAnimator?.draw(canvas)
+ }
+
+ fun setLineSpacingScale(scale: Float) {
+ lineSpacingScale = scale
+ setLineSpacing(0f, lineSpacingScale)
+ }
+
+ fun setColors(@ColorInt dozingColor: Int, lockScreenColor: Int) {
+ this.dozingColor = dozingColor
+ this.lockScreenColor = lockScreenColor
+ }
+
+ fun animateAppearOnLockscreen() {
+ if (textAnimator == null) {
+ return
+ }
+ setTextStyle(
+ weight = dozingWeight,
+ textSize = -1f,
+ color = lockScreenColor,
+ animate = false,
+ duration = 0,
+ delay = 0,
+ onAnimationEnd = null
+ )
+ setTextStyle(
+ weight = lockScreenWeight,
+ textSize = -1f,
+ color = lockScreenColor,
+ animate = true,
+ duration = APPEAR_ANIM_DURATION,
+ delay = 0,
+ onAnimationEnd = null
+ )
+ }
+
+ fun animateFoldAppear() {
+ if (textAnimator == null) {
+ return
+ }
+ setTextStyle(
+ weight = lockScreenWeightInternal,
+ textSize = -1f,
+ color = lockScreenColor,
+ animate = false,
+ duration = 0,
+ delay = 0,
+ onAnimationEnd = null
+ )
+ setTextStyle(
+ weight = dozingWeightInternal,
+ textSize = -1f,
+ color = dozingColor,
+ animate = true,
+ interpolator = Interpolators.EMPHASIZED_DECELERATE,
+ duration = StackStateAnimator.ANIMATION_DURATION_FOLD_TO_AOD.toLong(),
+ delay = 0,
+ onAnimationEnd = null
+ )
+ }
+
+ fun animateCharge(dozeStateGetter: DozeStateGetter) {
+ if (textAnimator == null || textAnimator!!.isRunning()) {
+ // Skip charge animation if dozing animation is already playing.
+ return
+ }
+ val startAnimPhase2 = Runnable {
+ setTextStyle(
+ weight = if (dozeStateGetter.isDozing) dozingWeight else lockScreenWeight,
+ textSize = -1f,
+ color = null,
+ animate = true,
+ duration = CHARGE_ANIM_DURATION_PHASE_1,
+ delay = 0,
+ onAnimationEnd = null
+ )
+ }
+ setTextStyle(
+ weight = if (dozeStateGetter.isDozing) lockScreenWeight else dozingWeight,
+ textSize = -1f,
+ color = null,
+ animate = true,
+ duration = CHARGE_ANIM_DURATION_PHASE_0,
+ delay = chargeAnimationDelay.toLong(),
+ onAnimationEnd = startAnimPhase2
+ )
+ }
+
+ fun animateDoze(isDozing: Boolean, animate: Boolean) {
+ setTextStyle(
+ weight = if (isDozing) dozingWeight else lockScreenWeight,
+ textSize = -1f,
+ color = if (isDozing) dozingColor else lockScreenColor,
+ animate = animate,
+ duration = DOZE_ANIM_DURATION,
+ delay = 0,
+ onAnimationEnd = null
+ )
+ }
+
+ /**
+ * Set text style with an optional animation.
+ *
+ * By passing -1 to weight, the view preserves its current weight.
+ * By passing -1 to textSize, the view preserves its current text size.
+ *
+ * @param weight text weight.
+ * @param textSize font size.
+ * @param animate true to animate the text style change, otherwise false.
+ */
+ private fun setTextStyle(
+ @IntRange(from = 0, to = 1000) weight: Int,
+ @FloatRange(from = 0.0) textSize: Float,
+ color: Int?,
+ animate: Boolean,
+ interpolator: TimeInterpolator?,
+ duration: Long,
+ delay: Long,
+ onAnimationEnd: Runnable?
+ ) {
+ if (textAnimator != null) {
+ textAnimator?.setTextStyle(
+ weight = weight,
+ textSize = textSize,
+ color = color,
+ animate = animate,
+ duration = duration,
+ interpolator = interpolator,
+ delay = delay,
+ onAnimationEnd = onAnimationEnd
+ )
+ } else {
+ // when the text animator is set, update its start values
+ onTextAnimatorInitialized = Runnable {
+ textAnimator?.setTextStyle(
+ weight = weight,
+ textSize = textSize,
+ color = color,
+ animate = false,
+ duration = duration,
+ interpolator = interpolator,
+ delay = delay,
+ onAnimationEnd = onAnimationEnd
+ )
+ }
+ }
+ }
+
+ private fun setTextStyle(
+ @IntRange(from = 0, to = 1000) weight: Int,
+ @FloatRange(from = 0.0) textSize: Float,
+ color: Int?,
+ animate: Boolean,
+ duration: Long,
+ delay: Long,
+ onAnimationEnd: Runnable?
+ ) {
+ setTextStyle(
+ weight = weight,
+ textSize = textSize,
+ color = color,
+ animate = animate,
+ interpolator = null,
+ duration = duration,
+ delay = delay,
+ onAnimationEnd = onAnimationEnd
+ )
+ }
+
+ fun refreshFormat() {
+ Patterns.update(context)
+ val use24HourFormat = DateFormat.is24HourFormat(context)
+
+ format = when {
+ isSingleLineInternal && use24HourFormat -> Patterns.sClockView24
+ !isSingleLineInternal && use24HourFormat -> DOUBLE_LINE_FORMAT_24_HOUR
+ isSingleLineInternal && !use24HourFormat -> Patterns.sClockView12
+ else -> DOUBLE_LINE_FORMAT_12_HOUR
+ }
+
+ descFormat = if (use24HourFormat) Patterns.sClockView24 else Patterns.sClockView12
+
+ refreshTime()
+ }
+
+ // DateFormat.getBestDateTimePattern is extremely expensive, and refresh is called often.
+ // This is an optimization to ensure we only recompute the patterns when the inputs change.
+ private object Patterns {
+ var sClockView12: String? = null
+ var sClockView24: String? = null
+ var sCacheKey: String? = null
+
+ fun update(context: Context) {
+ val locale = Locale.getDefault()
+ val res = context.resources
+ val clockView12Skel = res.getString(R.string.clock_12hr_format)
+ val clockView24Skel = res.getString(R.string.clock_24hr_format)
+ val key = locale.toString() + clockView12Skel + clockView24Skel
+ if (key == sCacheKey) return
+
+ val clockView12 = DateFormat.getBestDateTimePattern(locale, clockView12Skel)
+ sClockView12 = clockView12
+
+ // CLDR insists on adding an AM/PM indicator even though it wasn't in the skeleton
+ // format. The following code removes the AM/PM indicator if we didn't want it.
+ if (!clockView12Skel.contains("a")) {
+ sClockView12 = clockView12.replace("a".toRegex(), "").trim { it <= ' ' }
+ }
+ sClockView24 = DateFormat.getBestDateTimePattern(locale, clockView24Skel)
+ sCacheKey = key
+ }
+ }
+
+ interface DozeStateGetter {
+ val isDozing: Boolean
+ }
+}
+
+private const val DOUBLE_LINE_FORMAT_12_HOUR = "hh\nmm"
+private const val DOUBLE_LINE_FORMAT_24_HOUR = "HH\nmm"
+private const val DOZE_ANIM_DURATION: Long = 300
+private const val APPEAR_ANIM_DURATION: Long = 350
+private const val CHARGE_ANIM_DURATION_PHASE_0: Long = 500
+private const val CHARGE_ANIM_DURATION_PHASE_1: Long = 1000
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 9238b8226bbc..25dcdf9aa561 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -190,11 +190,15 @@ public class KeyguardClockSwitch extends RelativeLayout {
}
}
- private void animateClockChange(boolean useLargeClock) {
+ private void updateClockViews(boolean useLargeClock, boolean animate) {
if (mClockInAnim != null) mClockInAnim.cancel();
if (mClockOutAnim != null) mClockOutAnim.cancel();
if (mStatusAreaAnim != null) mStatusAreaAnim.cancel();
+ mClockInAnim = null;
+ mClockOutAnim = null;
+ mStatusAreaAnim = null;
+
View in, out;
int direction = 1;
float statusAreaYTranslation;
@@ -214,6 +218,14 @@ public class KeyguardClockSwitch extends RelativeLayout {
removeView(out);
}
+ if (!animate) {
+ out.setAlpha(0f);
+ in.setAlpha(1f);
+ in.setVisibility(VISIBLE);
+ mStatusArea.setTranslationY(statusAreaYTranslation);
+ return;
+ }
+
mClockOutAnim = new AnimatorSet();
mClockOutAnim.setDuration(CLOCK_OUT_MILLIS);
mClockOutAnim.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
@@ -273,7 +285,7 @@ public class KeyguardClockSwitch extends RelativeLayout {
*
* @return true if desired clock appeared and false if it was already visible
*/
- boolean switchToClock(@ClockSize int clockSize) {
+ boolean switchToClock(@ClockSize int clockSize, boolean animate) {
if (mDisplayedClockSize != null && clockSize == mDisplayedClockSize) {
return false;
}
@@ -281,7 +293,7 @@ public class KeyguardClockSwitch extends RelativeLayout {
// let's make sure clock is changed only after all views were laid out so we can
// translate them properly
if (mChildrenAreLaidOut) {
- animateClockChange(clockSize == LARGE);
+ updateClockViews(clockSize == LARGE, animate);
}
mDisplayedClockSize = clockSize;
@@ -293,7 +305,7 @@ public class KeyguardClockSwitch extends RelativeLayout {
super.onLayout(changed, l, t, r, b);
if (mDisplayedClockSize != null && !mChildrenAreLaidOut) {
- animateClockChange(mDisplayedClockSize == LARGE);
+ updateClockViews(mDisplayedClockSize == LARGE, /* animate */ true);
}
mChildrenAreLaidOut = true;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 032da789518c..25b551139b44 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -275,11 +275,9 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
}
private void updateClockLayout() {
- int largeClockTopMargin = 0;
- if (mSmartspaceController.isEnabled()) {
- largeClockTopMargin = getContext().getResources().getDimensionPixelSize(
- R.dimen.keyguard_large_clock_top_margin);
- }
+ int largeClockTopMargin = getContext().getResources().getDimensionPixelSize(
+ R.dimen.keyguard_large_clock_top_margin);
+
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT,
MATCH_PARENT);
lp.topMargin = largeClockTopMargin;
@@ -290,17 +288,24 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
* Set which clock should be displayed on the keyguard. The other one will be automatically
* hidden.
*/
- public void displayClock(@KeyguardClockSwitch.ClockSize int clockSize) {
+ public void displayClock(@KeyguardClockSwitch.ClockSize int clockSize, boolean animate) {
if (!mCanShowDoubleLineClock && clockSize == KeyguardClockSwitch.LARGE) {
return;
}
- boolean appeared = mView.switchToClock(clockSize);
- if (appeared && clockSize == LARGE) {
+ boolean appeared = mView.switchToClock(clockSize, animate);
+ if (animate && appeared && clockSize == LARGE) {
mLargeClockViewController.animateAppear();
}
}
+ public void animateFoldToAod() {
+ if (mClockViewController != null) {
+ mClockViewController.animateFoldAppear();
+ mLargeClockViewController.animateFoldAppear();
+ }
+ }
+
/**
* If we're presenting a custom clock of just the default one.
*/
@@ -356,8 +361,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
mKeyguardUnlockAnimationController.updateLockscreenSmartSpacePosition();
}
}
-
- mKeyguardSliceViewController.updatePosition(x, props, animate);
}
/** Sets an alpha value on every child view except for the smartspace. */
@@ -443,7 +446,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK, 1) != 0;
if (!mCanShowDoubleLineClock) {
- mUiExecutor.execute(() -> displayClock(KeyguardClockSwitch.SMALL));
+ mUiExecutor.execute(() -> displayClock(KeyguardClockSwitch.SMALL, /* animate */ true));
}
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 67ef02a6029b..b84cb19b9468 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -866,7 +866,6 @@ public class KeyguardSecurityContainer extends FrameLayout {
((LayerDrawable) mUserSwitcher.getBackground()).getDrawable(1).setAlpha(255);
}
- anchor.setClickable(true);
anchor.setOnClickListener((v) -> {
if (mFalsingManager.isFalseTap(LOW_PENALTY)) return;
@@ -877,8 +876,7 @@ public class KeyguardSecurityContainer extends FrameLayout {
public void onItemClick(AdapterView parent, View view, int pos, long id) {
if (mFalsingManager.isFalseTap(LOW_PENALTY)) return;
- // - 1 to account for the header view
- UserRecord user = adapter.getItem(pos - 1);
+ UserRecord user = adapter.getItem(pos);
if (!user.isCurrent) {
adapter.onUserListItemClicked(user);
}
@@ -907,9 +905,16 @@ public class KeyguardSecurityContainer extends FrameLayout {
== Configuration.ORIENTATION_PORTRAIT) {
updateViewGravity(mViewFlipper, Gravity.CENTER_HORIZONTAL);
updateViewGravity(mUserSwitcherViewGroup, Gravity.CENTER_HORIZONTAL);
+ mUserSwitcherViewGroup.setTranslationY(0);
} else {
updateViewGravity(mViewFlipper, Gravity.RIGHT | Gravity.BOTTOM);
- updateViewGravity(mUserSwitcherViewGroup, Gravity.LEFT | Gravity.TOP);
+ updateViewGravity(mUserSwitcherViewGroup, Gravity.LEFT | Gravity.CENTER_VERTICAL);
+
+ // Attempt to reposition a bit higher to make up for this frame being a bit lower
+ // on the device
+ int yTrans = mView.getContext().getResources().getDimensionPixelSize(
+ R.dimen.status_bar_height);
+ mUserSwitcherViewGroup.setTranslationY(-yTrans);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
index d05cc4ea8101..2af9244480e7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
@@ -43,9 +43,6 @@ import com.android.systemui.Dumpable;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardSliceProvider;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.statusbar.notification.AnimatableProperty;
-import com.android.systemui.statusbar.notification.PropertyAnimator;
-import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.ViewController;
@@ -203,13 +200,6 @@ public class KeyguardSliceViewController extends ViewController<KeyguardSliceVie
Trace.endSection();
}
- /**
- * Update position of the view, with optional animation
- */
- void updatePosition(int x, AnimationProperties props, boolean animate) {
- PropertyAnimator.setProperty(mView, AnimatableProperty.TRANSLATION_X, x, props, animate);
- }
-
void showSlice(Slice slice) {
Trace.beginSection("KeyguardSliceViewController#showSlice");
if (slice == null) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 986d0debb56a..8bf890d7df50 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -123,8 +123,17 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
* Set which clock should be displayed on the keyguard. The other one will be automatically
* hidden.
*/
- public void displayClock(@ClockSize int clockSize) {
- mKeyguardClockSwitchController.displayClock(clockSize);
+ public void displayClock(@ClockSize int clockSize, boolean animate) {
+ mKeyguardClockSwitchController.displayClock(clockSize, animate);
+ }
+
+ /**
+ * Performs fold to aod animation of the clocks (changes font weight from bold to thin).
+ * This animation is played when AOD is enabled and foldable device is fully folded, it is
+ * displayed on the outer screen
+ */
+ public void animateFoldToAod() {
+ mKeyguardClockSwitchController.animateFoldToAod();
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 237ca71027b5..2789e27178cc 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -830,7 +830,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
if (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) {
lockedOutStateChanged |= !mFingerprintLockedOutPermanent;
mFingerprintLockedOutPermanent = true;
- requireStrongAuthIfAllLockedOut();
+ Log.d(TAG, "Fingerprint locked out - requiring strong auth");
+ mLockPatternUtils.requireStrongAuth(
+ STRONG_AUTH_REQUIRED_AFTER_LOCKOUT, getCurrentUser());
}
if (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT
@@ -840,6 +842,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
if (isUdfpsEnrolled()) {
updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
}
+ stopListeningForFace();
}
for (int i = 0; i < mCallbacks.size(); i++) {
@@ -1049,7 +1052,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
if (msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT) {
lockedOutStateChanged = !mFaceLockedOutPermanent;
mFaceLockedOutPermanent = true;
- requireStrongAuthIfAllLockedOut();
}
if (isHwUnavailable && cameraPrivacyEnabled) {
@@ -1163,19 +1165,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
return faceAuthenticated;
}
- private void requireStrongAuthIfAllLockedOut() {
- final boolean faceLock =
- (mFaceLockedOutPermanent || !shouldListenForFace()) && !getIsFaceAuthenticated();
- final boolean fpLock =
- mFingerprintLockedOutPermanent || !shouldListenForFingerprint(isUdfpsEnrolled());
-
- if (faceLock && fpLock) {
- Log.d(TAG, "All biometrics locked out - requiring strong auth");
- mLockPatternUtils.requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_LOCKOUT,
- getCurrentUser());
- }
- }
-
public boolean getUserCanSkipBouncer(int userId) {
return getUserHasTrust(userId) || getUserUnlockedWithBiometric(userId);
}
@@ -2373,6 +2362,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_BOOT)
|| containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT);
+ // TODO: always disallow when fp is already locked out?
+ final boolean fpLockedout = mFingerprintLockedOut || mFingerprintLockedOutPermanent;
+
final boolean canBypass = mKeyguardBypassController != null
&& mKeyguardBypassController.canBypass();
// There's no reason to ask the HAL for authentication when the user can dismiss the
@@ -2407,7 +2399,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
&& !mKeyguardGoingAway && biometricEnabledForUser && !mLockIconPressed
&& strongAuthAllowsScanning && mIsPrimaryUser
&& (!mSecureCameraLaunched || mOccludingAppRequestingFace)
- && !faceAuthenticated;
+ && !faceAuthenticated
+ && !fpLockedout;
// Aggregate relevant fields for debug logging.
if (DEBUG_FACE || DEBUG_SPEW) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherPopupMenu.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherPopupMenu.java
index dfb466788ca7..7b6ce3e1c951 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherPopupMenu.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherPopupMenu.java
@@ -18,12 +18,10 @@ package com.android.keyguard;
import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Resources;
-import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ListPopupWindow;
import android.widget.ListView;
-import android.widget.TextView;
import com.android.systemui.R;
import com.android.systemui.plugins.FalsingManager;
@@ -68,12 +66,6 @@ public class KeyguardUserSwitcherPopupMenu extends ListPopupWindow {
// This will force the popupwindow to show upward instead of drop down
listView.addOnLayoutChangeListener(mLayoutListener);
- TextView header = (TextView) LayoutInflater.from(mContext).inflate(
- R.layout.keyguard_bouncer_user_switcher_item, listView, false);
- header.setText(mContext.getResources().getString(
- R.string.accessibility_multi_user_switch_switcher));
- listView.addHeaderView(header);
-
listView.setOnTouchListener((v, ev) -> {
if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
return mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY);
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index cd57af4ae97b..6626f59aae8c 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -278,10 +278,6 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
mView.setContentDescription(mUnlockedLabel);
mView.setVisibility(View.VISIBLE);
} else if (mShowAodLockIcon) {
- if (wasShowingUnlock) {
- // transition to the unlock icon first
- mView.updateIcon(ICON_LOCK, false);
- }
mView.updateIcon(ICON_LOCK, true);
mView.setContentDescription(mLockedLabel);
mView.setVisibility(View.VISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 8c405ca6bd39..63962fa6da11 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -16,6 +16,7 @@
package com.android.systemui;
+import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.Application;
import android.app.Notification;
@@ -27,6 +28,7 @@ import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Process;
+import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
@@ -111,6 +113,13 @@ public class SystemUIApplication extends Application implements
ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_HIGH_IMG);
}
+ // Enable binder tracing on system server for calls originating from SysUI
+ try {
+ ActivityManager.getService().enableBinderTracing();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to enable binder tracing", e);
+ }
+
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
diff --git a/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java b/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
index f13730e602a0..3f5c2c8f45d5 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
@@ -171,8 +171,8 @@ public final class PhoneStateMonitor {
return mStatusBarStateController.isDozing();
}
- private boolean isLauncherShowing(ActivityManager.RunningTaskInfo runningTaskInfo) {
- if (runningTaskInfo == null) {
+ private boolean isLauncherShowing(@Nullable ActivityManager.RunningTaskInfo runningTaskInfo) {
+ if (runningTaskInfo == null || runningTaskInfo.topActivity == null) {
return false;
} else {
return runningTaskInfo.topActivity.equals(mDefaultHome);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index e35b55841f2f..1496f170dffe 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -636,7 +636,9 @@ public abstract class AuthBiometricView extends LinearLayout {
mIndicatorView.setText(message);
mIndicatorView.setTextColor(mTextColorError);
mIndicatorView.setVisibility(View.VISIBLE);
- mIndicatorView.setSelected(true);
+ // select to enable marquee unless a screen reader is enabled
+ mIndicatorView.setSelected(!mAccessibilityManager.isEnabled()
+ || !mAccessibilityManager.isTouchExplorationEnabled());
mHandler.postDelayed(resetMessageRunnable, mInjector.getDelayAfterError());
Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
index 07aec6994bd0..73e3aecd8b63 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
@@ -75,7 +75,7 @@ abstract class UdfpsAnimationViewController<T extends UdfpsAnimationView>
@Override
protected void onViewDetached() {
mPanelExpansionStateManager.removeExpansionListener(mPanelExpansionListener);
- mDialogManager.registerListener(mDialogListener);
+ mDialogManager.unregisterListener(mDialogListener);
mDumpManger.unregisterDumpable(getDumpTag());
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java
index 4aa46f1813dc..58cf35f2917c 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java
@@ -16,12 +16,14 @@
package com.android.systemui.communal;
+import static com.android.systemui.communal.dagger.CommunalModule.COMMUNAL_CONDITIONS;
+
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.communal.conditions.CommunalConditionsMonitor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.util.condition.Monitor;
import com.google.android.collect.Lists;
@@ -31,6 +33,7 @@ import java.util.Iterator;
import java.util.concurrent.Executor;
import javax.inject.Inject;
+import javax.inject.Named;
/**
* A Monitor for reporting a {@link CommunalSource} presence.
@@ -42,7 +45,7 @@ public class CommunalSourceMonitor {
// A list of {@link Callback} that have registered to receive updates.
private final ArrayList<WeakReference<Callback>> mCallbacks = Lists.newArrayList();
- private final CommunalConditionsMonitor mConditionsMonitor;
+ private final Monitor mConditionsMonitor;
private final Executor mExecutor;
private CommunalSource mCurrentSource;
@@ -53,7 +56,7 @@ public class CommunalSourceMonitor {
// Whether the class is currently listening for condition changes.
private boolean mListeningForConditions = false;
- private final CommunalConditionsMonitor.Callback mConditionsCallback =
+ private final Monitor.Callback mConditionsCallback =
allConditionsMet -> {
if (mAllCommunalConditionsMet != allConditionsMet) {
if (DEBUG) Log.d(TAG, "communal conditions changed: " + allConditionsMet);
@@ -66,7 +69,7 @@ public class CommunalSourceMonitor {
@VisibleForTesting
@Inject
public CommunalSourceMonitor(@Main Executor executor,
- CommunalConditionsMonitor communalConditionsMonitor) {
+ @Named(COMMUNAL_CONDITIONS) Monitor communalConditionsMonitor) {
mExecutor = executor;
mConditionsMonitor = communalConditionsMonitor;
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSourcePrimer.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalSourcePrimer.java
index 13b1dc034a19..f965431a8001 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSourcePrimer.java
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSourcePrimer.java
@@ -142,7 +142,7 @@ public class CommunalSourcePrimer extends CoreStartable {
private void connect() {
if (DEBUG) {
- Log.d(TAG, "attempting to communal to communal source");
+ Log.d(TAG, "attempting to connect to communal source");
}
if (mCurrentConnection != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java
index f27ae344eb24..e1f1ac42884d 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java
@@ -34,6 +34,8 @@ import com.android.systemui.idle.AmbientLightModeMonitor;
import com.android.systemui.idle.LightSensorEventsDebounceAlgorithm;
import com.android.systemui.idle.dagger.IdleViewComponent;
import com.android.systemui.util.condition.Condition;
+import com.android.systemui.util.condition.Monitor;
+import com.android.systemui.util.condition.dagger.MonitorComponent;
import java.util.Collections;
import java.util.HashSet;
@@ -135,4 +137,14 @@ public interface CommunalModule {
return Optional.empty();
}
}
+
+ /** */
+ @Provides
+ @Named(COMMUNAL_CONDITIONS)
+ static Monitor provideCommunalSourceMonitor(
+ @Named(COMMUNAL_CONDITIONS) Set<Condition> communalConditions,
+ MonitorComponent.Factory factory) {
+ final MonitorComponent component = factory.create(communalConditions, new HashSet<>());
+ return component.getMonitor();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index e3376789c0d1..af0697996f43 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -28,6 +28,7 @@ import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper;
import com.android.systemui.people.PeopleProvider;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
+import com.android.systemui.unfold.UnfoldLatencyTracker;
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider;
import com.android.wm.shell.ShellCommandHandler;
import com.android.wm.shell.TaskViewFactory;
@@ -135,6 +136,7 @@ public interface SysUIComponent {
// No init method needed, just needs to be gotten so that it's created.
getMediaTttChipController();
getMediaTttCommandLineHelper();
+ getUnfoldLatencyTracker().init();
}
/**
@@ -156,6 +158,12 @@ public interface SysUIComponent {
ContextComponentHelper getContextComponentHelper();
/**
+ * Creates a UnfoldLatencyTracker.
+ */
+ @SysUISingleton
+ UnfoldLatencyTracker getUnfoldLatencyTracker();
+
+ /**
* Main dependency providing module.
*/
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index 471a327ad987..b2fe3bb94dd3 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -37,10 +37,14 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.tuner.TunerService;
+import com.android.systemui.unfold.FoldAodAnimationController;
+import com.android.systemui.unfold.FoldAodAnimationController.FoldAodAnimationStatus;
+import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.util.AlarmTimeout;
import com.android.systemui.util.wakelock.WakeLock;
import java.util.Calendar;
+import java.util.Optional;
import javax.inject.Inject;
@@ -49,7 +53,8 @@ import javax.inject.Inject;
*/
@DozeScope
public class DozeUi implements DozeMachine.Part, TunerService.Tunable,
- ConfigurationController.ConfigurationListener, StatusBarStateController.StateListener {
+ ConfigurationController.ConfigurationListener, FoldAodAnimationStatus,
+ StatusBarStateController.StateListener {
// if enabled, calls dozeTimeTick() whenever the time changes:
private static final boolean BURN_IN_TESTING_ENABLED = false;
private static final long TIME_TICK_DEADLINE_MILLIS = 90 * 1000; // 1.5min
@@ -57,6 +62,7 @@ public class DozeUi implements DozeMachine.Part, TunerService.Tunable,
private final DozeHost mHost;
private final Handler mHandler;
private final WakeLock mWakeLock;
+ private final FoldAodAnimationController mFoldAodAnimationController;
private DozeMachine mMachine;
private final AlarmTimeout mTimeTicker;
private final boolean mCanAnimateTransition;
@@ -100,6 +106,7 @@ public class DozeUi implements DozeMachine.Part, TunerService.Tunable,
DozeParameters params, KeyguardUpdateMonitor keyguardUpdateMonitor,
DozeLog dozeLog, TunerService tunerService,
StatusBarStateController statusBarStateController,
+ Optional<SysUIUnfoldComponent> sysUiUnfoldComponent,
ConfigurationController configurationController) {
mContext = context;
mWakeLock = wakeLock;
@@ -118,12 +125,23 @@ public class DozeUi implements DozeMachine.Part, TunerService.Tunable,
mConfigurationController = configurationController;
mConfigurationController.addCallback(this);
+
+ mFoldAodAnimationController = sysUiUnfoldComponent
+ .map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null);
+
+ if (mFoldAodAnimationController != null) {
+ mFoldAodAnimationController.addCallback(this);
+ }
}
@Override
public void destroy() {
mTunerService.removeTunable(this);
mConfigurationController.removeCallback(this);
+
+ if (mFoldAodAnimationController != null) {
+ mFoldAodAnimationController.removeCallback(this);
+ }
}
@Override
@@ -142,7 +160,8 @@ public class DozeUi implements DozeMachine.Part, TunerService.Tunable,
&& (mKeyguardShowing || mDozeParameters.shouldControlUnlockedScreenOff())
&& !mHost.isPowerSaveActive();
mDozeParameters.setControlScreenOffAnimation(controlScreenOff);
- mHost.setAnimateScreenOff(controlScreenOff);
+ mHost.setAnimateScreenOff(controlScreenOff
+ && mDozeParameters.shouldAnimateDozingChange());
}
}
@@ -299,4 +318,9 @@ public class DozeUi implements DozeMachine.Part, TunerService.Tunable,
public void onStatePostChange() {
updateAnimateScreenOff();
}
+
+ @Override
+ public void onFoldToAodAnimationChanged() {
+ updateAnimateScreenOff();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerView.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerView.java
new file mode 100644
index 000000000000..bc1f772e14bb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerView.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import androidx.constraintlayout.widget.ConstraintLayout;
+
+/**
+ * {@link DreamOverlayContainerView} contains a dream overlay and its status bar.
+ */
+public class DreamOverlayContainerView extends ConstraintLayout {
+ public DreamOverlayContainerView(Context context) {
+ this(context, null);
+ }
+
+ public DreamOverlayContainerView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public DreamOverlayContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public DreamOverlayContainerView(
+ Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index 8f0ea2fb2f87..393f039d91ea 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -29,11 +29,11 @@ import android.view.WindowInsets;
import android.view.WindowManager;
import androidx.annotation.NonNull;
-import androidx.constraintlayout.widget.ConstraintLayout;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.PhoneWindow;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import java.util.concurrent.Executor;
@@ -54,57 +54,75 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
private final Executor mExecutor;
// The state controller informs the service of updates to the overlays present.
private final DreamOverlayStateController mStateController;
+ // The component used to resolve dream overlay dependencies.
+ private final DreamOverlayComponent mDreamOverlayComponent;
- // The window is populated once the dream informs the service it has begun dreaming.
- private Window mWindow;
- private ConstraintLayout mLayout;
+ // The dream overlay's content view, which is located below the status bar (in z-order) and is
+ // the space into which widgets are placed.
+ private ViewGroup mDreamOverlayContentView;
private final DreamOverlayStateController.Callback mOverlayStateCallback =
new DreamOverlayStateController.Callback() {
- @Override
- public void onOverlayChanged() {
- mExecutor.execute(() -> reloadOverlaysLocked());
- }
- };
+ @Override
+ public void onOverlayChanged() {
+ mExecutor.execute(() -> reloadOverlaysLocked());
+ }
+ };
// The service listens to view changes in order to declare that input occurring in areas outside
// the overlay should be passed through to the dream underneath.
- private View.OnAttachStateChangeListener mRootViewAttachListener =
+ private final View.OnAttachStateChangeListener mRootViewAttachListener =
new View.OnAttachStateChangeListener() {
- @Override
- public void onViewAttachedToWindow(View v) {
- v.getViewTreeObserver()
- .addOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener);
- }
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ v.getViewTreeObserver()
+ .addOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener);
+ }
- @Override
- public void onViewDetachedFromWindow(View v) {
- v.getViewTreeObserver()
- .removeOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener);
- }
- };
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ v.getViewTreeObserver()
+ .removeOnComputeInternalInsetsListener(
+ mOnComputeInternalInsetsListener);
+ }
+ };
// A hook into the internal inset calculation where we declare the overlays as the only
// touchable regions.
- private ViewTreeObserver.OnComputeInternalInsetsListener mOnComputeInternalInsetsListener =
+ private final ViewTreeObserver.OnComputeInternalInsetsListener
+ mOnComputeInternalInsetsListener =
new ViewTreeObserver.OnComputeInternalInsetsListener() {
- @Override
- public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
- if (mLayout != null) {
- inoutInfo.setTouchableInsets(
- ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
- final Region region = new Region();
- for (int i = 0; i < mLayout.getChildCount(); i++) {
- View child = mLayout.getChildAt(i);
- final Rect rect = new Rect();
- child.getGlobalVisibleRect(rect);
- region.op(rect, Region.Op.UNION);
+ @Override
+ public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
+ if (mDreamOverlayContentView != null) {
+ inoutInfo.setTouchableInsets(
+ ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+ final Region region = new Region();
+ for (int i = 0; i < mDreamOverlayContentView.getChildCount(); i++) {
+ View child = mDreamOverlayContentView.getChildAt(i);
+ final Rect rect = new Rect();
+ child.getGlobalVisibleRect(rect);
+ region.op(rect, Region.Op.UNION);
+ }
+
+ inoutInfo.touchableRegion.set(region);
+ }
}
+ };
- inoutInfo.touchableRegion.set(region);
- }
- }
- };
+ @Inject
+ public DreamOverlayService(
+ Context context,
+ @Main Executor executor,
+ DreamOverlayStateController overlayStateController,
+ DreamOverlayComponent.Factory dreamOverlayComponentFactory) {
+ mContext = context;
+ mExecutor = executor;
+ mStateController = overlayStateController;
+ mDreamOverlayComponent = dreamOverlayComponentFactory.create();
+
+ mStateController.addCallback(mOverlayStateCallback);
+ }
@Override
public void onStartDream(@NonNull WindowManager.LayoutParams layoutParams) {
@@ -112,10 +130,10 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
}
private void reloadOverlaysLocked() {
- if (mLayout == null) {
+ if (mDreamOverlayContentView == null) {
return;
}
- mLayout.removeAllViews();
+ mDreamOverlayContentView.removeAllViews();
for (OverlayProvider overlayProvider : mStateController.getOverlays()) {
addOverlay(overlayProvider);
}
@@ -129,31 +147,32 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
* into the dream window.
*/
private void addOverlayWindowLocked(WindowManager.LayoutParams layoutParams) {
- mWindow = new PhoneWindow(mContext);
- mWindow.setAttributes(layoutParams);
- mWindow.setWindowManager(null, layoutParams.token, "DreamOverlay", true);
+ final PhoneWindow window = new PhoneWindow(mContext);
+ window.setAttributes(layoutParams);
+ window.setWindowManager(null, layoutParams.token, "DreamOverlay", true);
- mWindow.setBackgroundDrawable(new ColorDrawable(0));
+ window.setBackgroundDrawable(new ColorDrawable(0));
- mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
- mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
- mWindow.requestFeature(Window.FEATURE_NO_TITLE);
+ window.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
+ window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
+ window.requestFeature(Window.FEATURE_NO_TITLE);
// Hide all insets when the dream is showing
- mWindow.getDecorView().getWindowInsetsController().hide(WindowInsets.Type.systemBars());
- mWindow.setDecorFitsSystemWindows(false);
+ window.getDecorView().getWindowInsetsController().hide(WindowInsets.Type.systemBars());
+ window.setDecorFitsSystemWindows(false);
if (DEBUG) {
Log.d(TAG, "adding overlay window to dream");
}
- mLayout = new ConstraintLayout(mContext);
- mLayout.setLayoutParams(new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
- mLayout.addOnAttachStateChangeListener(mRootViewAttachListener);
- mWindow.setContentView(mLayout);
+ window.setContentView(mDreamOverlayComponent.getDreamOverlayContainerView());
+
+ mDreamOverlayContentView = mDreamOverlayComponent.getDreamOverlayContentView();
+ mDreamOverlayContentView.addOnAttachStateChangeListener(mRootViewAttachListener);
+
+ mDreamOverlayComponent.getDreamOverlayStatusBarViewController().init();
final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
- windowManager.addView(mWindow.getDecorView(), mWindow.getAttributes());
+ windowManager.addView(window.getDecorView(), window.getAttributes());
mExecutor.execute(this::reloadOverlaysLocked);
}
@@ -163,11 +182,11 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
(view, layoutParams) -> {
// Always move UI related work to the main thread.
mExecutor.execute(() -> {
- if (mLayout == null) {
+ if (mDreamOverlayContentView == null) {
return;
}
- mLayout.addView(view, layoutParams);
+ mDreamOverlayContentView.addView(view, layoutParams);
});
},
() -> {
@@ -178,15 +197,6 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
});
}
- @Inject
- public DreamOverlayService(Context context, @Main Executor executor,
- DreamOverlayStateController overlayStateController) {
- mContext = context;
- mExecutor = executor;
- mStateController = overlayStateController;
- mStateController.addCallback(mOverlayStateCallback);
- }
-
@Override
public void onDestroy() {
mStateController.removeCallback(mOverlayStateCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
new file mode 100644
index 000000000000..9847ef633bc1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.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 com.android.systemui.dreams;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageView;
+
+import androidx.constraintlayout.widget.ConstraintLayout;
+
+import com.android.internal.util.Preconditions;
+import com.android.systemui.R;
+import com.android.systemui.battery.BatteryMeterView;
+import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
+
+/**
+ * {@link DreamOverlayStatusBarView} is the view responsible for displaying the status bar in a
+ * dream. The status bar includes status icons such as battery and wifi.
+ */
+public class DreamOverlayStatusBarView extends ConstraintLayout implements
+ BatteryStateChangeCallback {
+
+ private BatteryMeterView mBatteryView;
+ private ImageView mWifiStatusView;
+
+ public DreamOverlayStatusBarView(Context context) {
+ this(context, null);
+ }
+
+ public DreamOverlayStatusBarView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public DreamOverlayStatusBarView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public DreamOverlayStatusBarView(
+ Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ mBatteryView = Preconditions.checkNotNull(findViewById(R.id.dream_overlay_battery),
+ "R.id.dream_overlay_battery must not be null");
+ mWifiStatusView = Preconditions.checkNotNull(findViewById(R.id.dream_overlay_wifi_status),
+ "R.id.dream_overlay_wifi_status must not be null");
+
+ mWifiStatusView.setImageDrawable(getContext().getDrawable(R.drawable.ic_signal_wifi_off));
+ }
+
+ /**
+ * Whether to show the battery percent text next to the battery status icons.
+ * @param show True if the battery percent text should be shown.
+ */
+ void showBatteryPercentText(boolean show) {
+ mBatteryView.setForceShowPercent(show);
+ }
+
+ /**
+ * Whether to show the wifi status icon.
+ * @param show True if the wifi status icon should be shown.
+ */
+ void showWifiStatus(boolean show) {
+ // Only show the wifi status icon when wifi isn't available.
+ mWifiStatusView.setVisibility(show ? View.VISIBLE : View.GONE);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
new file mode 100644
index 000000000000..5674b9f3f9fd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 android.annotation.IntDef;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+
+import com.android.systemui.battery.BatteryMeterViewController;
+import com.android.systemui.dreams.dagger.DreamOverlayComponent;
+import com.android.systemui.dreams.dagger.DreamOverlayModule;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.util.ViewController;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * View controller for {@link DreamOverlayStatusBarView}.
+ */
+@DreamOverlayComponent.DreamOverlayScope
+public class DreamOverlayStatusBarViewController extends ViewController<DreamOverlayStatusBarView> {
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "WIFI_STATUS_" }, value = {
+ WIFI_STATUS_UNKNOWN,
+ WIFI_STATUS_UNAVAILABLE,
+ WIFI_STATUS_AVAILABLE
+ })
+ private @interface WifiStatus {}
+ private static final int WIFI_STATUS_UNKNOWN = 0;
+ private static final int WIFI_STATUS_UNAVAILABLE = 1;
+ private static final int WIFI_STATUS_AVAILABLE = 2;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "BATTERY_STATUS_" }, value = {
+ BATTERY_STATUS_UNKNOWN,
+ BATTERY_STATUS_NOT_CHARGING,
+ BATTERY_STATUS_CHARGING
+ })
+ private @interface BatteryStatus {}
+ private static final int BATTERY_STATUS_UNKNOWN = 0;
+ private static final int BATTERY_STATUS_NOT_CHARGING = 1;
+ private static final int BATTERY_STATUS_CHARGING = 2;
+
+ private final BatteryController mBatteryController;
+ private final BatteryMeterViewController mBatteryMeterViewController;
+ private final ConnectivityManager mConnectivityManager;
+ private final boolean mShowPercentAvailable;
+
+ private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder()
+ .clearCapabilities()
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI).build();
+
+ private final NetworkCallback mNetworkCallback = new NetworkCallback() {
+ @Override
+ public void onCapabilitiesChanged(
+ Network network, NetworkCapabilities networkCapabilities) {
+ onWifiAvailabilityChanged(
+ networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI));
+ }
+
+ @Override
+ public void onAvailable(Network network) {
+ onWifiAvailabilityChanged(true);
+ }
+
+ @Override
+ public void onLost(Network network) {
+ onWifiAvailabilityChanged(false);
+ }
+ };
+
+ private final BatteryController.BatteryStateChangeCallback mBatteryStateChangeCallback =
+ new BatteryController.BatteryStateChangeCallback() {
+ @Override
+ public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
+ DreamOverlayStatusBarViewController.this.onBatteryLevelChanged(charging);
+ }
+ };
+
+ private @WifiStatus int mWifiStatus = WIFI_STATUS_UNKNOWN;
+ private @BatteryStatus int mBatteryStatus = BATTERY_STATUS_UNKNOWN;
+
+ @Inject
+ public DreamOverlayStatusBarViewController(
+ Context context,
+ DreamOverlayStatusBarView view,
+ BatteryController batteryController,
+ @Named(DreamOverlayModule.DREAM_OVERLAY_BATTERY_CONTROLLER)
+ BatteryMeterViewController batteryMeterViewController,
+ ConnectivityManager connectivityManager) {
+ super(view);
+ mBatteryController = batteryController;
+ mBatteryMeterViewController = batteryMeterViewController;
+ mConnectivityManager = connectivityManager;
+
+ mShowPercentAvailable = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_battery_percentage_setting_available);
+ }
+
+ @Override
+ protected void onInit() {
+ super.onInit();
+ mBatteryMeterViewController.init();
+ }
+
+ @Override
+ protected void onViewAttached() {
+ mBatteryController.addCallback(mBatteryStateChangeCallback);
+ mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback);
+
+ NetworkCapabilities capabilities =
+ mConnectivityManager.getNetworkCapabilities(
+ mConnectivityManager.getActiveNetwork());
+ onWifiAvailabilityChanged(
+ capabilities != null
+ && capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI));
+ }
+
+ @Override
+ protected void onViewDetached() {
+ mBatteryController.removeCallback(mBatteryStateChangeCallback);
+ mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
+ }
+
+ /**
+ * Wifi availability has changed. Update the wifi status icon as appropriate.
+ * @param available Whether wifi is available.
+ */
+ private void onWifiAvailabilityChanged(boolean available) {
+ final int newWifiStatus = available ? WIFI_STATUS_AVAILABLE : WIFI_STATUS_UNAVAILABLE;
+ if (mWifiStatus != newWifiStatus) {
+ mWifiStatus = newWifiStatus;
+ mView.showWifiStatus(mWifiStatus == WIFI_STATUS_UNAVAILABLE);
+ }
+ }
+
+ /**
+ * The battery level has changed. Update the battery status icon as appropriate.
+ * @param charging Whether the battery is currently charging.
+ */
+ private void onBatteryLevelChanged(boolean charging) {
+ final int newBatteryStatus =
+ charging ? BATTERY_STATUS_CHARGING : BATTERY_STATUS_NOT_CHARGING;
+ if (mBatteryStatus != newBatteryStatus) {
+ mBatteryStatus = newBatteryStatus;
+ mView.showBatteryPercentText(
+ mBatteryStatus == BATTERY_STATUS_CHARGING && mShowPercentAvailable);
+ }
+ }
+}
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 7bf2361e471c..ff5beb54bd89 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
@@ -21,8 +21,6 @@ import dagger.Module;
/**
* Dagger Module providing Communal-related functionality.
*/
-@Module(subcomponents = {
- AppWidgetOverlayComponent.class,
-})
+@Module(subcomponents = {AppWidgetOverlayComponent.class, DreamOverlayComponent.class})
public interface DreamModule {
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java
new file mode 100644
index 000000000000..a3a446a0dbca
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import android.view.ViewGroup;
+
+import com.android.systemui.dreams.DreamOverlayContainerView;
+import com.android.systemui.dreams.DreamOverlayStatusBarViewController;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Scope;
+
+import dagger.Subcomponent;
+
+/**
+ * Dagger subcomponent for {@link DreamOverlayModule}.
+ */
+@Subcomponent(modules = {DreamOverlayModule.class})
+@DreamOverlayComponent.DreamOverlayScope
+public interface DreamOverlayComponent {
+ /** Simple factory for {@link DreamOverlayComponent}. */
+ @Subcomponent.Factory
+ interface Factory {
+ DreamOverlayComponent create();
+ }
+
+ /** Scope annotation for singleton items within the {@link DreamOverlayComponent}. */
+ @Documented
+ @Retention(RUNTIME)
+ @Scope
+ @interface DreamOverlayScope {}
+
+ /** Builds a {@link DreamOverlayContainerView} */
+ @DreamOverlayScope
+ DreamOverlayContainerView getDreamOverlayContainerView();
+
+ /** Builds a content view for dream overlays */
+ @DreamOverlayScope
+ ViewGroup getDreamOverlayContentView();
+
+ /** Builds a {@link DreamOverlayStatusBarViewController}. */
+ @DreamOverlayScope
+ DreamOverlayStatusBarViewController getDreamOverlayStatusBarViewController();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
new file mode 100644
index 000000000000..d0a8fad9b1e5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.dagger;
+
+import android.content.ContentResolver;
+import android.os.Handler;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import com.android.internal.util.Preconditions;
+import com.android.systemui.R;
+import com.android.systemui.battery.BatteryMeterView;
+import com.android.systemui.battery.BatteryMeterViewController;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dreams.DreamOverlayContainerView;
+import com.android.systemui.dreams.DreamOverlayStatusBarView;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.tuner.TunerService;
+
+import javax.inject.Named;
+
+import dagger.Module;
+import dagger.Provides;
+
+/** Dagger module for {@link DreamOverlayComponent}. */
+@Module
+public abstract class DreamOverlayModule {
+ private static final String DREAM_OVERLAY_BATTERY_VIEW = "dream_overlay_battery_view";
+ public static final String DREAM_OVERLAY_BATTERY_CONTROLLER =
+ "dream_overlay_battery_controller";
+
+ /** */
+ @Provides
+ @DreamOverlayComponent.DreamOverlayScope
+ public static DreamOverlayContainerView providesDreamOverlayContainerView(
+ LayoutInflater layoutInflater) {
+ return Preconditions.checkNotNull((DreamOverlayContainerView)
+ layoutInflater.inflate(R.layout.dream_overlay_container, null),
+ "R.layout.dream_layout_container could not be properly inflated");
+ }
+
+ /** */
+ @Provides
+ @DreamOverlayComponent.DreamOverlayScope
+ public static ViewGroup providesDreamOverlayContentView(DreamOverlayContainerView view) {
+ return Preconditions.checkNotNull(view.findViewById(R.id.dream_overlay_content),
+ "R.id.dream_overlay_content must not be null");
+ }
+
+ /** */
+ @Provides
+ @DreamOverlayComponent.DreamOverlayScope
+ public static DreamOverlayStatusBarView providesDreamOverlayStatusBarView(
+ DreamOverlayContainerView view) {
+ return Preconditions.checkNotNull(view.findViewById(R.id.dream_overlay_status_bar),
+ "R.id.status_bar must not be null");
+ }
+
+ /** */
+ @Provides
+ @DreamOverlayComponent.DreamOverlayScope
+ @Named(DREAM_OVERLAY_BATTERY_VIEW)
+ static BatteryMeterView providesBatteryMeterView(DreamOverlayContainerView view) {
+ return Preconditions.checkNotNull(view.findViewById(R.id.dream_overlay_battery),
+ "R.id.battery must not be null");
+ }
+
+ /** */
+ @Provides
+ @DreamOverlayComponent.DreamOverlayScope
+ @Named(DREAM_OVERLAY_BATTERY_CONTROLLER)
+ static BatteryMeterViewController providesBatteryMeterViewController(
+ @Named(DREAM_OVERLAY_BATTERY_VIEW) BatteryMeterView batteryMeterView,
+ ConfigurationController configurationController,
+ TunerService tunerService,
+ BroadcastDispatcher broadcastDispatcher,
+ @Main Handler mainHandler,
+ ContentResolver contentResolver,
+ BatteryController batteryController) {
+ return new BatteryMeterViewController(
+ batteryMeterView,
+ configurationController,
+ tunerService,
+ broadcastDispatcher,
+ mainHandler,
+ contentResolver,
+ batteryController);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index a801647ba7d2..69bcf2ec8b8d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -24,6 +24,7 @@ import android.graphics.Matrix
import android.view.RemoteAnimationTarget
import android.view.SyncRtSurfaceTransactionApplier
import android.view.View
+import androidx.annotation.VisibleForTesting
import androidx.core.math.MathUtils
import com.android.internal.R
import com.android.keyguard.KeyguardClockSwitchController
@@ -33,6 +34,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController
+import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.policy.KeyguardStateController
import dagger.Lazy
import javax.inject.Inject
@@ -93,7 +95,8 @@ class KeyguardUnlockAnimationController @Inject constructor(
keyguardViewMediator: Lazy<KeyguardViewMediator>,
private val keyguardViewController: KeyguardViewController,
private val smartspaceTransitionController: SmartspaceTransitionController,
- private val featureFlags: FeatureFlags
+ private val featureFlags: FeatureFlags,
+ private val biometricUnlockController: BiometricUnlockController
) : KeyguardStateController.Callback {
/**
@@ -107,7 +110,8 @@ class KeyguardUnlockAnimationController @Inject constructor(
* If we're unlocking via biometrics, PIN entry, or from clicking a notification, a canned
* animation is started in [notifyStartKeyguardExitAnimation].
*/
- private var surfaceTransactionApplier: SyncRtSurfaceTransactionApplier? = null
+ @VisibleForTesting
+ var surfaceTransactionApplier: SyncRtSurfaceTransactionApplier? = null
private var surfaceBehindRemoteAnimationTarget: RemoteAnimationTarget? = null
private var surfaceBehindRemoteAnimationStartTime: Long = 0
@@ -134,7 +138,8 @@ class KeyguardUnlockAnimationController @Inject constructor(
* Animator that animates in the surface behind the keyguard. This is used to play a canned
* animation on the surface, if we're not doing a swipe gesture.
*/
- private val surfaceBehindEntryAnimator = ValueAnimator.ofFloat(0f, 1f)
+ @VisibleForTesting
+ val surfaceBehindEntryAnimator = ValueAnimator.ofFloat(0f, 1f)
/** Rounded corner radius to apply to the surface behind the keyguard. */
private var roundedCornerRadius = 0f
@@ -222,7 +227,18 @@ class KeyguardUnlockAnimationController @Inject constructor(
// to animate it in. Otherwise, the swipe touch events will continue animating it.
if (!requestedShowSurfaceBehindKeyguard) {
keyguardViewController.hide(startTime, 350)
- surfaceBehindEntryAnimator.start()
+
+ // If we're wake and unlocking, we don't want to animate the surface since we're going
+ // to do the light reveal scrim from the black AOD screen. Make it visible and end the
+ // remote aimation.
+ if (biometricUnlockController.isWakeAndUnlock) {
+ setSurfaceBehindAppearAmount(1f)
+ keyguardViewMediator.get().onKeyguardExitRemoteAnimationFinished(
+ false /* cancelled */)
+ } else {
+ // Otherwise, animate it in normally.
+ surfaceBehindEntryAnimator.start()
+ }
}
// Finish the keyguard remote animation if the dismiss amount has crossed the threshold.
@@ -266,7 +282,7 @@ class KeyguardUnlockAnimationController @Inject constructor(
* animations and swipe gestures to animate the surface's entry (and exit, if the swipe is
* cancelled).
*/
- private fun setSurfaceBehindAppearAmount(amount: Float) {
+ fun setSurfaceBehindAppearAmount(amount: Float) {
if (surfaceBehindRemoteAnimationTarget == null) {
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index c592431d360e..0512d488bef9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -122,6 +122,7 @@ import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.unfold.FoldAodAnimationController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
import com.android.systemui.util.DeviceConfigProxy;
@@ -131,7 +132,6 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Optional;
import java.util.concurrent.Executor;
-import java.util.concurrent.atomic.AtomicInteger;
import dagger.Lazy;
@@ -439,7 +439,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
private boolean mInGestureNavigationMode;
private boolean mWakeAndUnlocking;
- private IKeyguardDrawnCallback mDrawnCallback;
+ private Runnable mWakeAndUnlockingDrawnCallback;
private CharSequence mCustomMessage;
/**
@@ -817,7 +817,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
private DozeParameters mDozeParameters;
private final Optional<UnfoldLightRevealOverlayAnimation> mUnfoldLightRevealAnimation;
- private final AtomicInteger mPendingDrawnTasks = new AtomicInteger();
+ private final Optional<FoldAodAnimationController> mFoldAodAnimationController;
+ private final PendingDrawnTasksContainer mPendingDrawnTasks = new PendingDrawnTasksContainer();
private final KeyguardStateController mKeyguardStateController;
private final Lazy<KeyguardUnlockAnimationController> mKeyguardUnlockAnimationControllerLazy;
@@ -877,8 +878,12 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
mInGestureNavigationMode = QuickStepContract.isGesturalMode(mode);
}));
mDozeParameters = dozeParameters;
- mUnfoldLightRevealAnimation = unfoldComponent.map(
- c -> c.getUnfoldLightRevealOverlayAnimation());
+
+ mUnfoldLightRevealAnimation = unfoldComponent
+ .map(SysUIUnfoldComponent::getUnfoldLightRevealOverlayAnimation);
+ mFoldAodAnimationController = unfoldComponent
+ .map(SysUIUnfoldComponent::getFoldAodAnimationController);
+
mStatusBarStateController = statusBarStateController;
statusBarStateController.addCallback(this);
@@ -1028,7 +1033,10 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
if (!mExternallyEnabled) {
hideLocked();
}
- } else if (mShowing) {
+ } else if (mShowing && !mKeyguardStateController.isKeyguardGoingAway()) {
+ // If we are going to sleep but the keyguard is showing (and will continue to be
+ // showing, not in the process of going away) then reset its state. Otherwise, let
+ // this fall through and explicitly re-lock the keyguard.
mPendingReset = true;
} else if (
(offReason == WindowManagerPolicyConstants.OFF_BECAUSE_OF_TIMEOUT
@@ -1069,7 +1077,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
mDeviceInteractive = false;
mGoingToSleep = false;
mWakeAndUnlocking = false;
- mAnimatingScreenOff = mDozeParameters.shouldControlUnlockedScreenOff();
+ mAnimatingScreenOff = mDozeParameters.shouldAnimateDozingChange();
resetKeyguardDonePendingLocked();
mHideAnimationRun = false;
@@ -2099,15 +2107,6 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
private final Runnable mKeyguardGoingAwayRunnable = new Runnable() {
@Override
public void run() {
- // If the keyguard is already going away, or it's about to because we are going to
- // trigger the going-away remote animation to show the surface behind, don't do it
- // again. That will cause the current animation to be cancelled unnecessarily.
- if (mKeyguardStateController.isKeyguardGoingAway()
- || mSurfaceBehindRemoteAnimationRequested
- || mSurfaceBehindRemoteAnimationRunning) {
- return;
- }
-
Trace.beginSection("KeyguardViewMediator.mKeyGuardGoingAwayRunnable");
if (DEBUG) Log.d(TAG, "keyguardGoingAway");
mKeyguardViewControllerLazy.get().keyguardGoingAway();
@@ -2230,14 +2229,14 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
IRemoteAnimationRunner runner = mKeyguardExitAnimationRunner;
mKeyguardExitAnimationRunner = null;
- if (mWakeAndUnlocking && mDrawnCallback != null) {
+ if (mWakeAndUnlocking && mWakeAndUnlockingDrawnCallback != null) {
// Hack level over 9000: To speed up wake-and-unlock sequence, force it to report
// the next draw from here so we don't have to wait for window manager to signal
// this to our ViewRootImpl.
mKeyguardViewControllerLazy.get().getViewRootImpl().setReportNextDraw();
- notifyDrawn(mDrawnCallback);
- mDrawnCallback = null;
+ mWakeAndUnlockingDrawnCallback.run();
+ mWakeAndUnlockingDrawnCallback = null;
}
LatencyTracker.getInstance(mContext)
@@ -2573,31 +2572,27 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
synchronized (KeyguardViewMediator.this) {
if (DEBUG) Log.d(TAG, "handleNotifyScreenTurningOn");
- if (mUnfoldLightRevealAnimation.isPresent()) {
- mPendingDrawnTasks.set(2); // unfold overlay and keyguard drawn
+ mPendingDrawnTasks.reset();
+ if (mUnfoldLightRevealAnimation.isPresent()) {
mUnfoldLightRevealAnimation.get()
- .onScreenTurningOn(() -> {
- if (mPendingDrawnTasks.decrementAndGet() == 0) {
- try {
- callback.onDrawn();
- } catch (RemoteException e) {
- Slog.w(TAG, "Exception calling onDrawn():", e);
- }
- }
- });
- } else {
- mPendingDrawnTasks.set(1); // only keyguard drawn
+ .onScreenTurningOn(mPendingDrawnTasks.registerTask("unfold-reveal"));
+ }
+
+ if (mFoldAodAnimationController.isPresent()) {
+ mFoldAodAnimationController.get()
+ .onScreenTurningOn(mPendingDrawnTasks.registerTask("fold-to-aod"));
}
mKeyguardViewControllerLazy.get().onScreenTurningOn();
if (callback != null) {
if (mWakeAndUnlocking) {
- mDrawnCallback = callback;
- } else {
- notifyDrawn(callback);
+ mWakeAndUnlockingDrawnCallback =
+ mPendingDrawnTasks.registerTask("wake-and-unlocking");
}
}
+
+ mPendingDrawnTasks.onTasksComplete(() -> notifyDrawn(callback));
}
Trace.endSection();
}
@@ -2606,6 +2601,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
Trace.beginSection("KeyguardViewMediator#handleNotifyScreenTurnedOn");
synchronized (this) {
if (DEBUG) Log.d(TAG, "handleNotifyScreenTurnedOn");
+
+ mPendingDrawnTasks.reset();
mKeyguardViewControllerLazy.get().onScreenTurnedOn();
}
Trace.endSection();
@@ -2614,18 +2611,18 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
private void handleNotifyScreenTurnedOff() {
synchronized (this) {
if (DEBUG) Log.d(TAG, "handleNotifyScreenTurnedOff");
- mDrawnCallback = null;
+ mWakeAndUnlockingDrawnCallback = null;
}
}
private void notifyDrawn(final IKeyguardDrawnCallback callback) {
Trace.beginSection("KeyguardViewMediator#notifyDrawn");
- if (mPendingDrawnTasks.decrementAndGet() == 0) {
- try {
+ try {
+ if (callback != null) {
callback.onDrawn();
- } catch (RemoteException e) {
- Slog.w(TAG, "Exception calling onDrawn():", e);
}
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Exception calling onDrawn():", e);
}
Trace.endSection();
}
@@ -2784,9 +2781,9 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
pw.print(" mHideAnimationRun: "); pw.println(mHideAnimationRun);
pw.print(" mPendingReset: "); pw.println(mPendingReset);
pw.print(" mPendingLock: "); pw.println(mPendingLock);
- pw.print(" mPendingDrawnTasks: "); pw.println(mPendingDrawnTasks.get());
+ pw.print(" mPendingDrawnTasks: "); pw.println(mPendingDrawnTasks.getPendingCount());
pw.print(" mWakeAndUnlocking: "); pw.println(mWakeAndUnlocking);
- pw.print(" mDrawnCallback: "); pw.println(mDrawnCallback);
+ pw.print(" mDrawnCallback: "); pw.println(mWakeAndUnlockingDrawnCallback);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/PendingDrawnTasksContainer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/PendingDrawnTasksContainer.kt
new file mode 100644
index 000000000000..a60033cf40fe
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/PendingDrawnTasksContainer.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.keyguard
+
+import android.os.Trace
+import java.util.concurrent.atomic.AtomicInteger
+import java.util.concurrent.atomic.AtomicReference
+
+/**
+ * Allows to wait for multiple callbacks and notify when the last one is executed
+ */
+class PendingDrawnTasksContainer {
+
+ private var pendingDrawnTasksCount: AtomicInteger = AtomicInteger(0)
+ private var completionCallback: AtomicReference<Runnable> = AtomicReference()
+
+ /**
+ * Registers a task that we should wait for
+ * @return a runnable that should be invoked when the task is finished
+ */
+ fun registerTask(name: String): Runnable {
+ pendingDrawnTasksCount.incrementAndGet()
+
+ if (ENABLE_TRACE) {
+ Trace.beginAsyncSection("PendingDrawnTasksContainer#$name", 0)
+ }
+
+ return Runnable {
+ if (pendingDrawnTasksCount.decrementAndGet() == 0) {
+ val onComplete = completionCallback.getAndSet(null)
+ onComplete?.run()
+
+ if (ENABLE_TRACE) {
+ Trace.endAsyncSection("PendingDrawnTasksContainer#$name", 0)
+ }
+ }
+ }
+ }
+
+ /**
+ * Clears state and initializes the container
+ */
+ fun reset() {
+ // Create new objects in case if there are pending callbacks from the previous invocations
+ completionCallback = AtomicReference()
+ pendingDrawnTasksCount = AtomicInteger(0)
+ }
+
+ /**
+ * Starts waiting for all tasks to be completed
+ * When all registered tasks complete it will invoke the [onComplete] callback
+ */
+ fun onTasksComplete(onComplete: Runnable) {
+ completionCallback.set(onComplete)
+
+ if (pendingDrawnTasksCount.get() == 0) {
+ val currentOnComplete = completionCallback.getAndSet(null)
+ currentOnComplete?.run()
+ }
+ }
+
+ /**
+ * Returns current pending tasks count
+ */
+ fun getPendingCount(): Int = pendingDrawnTasksCount.get()
+}
+
+private const val ENABLE_TRACE = false
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
index 9e0038112dd3..492fdc699935 100644
--- a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
@@ -16,6 +16,7 @@
package com.android.systemui.log
+import android.os.Trace
import android.util.Log
import com.android.systemui.log.dagger.LogModule
import java.io.PrintWriter
@@ -176,7 +177,7 @@ class LogBuffer(
buffer.add(message as LogMessageImpl)
if (logcatEchoTracker.isBufferLoggable(name, message.level) ||
logcatEchoTracker.isTagLoggable(message.tag, message.level)) {
- echoToLogcat(message)
+ echo(message)
}
}
@@ -226,7 +227,7 @@ class LogBuffer(
pw.println(message.printer(message))
}
- private fun echoToLogcat(message: LogMessage) {
+ private fun echo(message: LogMessage) {
val strMessage = message.printer(message)
when (message.level) {
LogLevel.VERBOSE -> Log.v(message.tag, strMessage)
@@ -236,6 +237,7 @@ class LogBuffer(
LogLevel.ERROR -> Log.e(message.tag, strMessage)
LogLevel.WTF -> Log.wtf(message.tag, strMessage)
}
+ Trace.instantForTrack(Trace.TRACE_TAG_APP, "UI Events", strMessage)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index fb601e310702..c8cd43287c99 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -21,6 +21,7 @@ import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.annotation.IntDef
import android.content.Context
+import android.content.res.Configuration
import android.graphics.Rect
import android.util.MathUtils
import android.view.View
@@ -41,6 +42,7 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.Utils
import com.android.systemui.util.animation.UniqueObjectHostView
import javax.inject.Inject
@@ -186,6 +188,8 @@ class MediaHierarchyManager @Inject constructor(
@MediaLocation
private var currentAttachmentLocation = -1
+ private var inSplitShade = false
+
/**
* Is there any active media in the carousel?
*/
@@ -390,8 +394,9 @@ class MediaHierarchyManager @Inject constructor(
init {
updateConfiguration()
configurationController.addCallback(object : ConfigurationController.ConfigurationListener {
- override fun onDensityOrFontScaleChanged() {
+ override fun onConfigChanged(newConfig: Configuration?) {
updateConfiguration()
+ updateDesiredLocation(forceNoAnimation = true, forceStateUpdate = true)
}
})
statusBarStateController.addCallback(object : StatusBarStateController.StateListener {
@@ -467,6 +472,7 @@ class MediaHierarchyManager @Inject constructor(
private fun updateConfiguration() {
distanceForFullShadeTransition = context.resources.getDimensionPixelSize(
R.dimen.lockscreen_shade_media_transition_distance)
+ inSplitShade = Utils.shouldUseSplitNotificationShade(context.resources)
}
/**
@@ -803,7 +809,7 @@ class MediaHierarchyManager @Inject constructor(
private fun getQSTransformationProgress(): Float {
val currentHost = getHost(desiredLocation)
val previousHost = getHost(previousLocation)
- if (hasActiveMedia && currentHost?.location == LOCATION_QS) {
+ if (hasActiveMedia && (currentHost?.location == LOCATION_QS && !inSplitShade)) {
if (previousHost?.location == LOCATION_QQS) {
if (previousHost.visible || statusbarState != StatusBarState.KEYGUARD) {
return qsExpansion
@@ -934,7 +940,7 @@ class MediaHierarchyManager @Inject constructor(
statusbarState == StatusBarState.FULLSCREEN_USER_SWITCHER))
val allowedOnLockscreen = notifLockscreenUserManager.shouldShowLockscreenNotifications()
val location = when {
- qsExpansion > 0.0f && !onLockscreen -> LOCATION_QS
+ (qsExpansion > 0.0f || inSplitShade) && !onLockscreen -> LOCATION_QS
qsExpansion > 0.4f && onLockscreen -> LOCATION_QS
!hasActiveMedia -> LOCATION_QS
onLockscreen && isTransformingToFullShadeAndInQQS() -> LOCATION_QQS
diff --git a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
index 042a337322e9..1322adeb85a8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
@@ -16,6 +16,7 @@
package com.android.systemui.media
+import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -26,6 +27,8 @@ import android.widget.TextView
import com.android.systemui.R
import com.android.systemui.util.animation.TransitionLayout
+private const val TAG = "PlayerViewHolder"
+
/**
* ViewHolder for a media player.
*/
@@ -94,7 +97,12 @@ class PlayerViewHolder private constructor(itemView: View) {
}
fun marquee(start: Boolean, delay: Long) {
- longPressText.getHandler().postDelayed({ longPressText.setSelected(start) }, delay)
+ val longPressTextHandler = longPressText.getHandler()
+ if (longPressTextHandler == null) {
+ Log.d(TAG, "marquee while longPressText.getHandler() is null", Exception())
+ return
+ }
+ longPressTextHandler.postDelayed({ longPressText.setSelected(start) }, delay)
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/media/RecommendationViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/RecommendationViewHolder.kt
index 142628cff448..c0f79d575dc6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/RecommendationViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/RecommendationViewHolder.kt
@@ -21,7 +21,6 @@ import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
-import androidx.annotation.IntegerRes
import com.android.systemui.R
import com.android.systemui.util.animation.TransitionLayout
@@ -47,14 +46,14 @@ class RecommendationViewHolder private constructor(itemView: View) {
itemView.requireViewById(R.id.media_cover4_container),
itemView.requireViewById(R.id.media_cover5_container),
itemView.requireViewById(R.id.media_cover6_container))
- val mediaCoverItemsResIds = listOf<@IntegerRes Int>(
+ val mediaCoverItemsResIds = listOf<Int>(
R.id.media_cover1,
R.id.media_cover2,
R.id.media_cover3,
R.id.media_cover4,
R.id.media_cover5,
R.id.media_cover6)
- val mediaCoverContainersResIds = listOf<@IntegerRes Int>(
+ val mediaCoverContainersResIds = listOf<Int>(
R.id.media_cover1_container,
R.id.media_cover2_container,
R.id.media_cover3_container,
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
index a1f8455f9ac8..1174fa8e4061 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -20,6 +20,8 @@ import android.content.Context;
import android.view.WindowManager;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.media.MediaDataManager;
import com.android.systemui.media.MediaHierarchyManager;
import com.android.systemui.media.MediaHost;
@@ -28,8 +30,10 @@ import com.android.systemui.media.taptotransfer.MediaTttChipController;
import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper;
import com.android.systemui.media.taptotransfer.MediaTttFlags;
import com.android.systemui.statusbar.commandline.CommandRegistry;
+import com.android.systemui.util.concurrency.DelayableExecutor;
import java.util.Optional;
+import java.util.concurrent.Executor;
import javax.inject.Named;
@@ -79,11 +83,14 @@ public interface MediaModule {
static Optional<MediaTttChipController> providesMediaTttChipController(
MediaTttFlags mediaTttFlags,
Context context,
- WindowManager windowManager) {
+ WindowManager windowManager,
+ @Main Executor mainExecutor,
+ @Background Executor backgroundExecutor) {
if (!mediaTttFlags.isMediaTttEnabled()) {
return Optional.empty();
}
- return Optional.of(new MediaTttChipController(context, windowManager));
+ return Optional.of(new MediaTttChipController(
+ context, windowManager, mainExecutor, backgroundExecutor));
}
/** */
@@ -92,10 +99,12 @@ public interface MediaModule {
static Optional<MediaTttCommandLineHelper> providesMediaTttCommandLineHelper(
MediaTttFlags mediaTttFlags,
CommandRegistry commandRegistry,
- MediaTttChipController mediaTttChipController) {
+ MediaTttChipController mediaTttChipController,
+ @Main DelayableExecutor mainExecutor) {
if (!mediaTttFlags.isMediaTttEnabled()) {
return Optional.empty();
}
- return Optional.of(new MediaTttCommandLineHelper(commandRegistry, mediaTttChipController));
+ return Optional.of(new MediaTttCommandLineHelper(
+ commandRegistry, mediaTttChipController, mainExecutor));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt
index 1ec6a39347ca..baa469d43e22 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt
@@ -16,6 +16,7 @@
package com.android.systemui.media.taptotransfer
+import android.annotation.SuppressLint
import android.content.Context
import android.graphics.PixelFormat
import android.view.Gravity
@@ -26,8 +27,14 @@ import android.widget.LinearLayout
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 java.util.concurrent.Executor
+import java.util.concurrent.TimeUnit
import javax.inject.Inject
+const val TAG = "MediaTapToTransfer"
+
/**
* A controller to display and hide the Media Tap-To-Transfer chip. This chip is shown when a user
* is currently playing media on a local "media cast sender" device (e.g. a phone) and gets close
@@ -38,16 +45,18 @@ import javax.inject.Inject
class MediaTttChipController @Inject constructor(
private val context: Context,
private val windowManager: WindowManager,
+ @Main private val mainExecutor: Executor,
+ @Background private val backgroundExecutor: Executor,
) {
+ @SuppressLint("WrongConstant") // We're allowed to use TYPE_VOLUME_OVERLAY
private val windowLayoutParams = WindowManager.LayoutParams().apply {
width = WindowManager.LayoutParams.WRAP_CONTENT
height = WindowManager.LayoutParams.WRAP_CONTENT
gravity = Gravity.TOP.or(Gravity.CENTER_HORIZONTAL)
type = WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY
+ flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
title = "Media Tap-To-Transfer Chip View"
- flags = (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- or WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
format = PixelFormat.TRANSLUCENT
setTrustedOverlay()
}
@@ -76,10 +85,25 @@ class MediaTttChipController @Inject constructor(
if (showLoading) { View.VISIBLE } else { View.GONE }
// Undo
- val showUndo = chipState is TransferSucceeded
- currentChipView.requireViewById<View>(R.id.undo).visibility =
- if (showUndo) { View.VISIBLE } else { View.GONE }
+ 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
+ }
+ undoView.setOnClickListener(undoClickListener)
+
+ // Future handling
+ if (chipState is TransferInitiated) {
+ addFutureCallback(chipState)
+ }
+ // Add view if necessary
if (oldChipView == null) {
windowManager.addView(chipView, windowLayoutParams)
}
@@ -91,4 +115,29 @@ class MediaTttChipController @Inject constructor(
windowManager.removeView(chipView)
chipView = null
}
+
+ /**
+ * Adds the appropriate callbacks to [chipState.future] so that we update the chip correctly
+ * when the future resolves.
+ */
+ private fun addFutureCallback(chipState: TransferInitiated) {
+ // Listen to the future on a background thread so we don't occupy the main thread while we
+ // wait for it to complete.
+ backgroundExecutor.execute {
+ try {
+ val undoRunnable = chipState.future.get(TRANSFER_TIMEOUT_SECONDS, TimeUnit.SECONDS)
+ // Make UI changes on the main thread
+ mainExecutor.execute {
+ displayChip(TransferSucceeded(chipState.otherDeviceName, undoRunnable))
+ }
+ } catch (ex: Exception) {
+ // TODO(b/203800327): Maybe show a failure chip here if UX decides we need one.
+ mainExecutor.execute {
+ removeChip()
+ }
+ }
+ }
+ }
}
+
+private const val TRANSFER_TIMEOUT_SECONDS = 10L
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipState.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipState.kt
index 05b9552a0a71..1f308a9a6cd9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipState.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipState.kt
@@ -18,6 +18,7 @@ package com.android.systemui.media.taptotransfer
import androidx.annotation.StringRes
import com.android.systemui.R
+import java.util.concurrent.Future
/**
* A class that stores all the information necessary to display the media tap-to-transfer chip in
@@ -43,14 +44,24 @@ class MoveCloserToTransfer(
/**
* A state representing that a transfer has been initiated (but not completed).
+ *
+ * @property future a future that will be resolved when the transfer has either succeeded or failed.
+ * If the transfer succeeded, the future can optionally return an undo runnable (see
+ * [TransferSucceeded.undoRunnable]). [MediaTttChipController] is responsible for transitioning
+ * the chip to the [TransferSucceeded] state if the future resolves successfully.
*/
class TransferInitiated(
- otherDeviceName: String
+ otherDeviceName: String,
+ val future: Future<Runnable?>
) : MediaTttChipState(R.string.media_transfer_playing, otherDeviceName)
/**
* A state representing that a transfer has been successfully 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.
*/
class TransferSucceeded(
- otherDeviceName: String
+ otherDeviceName: String,
+ val undoRunnable: Runnable? = null
) : MediaTttChipState(R.string.media_transfer_playing, otherDeviceName)
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 9f05c92ff443..663037cc850f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
@@ -16,11 +16,15 @@
package com.android.systemui.media.taptotransfer
+import android.util.Log
import androidx.annotation.VisibleForTesting
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.util.concurrency.DelayableExecutor
import java.io.PrintWriter
+import java.util.concurrent.FutureTask
import javax.inject.Inject
/**
@@ -30,7 +34,8 @@ import javax.inject.Inject
@SysUISingleton
class MediaTttCommandLineHelper @Inject constructor(
commandRegistry: CommandRegistry,
- private val mediaTttChipController: MediaTttChipController
+ private val mediaTttChipController: MediaTttChipController,
+ @Main private val mainExecutor: DelayableExecutor,
) {
init {
commandRegistry.registerCommand(ADD_CHIP_COMMAND_TAG) { AddChipCommand() }
@@ -45,10 +50,17 @@ class MediaTttCommandLineHelper @Inject constructor(
mediaTttChipController.displayChip(MoveCloserToTransfer(otherDeviceName))
}
TRANSFER_INITIATED_COMMAND_NAME -> {
- mediaTttChipController.displayChip(TransferInitiated(otherDeviceName))
+ val futureTask = FutureTask { fakeUndoRunnable }
+ mediaTttChipController.displayChip(
+ TransferInitiated(otherDeviceName, futureTask)
+ )
+ mainExecutor.executeDelayed({ futureTask.run() }, FUTURE_WAIT_TIME)
+
}
TRANSFER_SUCCEEDED_COMMAND_NAME -> {
- mediaTttChipController.displayChip(TransferSucceeded(otherDeviceName))
+ mediaTttChipController.displayChip(
+ TransferSucceeded(otherDeviceName, fakeUndoRunnable)
+ )
}
else -> {
pw.println("Chip type must be one of " +
@@ -75,6 +87,10 @@ class MediaTttCommandLineHelper @Inject constructor(
pw.println("Usage: adb shell cmd statusbar $REMOVE_CHIP_COMMAND_TAG")
}
}
+
+ private val fakeUndoRunnable = Runnable {
+ Log.i(TAG, "Undo runnable triggered")
+ }
}
@VisibleForTesting
@@ -87,3 +103,5 @@ val MOVE_CLOSER_TO_TRANSFER_COMMAND_NAME = MoveCloserToTransfer::class.simpleNam
val TRANSFER_INITIATED_COMMAND_NAME = TransferInitiated::class.simpleName!!
@VisibleForTesting
val TRANSFER_SUCCEEDED_COMMAND_NAME = TransferSucceeded::class.simpleName!!
+
+private const val FUTURE_WAIT_TIME = 2000L
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 8026df747ff6..9c4227e25567 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -627,7 +627,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
mSplitScreenOptional.ifPresent(mNavigationBarView::registerDockedListener);
- mPipOptional.ifPresent(mNavigationBarView::registerPipExclusionBoundsChangeListener);
+ mPipOptional.ifPresent(mNavigationBarView::addPipExclusionBoundsChangeListener);
prepareNavigationBarView();
checkNavBarModes();
@@ -698,6 +698,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mHandler.removeCallbacks(mOnVariableDurationHomeLongClick);
mHandler.removeCallbacks(mEnableLayoutTransitions);
mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+ mPipOptional.ifPresent(mNavigationBarView::removePipExclusionBoundsChangeListener);
mFrame = null;
mNavigationBarView = null;
mOrientationHandle = null;
@@ -1251,6 +1252,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
private void onImeSwitcherClick(View v) {
mInputMethodManager.showInputMethodPickerFromSystem(
true /* showAuxiliarySubtypes */, mDisplayId);
+ mUiEventLogger.log(KeyButtonView.NavBarButtonEvent.NAVBAR_IME_SWITCHER_BUTTON_TAP);
};
private boolean onLongPressBackHome(View v) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index bfabf716803b..a984974c6bba 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -59,9 +59,11 @@ 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.pip.Pip;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.Optional;
import javax.inject.Inject;
@@ -106,7 +108,8 @@ public class NavigationBarController implements
NavigationBar.Factory navigationBarFactory,
DumpManager dumpManager,
AutoHideController autoHideController,
- LightBarController lightBarController) {
+ LightBarController lightBarController,
+ Optional<Pip> pipOptional) {
mContext = context;
mHandler = mainHandler;
mNavigationBarFactory = navigationBarFactory;
@@ -118,7 +121,7 @@ public class NavigationBarController implements
mTaskbarDelegate = taskbarDelegate;
mTaskbarDelegate.setDependencies(commandQueue, overviewProxyService,
navBarHelper, navigationModeController, sysUiFlagsContainer,
- dumpManager, autoHideController, lightBarController);
+ dumpManager, autoHideController, lightBarController, pipOptional);
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 7c8c3e0dce44..7adb7ac92dc1 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -75,12 +75,12 @@ import com.android.systemui.navigationbar.buttons.KeyButtonDrawable;
import com.android.systemui.navigationbar.buttons.NearestTouchFrame;
import com.android.systemui.navigationbar.buttons.RotationContextButton;
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
-import com.android.systemui.shared.rotation.FloatingRotationButton;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
+import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
+import com.android.systemui.shared.rotation.FloatingRotationButton;
import com.android.systemui.shared.rotation.RotationButton.RotationButtonUpdatesCallback;
import com.android.systemui.shared.rotation.RotationButtonController;
-import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.SysUiStatsLog;
@@ -1386,8 +1386,12 @@ public class NavigationBarView extends FrameLayout implements
legacySplitScreen.registerInSplitScreenListener(mDockedListener);
}
- void registerPipExclusionBoundsChangeListener(Pip pip) {
- pip.setPipExclusionBoundsChangeListener(mPipListener);
+ void addPipExclusionBoundsChangeListener(Pip pip) {
+ pip.addPipExclusionBoundsChangeListener(mPipListener);
+ }
+
+ void removePipExclusionBoundsChangeListener(Pip pip) {
+ pip.removePipExclusionBoundsChangeListener(mPipListener);
}
private static void dumpButton(PrintWriter pw, String caption, ButtonDispatcher button) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 68f4aeaa7f17..feda99fd3471 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -40,6 +40,7 @@ import android.app.StatusBarManager.WindowVisibleState;
import android.content.ComponentCallbacks;
import android.content.Context;
import android.content.res.Configuration;
+import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
@@ -68,9 +69,12 @@ 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.pip.Pip;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.Optional;
+import java.util.function.Consumer;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -91,6 +95,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
private AutoHideController mAutoHideController;
private LightBarController mLightBarController;
private LightBarTransitionsController mLightBarTransitionsController;
+ private Optional<Pip> mPipOptional;
private int mDisplayId;
private int mNavigationIconHints;
private final NavBarHelper.NavbarTaskbarStateUpdater mNavbarTaskbarStateUpdater =
@@ -113,6 +118,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
private Context mWindowContext;
private ScreenPinningNotify mScreenPinningNotify;
private int mNavigationMode;
+ private final Consumer<Rect> mPipListener;
/**
* Tracks the system calls for when taskbar should transiently show or hide so we can return
@@ -143,6 +149,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
.create(context);
mContext = context;
mDisplayManager = mContext.getSystemService(DisplayManager.class);
+ mPipListener = mEdgeBackGestureHandler::setPipStashExclusionBounds;
}
public void setDependencies(CommandQueue commandQueue,
@@ -151,7 +158,8 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
NavigationModeController navigationModeController,
SysUiState sysUiState, DumpManager dumpManager,
AutoHideController autoHideController,
- LightBarController lightBarController) {
+ LightBarController lightBarController,
+ Optional<Pip> pipOptional) {
// TODO: adding this in the ctor results in a dagger dependency cycle :(
mCommandQueue = commandQueue;
mOverviewProxyService = overviewProxyService;
@@ -162,6 +170,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
mAutoHideController = autoHideController;
mLightBarController = lightBarController;
mLightBarTransitionsController = createLightBarTransitionsController();
+ mPipOptional = pipOptional;
}
// Separated into a method to keep setDependencies() clean/readable.
@@ -207,6 +216,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
updateSysuiFlags();
mAutoHideController.setNavigationBar(mAutoHideUiElement);
mLightBarController.setNavigationBar(mLightBarTransitionsController);
+ mPipOptional.ifPresent(this::addPipExclusionBoundsChangeListener);
mInitialized = true;
}
@@ -228,9 +238,18 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
mAutoHideController.setNavigationBar(null);
mLightBarTransitionsController.destroy(mContext);
mLightBarController.setNavigationBar(null);
+ mPipOptional.ifPresent(this::removePipExclusionBoundsChangeListener);
mInitialized = false;
}
+ void addPipExclusionBoundsChangeListener(Pip pip) {
+ pip.addPipExclusionBoundsChangeListener(mPipListener);
+ }
+
+ void removePipExclusionBoundsChangeListener(Pip pip) {
+ pip.removePipExclusionBoundsChangeListener(mPipListener);
+ }
+
/**
* Returns {@code true} if this taskBar is {@link #init(int)}. Returns {@code false} if this
* taskbar has not yet been {@link #init(int)} or has been {@link #destroy()}.
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
index debd2ebb51be..d27b71673ce5 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
@@ -96,6 +96,9 @@ public class KeyButtonView extends ImageView implements ButtonInterface {
@UiEvent(doc = "The overview button was pressed in the navigation bar.")
NAVBAR_OVERVIEW_BUTTON_TAP(535),
+ @UiEvent(doc = "The ime switcher button was pressed in the navigation bar.")
+ NAVBAR_IME_SWITCHER_BUTTON_TAP(923),
+
@UiEvent(doc = "The home button was long-pressed in the navigation bar.")
NAVBAR_HOME_BUTTON_LONGPRESS(536),
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 5bd02cc207b9..10efec309971 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -48,6 +48,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Dependency;
@@ -562,7 +563,8 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
return this;
}
- CustomTile build() {
+ @VisibleForTesting
+ public CustomTile build() {
if (mUserContext == null) {
throw new NullPointerException("UserContext cannot be null");
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index ac95bf50bb98..c2a9e3adb0b8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -169,7 +169,7 @@ public class QSFactoryImpl implements QSFactory {
mFgsManagerTileProvider = fgsManagerTileProvider;
}
- public QSTile createTile(String tileSpec) {
+ public final QSTile createTile(String tileSpec) {
QSTileImpl tile = createTileInternal(tileSpec);
if (tile != null) {
tile.initialize();
@@ -178,7 +178,7 @@ public class QSFactoryImpl implements QSFactory {
return tile;
}
- private QSTileImpl createTileInternal(String tileSpec) {
+ protected QSTileImpl createTileInternal(String tileSpec) {
// Stock tiles.
switch (tileSpec) {
case "wifi":
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
index b8ef3128f9ea..7d8a28fc011e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
@@ -18,6 +18,7 @@ package com.android.systemui.qs.tiles;
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
+import android.provider.Settings;
import android.provider.Settings.Secure;
import android.service.quicksettings.Tile;
import android.view.View;
@@ -119,7 +120,7 @@ public class BatterySaverTile extends QSTileImpl<BooleanState> implements
@Override
public Intent getLongClickIntent() {
- return new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);
+ return new Intent(Settings.ACTION_BATTERY_SAVER_SETTINGS);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 1fb608a3c7db..6e0ae29630bc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -78,7 +78,6 @@ public class CastTile extends QSTileImpl<BooleanState> {
private final NetworkController mNetworkController;
private final DialogLaunchAnimator mDialogLaunchAnimator;
private final Callback mCallback = new Callback();
- private Dialog mDialog;
private boolean mWifiConnected;
private boolean mHotspotConnected;
@@ -196,24 +195,36 @@ public class CastTile extends QSTileImpl<BooleanState> {
showDetail(null /* view */);
}
+ private static class DialogHolder {
+ private Dialog mDialog;
+
+ private void init(Dialog dialog) {
+ mDialog = dialog;
+ }
+ }
+
private void showDetail(@Nullable View view) {
mUiHandler.post(() -> {
- mDialog = MediaRouteDialogPresenter.createDialog(mContext, ROUTE_TYPE_REMOTE_DISPLAY,
+ final DialogHolder holder = new DialogHolder();
+ final Dialog dialog = MediaRouteDialogPresenter.createDialog(
+ mContext,
+ ROUTE_TYPE_REMOTE_DISPLAY,
v -> {
mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
- mDialog.dismiss();
+ holder.mDialog.dismiss();
mActivityStarter
.postStartActivityDismissingKeyguard(getLongClickIntent(), 0);
});
- SystemUIDialog.setShowForAllUsers(mDialog, true);
- SystemUIDialog.registerDismissListener(mDialog);
- SystemUIDialog.setWindowOnTop(mDialog);
+ holder.init(dialog);
+ SystemUIDialog.setShowForAllUsers(dialog, true);
+ SystemUIDialog.registerDismissListener(dialog);
+ SystemUIDialog.setWindowOnTop(dialog);
mUiHandler.post(() -> {
if (view != null) {
- mDialogLaunchAnimator.showFromView(mDialog, view);
+ mDialogLaunchAnimator.showFromView(dialog, view);
} else {
- mDialog.show();
+ dialog.show();
}
});
});
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FgsManagerTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/FgsManagerTile.kt
index 75cf4d1ace8e..939a29711f45 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FgsManagerTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FgsManagerTile.kt
@@ -54,7 +54,7 @@ class FgsManagerTile @Inject constructor(
qsLogger: QSLogger?,
private val fgsManagerDialogFactory: FgsManagerDialogFactory,
private val runningFgsController: RunningFgsController
-) : QSTileImpl<QSTile.State?>(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+) : QSTileImpl<QSTile.State>(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger), RunningFgsController.Callback {
override fun handleInitialize() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
index 99eb5b6519bc..544246ee97aa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
@@ -53,7 +53,10 @@ public class InternetAdapter extends RecyclerView.Adapter<InternetAdapter.Intern
private final InternetDialogController mInternetDialogController;
private List<WifiEntry> mWifiEntries;
- private int mWifiEntriesCount;
+ @VisibleForTesting
+ protected int mWifiEntriesCount;
+ @VisibleForTesting
+ protected int mMaxEntriesCount = InternetDialogController.MAX_WIFI_ENTRY_COUNT;
protected View mHolderView;
protected Context mContext;
@@ -87,7 +90,8 @@ public class InternetAdapter extends RecyclerView.Adapter<InternetAdapter.Intern
*/
public void setWifiEntries(@Nullable List<WifiEntry> wifiEntries, int wifiEntriesCount) {
mWifiEntries = wifiEntries;
- mWifiEntriesCount = wifiEntriesCount;
+ mWifiEntriesCount =
+ (wifiEntriesCount < mMaxEntriesCount) ? wifiEntriesCount : mMaxEntriesCount;
}
/**
@@ -101,6 +105,20 @@ public class InternetAdapter extends RecyclerView.Adapter<InternetAdapter.Intern
}
/**
+ * Sets the maximum number of Wi-Fi networks.
+ */
+ public void setMaxEntriesCount(int count) {
+ if (count < 0 || mMaxEntriesCount == count) {
+ return;
+ }
+ mMaxEntriesCount = count;
+ if (mWifiEntriesCount > count) {
+ mWifiEntriesCount = count;
+ notifyDataSetChanged();
+ }
+ }
+
+ /**
* ViewHolder for binding Wi-Fi view.
*/
static class InternetViewHolder extends RecyclerView.ViewHolder {
@@ -133,7 +151,8 @@ public class InternetAdapter extends RecyclerView.Adapter<InternetAdapter.Intern
}
void onBind(@NonNull WifiEntry wifiEntry) {
- mWifiIcon.setImageDrawable(getWifiDrawable(wifiEntry));
+ mWifiIcon.setImageDrawable(
+ getWifiDrawable(wifiEntry.getLevel(), wifiEntry.shouldShowXLevelIcon()));
setWifiNetworkLayout(wifiEntry.getTitle(),
Html.fromHtml(wifiEntry.getSummary(false), Html.FROM_HTML_MODE_LEGACY));
@@ -170,12 +189,13 @@ public class InternetAdapter extends RecyclerView.Adapter<InternetAdapter.Intern
mWifiSummaryText.setText(summary);
}
- Drawable getWifiDrawable(@NonNull WifiEntry wifiEntry) {
- if (wifiEntry.getLevel() == WifiEntry.WIFI_LEVEL_UNREACHABLE) {
+ Drawable getWifiDrawable(int level, boolean hasNoInternet) {
+ // If the Wi-Fi level is equal to WIFI_LEVEL_UNREACHABLE(-1), then a null drawable
+ // will be returned.
+ if (level == WifiEntry.WIFI_LEVEL_UNREACHABLE) {
return null;
}
- final Drawable drawable = mWifiIconInjector.getIcon(wifiEntry.shouldShowXLevelIcon(),
- wifiEntry.getLevel());
+ final Drawable drawable = mWifiIconInjector.getIcon(hasNoInternet, level);
if (drawable == null) {
return null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index 2a398496af77..e7982bfb7bcd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -16,6 +16,7 @@
package com.android.systemui.qs.tiles.dialog;
import static com.android.systemui.Prefs.Key.QS_HAS_TURNED_OFF_MOBILE_DATA;
+import static com.android.systemui.qs.tiles.dialog.InternetDialogController.MAX_WIFI_ENTRY_COUNT;
import android.app.AlertDialog;
import android.content.Context;
@@ -40,6 +41,7 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
+import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -79,6 +81,7 @@ public class InternetDialog extends SystemUIDialog implements
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
static final long PROGRESS_DELAY_MS = 1500L;
+ static final int MAX_NETWORK_COUNT = 4;
private final Handler mHandler;
private final Executor mBackgroundExecutor;
@@ -124,8 +127,8 @@ public class InternetDialog extends SystemUIDialog implements
private Switch mMobileDataToggle;
private View mMobileToggleDivider;
private Switch mWiFiToggle;
- private FrameLayout mDoneLayout;
- private FrameLayout mAirplaneModeLayout;
+ private Button mDoneButton;
+ private Button mAirplaneModeButton;
private Drawable mBackgroundOn;
private Drawable mBackgroundOff = null;
private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -138,7 +141,7 @@ public class InternetDialog extends SystemUIDialog implements
@VisibleForTesting
protected int mWifiEntriesCount;
@VisibleForTesting
- protected boolean mHasMoreEntry;
+ protected boolean mHasMoreWifiEntries;
// Wi-Fi scanning progress bar
protected boolean mIsProgressBarVisible;
@@ -215,8 +218,8 @@ public class InternetDialog extends SystemUIDialog implements
mWifiSettingsIcon = mDialogView.requireViewById(R.id.wifi_settings_icon);
mWifiRecyclerView = mDialogView.requireViewById(R.id.wifi_list_layout);
mSeeAllLayout = mDialogView.requireViewById(R.id.see_all_layout);
- mDoneLayout = mDialogView.requireViewById(R.id.done_layout);
- mAirplaneModeLayout = mDialogView.requireViewById(R.id.apm_layout);
+ mDoneButton = mDialogView.requireViewById(R.id.done_button);
+ mAirplaneModeButton = mDialogView.requireViewById(R.id.apm_button);
mSignalIcon = mDialogView.requireViewById(R.id.signal_icon);
mMobileTitleText = mDialogView.requireViewById(R.id.mobile_title);
mMobileSummaryText = mDialogView.requireViewById(R.id.mobile_summary);
@@ -238,7 +241,7 @@ public class InternetDialog extends SystemUIDialog implements
setOnClickListener();
mTurnWifiOnLayout.setBackground(null);
- mAirplaneModeLayout.setVisibility(
+ mAirplaneModeButton.setVisibility(
mInternetDialogController.isAirplaneModeEnabled() ? View.VISIBLE : View.GONE);
mWifiRecyclerView.setLayoutManager(new LinearLayoutManager(mContext));
mWifiRecyclerView.setAdapter(mAdapter);
@@ -278,8 +281,8 @@ public class InternetDialog extends SystemUIDialog implements
mConnectedWifListLayout.setOnClickListener(null);
mSeeAllLayout.setOnClickListener(null);
mWiFiToggle.setOnCheckedChangeListener(null);
- mDoneLayout.setOnClickListener(null);
- mAirplaneModeLayout.setOnClickListener(null);
+ mDoneButton.setOnClickListener(null);
+ mAirplaneModeButton.setOnClickListener(null);
mInternetDialogController.onStop();
mInternetDialogFactory.destroyDialog();
}
@@ -305,7 +308,7 @@ public class InternetDialog extends SystemUIDialog implements
}
mInternetDialogTitle.setText(getDialogTitleText());
mInternetDialogSubTitle.setText(getSubtitleText());
- mAirplaneModeLayout.setVisibility(
+ mAirplaneModeButton.setVisibility(
mInternetDialogController.isAirplaneModeEnabled() ? View.VISIBLE : View.GONE);
updateEthernet();
@@ -320,7 +323,7 @@ public class InternetDialog extends SystemUIDialog implements
showProgressBar();
final boolean isDeviceLocked = mInternetDialogController.isDeviceLocked();
- final boolean isWifiEnabled = mWifiManager.isWifiEnabled();
+ final boolean isWifiEnabled = mWifiManager != null && mWifiManager.isWifiEnabled();
final boolean isWifiScanEnabled = mInternetDialogController.isWifiScanEnabled();
updateWifiToggle(isWifiEnabled, isDeviceLocked);
updateConnectedWifi(isWifiEnabled, isDeviceLocked);
@@ -350,11 +353,12 @@ public class InternetDialog extends SystemUIDialog implements
mSeeAllLayout.setOnClickListener(v -> onClickSeeMoreButton());
mWiFiToggle.setOnCheckedChangeListener(
(buttonView, isChecked) -> {
+ if (mWifiManager == null) return;
buttonView.setChecked(isChecked);
mWifiManager.setWifiEnabled(isChecked);
});
- mDoneLayout.setOnClickListener(v -> dismiss());
- mAirplaneModeLayout.setOnClickListener(v -> {
+ mDoneButton.setOnClickListener(v -> dismiss());
+ mAirplaneModeButton.setOnClickListener(v -> {
mInternetDialogController.setAirplaneModeDisabled();
});
}
@@ -375,8 +379,9 @@ public class InternetDialog extends SystemUIDialog implements
Log.d(TAG, "setMobileDataLayout, isCarrierNetworkActive = " + isCarrierNetworkActive);
}
+ boolean isWifiEnabled = mWifiManager != null && mWifiManager.isWifiEnabled();
if (!mInternetDialogController.hasActiveSubId()
- && (!mWifiManager.isWifiEnabled() || !isCarrierNetworkActive)) {
+ && (!isWifiEnabled || !isCarrierNetworkActive)) {
mMobileNetworkLayout.setVisibility(View.GONE);
} else {
mMobileNetworkLayout.setVisibility(View.VISIBLE);
@@ -462,21 +467,36 @@ public class InternetDialog extends SystemUIDialog implements
mSeeAllLayout.setVisibility(View.GONE);
return;
}
- mWifiRecyclerView.setMinimumHeight(mWifiNetworkHeight * getWifiListMaxCount());
+ final int wifiListMaxCount = getWifiListMaxCount();
+ if (mAdapter.getItemCount() > wifiListMaxCount) {
+ mHasMoreWifiEntries = true;
+ }
+ mAdapter.setMaxEntriesCount(wifiListMaxCount);
+ final int wifiListMinHeight = mWifiNetworkHeight * wifiListMaxCount;
+ if (mWifiRecyclerView.getMinimumHeight() != wifiListMinHeight) {
+ mWifiRecyclerView.setMinimumHeight(wifiListMinHeight);
+ }
mWifiRecyclerView.setVisibility(View.VISIBLE);
- mSeeAllLayout.setVisibility(mHasMoreEntry ? View.VISIBLE : View.INVISIBLE);
+ mSeeAllLayout.setVisibility(mHasMoreWifiEntries ? View.VISIBLE : View.INVISIBLE);
}
@VisibleForTesting
@MainThread
int getWifiListMaxCount() {
- int count = InternetDialogController.MAX_WIFI_ENTRY_COUNT;
+ // Use the maximum count of networks to calculate the remaining count for Wi-Fi networks.
+ int count = MAX_NETWORK_COUNT;
if (mEthernetLayout.getVisibility() == View.VISIBLE) {
count -= 1;
}
if (mMobileNetworkLayout.getVisibility() == View.VISIBLE) {
count -= 1;
}
+
+ // If the remaining count is greater than the maximum count of the Wi-Fi network, the
+ // maximum count of the Wi-Fi network is used.
+ if (count > MAX_WIFI_ENTRY_COUNT) {
+ count = MAX_WIFI_ENTRY_COUNT;
+ }
if (mConnectedWifListLayout.getVisibility() == View.VISIBLE) {
count -= 1;
}
@@ -654,14 +674,14 @@ public class InternetDialog extends SystemUIDialog implements
@Override
@WorkerThread
public void onAccessPointsChanged(@Nullable List<WifiEntry> wifiEntries,
- @Nullable WifiEntry connectedEntry, boolean hasMoreEntry) {
+ @Nullable WifiEntry connectedEntry, boolean hasMoreWifiEntries) {
// Should update the carrier network layout when it is connected under airplane mode ON.
boolean shouldUpdateCarrierNetwork = mMobileNetworkLayout.getVisibility() == View.VISIBLE
&& mInternetDialogController.isAirplaneModeEnabled();
mHandler.post(() -> {
mConnectedWifiEntry = connectedEntry;
mWifiEntriesCount = wifiEntries == null ? 0 : wifiEntries.size();
- mHasMoreEntry = hasMoreEntry;
+ mHasMoreWifiEntries = hasMoreWifiEntries;
updateDialog(shouldUpdateCarrierNetwork /* shouldUpdateMobileNetwork */);
mAdapter.setWifiEntries(wifiEntries, mWifiEntriesCount);
mAdapter.notifyDataSetChanged();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index 4908d4fb214e..3189d2f95c28 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -18,6 +18,7 @@ package com.android.systemui.qs.tiles.dialog;
import static com.android.settingslib.mobile.MobileMappings.getIconKey;
import static com.android.settingslib.mobile.MobileMappings.mapIconSets;
+import static com.android.wifitrackerlib.WifiEntry.CONNECTED_STATE_CONNECTED;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -90,6 +91,7 @@ import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;
@@ -125,7 +127,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi
R.string.all_network_unavailable;
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- static final int MAX_WIFI_ENTRY_COUNT = 4;
+ static final int MAX_WIFI_ENTRY_COUNT = 3;
private WifiManager mWifiManager;
private Context mContext;
@@ -143,8 +145,6 @@ public class InternetDialogController implements AccessPointController.AccessPoi
private AccessPointController mAccessPointController;
private IntentFilter mConnectionStateFilter;
private InternetDialogCallback mCallback;
- private WifiEntry mConnectedEntry;
- private int mWifiEntriesCount;
private UiEventLogger mUiEventLogger;
private BroadcastDispatcher mBroadcastDispatcher;
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -156,6 +156,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi
private SignalDrawable mSignalDrawable;
private LocationController mLocationController;
private DialogLaunchAnimator mDialogLaunchAnimator;
+ private boolean mHasWifiEntries;
@VisibleForTesting
static final float TOAST_PARAMS_HORIZONTAL_WEIGHT = 1.0f;
@@ -177,6 +178,8 @@ public class InternetDialogController implements AccessPointController.AccessPoi
protected KeyguardStateController mKeyguardStateController;
@VisibleForTesting
protected boolean mHasEthernet = false;
+ @VisibleForTesting
+ protected ConnectedWifiInternetMonitor mConnectedWifiInternetMonitor;
private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
new KeyguardUpdateMonitorCallback() {
@@ -237,6 +240,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi
mSignalDrawable = new SignalDrawable(mContext);
mLocationController = locationController;
mDialogLaunchAnimator = dialogLaunchAnimator;
+ mConnectedWifiInternetMonitor = new ConnectedWifiInternetMonitor();
}
void onStart(@NonNull InternetDialogCallback callback, boolean canConfigWifi) {
@@ -277,6 +281,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi
mAccessPointController.removeAccessPointCallback(this);
mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
mConnectivityManager.unregisterNetworkCallback(mConnectivityManagerNetworkCallback);
+ mConnectedWifiInternetMonitor.unregisterCallback();
}
@VisibleForTesting
@@ -334,7 +339,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi
return mContext.getText(SUBTITLE_TEXT_UNLOCK_TO_VIEW_NETWORKS);
}
- if (mConnectedEntry != null || mWifiEntriesCount > 0) {
+ if (mHasWifiEntries) {
return mCanConfigWifi ? mContext.getText(SUBTITLE_TEXT_TAP_A_NETWORK_TO_CONNECT) : null;
}
@@ -485,6 +490,11 @@ public class InternetDialogController implements AccessPointController.AccessPoi
private Map<Integer, CharSequence> getUniqueSubscriptionDisplayNames(Context context) {
class DisplayInfo {
+ DisplayInfo(SubscriptionInfo subscriptionInfo, CharSequence originalName) {
+ this.subscriptionInfo = subscriptionInfo;
+ this.originalName = originalName;
+ }
+
public SubscriptionInfo subscriptionInfo;
public CharSequence originalName;
public CharSequence uniqueName;
@@ -498,12 +508,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi
// Filter out null values.
return (i != null && i.getDisplayName() != null);
})
- .map(i -> {
- DisplayInfo info = new DisplayInfo();
- info.subscriptionInfo = i;
- info.originalName = i.getDisplayName().toString().trim();
- return info;
- });
+ .map(i -> new DisplayInfo(i, i.getDisplayName().toString().trim()));
// A Unique set of display names
Set<CharSequence> uniqueNames = new HashSet<>();
@@ -582,7 +587,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi
return "";
}
- int resId = mapIconSets(config).get(iconKey).dataContentDescription;
+ int resId = Objects.requireNonNull(mapIconSets(config).get(iconKey)).dataContentDescription;
if (isCarrierNetworkActive()) {
SignalIcon.MobileIconGroup carrierMergedWifiIconGroup =
TelephonyIcons.CARRIER_MERGED_WIFI;
@@ -826,7 +831,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi
if (!mLocationController.isLocationEnabled()) {
return false;
}
- return mWifiManager.isScanAlwaysAvailable();
+ return mWifiManager != null && mWifiManager.isScanAlwaysAvailable();
}
static class WifiEntryConnectCallback implements WifiEntry.ConnectCallback {
@@ -875,43 +880,30 @@ public class InternetDialogController implements AccessPointController.AccessPoi
return;
}
- if (accessPoints == null || accessPoints.size() == 0) {
- mConnectedEntry = null;
- mWifiEntriesCount = 0;
- mCallback.onAccessPointsChanged(null /* wifiEntries */, null /* connectedEntry */,
- false /* hasMoreEntry */);
- return;
- }
-
- boolean hasMoreEntry = false;
- int count = MAX_WIFI_ENTRY_COUNT;
- if (mHasEthernet) {
- count -= 1;
- }
- if (hasActiveSubId() || isCarrierNetworkActive()) {
- count -= 1;
- }
- final int wifiTotalCount = accessPoints.size();
- if (count > wifiTotalCount) {
- count = wifiTotalCount;
- } else if (count < wifiTotalCount) {
- hasMoreEntry = true;
- }
-
WifiEntry connectedEntry = null;
- final List<WifiEntry> wifiEntries = new ArrayList<>();
- for (int i = 0; i < count; i++) {
- WifiEntry entry = accessPoints.get(i);
- if (connectedEntry == null && entry.isDefaultNetwork() && entry.hasInternetAccess()) {
- connectedEntry = entry;
- } else {
- wifiEntries.add(entry);
+ List<WifiEntry> wifiEntries = null;
+ final int accessPointsSize = (accessPoints == null) ? 0 : accessPoints.size();
+ final boolean hasMoreWifiEntries = (accessPointsSize > MAX_WIFI_ENTRY_COUNT);
+ if (accessPointsSize > 0) {
+ wifiEntries = new ArrayList<>();
+ final int count = hasMoreWifiEntries ? MAX_WIFI_ENTRY_COUNT : accessPointsSize;
+ mConnectedWifiInternetMonitor.unregisterCallback();
+ for (int i = 0; i < count; i++) {
+ WifiEntry entry = accessPoints.get(i);
+ mConnectedWifiInternetMonitor.registerCallbackIfNeed(entry);
+ if (connectedEntry == null && entry.isDefaultNetwork()
+ && entry.hasInternetAccess()) {
+ connectedEntry = entry;
+ } else {
+ wifiEntries.add(entry);
+ }
}
+ mHasWifiEntries = true;
+ } else {
+ mHasWifiEntries = false;
}
- mConnectedEntry = connectedEntry;
- mWifiEntriesCount = wifiEntries.size();
- mCallback.onAccessPointsChanged(wifiEntries, mConnectedEntry, hasMoreEntry);
+ mCallback.onAccessPointsChanged(wifiEntries, connectedEntry, hasMoreWifiEntries);
}
@Override
@@ -987,6 +979,55 @@ public class InternetDialogController implements AccessPointController.AccessPoi
}
/**
+ * Helper class for monitoring the Internet access of the connected WifiEntry.
+ */
+ @VisibleForTesting
+ protected class ConnectedWifiInternetMonitor implements WifiEntry.WifiEntryCallback {
+
+ private WifiEntry mWifiEntry;
+
+ public void registerCallbackIfNeed(WifiEntry entry) {
+ if (entry == null || mWifiEntry != null) {
+ return;
+ }
+ // If the Wi-Fi is not connected yet, or it's the connected Wi-Fi with Internet
+ // access. Then we don't need to listen to the callback to update the Wi-Fi entries.
+ if (entry.getConnectedState() != CONNECTED_STATE_CONNECTED
+ || (entry.isDefaultNetwork() && entry.hasInternetAccess())) {
+ return;
+ }
+ mWifiEntry = entry;
+ entry.setListener(this);
+ }
+
+ public void unregisterCallback() {
+ if (mWifiEntry == null) {
+ return;
+ }
+ mWifiEntry.setListener(null);
+ mWifiEntry = null;
+ }
+
+ @MainThread
+ @Override
+ public void onUpdated() {
+ if (mWifiEntry == null) {
+ return;
+ }
+ WifiEntry entry = mWifiEntry;
+ if (entry.getConnectedState() != CONNECTED_STATE_CONNECTED) {
+ unregisterCallback();
+ return;
+ }
+ if (entry.isDefaultNetwork() && entry.hasInternetAccess()) {
+ unregisterCallback();
+ // Trigger onAccessPointsChanged() to update the Wi-Fi entries.
+ scanWifiAccessPoints();
+ }
+ }
+ }
+
+ /**
* Return {@code true} If the Ethernet exists
*/
@MainThread
@@ -1061,7 +1102,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi
void dismissDialog();
void onAccessPointsChanged(@Nullable List<WifiEntry> wifiEntries,
- @Nullable WifiEntry connectedEntry, boolean hasMoreEntry);
+ @Nullable WifiEntry connectedEntry, boolean hasMoreWifiEntries);
}
void makeOverlayToast(int stringId) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 3ed7e84af020..60060aaf72da 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -76,6 +76,7 @@ import androidx.annotation.NonNull;
import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.UiEventLogger;
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.internal.util.ScreenshotHelper;
import com.android.systemui.Dumpable;
@@ -88,6 +89,7 @@ import com.android.systemui.navigationbar.NavigationBar;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.navigationbar.buttons.KeyButtonView;
import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
import com.android.systemui.settings.CurrentUserTracker;
import com.android.systemui.shared.recents.IOverviewProxy;
@@ -161,6 +163,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
private final Optional<StartingSurface> mStartingSurface;
private final SmartspaceTransitionController mSmartspaceTransitionController;
private final Optional<RecentTasks> mRecentTasks;
+ private final UiEventLogger mUiEventLogger;
private Region mActiveNavBarRegion;
@@ -248,6 +251,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
mContext.getSystemService(InputMethodManager.class)
.showInputMethodPickerFromSystem(true /* showAuxiliarySubtypes */,
DEFAULT_DISPLAY);
+ mUiEventLogger.log(KeyButtonView.NavBarButtonEvent.NAVBAR_IME_SWITCHER_BUTTON_TAP);
}
@Override
@@ -400,6 +404,13 @@ public class OverviewProxyService extends CurrentUserTracker implements
() -> mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN));
}
+ @Override
+ public void toggleNotificationPanel() {
+ verifyCallerAndClearCallingIdentityPostMain("toggleNotificationPanel", () ->
+ mStatusBarOptionalLazy.get().ifPresent(StatusBar::togglePanel));
+ }
+
+
private boolean verifyCaller(String reason) {
final int callerId = Binder.getCallingUserHandle().getIdentifier();
if (callerId != mCurrentBoundedUserId) {
@@ -560,6 +571,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
ShellTransitions shellTransitions,
ScreenLifecycle screenLifecycle,
SmartspaceTransitionController smartspaceTransitionController,
+ UiEventLogger uiEventLogger,
DumpManager dumpManager) {
super(broadcastDispatcher);
mContext = context;
@@ -581,6 +593,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
mOneHandedOptional = oneHandedOptional;
mShellTransitions = shellTransitions;
mRecentTasks = recentTasks;
+ mUiEventLogger = uiEventLogger;
// Assumes device always starts with back button until launcher tells it that it does not
mNavBarButtonAlpha = 1.0f;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 6d78b9f37c74..ce571e54e65c 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -39,11 +39,13 @@ import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ExitTransitionCoordinator;
import android.app.ExitTransitionCoordinator.ExitTransitionCallbacks;
+import android.app.ICompatCameraControlCallback;
import android.app.Notification;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Insets;
import android.graphics.PixelFormat;
@@ -72,6 +74,7 @@ import android.view.RemoteAnimationTarget;
import android.view.ScrollCaptureResponse;
import android.view.SurfaceControl;
import android.view.View;
+import android.view.ViewRootImpl;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.WindowInsets;
@@ -595,20 +598,35 @@ public class ScreenshotController {
withWindowAttached(() -> {
requestScrollCapture();
mWindow.peekDecorView().getViewRootImpl().setActivityConfigCallback(
- (overrideConfig, newDisplayId) -> {
- if (mConfigChanges.applyNewConfig(mContext.getResources())) {
- // Hide the scroll chip until we know it's available in this orientation
- mScreenshotView.hideScrollChip();
- // Delay scroll capture eval a bit to allow the underlying activity
- // to set up in the new orientation.
- mScreenshotHandler.postDelayed(this::requestScrollCapture, 150);
- mScreenshotView.updateInsets(
- mWindowManager.getCurrentWindowMetrics().getWindowInsets());
- // screenshot animation calculations won't be valid anymore, so just end
- if (mScreenshotAnimation != null && mScreenshotAnimation.isRunning()) {
- mScreenshotAnimation.end();
+ new ViewRootImpl.ActivityConfigCallback() {
+ @Override
+ public void onConfigurationChanged(Configuration overrideConfig,
+ int newDisplayId) {
+ if (mConfigChanges.applyNewConfig(mContext.getResources())) {
+ // Hide the scroll chip until we know it's available in this
+ // orientation
+ mScreenshotView.hideScrollChip();
+ // Delay scroll capture eval a bit to allow the underlying activity
+ // to set up in the new orientation.
+ mScreenshotHandler.postDelayed(
+ ScreenshotController.this::requestScrollCapture, 150);
+ mScreenshotView.updateInsets(
+ mWindowManager.getCurrentWindowMetrics()
+ .getWindowInsets());
+ // Screenshot animation calculations won't be valid anymore,
+ // so just end
+ if (mScreenshotAnimation != null
+ && mScreenshotAnimation.isRunning()) {
+ mScreenshotAnimation.end();
+ }
}
}
+ @Override
+ public void requestCompatCameraControl(boolean showControl,
+ boolean transformationApplied,
+ ICompatCameraControlCallback callback) {
+ Log.w(TAG, "Unexpected requestCompatCameraControl callback");
+ }
});
});
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 0fb08e403483..491a1750a93e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -298,8 +298,8 @@ class LockscreenShadeTransitionController @Inject constructor(
nsslController.setTransitionToFullShadeAmount(field)
notificationPanelController.setTransitionToFullShadeAmount(field,
false /* animate */, 0 /* delay */)
- val progress = MathUtils.saturate(dragDownAmount / scrimTransitionDistance)
- qS.setTransitionToFullShadeAmount(field, progress)
+ dragProgress = MathUtils.saturate(dragDownAmount / scrimTransitionDistance)
+ qS.setTransitionToFullShadeAmount(field, dragProgress)
// TODO: appear media also in split shade
val mediaAmount = if (useSplitShade) 0f else field
mediaHierarchyManager.setTransitionToFullShadeAmount(mediaAmount)
@@ -308,6 +308,9 @@ class LockscreenShadeTransitionController @Inject constructor(
}
}
+ var dragProgress = 0f
+ private set
+
private fun transitionToShadeAmountCommon(dragDownAmount: Float) {
val scrimProgress = MathUtils.saturate(dragDownAmount / scrimTransitionDistance)
scrimController.setTransitionToFullShadeProgress(scrimProgress)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
index fa993538941f..842be5bce52c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
@@ -94,9 +94,8 @@ class WiredChargingRippleController @Inject constructor(
nowPluggedIn: Boolean,
charging: Boolean
) {
- // Suppresses the ripple when it's disabled, or when the state change comes
- // from wireless charging.
- if (!rippleEnabled || batteryController.isPluggedInWireless) {
+ // Suppresses the ripple when the state change comes from wireless charging.
+ if (batteryController.isPluggedInWireless) {
return
}
val wasPluggedIn = pluggedIn
@@ -146,7 +145,7 @@ class WiredChargingRippleController @Inject constructor(
}
fun startRipple() {
- if (!rippleEnabled || rippleView.rippleInProgress || rippleView.parent != null) {
+ if (rippleView.rippleInProgress || rippleView.parent != null) {
// Skip if ripple is still playing, or not playing but already added the parent
// (which might happen just before the animation starts or right after
// the animation ends.)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java
index 7bf710c053a7..c9ce12a6859e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java
@@ -117,6 +117,7 @@ public class AccessPointControllerImpl implements AccessPointController,
}
public boolean canConfigWifi() {
+ if (!mWifiPickerTrackerFactory.isSupported()) return false;
return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI,
new UserHandle(mCurrentUser));
}
@@ -312,6 +313,10 @@ public class AccessPointControllerImpl implements AccessPointController,
mWorkerHandler = workerHandler;
}
+ private boolean isSupported() {
+ return mWifiManager != null;
+ }
+
/**
* Create a {@link WifiPickerTracker}
*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
index 55d549ddf60b..d65fa3acfa37 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.core
import android.app.Fragment
import com.android.systemui.R
import com.android.systemui.fragments.FragmentHostManager
+import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions
import com.android.systemui.statusbar.phone.PhoneStatusBarView
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent
@@ -50,12 +51,11 @@ class StatusBarInitializer @Inject constructor(
override fun onFragmentViewCreated(tag: String, fragment: Fragment) {
val statusBarFragmentComponent = (fragment as CollapsedStatusBarFragment)
.statusBarFragmentComponent ?: throw IllegalStateException()
- val statusBarView = statusBarFragmentComponent.phoneStatusBarView
- val sbViewController =
- statusBarFragmentComponent.phoneStatusBarViewController
-
- statusBarViewUpdatedListener
- ?.onStatusBarViewUpdated(statusBarView, sbViewController)
+ statusBarViewUpdatedListener?.onStatusBarViewUpdated(
+ statusBarFragmentComponent.phoneStatusBarView,
+ statusBarFragmentComponent.phoneStatusBarViewController,
+ statusBarFragmentComponent.phoneStatusBarTransitions
+ )
}
override fun onFragmentViewDestroyed(tag: String?, fragment: Fragment?) {
@@ -72,7 +72,8 @@ class StatusBarInitializer @Inject constructor(
interface OnStatusBarViewUpdatedListener {
fun onStatusBarViewUpdated(
statusBarView: PhoneStatusBarView,
- statusBarViewController: PhoneStatusBarViewController
+ statusBarViewController: PhoneStatusBarViewController,
+ statusBarTransitions: PhoneStatusBarTransitions
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index a44de2ce5699..a4e2d5ec0829 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -241,6 +241,10 @@ class LockscreenSmartspaceController @Inject constructor(
configurationController.addCallback(configChangeListener)
statusBarStateController.addCallback(statusBarStateListener)
+ plugin.registerSmartspaceEventNotifier {
+ e -> session?.notifySmartspaceEvent(e)
+ }
+
reloadSmartspace()
}
@@ -266,6 +270,7 @@ class LockscreenSmartspaceController @Inject constructor(
statusBarStateController.removeCallback(statusBarStateListener)
session = null
+ plugin?.registerSmartspaceEventNotifier(null)
plugin?.onTargetsAvailable(emptyList())
Log.d(TAG, "Ending smartspace session for lockscreen")
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
index d21d5a7a358f..757fb5a2fe9a 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
@@ -91,7 +91,6 @@ class NotifCoordinatorsImpl @Inject constructor(
mCoordinators.add(conversationCoordinator)
mCoordinators.add(groupCountCoordinator)
mCoordinators.add(mediaCoordinator)
- mCoordinators.add(remoteInputCoordinator)
mCoordinators.add(rowAppearanceCoordinator)
mCoordinators.add(stackCoordinator)
mCoordinators.add(shadeEventCoordinator)
@@ -105,6 +104,7 @@ class NotifCoordinatorsImpl @Inject constructor(
mCoordinators.add(headsUpCoordinator)
mCoordinators.add(gutsCoordinator)
mCoordinators.add(preparationCoordinator)
+ mCoordinators.add(remoteInputCoordinator)
}
// Manually add Ordered Sections
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt
index f8d6c6d8ec10..b2e15f48004c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt
@@ -25,8 +25,8 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.NotificationMediaManager
import com.android.systemui.statusbar.StatusBarState
-import com.android.systemui.statusbar.notification.NotificationEntryManager
import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.tuner.TunerService
@@ -44,7 +44,7 @@ class BypassHeadsUpNotifier @Inject constructor(
private val headsUpManager: HeadsUpManagerPhone,
private val notificationLockscreenUserManager: NotificationLockscreenUserManager,
private val mediaManager: NotificationMediaManager,
- private val entryManager: NotificationEntryManager,
+ private val commonNotifCollection: CommonNotifCollection,
tunerService: TunerService
) : StatusBarStateController.StateListener, NotificationMediaManager.MediaListener {
@@ -77,8 +77,7 @@ class BypassHeadsUpNotifier @Inject constructor(
override fun onPrimaryMetadataOrStateChanged(metadata: MediaMetadata?, state: Int) {
val previous = currentMediaEntry
- var newEntry = entryManager
- .getActiveNotificationUnfiltered(mediaManager.mediaNotificationKey)
+ var newEntry = commonNotifCollection.getEntry(mediaManager.mediaNotificationKey)
if (!NotificationMediaManager.isPlayingState(state)) {
newEntry = null
}
@@ -112,7 +111,7 @@ class BypassHeadsUpNotifier @Inject constructor(
// filter notifications invisible on Keyguard
return false
}
- if (entryManager.getActiveNotificationUnfiltered(entry.key) != null) {
+ if (commonNotifCollection.getEntry(entry.key) != null) {
// filter notifications not the active list currently
return false
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
index bfe352d57c24..aa511c66eabb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
@@ -146,6 +146,7 @@ public class NotificationSnooze extends LinearLayout
protected void onAttachedToWindow() {
super.onAttachedToWindow();
logOptionSelection(MetricsEvent.NOTIFICATION_SNOOZE_CLICKED, mDefaultOption);
+ dispatchConfigurationChanged(getResources().getConfiguration());
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index e1dbf4efa4de..79b05c910705 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -412,7 +412,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private NotificationShelf mShelf;
private int mMaxDisplayedNotifications = -1;
private float mKeyguardBottomPadding = -1;
- private int mStatusBarHeight;
+ @VisibleForTesting int mStatusBarHeight;
private int mMinInteractionHeight;
private final Rect mClipRect = new Rect();
private boolean mIsClipped;
@@ -669,6 +669,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
public void setIsRemoteInputActive(boolean isActive) {
mIsRemoteInputActive = isActive;
+ updateFooter();
}
@VisibleForTesting
@@ -4853,8 +4854,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
public int getMinExpansionHeight() {
+ // shelf height is defined in dp but status bar height can be defined in px, that makes
+ // relation between them variable - sometimes one might be bigger than the other when
+ // changing density. That’s why we need to ensure we’re not subtracting negative value below
return mShelf.getIntrinsicHeight()
- - (mShelf.getIntrinsicHeight() - mStatusBarHeight + mWaterfallTopInset) / 2
+ - Math.max(0,
+ (mShelf.getIntrinsicHeight() - mStatusBarHeight + mWaterfallTopInset) / 2)
+ mWaterfallTopInset;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index e3a4bf0170fb..2dc92764a4af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -51,6 +51,7 @@ public class StackStateAnimator {
public static final int ANIMATION_DURATION_CLOSE_REMOTE_INPUT = 150;
public static final int ANIMATION_DURATION_HEADS_UP_APPEAR = 400;
public static final int ANIMATION_DURATION_HEADS_UP_DISAPPEAR = 400;
+ public static final int ANIMATION_DURATION_FOLD_TO_AOD = 600;
public static final int ANIMATION_DURATION_PULSE_APPEAR =
KeyguardSliceView.DEFAULT_ANIM_DURATION;
public static final int ANIMATION_DURATION_BLOCKING_HELPER_FADE = 240;
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 4b8b580eba0b..d87a02493a23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -243,6 +243,10 @@ public class DozeParameters implements
return mScreenOffAnimationController.shouldShowLightRevealScrim();
}
+ public boolean shouldAnimateDozingChange() {
+ return mScreenOffAnimationController.shouldAnimateDozingChange();
+ }
+
/**
* 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.
@@ -324,6 +328,7 @@ public class DozeParameters implements
for (Callback callback : mCallbacks) {
callback.onAlwaysOnChange();
}
+ mScreenOffAnimationController.onAlwaysOnChanged(getAlwaysOn());
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index e7d5724fa9bf..81871634fbaf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -392,7 +392,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
}
float alphaQsExpansion = 1 - Math.min(
- 1, mNotificationPanelViewStateProvider.getQsExpansionFraction() * 2);
+ 1, mNotificationPanelViewStateProvider.getLockscreenShadeDragProgress() * 2);
float newAlpha = Math.min(getKeyguardContentsAlpha(), alphaQsExpansion)
* mKeyguardStatusBarAnimateAlpha
* (1.0f - mKeyguardHeadsUpShowingAmount);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 097c0d1677f9..434671c02035 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone;
import static android.view.View.GONE;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static androidx.constraintlayout.widget.ConstraintSet.END;
import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID;
@@ -26,14 +27,15 @@ import static androidx.constraintlayout.widget.ConstraintSet.TOP;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE;
import static com.android.keyguard.KeyguardClockSwitch.LARGE;
import static com.android.keyguard.KeyguardClockSwitch.SMALL;
+import static com.android.systemui.animation.Interpolators.EMPHASIZED_DECELERATE;
import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
-import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
+import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_FOLD_TO_AOD;
import static com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManagerKt.STATE_CLOSED;
import static com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManagerKt.STATE_OPEN;
import static com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManagerKt.STATE_OPENING;
@@ -464,6 +466,9 @@ public class NotificationPanelViewController extends PanelViewController {
private boolean mIsFullWidth;
private boolean mBlockingExpansionForCurrentTouch;
+ // TODO (b/204204226): no longer needed once refactor is complete
+ private final boolean mUseCombinedQSHeaders;
+
/**
* Following variables maintain state of events when input focus transfer may occur.
*/
@@ -911,6 +916,8 @@ public class NotificationPanelViewController extends PanelViewController {
mQsFrameTranslateController = qsFrameTranslateController;
updateUserSwitcherFlags();
onFinishInflate();
+
+ mUseCombinedQSHeaders = featureFlags.isEnabled(Flags.COMBINED_QS_HEADERS);
}
private void onFinishInflate() {
@@ -1140,6 +1147,9 @@ public class NotificationPanelViewController extends PanelViewController {
} else {
constraintSet.connect(R.id.qs_frame, END, PARENT_ID, END);
constraintSet.connect(R.id.notification_stack_scroller, START, PARENT_ID, START);
+ if (mUseCombinedQSHeaders) {
+ constraintSet.constrainHeight(R.id.split_shade_status_bar, WRAP_CONTENT);
+ }
}
constraintSet.getConstraint(R.id.notification_stack_scroller).layout.mWidth = panelWidth;
constraintSet.getConstraint(R.id.qs_frame).layout.mWidth = qsWidth;
@@ -1390,7 +1400,6 @@ public class NotificationPanelViewController extends PanelViewController {
stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded;
}
- mSplitShadeHeaderController.setShadeExpandedFraction(getExpandedFraction());
mNotificationStackScrollLayoutController.setIntrinsicPadding(stackScrollerPadding);
mKeyguardBottomArea.setAntiBurnInOffsetX(mClockPositionResult.clockX);
@@ -1423,11 +1432,12 @@ public class NotificationPanelViewController extends PanelViewController {
.getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia();
boolean splitShadeWithActiveMedia =
mShouldUseSplitNotificationShade && mMediaDataManager.hasActiveMedia();
+ boolean shouldAnimateClockChange = mScreenOffAnimationController.shouldAnimateClockChange();
if ((hasVisibleNotifications && !mShouldUseSplitNotificationShade)
|| (splitShadeWithActiveMedia && !mDozing)) {
- mKeyguardStatusViewController.displayClock(SMALL);
+ mKeyguardStatusViewController.displayClock(SMALL, shouldAnimateClockChange);
} else {
- mKeyguardStatusViewController.displayClock(LARGE);
+ mKeyguardStatusViewController.displayClock(LARGE, shouldAnimateClockChange);
}
updateKeyguardStatusViewAlignment(true /* animate */);
int userIconHeight = mKeyguardQsUserSwitchController != null
@@ -1463,7 +1473,7 @@ public class NotificationPanelViewController extends PanelViewController {
mKeyguardStatusViewController.isClockTopAligned());
mClockPositionAlgorithm.run(mClockPositionResult);
boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
- boolean animateClock = animate || mAnimateNextPositionUpdate;
+ boolean animateClock = (animate || mAnimateNextPositionUpdate) && shouldAnimateClockChange;
mKeyguardStatusViewController.updatePosition(
mClockPositionResult.clockX, mClockPositionResult.clockY,
mClockPositionResult.clockScale, animateClock);
@@ -2398,7 +2408,6 @@ public class NotificationPanelViewController extends PanelViewController {
? 1f : computeQsExpansionFraction();
mQs.setQsExpansion(adjustedExpansionFraction, getExpandedFraction(), getHeaderTranslation(),
squishiness);
- mSplitShadeHeaderController.setQsExpandedFraction(qsExpansionFraction);
mMediaHierarchyManager.setQsExpansion(qsExpansionFraction);
int qsPanelBottomY = calculateQsBottomPosition(qsExpansionFraction);
mScrimController.setQsPosition(qsExpansionFraction, qsPanelBottomY);
@@ -2412,6 +2421,17 @@ public class NotificationPanelViewController extends PanelViewController {
mDepthController.setQsPanelExpansion(qsExpansionFraction);
+ // updateQsExpansion will get called whenever mTransitionToFullShadeProgress or
+ // mLockscreenShadeTransitionController.getDragProgress change.
+ // When in lockscreen, getDragProgress indicates the true expanded fraction of QS
+ float shadeExpandedFraction = mTransitioningToFullShadeProgress > 0
+ ? mLockscreenShadeTransitionController.getDragProgress()
+ : getExpandedFraction();
+ mSplitShadeHeaderController.setShadeExpandedFraction(shadeExpandedFraction);
+ mSplitShadeHeaderController.setQsExpandedFraction(qsExpansionFraction);
+ mSplitShadeHeaderController.setShadeExpanded(mQsVisible);
+
+
if (mCommunalViewController != null) {
mCommunalViewController.updateQsExpansion(qsExpansionFraction);
}
@@ -3626,11 +3646,15 @@ public class NotificationPanelViewController extends PanelViewController {
return !isFullWidth() || !mShowIconsWhenExpanded;
}
- public final QS.ScrollListener mScrollListener = scrollY -> {
- if (scrollY > 0 && !mQsFullyExpanded) {
- if (DEBUG) Log.d(TAG, "Scrolling while not expanded. Forcing expand");
- // If we are scrolling QS, we should be fully expanded.
- expandWithQs();
+ public final QS.ScrollListener mScrollListener = new QS.ScrollListener() {
+ @Override
+ public void onQsPanelScrollChanged(int scrollY) {
+ mSplitShadeHeaderController.setQsScrollY(scrollY);
+ if (scrollY > 0 && !mQsFullyExpanded) {
+ if (DEBUG) Log.d(TAG, "Scrolling while not expanded. Forcing expand");
+ // If we are scrolling QS, we should be fully expanded.
+ expandWithQs();
+ }
}
};
@@ -3821,6 +3845,45 @@ public class NotificationPanelViewController extends PanelViewController {
}
}
+ /**
+ * Updates the views to the initial state for the fold to AOD animation
+ */
+ public void prepareFoldToAodAnimation() {
+ // Force show AOD UI even if we are not locked
+ showAodUi();
+
+ // Move the content of the AOD all the way to the left
+ // so we can animate to the initial position
+ final int translationAmount = mView.getResources().getDimensionPixelSize(
+ R.dimen.below_clock_padding_start);
+ mView.setTranslationX(-translationAmount);
+ mView.setAlpha(0);
+ }
+
+ /**
+ * Starts fold to AOD animation
+ */
+ public void startFoldToAodAnimation(Runnable endAction) {
+ mView.animate()
+ .translationX(0)
+ .alpha(1f)
+ .setDuration(ANIMATION_DURATION_FOLD_TO_AOD)
+ .setInterpolator(EMPHASIZED_DECELERATE)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ endAction.run();
+ }
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ endAction.run();
+ }
+ })
+ .start();
+
+ mKeyguardStatusViewController.animateFoldToAod();
+ }
+
/** */
public void setImportantForAccessibility(int mode) {
mView.setImportantForAccessibility(mode);
@@ -3935,6 +3998,10 @@ public class NotificationPanelViewController extends PanelViewController {
mView.setAlpha(alpha);
}
+ public void resetTranslation() {
+ mView.setTranslationX(0f);
+ }
+
public ViewPropertyAnimator fadeOut(long startDelayMs, long durationMs, Runnable endAction) {
return mView.animate().alpha(0).setStartDelay(startDelayMs).setDuration(
durationMs).setInterpolator(Interpolators.ALPHA_OUT).withLayer().withEndAction(
@@ -4638,8 +4705,6 @@ public class NotificationPanelViewController extends PanelViewController {
// would reset
maybeAnimateBottomAreaAlpha();
updateQsState();
- mSplitShadeHeaderController.setShadeExpanded(
- mBarState == SHADE || mBarState == SHADE_LOCKED);
}
@Override
@@ -4669,6 +4734,9 @@ public class NotificationPanelViewController extends PanelViewController {
* {@link KeyguardStatusBarViewController} and remove this method.
*/
boolean shouldHeadsUpBeVisible();
+
+ /** Return the fraction of the shade that's expanded, when in lockscreen. */
+ float getLockscreenShadeDragProgress();
}
private final NotificationPanelViewStateProvider mNotificationPanelViewStateProvider =
@@ -4687,6 +4755,11 @@ public class NotificationPanelViewController extends PanelViewController {
public boolean shouldHeadsUpBeVisible() {
return mHeadsUpAppearanceController.shouldBeVisible();
}
+
+ @Override
+ public float getLockscreenShadeDragProgress() {
+ return mLockscreenShadeTransitionController.getDragProgress();
+ }
};
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
index 55f14500f8fc..81d4bbf1e3c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
@@ -98,7 +98,6 @@ public class NotificationShadeWindowViewController {
private boolean mExpandAnimationRunning;
private NotificationStackScrollLayout mStackScrollLayout;
private PhoneStatusBarView mStatusBarView;
- private PhoneStatusBarTransitions mBarTransitions;
private StatusBar mService;
private NotificationShadeWindowController mNotificationShadeWindowController;
private DragDownHelper mDragDownHelper;
@@ -497,17 +496,8 @@ public class NotificationShadeWindowViewController {
}
}
- public PhoneStatusBarTransitions getBarTransitions() {
- return mBarTransitions;
- }
-
public void setStatusBarView(PhoneStatusBarView statusBarView) {
mStatusBarView = statusBarView;
- if (statusBarView != null) {
- mBarTransitions = new PhoneStatusBarTransitions(
- statusBarView,
- mStatusBarWindowController.getBackgroundView());
- }
}
public void setService(StatusBar statusBar, NotificationShadeWindowController controller) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index f67d18183c10..1e71ceb8cae8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -38,7 +38,6 @@ import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
-import com.android.systemui.statusbar.window.StatusBarWindowView;
import com.android.systemui.util.leak.RotationUtils;
import java.util.Objects;
@@ -76,6 +75,7 @@ public class PhoneStatusBarView extends FrameLayout {
@Override
public void onFinishInflate() {
+ super.onFinishInflate();
mBattery = findViewById(R.id.battery);
mClock = findViewById(R.id.clock);
mCutoutSpace = findViewById(R.id.cutout_space_view);
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 497e7d78ac4e..e806ca0d9005 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
@@ -19,16 +19,23 @@ import android.view.View
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.statusbar.LightRevealScrim
+import com.android.systemui.unfold.FoldAodAnimationController
+import com.android.systemui.unfold.SysUIUnfoldComponent
+import java.util.Optional
import javax.inject.Inject
@SysUISingleton
class ScreenOffAnimationController @Inject constructor(
+ sysUiUnfoldComponent: Optional<SysUIUnfoldComponent>,
unlockedScreenOffAnimation: UnlockedScreenOffAnimationController,
private val wakefulnessLifecycle: WakefulnessLifecycle,
) : WakefulnessLifecycle.Observer {
- // TODO(b/202844967) add fold to aod animation here
- private val animations: List<ScreenOffAnimation> = listOf(unlockedScreenOffAnimation)
+ private val foldToAodAnimation: FoldAodAnimationController? = sysUiUnfoldComponent
+ .orElse(null)?.getFoldAodAnimationController()
+
+ private val animations: List<ScreenOffAnimation> =
+ listOfNotNull(foldToAodAnimation, unlockedScreenOffAnimation)
fun initialize(statusBar: StatusBar, lightRevealScrim: LightRevealScrim) {
animations.forEach { it.initialize(statusBar, lightRevealScrim) }
@@ -43,6 +50,19 @@ class ScreenOffAnimationController @Inject constructor(
}
/**
+ * Called when opaqueness of the light reveal scrim has change
+ * When [isOpaque] is true then scrim is visible and covers the screen
+ */
+ fun onScrimOpaqueChanged(isOpaque: Boolean) =
+ animations.forEach { it.onScrimOpaqueChanged(isOpaque) }
+
+ /**
+ * Called when always on display setting changed
+ */
+ fun onAlwaysOnChanged(alwaysOn: Boolean) =
+ animations.forEach { it.onAlwaysOnChanged(alwaysOn) }
+
+ /**
* If returns true we are taking over the screen off animation from display manager to SysUI.
* We can play our custom animation instead of default fade out animation.
*/
@@ -103,6 +123,12 @@ class ScreenOffAnimationController @Inject constructor(
animations.any { it.isAnimationPlaying() }
/**
+ * Return true to ignore requests to hide keyguard
+ */
+ fun isKeyguardHideDelayed(): Boolean =
+ animations.any { it.isKeyguardHideDelayed() }
+
+ /**
* Return true to make the StatusBar expanded so we can animate [LightRevealScrim]
*/
fun shouldShowLightRevealScrim(): Boolean =
@@ -145,6 +171,19 @@ class ScreenOffAnimationController @Inject constructor(
*/
fun shouldAnimateAodIcons(): Boolean =
animations.all { it.shouldAnimateAodIcons() }
+
+ /**
+ * Return true to animate doze state change, if returns false dozing will be applied without
+ * animation (sends only 0.0f or 1.0f dozing progress)
+ */
+ fun shouldAnimateDozingChange(): Boolean =
+ animations.all { it.shouldAnimateDozingChange() }
+
+ /**
+ * Return true to animate large <-> small clock transition
+ */
+ fun shouldAnimateClockChange(): Boolean =
+ animations.all { it.shouldAnimateClockChange() }
}
interface ScreenOffAnimation {
@@ -158,11 +197,17 @@ interface ScreenOffAnimation {
fun shouldPlayAnimation(): Boolean = false
fun isAnimationPlaying(): Boolean = false
+ fun onScrimOpaqueChanged(isOpaque: Boolean) {}
+ fun onAlwaysOnChanged(alwaysOn: Boolean) {}
+
fun shouldAnimateInKeyguard(): Boolean = false
fun animateInKeyguard(keyguardView: View, after: Runnable) = after.run()
+ fun isKeyguardHideDelayed(): Boolean = false
fun shouldHideScrimOnWakeUp(): Boolean = false
fun overrideNotificationsDozeAmount(): Boolean = false
fun shouldShowAodIconsWhenShade(): 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/SplitShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
index f6e19bff8f9b..a1be5acdac13 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
@@ -19,17 +19,22 @@ package com.android.systemui.statusbar.phone
import android.view.View
import androidx.constraintlayout.motion.widget.MotionLayout
import com.android.settingslib.Utils
+import com.android.systemui.Dumpable
import com.android.systemui.R
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
+import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.qs.ChipVisibilityListener
import com.android.systemui.qs.HeaderPrivacyIconsController
import com.android.systemui.qs.carrier.QSCarrierGroupController
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarScope
import com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.SPLIT_SHADE_BATTERY_CONTROLLER
import com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.SPLIT_SHADE_HEADER
+import java.io.FileDescriptor
+import java.io.PrintWriter
import javax.inject.Inject
import javax.inject.Named
@@ -40,17 +45,29 @@ class SplitShadeHeaderController @Inject constructor(
private val privacyIconsController: HeaderPrivacyIconsController,
qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder,
featureFlags: FeatureFlags,
- @Named(SPLIT_SHADE_BATTERY_CONTROLLER) batteryMeterViewController: BatteryMeterViewController
-) {
+ @Named(SPLIT_SHADE_BATTERY_CONTROLLER) batteryMeterViewController: BatteryMeterViewController,
+ dumpManager: DumpManager
+) : Dumpable {
companion object {
private val HEADER_TRANSITION_ID = R.id.header_transition
private val SPLIT_HEADER_TRANSITION_ID = R.id.split_header_transition
+ private val QQS_HEADER_CONSTRAINT = R.id.qqs_header_constraint
+ private val QS_HEADER_CONSTRAINT = R.id.qs_header_constraint
+ private val SPLIT_HEADER_CONSTRAINT = R.id.split_header_constraint
+
+ private fun Int.stateToString() = when (this) {
+ QQS_HEADER_CONSTRAINT -> "QQS Header"
+ QS_HEADER_CONSTRAINT -> "QS Header"
+ SPLIT_HEADER_CONSTRAINT -> "Split Header"
+ else -> "Unknown state"
+ }
}
private val combinedHeaders = featureFlags.isEnabled(Flags.COMBINED_QS_HEADERS)
- // TODO(b/194178072) Handle RSSI hiding when multi carrier
private val iconManager: StatusBarIconController.TintedIconManager
+ private val iconContainer: StatusIconContainer
+ private val carrierIconSlots: List<String>
private val qsCarrierGroupController: QSCarrierGroupController
private var visible = false
set(value) {
@@ -96,16 +113,37 @@ class SplitShadeHeaderController @Inject constructor(
}
}
+ var qsScrollY = 0
+ set(value) {
+ if (field != value) {
+ field = value
+ updateScrollY()
+ }
+ }
+
+ private val chipVisibilityListener: ChipVisibilityListener = object : ChipVisibilityListener {
+ override fun onChipVisibilityRefreshed(visible: Boolean) {
+ if (statusBar is MotionLayout) {
+ val state = statusBar.getConstraintSet(QQS_HEADER_CONSTRAINT).apply {
+ setAlpha(R.id.statusIcons, if (visible) 0f else 1f)
+ setAlpha(R.id.batteryRemainingIcon, if (visible) 0f else 1f)
+ }
+ statusBar.updateState(QQS_HEADER_CONSTRAINT, state)
+ }
+ }
+ }
+
init {
if (statusBar is MotionLayout) {
val context = statusBar.context
val resources = statusBar.resources
- statusBar.getConstraintSet(R.id.qqs_header_constraint)
+ statusBar.getConstraintSet(QQS_HEADER_CONSTRAINT)
.load(context, resources.getXml(R.xml.qqs_header))
- statusBar.getConstraintSet(R.id.qs_header_constraint)
+ statusBar.getConstraintSet(QS_HEADER_CONSTRAINT)
.load(context, resources.getXml(R.xml.qs_header))
- statusBar.getConstraintSet(R.id.split_header_constraint)
+ statusBar.getConstraintSet(SPLIT_HEADER_CONSTRAINT)
.load(context, resources.getXml(R.xml.split_header))
+ privacyIconsController.chipVisibilityListener = chipVisibilityListener
}
}
@@ -117,17 +155,35 @@ class SplitShadeHeaderController @Inject constructor(
batteryMeterViewController.ignoreTunerUpdates()
batteryIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE)
- val iconContainer: StatusIconContainer = statusBar.findViewById(R.id.statusIcons)
+ iconContainer = statusBar.findViewById(R.id.statusIcons)
iconManager = StatusBarIconController.TintedIconManager(iconContainer, featureFlags)
iconManager.setTint(Utils.getColorAttrDefaultColor(statusBar.context,
android.R.attr.textColorPrimary))
+
+ carrierIconSlots = if (featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)) {
+ listOf(
+ statusBar.context.getString(com.android.internal.R.string.status_bar_no_calling),
+ statusBar.context.getString(com.android.internal.R.string.status_bar_call_strength)
+ )
+ } else {
+ listOf(statusBar.context.getString(com.android.internal.R.string.status_bar_mobile))
+ }
qsCarrierGroupController = qsCarrierGroupControllerBuilder
.setQSCarrierGroup(statusBar.findViewById(R.id.carrier_group))
.build()
+
+ dumpManager.registerDumpable(this)
+
updateVisibility()
updateConstraints()
}
+ private fun updateScrollY() {
+ if (!splitShadeMode && combinedHeaders) {
+ statusBar.scrollY = qsScrollY
+ }
+ }
+
private fun onShadeExpandedChanged() {
if (shadeExpanded) {
privacyIconsController.startListening()
@@ -139,7 +195,7 @@ class SplitShadeHeaderController @Inject constructor(
}
private fun onSplitShadeModeChanged() {
- if (splitShadeMode) {
+ if (splitShadeMode || combinedHeaders) {
privacyIconsController.onParentVisible()
} else {
privacyIconsController.onParentInvisible()
@@ -173,6 +229,7 @@ class SplitShadeHeaderController @Inject constructor(
statusBar.setTransition(HEADER_TRANSITION_ID)
statusBar.transitionToStart()
updatePosition()
+ updateScrollY()
}
}
@@ -185,9 +242,33 @@ class SplitShadeHeaderController @Inject constructor(
private fun updateListeners() {
qsCarrierGroupController.setListening(visible)
if (visible) {
+ updateSingleCarrier(qsCarrierGroupController.isSingleCarrier)
+ qsCarrierGroupController.setOnSingleCarrierChangedListener { updateSingleCarrier(it) }
statusBarIconController.addIconGroup(iconManager)
} else {
+ qsCarrierGroupController.setOnSingleCarrierChangedListener(null)
statusBarIconController.removeIconGroup(iconManager)
}
}
+
+ private fun updateSingleCarrier(singleCarrier: Boolean) {
+ if (singleCarrier) {
+ iconContainer.removeIgnoredSlots(carrierIconSlots)
+ } else {
+ iconContainer.addIgnoredSlots(carrierIconSlots)
+ }
+ }
+
+ override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
+ pw.println("visible: $visible")
+ pw.println("shadeExpanded: $shadeExpanded")
+ pw.println("shadeExpandedFraction: $shadeExpandedFraction")
+ pw.println("splitShadeMode: $splitShadeMode")
+ pw.println("qsExpandedFraction: $qsExpandedFraction")
+ pw.println("qsScrollY: $qsScrollY")
+ if (combinedHeaders) {
+ statusBar as MotionLayout
+ pw.println("currentState: ${statusBar.currentState.stateToString()}")
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 6c0b717fb85c..07914cf1d9c4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -341,7 +341,7 @@ public class StatusBar extends CoreStartable implements
mStatusBarWindowState = state;
mStatusBarWindowHidden = state == WINDOW_STATE_HIDDEN;
mStatusBarHideIconsForBouncerManager.setStatusBarWindowHidden(mStatusBarWindowHidden);
- if (getStatusBarView() != null) {
+ if (mStatusBarView != null) {
// Should #updateHideIconsForBouncer always be called, regardless of whether we have a
// status bar view? If so, we can make #updateHideIconsForBouncer private.
mStatusBarHideIconsForBouncerManager.updateHideIconsForBouncer(/* animate= */ false);
@@ -434,6 +434,10 @@ public class StatusBar extends CoreStartable implements
mCommandQueueCallbacks.animateCollapsePanels(flags, force);
}
+ /** */
+ public void togglePanel() {
+ mCommandQueueCallbacks.togglePanel();
+ }
/**
* The {@link StatusBarState} of the status bar.
*/
@@ -458,6 +462,7 @@ public class StatusBar extends CoreStartable implements
protected NotificationShadeWindowView mNotificationShadeWindowView;
protected PhoneStatusBarView mStatusBarView;
private PhoneStatusBarViewController mPhoneStatusBarViewController;
+ private PhoneStatusBarTransitions mStatusBarTransitions;
private AuthRippleController mAuthRippleController;
private int mStatusBarWindowState = WINDOW_STATE_SHOWING;
protected NotificationShadeWindowController mNotificationShadeWindowController;
@@ -1124,23 +1129,19 @@ public class StatusBar extends CoreStartable implements
// Set up CollapsedStatusBarFragment and PhoneStatusBarView
StatusBarInitializer initializer = mStatusBarComponent.getStatusBarInitializer();
initializer.setStatusBarViewUpdatedListener(
- new StatusBarInitializer.OnStatusBarViewUpdatedListener() {
- @Override
- public void onStatusBarViewUpdated(
- @NonNull PhoneStatusBarView statusBarView,
- @NonNull PhoneStatusBarViewController statusBarViewController) {
- mStatusBarView = statusBarView;
- mPhoneStatusBarViewController = statusBarViewController;
- mNotificationShadeWindowViewController.setStatusBarView(mStatusBarView);
- // Ensure we re-propagate panel expansion values to the panel controller and
- // any listeners it may have, such as PanelBar. This will also ensure we
- // re-display the notification panel if necessary (for example, if
- // a heads-up notification was being displayed and should continue being
- // displayed).
- mNotificationPanelViewController.updatePanelExpansionAndVisibility();
- setBouncerShowingForStatusBarComponents(mBouncerShowing);
- checkBarModes();
- }
+ (statusBarView, statusBarViewController, statusBarTransitions) -> {
+ mStatusBarView = statusBarView;
+ mPhoneStatusBarViewController = statusBarViewController;
+ mStatusBarTransitions = statusBarTransitions;
+ mNotificationShadeWindowViewController.setStatusBarView(mStatusBarView);
+ // Ensure we re-propagate panel expansion values to the panel controller and
+ // any listeners it may have, such as PanelBar. This will also ensure we
+ // re-display the notification panel if necessary (for example, if
+ // a heads-up notification was being displayed and should continue being
+ // displayed).
+ mNotificationPanelViewController.updatePanelExpansionAndVisibility();
+ setBouncerShowingForStatusBarComponents(mBouncerShowing);
+ checkBarModes();
});
initializer.initializeStatusBar(mStatusBarComponent);
@@ -1199,6 +1200,8 @@ public class StatusBar extends CoreStartable implements
Runnable updateOpaqueness = () -> {
mNotificationShadeWindowController.setLightRevealScrimOpaque(
mLightRevealScrim.isScrimOpaque());
+ mScreenOffAnimationController
+ .onScrimOpaqueChanged(mLightRevealScrim.isScrimOpaque());
};
if (opaque) {
// Delay making the view opaque for a frame, because it needs some time to render
@@ -1578,10 +1581,6 @@ public class StatusBar extends CoreStartable implements
Trace.endSection();
}
- protected PhoneStatusBarView getStatusBarView() {
- return mStatusBarView;
- }
-
public NotificationShadeWindowView getNotificationShadeWindowView() {
return mNotificationShadeWindowView;
}
@@ -2192,14 +2191,10 @@ public class StatusBar extends CoreStartable implements
}, false, sUiEventLogger).show(animationDelay);
}
- protected BarTransitions getStatusBarTransitions() {
- return mNotificationShadeWindowViewController.getBarTransitions();
- }
-
public void checkBarModes() {
if (mDemoModeController.isInDemoMode()) return;
- if (mNotificationShadeWindowViewController != null && getStatusBarTransitions() != null) {
- checkBarMode(mStatusBarMode, mStatusBarWindowState, getStatusBarTransitions());
+ if (mStatusBarTransitions != null) {
+ checkBarMode(mStatusBarMode, mStatusBarWindowState, mStatusBarTransitions);
}
mNavigationBarController.checkNavBarModes(mDisplayId);
mNoAnimationOnNextBarModeChange = false;
@@ -2226,9 +2221,8 @@ public class StatusBar extends CoreStartable implements
}
private void finishBarAnimations() {
- if (mNotificationShadeWindowController != null
- && mNotificationShadeWindowViewController.getBarTransitions() != null) {
- mNotificationShadeWindowViewController.getBarTransitions().finishAnimations();
+ if (mStatusBarTransitions != null) {
+ mStatusBarTransitions.finishAnimations();
}
mNavigationBarController.finishBarAnimations(mDisplayId);
}
@@ -2282,8 +2276,7 @@ public class StatusBar extends CoreStartable implements
pw.println(" ShadeWindowView: ");
if (mNotificationShadeWindowViewController != null) {
mNotificationShadeWindowViewController.dump(fd, pw, args);
- dumpBarTransitions(pw, "PhoneStatusBarTransitions",
- mNotificationShadeWindowViewController.getBarTransitions());
+ dumpBarTransitions(pw, "PhoneStatusBarTransitions", mStatusBarTransitions);
}
pw.println(" mMediaManager: ");
@@ -2915,7 +2908,17 @@ public class StatusBar extends CoreStartable implements
showKeyguardImpl();
}
} else {
- return hideKeyguardImpl(force);
+ // During folding a foldable device this might be called as a result of
+ // 'onScreenTurnedOff' call for the inner display.
+ // In this case:
+ // * When phone is locked on folding: it doesn't make sense to hide keyguard as it
+ // will be immediately locked again
+ // * When phone is unlocked: we still don't want to execute hiding of the keyguard
+ // 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 false;
}
@@ -3078,6 +3081,7 @@ public class StatusBar extends CoreStartable implements
mNotificationPanelViewController.onAffordanceLaunchEnded();
mNotificationPanelViewController.cancelAnimation();
mNotificationPanelViewController.setAlpha(1f);
+ mNotificationPanelViewController.resetTranslation();
mNotificationPanelViewController.resetViewGroupFade();
updateDozingState();
updateScrimController();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarDemoMode.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarDemoMode.java
index 3c09b78740a3..29c13723ca89 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarDemoMode.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarDemoMode.java
@@ -32,7 +32,6 @@ import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeCommandReceiver;
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.navigationbar.NavigationBarController;
-import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentScope;
import com.android.systemui.statusbar.policy.Clock;
import com.android.systemui.util.ViewController;
@@ -54,8 +53,7 @@ public class StatusBarDemoMode extends ViewController<View> implements DemoMode
private final Clock mClockView;
private final View mOperatorNameView;
private final DemoModeController mDemoModeController;
- private final NotificationShadeWindowController mNotificationShadeWindowController;
- private final NotificationShadeWindowViewController mNotificationShadeWindowViewController;
+ private final PhoneStatusBarTransitions mPhoneStatusBarTransitions;
private final NavigationBarController mNavigationBarController;
private final int mDisplayId;
@@ -64,16 +62,14 @@ public class StatusBarDemoMode extends ViewController<View> implements DemoMode
Clock clockView,
@Named(OPERATOR_NAME_VIEW) View operatorNameView,
DemoModeController demoModeController,
- NotificationShadeWindowController notificationShadeWindowController,
- NotificationShadeWindowViewController notificationShadeWindowViewController,
+ PhoneStatusBarTransitions phoneStatusBarTransitions,
NavigationBarController navigationBarController,
@DisplayId int displayId) {
super(clockView);
mClockView = clockView;
mOperatorNameView = operatorNameView;
mDemoModeController = demoModeController;
- mNotificationShadeWindowController = notificationShadeWindowController;
- mNotificationShadeWindowViewController = notificationShadeWindowViewController;
+ mPhoneStatusBarTransitions = phoneStatusBarTransitions;
mNavigationBarController = navigationBarController;
mDisplayId = displayId;
}
@@ -128,11 +124,7 @@ public class StatusBarDemoMode extends ViewController<View> implements DemoMode
-1;
if (barMode != -1) {
boolean animate = true;
- if (mNotificationShadeWindowController != null
- && mNotificationShadeWindowViewController.getBarTransitions() != null) {
- mNotificationShadeWindowViewController.getBarTransitions().transitionTo(
- barMode, animate);
- }
+ mPhoneStatusBarTransitions.transitionTo(barMode, animate);
mNavigationBarController.transitionTo(mDisplayId, barMode, animate);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index b84e6e6f37cc..875b7e59d15a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -111,8 +111,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private final NavigationModeController mNavigationModeController;
private final NotificationShadeWindowController mNotificationShadeWindowController;
private final KeyguardBouncer.Factory mKeyguardBouncerFactory;
- private final WakefulnessLifecycle mWakefulnessLifecycle;
- private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
private final KeyguardMessageAreaController.Factory mKeyguardMessageAreaFactory;
private KeyguardMessageAreaController mKeyguardMessageAreaController;
private final Lazy<ShadeController> mShadeController;
@@ -244,8 +242,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
KeyguardStateController keyguardStateController,
NotificationMediaManager notificationMediaManager,
KeyguardBouncer.Factory keyguardBouncerFactory,
- WakefulnessLifecycle wakefulnessLifecycle,
- UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
KeyguardMessageAreaController.Factory keyguardMessageAreaFactory,
Lazy<ShadeController> shadeController) {
mContext = context;
@@ -260,8 +256,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mStatusBarStateController = sysuiStatusBarStateController;
mDockManager = dockManager;
mKeyguardBouncerFactory = keyguardBouncerFactory;
- mWakefulnessLifecycle = wakefulnessLifecycle;
- mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
mKeyguardMessageAreaFactory = keyguardMessageAreaFactory;
mShadeController = shadeController;
}
@@ -416,7 +410,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
* dragging it and translation should be deferred {@see KeyguardBouncer#show(boolean, boolean)}
*/
public void showGenericBouncer(boolean scrimmed) {
- if (mAlternateAuthInterceptor != null) {
+ if (shouldShowAltAuth()) {
updateAlternateAuthShowing(mAlternateAuthInterceptor.showAlternateAuthBouncer());
return;
}
@@ -424,6 +418,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
showBouncer(scrimmed);
}
+ private boolean shouldShowAltAuth() {
+ return mAlternateAuthInterceptor != null
+ && mKeyguardUpdateManager.isUnlockingWithBiometricAllowed(true);
+ }
+
/**
* Hides the input bouncer (pin/password/pattern).
*/
@@ -479,7 +478,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
// If there is an an alternate auth interceptor (like the UDFPS), show that one instead
// of the bouncer.
- if (mAlternateAuthInterceptor != null) {
+ if (shouldShowAltAuth()) {
if (!afterKeyguardGone) {
mBouncer.setDismissAction(mAfterKeyguardGoneAction, mKeyguardGoneCancelAction);
mAfterKeyguardGoneAction = null;
@@ -1155,7 +1154,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
@Override
public ViewRootImpl getViewRootImpl() {
- return mStatusBar.getStatusBarView().getViewRootImpl();
+ return mNotificationShadeWindowController.getNotificationShadeView().getViewRootImpl();
}
public void launchPendingWakeupAction() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
index a4ebab9b763c..22b7f649775e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
@@ -20,6 +20,7 @@ import com.android.systemui.battery.BatteryMeterViewController;
import com.android.systemui.dagger.qualifiers.RootView;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.LightsOutNotifController;
+import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions;
import com.android.systemui.statusbar.phone.PhoneStatusBarView;
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
import com.android.systemui.statusbar.phone.StatusBarDemoMode;
@@ -36,6 +37,9 @@ import dagger.Subcomponent;
* controllers need access to that view, so those controllers will be re-created whenever the
* fragment is recreated.
*
+ * Anything that depends on {@link CollapsedStatusBarFragment} or {@link PhoneStatusBarView}
+ * should be included here or in {@link StatusBarFragmentModule}.
+ *
* Note that this is completely separate from
* {@link com.android.systemui.statusbar.phone.dagger.StatusBarComponent}. This component gets
* re-created on each new fragment creation, whereas
@@ -90,4 +94,8 @@ public interface StatusBarFragmentComponent {
/** */
@StatusBarFragmentScope
StatusBarDemoMode getStatusBarDemoMode();
+
+ /** */
+ @StatusBarFragmentScope
+ PhoneStatusBarTransitions getPhoneStatusBarTransitions();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
index 0cbd401916b7..dea1b43f579f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
@@ -22,10 +22,12 @@ import com.android.systemui.R;
import com.android.systemui.battery.BatteryMeterView;
import com.android.systemui.dagger.qualifiers.RootView;
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
+import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions;
import com.android.systemui.statusbar.phone.PhoneStatusBarView;
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.policy.Clock;
+import com.android.systemui.statusbar.window.StatusBarWindowController;
import javax.inject.Named;
@@ -89,4 +91,14 @@ public interface StatusBarFragmentModule {
phoneStatusBarView,
notificationPanelViewController.getStatusBarTouchEventHandler());
}
+
+ /** */
+ @Provides
+ @StatusBarFragmentScope
+ static PhoneStatusBarTransitions providePhoneStatusBarTransitions(
+ @RootView PhoneStatusBarView view,
+ StatusBarWindowController statusBarWindowController
+ ) {
+ return new PhoneStatusBarTransitions(view, statusBarWindowController.getBackgroundView());
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
new file mode 100644
index 000000000000..fb9df01a0251
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.unfold
+
+import android.os.PowerManager
+import android.provider.Settings
+import com.android.systemui.keyguard.KeyguardViewMediator
+import com.android.systemui.keyguard.ScreenLifecycle
+import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.statusbar.LightRevealScrim
+import com.android.systemui.statusbar.phone.ScreenOffAnimation
+import com.android.systemui.statusbar.phone.StatusBar
+import com.android.systemui.statusbar.policy.CallbackController
+import com.android.systemui.unfold.FoldAodAnimationController.FoldAodAnimationStatus
+import com.android.systemui.util.settings.GlobalSettings
+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
+ */
+@SysUIUnfoldScope
+class FoldAodAnimationController @Inject constructor(
+ private val screenLifecycle: ScreenLifecycle,
+ private val keyguardViewMediatorLazy: Lazy<KeyguardViewMediator>,
+ private val wakefulnessLifecycle: WakefulnessLifecycle,
+ private val globalSettings: GlobalSettings
+) : ScreenLifecycle.Observer,
+ CallbackController<FoldAodAnimationStatus>,
+ ScreenOffAnimation,
+ WakefulnessLifecycle.Observer {
+
+ private var alwaysOnEnabled: Boolean = false
+ private var isScrimOpaque: Boolean = false
+ private lateinit var statusBar: StatusBar
+ private var pendingScrimReadyCallback: Runnable? = null
+
+ private var shouldPlayAnimation = false
+ private val statusListeners = arrayListOf<FoldAodAnimationStatus>()
+
+ private var isAnimationPlaying = false
+
+ override fun initialize(statusBar: StatusBar, lightRevealScrim: LightRevealScrim) {
+ this.statusBar = statusBar
+
+ screenLifecycle.addObserver(this)
+ wakefulnessLifecycle.addObserver(this)
+ }
+
+ /**
+ * 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"
+ ) {
+ shouldPlayAnimation = true
+
+ isAnimationPlaying = true
+ statusBar.notificationPanelViewController.prepareFoldToAodAnimation()
+
+ statusListeners.forEach(FoldAodAnimationStatus::onFoldToAodAnimationChanged)
+
+ true
+ } else {
+ shouldPlayAnimation = false
+ false
+ }
+
+ override fun onStartedWakingUp() {
+ shouldPlayAnimation = false
+ isAnimationPlaying = false
+ }
+
+ /**
+ * Called when screen starts turning on, the contents of the screen might not be visible yet.
+ * This method reports back that the animation is ready in [onReady] callback.
+ *
+ * @param onReady callback when the animation is ready
+ * @see [com.android.systemui.keyguard.KeyguardViewMediator]
+ */
+ fun onScreenTurningOn(onReady: Runnable) {
+ if (shouldPlayAnimation) {
+ if (isScrimOpaque) {
+ onReady.run()
+ } else {
+ pendingScrimReadyCallback = onReady
+ }
+ } else {
+ // No animation, call ready callback immediately
+ onReady.run()
+ }
+ }
+
+ /**
+ * Called when keyguard scrim opaque changed
+ */
+ override fun onScrimOpaqueChanged(isOpaque: Boolean) {
+ isScrimOpaque = isOpaque
+
+ if (isOpaque) {
+ pendingScrimReadyCallback?.run()
+ pendingScrimReadyCallback = null
+ }
+ }
+
+ override fun onScreenTurnedOn() {
+ if (shouldPlayAnimation) {
+ statusBar.notificationPanelViewController.startFoldToAodAnimation {
+ // End action
+ isAnimationPlaying = false
+ keyguardViewMediatorLazy.get().maybeHandlePendingLock()
+ }
+ shouldPlayAnimation = false
+ }
+ }
+
+ override fun isAnimationPlaying(): Boolean =
+ isAnimationPlaying
+
+ override fun isKeyguardHideDelayed(): Boolean =
+ isAnimationPlaying()
+
+ override fun shouldShowAodIconsWhenShade(): Boolean =
+ shouldPlayAnimation()
+
+ override fun shouldAnimateAodIcons(): Boolean =
+ !shouldPlayAnimation()
+
+ override fun shouldAnimateDozingChange(): Boolean =
+ !shouldPlayAnimation()
+
+ override fun shouldAnimateClockChange(): Boolean =
+ !isAnimationPlaying()
+
+ /**
+ * Called when AOD status is changed
+ */
+ override fun onAlwaysOnChanged(alwaysOn: Boolean) {
+ alwaysOnEnabled = alwaysOn
+ }
+
+ override fun addCallback(listener: FoldAodAnimationStatus) {
+ statusListeners += listener
+ }
+
+ override fun removeCallback(listener: FoldAodAnimationStatus) {
+ statusListeners.remove(listener)
+ }
+
+ interface FoldAodAnimationStatus {
+ fun onFoldToAodAnimationChanged()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
index b53ab210424f..ccde3162b177 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
@@ -78,6 +78,8 @@ interface SysUIUnfoldComponent {
fun getStatusBarMoveFromCenterAnimationController(): StatusBarMoveFromCenterAnimationController
+ fun getFoldAodAnimationController(): FoldAodAnimationController
+
fun getUnfoldTransitionWallpaperController(): UnfoldTransitionWallpaperController
fun getUnfoldLightRevealOverlayAnimation(): UnfoldLightRevealOverlayAnimation
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt
new file mode 100644
index 000000000000..7f63d6c5e778
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.unfold
+
+import android.content.Context
+import android.hardware.devicestate.DeviceStateManager
+import com.android.internal.util.LatencyTracker
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.UiBackground
+import com.android.systemui.keyguard.ScreenLifecycle
+import java.util.concurrent.Executor
+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(
+ private val latencyTracker: LatencyTracker,
+ private val deviceStateManager: DeviceStateManager,
+ @UiBackground private val uiBgExecutor: Executor,
+ private val context: Context,
+ private val screenLifecycle: ScreenLifecycle
+) : ScreenLifecycle.Observer {
+
+ 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()
+
+ /** Registers for relevant events only if the device is foldable. */
+ fun init() {
+ if (!isFoldable) {
+ return
+ }
+ deviceStateManager.registerCallback(uiBgExecutor, foldStateListener)
+ screenLifecycle.addObserver(this)
+ }
+
+ /**
+ * To be called when the screen becomes visible.
+ *
+ * This is safe to call also when unsure whether the device is not a foldable, as it emits the
+ * end action event only if we previously received a fold state.
+ */
+ override fun onScreenTurnedOn() {
+ if (folded == false) {
+ latencyTracker.onActionEnd(LatencyTracker.ACTION_SWITCH_DISPLAY_UNFOLD)
+ }
+ }
+
+ private fun onFoldEvent(folded: Boolean) {
+ if (this.folded != folded) {
+ this.folded = folded
+ if (!folded) { // unfolding started
+ latencyTracker.onActionStart(LatencyTracker.ACTION_SWITCH_DISPLAY_UNFOLD)
+ }
+ }
+ }
+
+ private inner class FoldStateListener(context: Context) :
+ DeviceStateManager.FoldStateListener(context, { onFoldEvent(it) })
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java b/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java
index a7e9cdbf1a18..8b6e982be55b 100644
--- a/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java
@@ -23,7 +23,6 @@ import com.android.systemui.statusbar.policy.CallbackController;
import org.jetbrains.annotations.NotNull;
-import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
@@ -38,7 +37,7 @@ import javax.inject.Inject;
public class Monitor implements CallbackController<Monitor.Callback> {
private final String mTag = getClass().getSimpleName();
- private final ArrayList<WeakReference<Callback>> mCallbacks = new ArrayList<>();
+ private final ArrayList<Callback> mCallbacks = new ArrayList<>();
// Set of all conditions that need to be monitored.
private final Set<Condition> mConditions;
@@ -66,9 +65,9 @@ public class Monitor implements CallbackController<Monitor.Callback> {
mAllConditionsMet = newAllConditionsMet;
// Updates all callbacks.
- final Iterator<WeakReference<Callback>> iterator = mCallbacks.iterator();
+ final Iterator<Callback> iterator = mCallbacks.iterator();
while (iterator.hasNext()) {
- final Callback callback = iterator.next().get();
+ final Callback callback = iterator.next();
if (callback == null) {
iterator.remove();
} else {
@@ -78,7 +77,7 @@ public class Monitor implements CallbackController<Monitor.Callback> {
};
@Inject
- public Monitor(Set<Condition> conditions) {
+ public Monitor(Set<Condition> conditions, Set<Callback> callbacks) {
mConditions = conditions;
// If there is no condition, give green pass.
@@ -89,12 +88,20 @@ public class Monitor implements CallbackController<Monitor.Callback> {
// Initializes the conditions map and registers a callback for each condition.
mConditions.forEach((condition -> mConditionsMap.put(condition, false)));
+
+ if (callbacks == null) {
+ return;
+ }
+
+ for (Callback callback : callbacks) {
+ addCallback(callback);
+ }
}
@Override
public void addCallback(@NotNull Callback callback) {
if (shouldLog()) Log.d(mTag, "adding callback");
- mCallbacks.add(new WeakReference<>(callback));
+ mCallbacks.add(callback);
// Updates the callback immediately.
callback.onConditionsChanged(mAllConditionsMet);
@@ -109,9 +116,9 @@ public class Monitor implements CallbackController<Monitor.Callback> {
@Override
public void removeCallback(@NotNull Callback callback) {
if (shouldLog()) Log.d(mTag, "removing callback");
- final Iterator<WeakReference<Callback>> iterator = mCallbacks.iterator();
+ final Iterator<Callback> iterator = mCallbacks.iterator();
while (iterator.hasNext()) {
- final Callback cb = iterator.next().get();
+ final Callback cb = iterator.next();
if (cb == null || cb == callback) {
iterator.remove();
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalConditionsMonitor.java b/packages/SystemUI/src/com/android/systemui/util/condition/dagger/MonitorComponent.java
index 1197816e24d2..fc67973fe278 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalConditionsMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/condition/dagger/MonitorComponent.java
@@ -14,29 +14,33 @@
* limitations under the License.
*/
-package com.android.systemui.communal.conditions;
+package com.android.systemui.util.condition.dagger;
-
-import static com.android.systemui.communal.dagger.CommunalModule.COMMUNAL_CONDITIONS;
-
-import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.util.condition.Condition;
import com.android.systemui.util.condition.Monitor;
import java.util.Set;
-import javax.inject.Inject;
-import javax.inject.Named;
+import dagger.BindsInstance;
+import dagger.Subcomponent;
/**
- * A concrete implementation of {@Monitor} with conditions for monitoring when communal mode should
- * be enabled.
+ * Component for {@link Monitor}.
*/
-@SysUISingleton
-public class CommunalConditionsMonitor extends Monitor {
- @Inject
- public CommunalConditionsMonitor(
- @Named(COMMUNAL_CONDITIONS) Set<Condition> communalConditions) {
- super(communalConditions);
+@Subcomponent
+public interface MonitorComponent {
+ /**
+ * Factory for {@link MonitorComponent}.
+ */
+ @Subcomponent.Factory
+ interface Factory {
+ MonitorComponent create(@BindsInstance Set<Condition> conditions,
+ @BindsInstance Set<Monitor.Callback> callbacks);
}
+
+ /**
+ * Provides {@link Monitor}.
+ * @return
+ */
+ Monitor getMonitor();
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java b/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java
index 981bf01164e3..7892d6eec98d 100644
--- a/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java
@@ -18,6 +18,7 @@ package com.android.systemui.util.dagger;
import com.android.systemui.util.RingerModeTracker;
import com.android.systemui.util.RingerModeTrackerImpl;
+import com.android.systemui.util.condition.dagger.MonitorComponent;
import com.android.systemui.util.wrapper.UtilWrapperModule;
import dagger.Binds;
@@ -26,6 +27,9 @@ import dagger.Module;
/** Dagger Module for code in the util package. */
@Module(includes = {
UtilWrapperModule.class
+ },
+ subcomponents = {
+ MonitorComponent.class,
})
public interface UtilModule {
/** */
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index e967033b69a2..74e0f4002026 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -264,7 +264,7 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
reset(mView);
observer.onChange(true);
mExecutor.runAllReady();
- verify(mView).switchToClock(KeyguardClockSwitch.SMALL);
+ verify(mView).switchToClock(KeyguardClockSwitch.SMALL, /* animate */ true);
}
private void verifyAttachment(VerificationMode times) {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index e4336fe07dbb..8717a0eaf57f 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -253,8 +253,8 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
}
@Test
- public void switchingToBigClock_makesSmallClockDisappear() {
- mKeyguardClockSwitch.switchToClock(LARGE);
+ public void switchingToBigClockWithAnimation_makesSmallClockDisappear() {
+ mKeyguardClockSwitch.switchToClock(LARGE, /* animate */ true);
mKeyguardClockSwitch.mClockInAnim.end();
mKeyguardClockSwitch.mClockOutAnim.end();
@@ -265,8 +265,17 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
}
@Test
- public void switchingToSmallClock_makesBigClockDisappear() {
- mKeyguardClockSwitch.switchToClock(SMALL);
+ public void switchingToBigClockNoAnimation_makesSmallClockDisappear() {
+ mKeyguardClockSwitch.switchToClock(LARGE, /* animate */ false);
+
+ assertThat(mLargeClockFrame.getAlpha()).isEqualTo(1);
+ assertThat(mLargeClockFrame.getVisibility()).isEqualTo(VISIBLE);
+ assertThat(mClockFrame.getAlpha()).isEqualTo(0);
+ }
+
+ @Test
+ public void switchingToSmallClockWithAnimation_makesBigClockDisappear() {
+ mKeyguardClockSwitch.switchToClock(SMALL, /* animate */ true);
mKeyguardClockSwitch.mClockInAnim.end();
mKeyguardClockSwitch.mClockOutAnim.end();
@@ -279,8 +288,19 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
}
@Test
+ public void switchingToSmallClockNoAnimation_makesBigClockDisappear() {
+ mKeyguardClockSwitch.switchToClock(SMALL, false);
+
+ assertThat(mClockFrame.getAlpha()).isEqualTo(1);
+ assertThat(mClockFrame.getVisibility()).isEqualTo(VISIBLE);
+ // only big clock is removed at switch
+ assertThat(mLargeClockFrame.getParent()).isNull();
+ assertThat(mLargeClockFrame.getAlpha()).isEqualTo(0);
+ }
+
+ @Test
public void switchingToBigClock_returnsTrueOnlyWhenItWasNotVisibleBefore() {
- assertThat(mKeyguardClockSwitch.switchToClock(LARGE)).isTrue();
- assertThat(mKeyguardClockSwitch.switchToClock(LARGE)).isFalse();
+ assertThat(mKeyguardClockSwitch.switchToClock(LARGE, /* animate */ true)).isTrue();
+ assertThat(mKeyguardClockSwitch.switchToClock(LARGE, /* animate */ true)).isFalse();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index 77762859de18..24b01e079b42 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -244,7 +244,7 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
ViewGroup userSwitcher = mKeyguardSecurityContainer.findViewById(
R.id.keyguard_bouncer_user_switcher);
assertThat(((FrameLayout.LayoutParams) userSwitcher.getLayoutParams()).gravity)
- .isEqualTo(Gravity.LEFT | Gravity.TOP);
+ .isEqualTo(Gravity.LEFT | Gravity.CENTER_VERTICAL);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index ef9b850c99c2..70792cfee301 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -669,7 +669,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback
.onAuthenticationError(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT, "");
- verify(mLockPatternUtils, never()).requireStrongAuth(anyInt(), anyInt());
+ verify(mLockPatternUtils).requireStrongAuth(anyInt(), anyInt());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalManagerUpdaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalManagerUpdaterTest.java
index 9a9b7c4f62eb..4a29ada8a998 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalManagerUpdaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalManagerUpdaterTest.java
@@ -28,8 +28,8 @@ import android.testing.AndroidTestingRunner;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.communal.conditions.CommunalConditionsMonitor;
import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.condition.Monitor;
import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
@@ -45,7 +45,7 @@ public class CommunalManagerUpdaterTest extends SysuiTestCase {
@Mock
private CommunalManager mCommunalManager;
@Mock
- private CommunalConditionsMonitor mCommunalConditionsMonitor;
+ private Monitor mCommunalConditionsMonitor;
private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
@@ -55,7 +55,7 @@ public class CommunalManagerUpdaterTest extends SysuiTestCase {
mContext.addMockSystemService(CommunalManager.class, mCommunalManager);
doAnswer(invocation -> {
- final CommunalConditionsMonitor.Callback callback = invocation.getArgument(0);
+ final Monitor.Callback callback = invocation.getArgument(0);
callback.onConditionsChanged(true);
return null;
}).when(mCommunalConditionsMonitor).addCallback(any());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java
index 409dd940ceb6..df1cc766d162 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java
@@ -31,8 +31,8 @@ import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.communal.conditions.CommunalConditionsMonitor;
import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.condition.Monitor;
import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
@@ -49,9 +49,9 @@ import java.lang.ref.WeakReference;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class CommunalSourceMonitorTest extends SysuiTestCase {
- @Mock private CommunalConditionsMonitor mCommunalConditionsMonitor;
+ @Mock private Monitor mCommunalConditionsMonitor;
- @Captor private ArgumentCaptor<CommunalConditionsMonitor.Callback> mConditionsCallbackCaptor;
+ @Captor private ArgumentCaptor<Monitor.Callback> mConditionsCallbackCaptor;
private CommunalSourceMonitor mCommunalSourceMonitor;
private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
@@ -156,7 +156,7 @@ public class CommunalSourceMonitorTest extends SysuiTestCase {
private void setConditionsMet(boolean value) {
mExecutor.runAllReady();
verify(mCommunalConditionsMonitor).addCallback(mConditionsCallbackCaptor.capture());
- final CommunalConditionsMonitor.Callback conditionsCallback =
+ final Monitor.Callback conditionsCallback =
mConditionsCallbackCaptor.getValue();
conditionsCallback.onConditionsChanged(value);
mExecutor.runAllReady();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
index 873f7a45571b..55af51d3fddf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
@@ -45,6 +45,8 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.tuner.TunerService;
+import com.android.systemui.unfold.FoldAodAnimationController;
+import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.util.wakelock.WakeLockFake;
import org.junit.After;
@@ -54,6 +56,8 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Optional;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
public class DozeUiTest extends SysuiTestCase {
@@ -79,6 +83,10 @@ public class DozeUiTest extends SysuiTestCase {
@Mock
private StatusBarStateController mStatusBarStateController;
@Mock
+ private FoldAodAnimationController mFoldAodAnimationController;
+ @Mock
+ private SysUIUnfoldComponent mSysUIUnfoldComponent;
+ @Mock
private ConfigurationController mConfigurationController;
@Before
@@ -90,9 +98,13 @@ public class DozeUiTest extends SysuiTestCase {
mWakeLock = new WakeLockFake();
mHandler = mHandlerThread.getThreadHandler();
+ when(mSysUIUnfoldComponent.getFoldAodAnimationController())
+ .thenReturn(mFoldAodAnimationController);
+
mDozeUi = new DozeUi(mContext, mAlarmManager, mWakeLock, mHost, mHandler,
mDozeParameters, mKeyguardUpdateMonitor, mDozeLog, mTunerService,
- mStatusBarStateController, mConfigurationController);
+ mStatusBarStateController, Optional.of(mSysUIUnfoldComponent),
+ mConfigurationController);
mDozeUi.setDozeMachine(mMachine);
}
@@ -121,6 +133,7 @@ public class DozeUiTest extends SysuiTestCase {
reset(mHost);
when(mDozeParameters.getAlwaysOn()).thenReturn(false);
when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(false);
+ when(mDozeParameters.shouldAnimateDozingChange()).thenReturn(true);
mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(false);
verify(mHost).setAnimateScreenOff(eq(false));
@@ -131,6 +144,7 @@ public class DozeUiTest extends SysuiTestCase {
reset(mHost);
when(mDozeParameters.getAlwaysOn()).thenReturn(true);
when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(false);
+ when(mDozeParameters.shouldAnimateDozingChange()).thenReturn(true);
// Take over when the keyguard is visible.
mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(true);
@@ -142,6 +156,18 @@ public class DozeUiTest extends SysuiTestCase {
}
@Test
+ public void propagatesAnimateScreenOff_alwaysOn_shouldAnimateDozingChangeIsFalse() {
+ reset(mHost);
+ when(mDozeParameters.getAlwaysOn()).thenReturn(true);
+ when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(false);
+ when(mDozeParameters.shouldAnimateDozingChange()).thenReturn(false);
+
+ // Take over when the keyguard is visible.
+ mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(true);
+ verify(mHost).setAnimateScreenOff(eq(false));
+ }
+
+ @Test
public void neverAnimateScreenOff_whenNotSupported() {
// Re-initialize DozeParameters saying that the display requires blanking.
reset(mDozeParameters);
@@ -149,7 +175,8 @@ public class DozeUiTest extends SysuiTestCase {
when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(true);
mDozeUi = new DozeUi(mContext, mAlarmManager, mWakeLock, mHost, mHandler,
mDozeParameters, mKeyguardUpdateMonitor, mDozeLog, mTunerService,
- mStatusBarStateController, mConfigurationController);
+ mStatusBarStateController, Optional.of(mSysUIUnfoldComponent),
+ mConfigurationController);
mDozeUi.setDozeMachine(mMachine);
// Never animate if display doesn't support it.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
index 53bfeee9135a..904ee91c2642 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
@@ -37,6 +37,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.SysuiTestableContext;
+import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.utils.leaks.LeakCheckedTest;
@@ -78,16 +79,41 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
@Mock
DreamOverlayStateController mDreamOverlayStateController;
+ @Mock
+ DreamOverlayComponent.Factory mDreamOverlayStatusBarViewComponentFactory;
+
+ @Mock
+ DreamOverlayComponent mDreamOverlayComponent;
+
+ @Mock
+ DreamOverlayStatusBarViewController mDreamOverlayStatusBarViewController;
+
+ @Mock
+ DreamOverlayContainerView mDreamOverlayContainerView;
+
+ @Mock
+ ViewGroup mDreamOverlayContentView;
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mContext.addMockSystemService(WindowManager.class, mWindowManager);
+
+ when(mDreamOverlayComponent.getDreamOverlayContentView())
+ .thenReturn(mDreamOverlayContentView);
+ when(mDreamOverlayComponent.getDreamOverlayContainerView())
+ .thenReturn(mDreamOverlayContainerView);
+ when(mDreamOverlayComponent.getDreamOverlayStatusBarViewController())
+ .thenReturn(mDreamOverlayStatusBarViewController);
+ when(mDreamOverlayStatusBarViewComponentFactory.create())
+ .thenReturn(mDreamOverlayComponent);
+
}
@Test
public void testInteraction() throws Exception {
final DreamOverlayService service = new DreamOverlayService(mContext, mMainExecutor,
- mDreamOverlayStateController);
+ mDreamOverlayStateController, mDreamOverlayStatusBarViewComponentFactory);
final IBinder proxy = service.onBind(new Intent());
final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
clearInvocations(mWindowManager);
@@ -128,7 +154,7 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
@Test
public void testListening() throws Exception {
final DreamOverlayService service = new DreamOverlayService(mContext, mMainExecutor,
- mDreamOverlayStateController);
+ mDreamOverlayStateController, mDreamOverlayStatusBarViewComponentFactory);
final IBinder proxy = service.onBind(new Intent());
final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
@@ -150,4 +176,35 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
// Verify provider is asked to create overlay.
verify(mProvider).onCreateOverlay(any(), any(), any());
}
+
+ @Test
+ public void testDreamOverlayStatusBarViewControllerInitialized() throws Exception {
+ final DreamOverlayService service = new DreamOverlayService(mContext, mMainExecutor,
+ mDreamOverlayStateController, mDreamOverlayStatusBarViewComponentFactory);
+
+ final IBinder proxy = service.onBind(new Intent());
+ final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
+
+ // Inform the overlay service of dream starting.
+ overlay.startDream(mWindowParams, mDreamOverlayCallback);
+ mMainExecutor.runAllReady();
+
+ verify(mDreamOverlayStatusBarViewController).init();
+ }
+
+ @Test
+ public void testRootViewAttachListenerIsAddedToDreamOverlayContentView() throws Exception {
+ final DreamOverlayService service = new DreamOverlayService(mContext, mMainExecutor,
+ mDreamOverlayStateController, mDreamOverlayStatusBarViewComponentFactory);
+
+ final IBinder proxy = service.onBind(new Intent());
+ final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
+
+ // Inform the overlay service of dream starting.
+ overlay.startDream(mWindowParams, mDreamOverlayCallback);
+ mMainExecutor.runAllReady();
+
+ verify(mDreamOverlayContentView).addOnAttachStateChangeListener(
+ any(View.OnAttachStateChangeListener.class));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
new file mode 100644
index 000000000000..7f72dda441d2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.battery.BatteryMeterViewController;
+import com.android.systemui.statusbar.policy.BatteryController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
+
+ @Mock
+ DreamOverlayStatusBarView mView;
+ @Mock
+ BatteryController mBatteryController;
+ @Mock
+ BatteryMeterViewController mBatteryMeterViewController;
+ @Mock
+ ConnectivityManager mConnectivityManager;
+ @Mock
+ NetworkCapabilities mNetworkCapabilities;
+ @Mock
+ Network mNetwork;
+
+ DreamOverlayStatusBarViewController mController;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mController = new DreamOverlayStatusBarViewController(
+ mContext, mView, mBatteryController, mBatteryMeterViewController,
+ mConnectivityManager);
+ }
+
+ @Test
+ public void testOnInitInitializesControllers() {
+ mController.onInit();
+ verify(mBatteryMeterViewController).init();
+ }
+
+ @Test
+ public void testOnViewAttachedAddsBatteryControllerCallback() {
+ mController.onViewAttached();
+ verify(mBatteryController)
+ .addCallback(any(BatteryController.BatteryStateChangeCallback.class));
+ }
+
+ @Test
+ public void testOnViewAttachedRegistersNetworkCallback() {
+ mController.onViewAttached();
+ verify(mConnectivityManager)
+ .registerNetworkCallback(any(NetworkRequest.class), any(
+ ConnectivityManager.NetworkCallback.class));
+ }
+
+ @Test
+ public void testOnViewAttachedShowsWifiStatusWhenWifiUnavailable() {
+ when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
+ .thenReturn(false);
+ when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities);
+ mController.onViewAttached();
+ verify(mView).showWifiStatus(true);
+ }
+
+ @Test
+ public void testOnViewAttachedHidesWifiStatusWhenWifiAvailable() {
+ when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
+ .thenReturn(true);
+ when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities);
+ mController.onViewAttached();
+ verify(mView).showWifiStatus(false);
+ }
+
+ @Test
+ public void testOnViewAttachedShowsWifiStatusWhenNetworkCapabilitiesUnavailable() {
+ when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(null);
+ mController.onViewAttached();
+ verify(mView).showWifiStatus(true);
+ }
+
+ @Test
+ public void testOnViewDetachedRemovesBatteryControllerCallback() {
+ mController.onViewDetached();
+ verify(mBatteryController)
+ .removeCallback(any(BatteryController.BatteryStateChangeCallback.class));
+ }
+
+ @Test
+ public void testOnViewDetachedUnregistersNetworkCallback() {
+ mController.onViewDetached();
+ verify(mConnectivityManager)
+ .unregisterNetworkCallback(any(ConnectivityManager.NetworkCallback.class));
+ }
+
+ @Test
+ public void testBatteryPercentTextShownWhenBatteryLevelChangesWhileCharging() {
+ final ArgumentCaptor<BatteryController.BatteryStateChangeCallback> callbackCapture =
+ ArgumentCaptor.forClass(BatteryController.BatteryStateChangeCallback.class);
+ mController.onViewAttached();
+ verify(mBatteryController).addCallback(callbackCapture.capture());
+ callbackCapture.getValue().onBatteryLevelChanged(1, true, true);
+ verify(mView).showBatteryPercentText(true);
+ }
+
+ @Test
+ public void testBatteryPercentTextHiddenWhenBatteryLevelChangesWhileNotCharging() {
+ final ArgumentCaptor<BatteryController.BatteryStateChangeCallback> callbackCapture =
+ ArgumentCaptor.forClass(BatteryController.BatteryStateChangeCallback.class);
+ mController.onViewAttached();
+ verify(mBatteryController).addCallback(callbackCapture.capture());
+ callbackCapture.getValue().onBatteryLevelChanged(1, true, false);
+ verify(mView).showBatteryPercentText(false);
+ }
+
+ @Test
+ public void testWifiStatusHiddenWhenWifiBecomesAvailable() {
+ // Make sure wifi starts out unavailable when onViewAttached is called.
+ when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
+ .thenReturn(false);
+ when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities);
+ mController.onViewAttached();
+
+ final ArgumentCaptor<ConnectivityManager.NetworkCallback> callbackCapture =
+ ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
+ verify(mConnectivityManager).registerNetworkCallback(any(), callbackCapture.capture());
+ callbackCapture.getValue().onAvailable(mNetwork);
+ verify(mView).showWifiStatus(false);
+ }
+
+ @Test
+ public void testWifiStatusShownWhenWifiBecomesUnavailable() {
+ // Make sure wifi starts out available when onViewAttached is called.
+ when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
+ .thenReturn(true);
+ when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities);
+ mController.onViewAttached();
+
+ final ArgumentCaptor<ConnectivityManager.NetworkCallback> callbackCapture =
+ ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
+ verify(mConnectivityManager).registerNetworkCallback(any(), callbackCapture.capture());
+ callbackCapture.getValue().onLost(mNetwork);
+ verify(mView).showWifiStatus(true);
+ }
+
+ @Test
+ public void testWifiStatusHiddenWhenCapabilitiesChange() {
+ // Make sure wifi starts out unavailable when onViewAttached is called.
+ when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
+ .thenReturn(false);
+ when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities);
+ mController.onViewAttached();
+
+ final ArgumentCaptor<ConnectivityManager.NetworkCallback> callbackCapture =
+ ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
+ verify(mConnectivityManager).registerNetworkCallback(any(), callbackCapture.capture());
+ when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
+ .thenReturn(true);
+ callbackCapture.getValue().onCapabilitiesChanged(mNetwork, mNetworkCapabilities);
+ verify(mView).showWifiStatus(false);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
new file mode 100644
index 000000000000..f3043e934c8a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
@@ -0,0 +1,134 @@
+package com.android.systemui.keyguard
+
+import android.app.ActivityManager
+import android.app.WindowConfiguration
+import android.graphics.Point
+import android.graphics.Rect
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.RemoteAnimationTarget
+import android.view.SurfaceControl
+import android.view.SyncRtSurfaceTransactionApplier
+import android.view.ViewRootImpl
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardViewController
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController
+import com.android.systemui.statusbar.phone.BiometricUnlockController
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import junit.framework.Assert.assertEquals
+import junit.framework.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor.forClass
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+@SmallTest
+class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
+ private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
+
+ @Mock
+ private lateinit var keyguardViewMediator: KeyguardViewMediator
+ @Mock
+ private lateinit var keyguardStateController: KeyguardStateController
+ @Mock
+ private lateinit var keyguardViewController: KeyguardViewController
+ @Mock
+ private lateinit var smartspaceTransitionController: SmartspaceTransitionController
+ @Mock
+ private lateinit var featureFlags: FeatureFlags
+ @Mock
+ private lateinit var biometricUnlockController: BiometricUnlockController
+ @Mock
+ private lateinit var surfaceTransactionApplier: SyncRtSurfaceTransactionApplier
+
+ private lateinit var remoteAnimationTarget: RemoteAnimationTarget
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ keyguardUnlockAnimationController = KeyguardUnlockAnimationController(
+ context, keyguardStateController, { keyguardViewMediator }, keyguardViewController,
+ smartspaceTransitionController, featureFlags, biometricUnlockController
+ )
+
+ `when`(keyguardViewController.viewRootImpl).thenReturn(mock(ViewRootImpl::class.java))
+
+ // All of these fields are final, so we can't mock them, but are needed so that the surface
+ // appear amount setter doesn't short circuit.
+ remoteAnimationTarget = RemoteAnimationTarget(
+ 0, 0, null, false, Rect(), Rect(), 0, Point(), Rect(), Rect(),
+ mock(WindowConfiguration::class.java), false, mock(SurfaceControl::class.java), Rect(),
+ mock(ActivityManager.RunningTaskInfo::class.java), false)
+
+ // Set the surface applier to our mock so that we can verify the arguments passed to it.
+ // This applier does not have any side effects within the unlock animation controller, so
+ // this is a reasonable way to test.
+ keyguardUnlockAnimationController.surfaceTransactionApplier = surfaceTransactionApplier
+ }
+
+ /**
+ * If we're wake and unlocking, we are animating from the black/AOD screen to the app/launcher
+ * underneath. The LightRevealScrim will animate circularly from the fingerprint reader,
+ * revealing the app/launcher below. In this case, we want to make sure we are not animating the
+ * surface, or the user will see the wallpaper briefly as the app animates in.
+ */
+ @Test
+ fun noSurfaceAnimation_ifWakeAndUnlocking() {
+ `when`(biometricUnlockController.isWakeAndUnlock).thenReturn(true)
+
+ keyguardUnlockAnimationController.notifyStartKeyguardExitAnimation(
+ remoteAnimationTarget,
+ 0 /* startTime */,
+ false /* requestedShowSurfaceBehindKeyguard */
+ )
+
+ val captor = forClass(SyncRtSurfaceTransactionApplier.SurfaceParams::class.java)
+ verify(surfaceTransactionApplier, times(1)).scheduleApply(captor.capture())
+
+ val params = captor.value
+
+ // We expect that we've instantly set the surface behind to alpha = 1f, and have no
+ // transforms (translate, scale) on its matrix.
+ assertEquals(params.alpha, 1f)
+ assertTrue(params.matrix.isIdentity)
+
+ // Also expect we've immediately asked the keyguard view mediator to finish the remote
+ // animation.
+ verify(keyguardViewMediator, times(1)).onKeyguardExitRemoteAnimationFinished(
+ false /* cancelled */)
+
+ verifyNoMoreInteractions(surfaceTransactionApplier)
+ }
+
+ /**
+ * If we are not wake and unlocking, we expect the unlock animation to play normally.
+ */
+ @Test
+ fun surfaceAnimation_ifNotWakeAndUnlocking() {
+ `when`(biometricUnlockController.isWakeAndUnlock).thenReturn(false)
+
+ keyguardUnlockAnimationController.notifyStartKeyguardExitAnimation(
+ remoteAnimationTarget,
+ 0 /* startTime */,
+ false /* requestedShowSurfaceBehindKeyguard */
+ )
+
+ // Make sure the animator was started.
+ assertTrue(keyguardUnlockAnimationController.surfaceBehindEntryAnimator.isRunning)
+
+ // Since the animation is running, we should not have finished the remote animation.
+ verify(keyguardViewMediator, times(0)).onKeyguardExitRemoteAnimationFinished(
+ false /* cancelled */)
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 8d329c56af36..27fcb11b8dfc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -58,6 +58,7 @@ import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.unfold.FoldAodAnimationController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
import com.android.systemui.util.DeviceConfigProxy;
@@ -69,7 +70,6 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
-import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -95,62 +95,40 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
private @Mock NavigationModeController mNavigationModeController;
private @Mock KeyguardDisplayManager mKeyguardDisplayManager;
private @Mock DozeParameters mDozeParameters;
- private @Mock Optional<SysUIUnfoldComponent> mSysUIUnfoldComponent;
- private @Mock Optional<UnfoldLightRevealOverlayAnimation> mUnfoldAnimationOptional;
+ private @Mock SysUIUnfoldComponent mSysUIUnfoldComponent;
private @Mock UnfoldLightRevealOverlayAnimation mUnfoldAnimation;
private @Mock SysuiStatusBarStateController mStatusBarStateController;
private @Mock KeyguardStateController mKeyguardStateController;
private @Mock NotificationShadeDepthController mNotificationShadeDepthController;
private @Mock KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
private @Mock ScreenOffAnimationController mScreenOffAnimationController;
+ private @Mock FoldAodAnimationController mFoldAodAnimationController;
private @Mock IKeyguardDrawnCallback mKeyguardDrawnCallback;
private @Mock InteractionJankMonitor mInteractionJankMonitor;
private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake();
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
+ private Optional<SysUIUnfoldComponent> mSysUiUnfoldComponentOptional;
+
private FalsingCollectorFake mFalsingCollector;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mFalsingCollector = new FalsingCollectorFake();
+ mSysUiUnfoldComponentOptional = Optional.of(mSysUIUnfoldComponent);
when(mLockPatternUtils.getDevicePolicyManager()).thenReturn(mDevicePolicyManager);
when(mPowerManager.newWakeLock(anyInt(), any())).thenReturn(mock(WakeLock.class));
- when(mSysUIUnfoldComponent.map(
- ArgumentMatchers.<Function<SysUIUnfoldComponent, UnfoldLightRevealOverlayAnimation>>
- any()))
- .thenReturn(mUnfoldAnimationOptional);
- when(mUnfoldAnimationOptional.isPresent()).thenReturn(true);
- when(mUnfoldAnimationOptional.get()).thenReturn(mUnfoldAnimation);
+ when(mSysUIUnfoldComponent.getUnfoldLightRevealOverlayAnimation())
+ .thenReturn(mUnfoldAnimation);
+ when(mSysUIUnfoldComponent.getFoldAodAnimationController())
+ .thenReturn(mFoldAodAnimationController);
+
when(mInteractionJankMonitor.begin(any(), anyInt())).thenReturn(true);
when(mInteractionJankMonitor.end(anyInt())).thenReturn(true);
- mViewMediator = new KeyguardViewMediator(
- mContext,
- mFalsingCollector,
- mLockPatternUtils,
- mBroadcastDispatcher,
- () -> mStatusBarKeyguardViewManager,
- mDismissCallbackRegistry,
- mUpdateMonitor,
- mDumpManager,
- mUiBgExecutor,
- mPowerManager,
- mTrustManager,
- mUserSwitcherController,
- mDeviceConfig,
- mNavigationModeController,
- mKeyguardDisplayManager,
- mDozeParameters,
- mSysUIUnfoldComponent,
- mStatusBarStateController,
- mKeyguardStateController,
- () -> mKeyguardUnlockAnimationController,
- mScreenOffAnimationController,
- () -> mNotificationShadeDepthController,
- mInteractionJankMonitor);
- mViewMediator.start();
+ createAndStartViewMediator();
}
@Test
@@ -179,6 +157,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
mViewMediator.onScreenTurningOn(mKeyguardDrawnCallback);
TestableLooper.get(this).processAllMessages();
onUnfoldOverlayReady();
+ onFoldAodReady();
// Should be called when both unfold overlay and keyguard drawn ready
verify(mKeyguardDrawnCallback).onDrawn();
@@ -188,7 +167,8 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public void testUnfoldTransitionDisabledDrawnTasksReady_onScreenTurningOn_callsDrawnCallback()
throws RemoteException {
- when(mUnfoldAnimationOptional.isPresent()).thenReturn(false);
+ mSysUiUnfoldComponentOptional = Optional.empty();
+ createAndStartViewMediator();
mViewMediator.onScreenTurningOn(mKeyguardDrawnCallback);
TestableLooper.get(this).processAllMessages();
@@ -200,6 +180,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
@Test
public void testIsAnimatingScreenOff() {
when(mDozeParameters.shouldControlUnlockedScreenOff()).thenReturn(true);
+ when(mDozeParameters.shouldAnimateDozingChange()).thenReturn(true);
mViewMediator.onFinishedGoingToSleep(OFF_BECAUSE_OF_USER, false);
mViewMediator.setDozing(true);
@@ -244,4 +225,39 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
overlayReadyCaptor.getValue().run();
TestableLooper.get(this).processAllMessages();
}
+
+ private void onFoldAodReady() {
+ ArgumentCaptor<Runnable> ready = ArgumentCaptor.forClass(Runnable.class);
+ verify(mFoldAodAnimationController).onScreenTurningOn(ready.capture());
+ ready.getValue().run();
+ TestableLooper.get(this).processAllMessages();
+ }
+
+ private void createAndStartViewMediator() {
+ mViewMediator = new KeyguardViewMediator(
+ mContext,
+ mFalsingCollector,
+ mLockPatternUtils,
+ mBroadcastDispatcher,
+ () -> mStatusBarKeyguardViewManager,
+ mDismissCallbackRegistry,
+ mUpdateMonitor,
+ mDumpManager,
+ mUiBgExecutor,
+ mPowerManager,
+ mTrustManager,
+ mUserSwitcherController,
+ mDeviceConfig,
+ mNavigationModeController,
+ mKeyguardDisplayManager,
+ mDozeParameters,
+ mSysUiUnfoldComponentOptional,
+ mStatusBarStateController,
+ mKeyguardStateController,
+ () -> mKeyguardUnlockAnimationController,
+ mScreenOffAnimationController,
+ () -> mNotificationShadeDepthController,
+ mInteractionJankMonitor);
+ mViewMediator.start();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
index bf87a4a59c49..a3ffb2fe4b8d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
@@ -22,6 +22,7 @@ import android.testing.TestableLooper
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
+import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq
import com.android.systemui.keyguard.WakefulnessLifecycle
@@ -82,6 +83,8 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
@Mock
private lateinit var configurationController: ConfigurationController
+ @Mock
+ private lateinit var uniqueObjectHostView: UniqueObjectHostView
@Captor
private lateinit var wakefullnessObserver: ArgumentCaptor<(WakefulnessLifecycle.Observer)>
@Captor
@@ -94,6 +97,8 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
@Before
fun setup() {
+ context.getOrCreateTestableResources().addOverride(
+ R.bool.config_use_split_notification_shade, false)
mediaFrame = FrameLayout(context)
`when`(mediaCarouselController.mediaFrame).thenReturn(mediaFrame)
mediaHiearchyManager = MediaHierarchyManager(
@@ -124,7 +129,7 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
private fun setupHost(host: MediaHost, location: Int) {
`when`(host.location).thenReturn(location)
`when`(host.currentBounds).thenReturn(Rect())
- `when`(host.hostView).thenReturn(UniqueObjectHostView(context))
+ `when`(host.hostView).thenReturn(uniqueObjectHostView)
`when`(host.visible).thenReturn(true)
mediaHiearchyManager.register(host)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt
index 50d94dd6ca5f..87499172fc46 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt
@@ -23,8 +23,11 @@ import android.widget.TextView
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
+import com.google.common.util.concurrent.SettableFuture
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentCaptor
@@ -37,6 +40,11 @@ import org.mockito.MockitoAnnotations
@SmallTest
class MediaTttChipControllerTest : SysuiTestCase() {
+ private lateinit var fakeMainClock: FakeSystemClock
+ private lateinit var fakeMainExecutor: FakeExecutor
+ private lateinit var fakeBackgroundClock: FakeSystemClock
+ private lateinit var fakeBackgroundExecutor: FakeExecutor
+
private lateinit var mediaTttChipController: MediaTttChipController
@Mock
@@ -45,7 +53,13 @@ class MediaTttChipControllerTest : SysuiTestCase() {
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- mediaTttChipController = MediaTttChipController(context, windowManager)
+ fakeMainClock = FakeSystemClock()
+ fakeMainExecutor = FakeExecutor(fakeMainClock)
+ fakeBackgroundClock = FakeSystemClock()
+ fakeBackgroundExecutor = FakeExecutor(fakeBackgroundClock)
+ mediaTttChipController = MediaTttChipController(
+ context, windowManager, fakeMainExecutor, fakeBackgroundExecutor
+ )
}
@Test
@@ -89,40 +103,125 @@ class MediaTttChipControllerTest : SysuiTestCase() {
val chipView = getChipView()
assertThat(chipView.getChipText()).contains(DEVICE_NAME)
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
- assertThat(chipView.getUndoButtonVisibility()).isEqualTo(View.GONE)
+ assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
}
@Test
- fun transferInitiated_chipTextContainsDeviceName_loadingIcon_noUndo() {
- mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME))
+ fun transferInitiated_futureNotResolvedYet_loadingIcon_noUndo() {
+ val future: SettableFuture<Runnable?> = SettableFuture.create()
+ mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME, future))
+
+ // Don't resolve the future in any way and don't run our executors
+ // Assert we're still in the loading state
val chipView = getChipView()
assertThat(chipView.getChipText()).contains(DEVICE_NAME)
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
- assertThat(chipView.getUndoButtonVisibility()).isEqualTo(View.GONE)
+ assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
}
@Test
- fun transferSucceeded_chipTextContainsDeviceName_noLoadingIcon_undo() {
- mediaTttChipController.displayChip(TransferSucceeded(DEVICE_NAME))
+ fun transferInitiated_futureResolvedSuccessfully_switchesToTransferSucceeded() {
+ val future: SettableFuture<Runnable?> = SettableFuture.create()
+ val undoRunnable = Runnable { }
+
+ mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME, future))
+
+ future.set(undoRunnable)
+ fakeBackgroundExecutor.advanceClockToLast()
+ fakeBackgroundExecutor.runAllReady()
+ fakeMainExecutor.advanceClockToLast()
+ val numRun = fakeMainExecutor.runAllReady()
+
+ // Assert we ran the future callback
+ assertThat(numRun).isEqualTo(1)
+ // Assert that we've moved to the successful state
+ val chipView = getChipView()
+ assertThat(chipView.getChipText()).contains(DEVICE_NAME)
+ assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
+ assertThat(chipView.getUndoButton().visibility).isEqualTo(View.VISIBLE)
+ }
+
+ @Test
+ fun transferInitiated_futureCancelled_chipRemoved() {
+ val future: SettableFuture<Runnable?> = SettableFuture.create()
+
+ mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME, future))
+
+ future.cancel(true)
+ fakeBackgroundExecutor.advanceClockToLast()
+ fakeBackgroundExecutor.runAllReady()
+ fakeMainExecutor.advanceClockToLast()
+ val numRun = fakeMainExecutor.runAllReady()
+
+ // Assert we ran the future callback
+ assertThat(numRun).isEqualTo(1)
+ // Assert that we've hidden the chip
+ verify(windowManager).removeView(any())
+ }
+
+ @Test
+ fun transferInitiated_futureNotResolvedAfterTimeout_chipRemoved() {
+ val future: SettableFuture<Runnable?> = SettableFuture.create()
+ mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME, future))
+
+ // We won't set anything on the future, but we will still run the executors so that we're
+ // waiting on the future resolving. If we have a bug in our code, then this test will time
+ // out because we're waiting on the future indefinitely.
+ fakeBackgroundExecutor.advanceClockToLast()
+ fakeBackgroundExecutor.runAllReady()
+ fakeMainExecutor.advanceClockToLast()
+ val numRun = fakeMainExecutor.runAllReady()
+
+ // Assert we eventually decide to not wait for the future anymore
+ assertThat(numRun).isEqualTo(1)
+ // Assert we've hidden the chip
+ verify(windowManager).removeView(any())
+ }
+
+ @Test
+ fun transferSucceededNullUndoRunnable_chipTextContainsDeviceName_noLoadingIcon_noUndo() {
+ mediaTttChipController.displayChip(TransferSucceeded(DEVICE_NAME, undoRunnable = null))
+
+ val chipView = getChipView()
+ assertThat(chipView.getChipText()).contains(DEVICE_NAME)
+ assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
+ assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
+ }
+
+ @Test
+ fun transferSucceededWithUndoRunnable_chipTextContainsDeviceName_noLoadingIcon_undoWithClick() {
+ mediaTttChipController.displayChip(TransferSucceeded(DEVICE_NAME) { })
val chipView = getChipView()
assertThat(chipView.getChipText()).contains(DEVICE_NAME)
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
- assertThat(chipView.getUndoButtonVisibility()).isEqualTo(View.VISIBLE)
+ assertThat(chipView.getUndoButton().visibility).isEqualTo(View.VISIBLE)
+ assertThat(chipView.getUndoButton().hasOnClickListeners()).isTrue()
+ }
+
+ @Test
+ fun transferSucceededWithUndoRunnable_undoButtonClickRunsRunnable() {
+ var runnableRun = false
+ val runnable = Runnable { runnableRun = true }
+
+ mediaTttChipController.displayChip(TransferSucceeded(DEVICE_NAME, runnable))
+ getChipView().getUndoButton().performClick()
+
+ assertThat(runnableRun).isTrue()
}
@Test
fun changeFromCloserToTransferToTransferInitiated_loadingIconAppears() {
mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME))
- mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME))
+ mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME, TEST_FUTURE))
assertThat(getChipView().getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
}
@Test
fun changeFromTransferInitiatedToTransferSucceeded_loadingIconDisappears() {
- mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME))
+ mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME, TEST_FUTURE))
mediaTttChipController.displayChip(TransferSucceeded(DEVICE_NAME))
assertThat(getChipView().getLoadingIconVisibility()).isEqualTo(View.GONE)
@@ -130,10 +229,10 @@ class MediaTttChipControllerTest : SysuiTestCase() {
@Test
fun changeFromTransferInitiatedToTransferSucceeded_undoButtonAppears() {
- mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME))
- mediaTttChipController.displayChip(TransferSucceeded(DEVICE_NAME))
+ mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME, TEST_FUTURE))
+ mediaTttChipController.displayChip(TransferSucceeded(DEVICE_NAME) { })
- assertThat(getChipView().getUndoButtonVisibility()).isEqualTo(View.VISIBLE)
+ assertThat(getChipView().getUndoButton().visibility).isEqualTo(View.VISIBLE)
}
@Test
@@ -141,7 +240,7 @@ class MediaTttChipControllerTest : SysuiTestCase() {
mediaTttChipController.displayChip(TransferSucceeded(DEVICE_NAME))
mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME))
- assertThat(getChipView().getUndoButtonVisibility()).isEqualTo(View.GONE)
+ assertThat(getChipView().getUndoButton().visibility).isEqualTo(View.GONE)
}
private fun LinearLayout.getChipText(): String =
@@ -150,8 +249,7 @@ class MediaTttChipControllerTest : SysuiTestCase() {
private fun LinearLayout.getLoadingIconVisibility(): Int =
this.requireViewById<View>(R.id.loading).visibility
- private fun LinearLayout.getUndoButtonVisibility(): Int =
- this.requireViewById<View>(R.id.undo).visibility
+ private fun LinearLayout.getUndoButton(): View = this.requireViewById(R.id.undo)
private fun getChipView(): LinearLayout {
val viewCaptor = ArgumentCaptor.forClass(View::class.java)
@@ -161,3 +259,6 @@ class MediaTttChipControllerTest : SysuiTestCase() {
}
private const val DEVICE_NAME = "My Tablet"
+// Use a settable future that hasn't yet been set so that we don't immediately switch to the success
+// state.
+private val TEST_FUTURE: SettableFuture<Runnable?> = SettableFuture.create()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
index 4b85fa9fb75c..9b2d3eb17568 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
@@ -20,7 +20,9 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.time.FakeSystemClock
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
@@ -46,7 +48,9 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
mediaTttCommandLineHelper =
- MediaTttCommandLineHelper(commandRegistry, mediaTttChipController)
+ MediaTttCommandLineHelper(
+ commandRegistry, mediaTttChipController, FakeExecutor(FakeSystemClock())
+ )
}
@Test(expected = IllegalStateException::class)
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 9d2541c0150f..3e8e8748a679 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.pip.Pip;
import org.junit.After;
import org.junit.Before;
@@ -55,6 +56,8 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Optional;
+
/** atest NavigationBarControllerTest */
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@@ -88,7 +91,8 @@ public class NavigationBarControllerTest extends SysuiTestCase {
mNavigationBarFactory,
mock(DumpManager.class),
mock(AutoHideController.class),
- mock(LightBarController.class)));
+ mock(LightBarController.class),
+ Optional.of(mock(Pip.class))));
initializeNavigationBars();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
new file mode 100644
index 000000000000..af33dafb6107
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tileimpl
+
+import android.content.ComponentName
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.external.CustomTile
+import com.android.systemui.qs.tiles.AirplaneModeTile
+import com.android.systemui.qs.tiles.AlarmTile
+import com.android.systemui.qs.tiles.BatterySaverTile
+import com.android.systemui.qs.tiles.BluetoothTile
+import com.android.systemui.qs.tiles.CameraToggleTile
+import com.android.systemui.qs.tiles.CastTile
+import com.android.systemui.qs.tiles.CellularTile
+import com.android.systemui.qs.tiles.ColorInversionTile
+import com.android.systemui.qs.tiles.DataSaverTile
+import com.android.systemui.qs.tiles.DeviceControlsTile
+import com.android.systemui.qs.tiles.DndTile
+import com.android.systemui.qs.tiles.FgsManagerTile
+import com.android.systemui.qs.tiles.FlashlightTile
+import com.android.systemui.qs.tiles.HotspotTile
+import com.android.systemui.qs.tiles.InternetTile
+import com.android.systemui.qs.tiles.LocationTile
+import com.android.systemui.qs.tiles.MicrophoneToggleTile
+import com.android.systemui.qs.tiles.NfcTile
+import com.android.systemui.qs.tiles.NightDisplayTile
+import com.android.systemui.qs.tiles.OneHandedModeTile
+import com.android.systemui.qs.tiles.QRCodeScannerTile
+import com.android.systemui.qs.tiles.QuickAccessWalletTile
+import com.android.systemui.qs.tiles.ReduceBrightColorsTile
+import com.android.systemui.qs.tiles.RotationLockTile
+import com.android.systemui.qs.tiles.ScreenRecordTile
+import com.android.systemui.qs.tiles.UiModeNightTile
+import com.android.systemui.qs.tiles.UserTile
+import com.android.systemui.qs.tiles.WifiTile
+import com.android.systemui.qs.tiles.WorkModeTile
+import com.android.systemui.util.leak.GarbageMonitor
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Answers
+import org.mockito.Mock
+import org.mockito.Mockito.inOrder
+import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
+
+private val specMap = mapOf(
+ "wifi" to WifiTile::class.java,
+ "internet" to InternetTile::class.java,
+ "bt" to BluetoothTile::class.java,
+ "cell" to CellularTile::class.java,
+ "dnd" to DndTile::class.java,
+ "inversion" to ColorInversionTile::class.java,
+ "airplane" to AirplaneModeTile::class.java,
+ "work" to WorkModeTile::class.java,
+ "rotation" to RotationLockTile::class.java,
+ "flashlight" to FlashlightTile::class.java,
+ "location" to LocationTile::class.java,
+ "cast" to CastTile::class.java,
+ "hotspot" to HotspotTile::class.java,
+ "user" to UserTile::class.java,
+ "battery" to BatterySaverTile::class.java,
+ "saver" to DataSaverTile::class.java,
+ "night" to NightDisplayTile::class.java,
+ "nfc" to NfcTile::class.java,
+ "dark" to UiModeNightTile::class.java,
+ "screenrecord" to ScreenRecordTile::class.java,
+ "reduce_brightness" to ReduceBrightColorsTile::class.java,
+ "cameratoggle" to CameraToggleTile::class.java,
+ "mictoggle" to MicrophoneToggleTile::class.java,
+ "controls" to DeviceControlsTile::class.java,
+ "alarm" to AlarmTile::class.java,
+ "wallet" to QuickAccessWalletTile::class.java,
+ "qr_code_scanner" to QRCodeScannerTile::class.java,
+ "onehanded" to OneHandedModeTile::class.java,
+ "fgsmanager" to FgsManagerTile::class.java
+)
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class QSFactoryImplTest : SysuiTestCase() {
+
+ @Mock private lateinit var qsHost: QSHost
+ @Mock(answer = Answers.RETURNS_SELF) private lateinit var customTileBuilder: CustomTile.Builder
+ @Mock private lateinit var customTile: CustomTile
+
+ @Mock private lateinit var wifiTile: WifiTile
+ @Mock private lateinit var internetTile: InternetTile
+ @Mock private lateinit var bluetoothTile: BluetoothTile
+ @Mock private lateinit var cellularTile: CellularTile
+ @Mock private lateinit var dndTile: DndTile
+ @Mock private lateinit var colorInversionTile: ColorInversionTile
+ @Mock private lateinit var airplaneTile: AirplaneModeTile
+ @Mock private lateinit var workTile: WorkModeTile
+ @Mock private lateinit var rotationTile: RotationLockTile
+ @Mock private lateinit var flashlightTile: FlashlightTile
+ @Mock private lateinit var locationTile: LocationTile
+ @Mock private lateinit var castTile: CastTile
+ @Mock private lateinit var hotspotTile: HotspotTile
+ @Mock private lateinit var userTile: UserTile
+ @Mock private lateinit var batterySaverTile: BatterySaverTile
+ @Mock private lateinit var dataSaverTile: DataSaverTile
+ @Mock private lateinit var nightDisplayTile: NightDisplayTile
+ @Mock private lateinit var nfcTile: NfcTile
+ @Mock private lateinit var memoryTile: GarbageMonitor.MemoryTile
+ @Mock private lateinit var darkModeTile: UiModeNightTile
+ @Mock private lateinit var screenRecordTile: ScreenRecordTile
+ @Mock private lateinit var reduceBrightColorsTile: ReduceBrightColorsTile
+ @Mock private lateinit var cameraToggleTile: CameraToggleTile
+ @Mock private lateinit var microphoneToggleTile: MicrophoneToggleTile
+ @Mock private lateinit var deviceControlsTile: DeviceControlsTile
+ @Mock private lateinit var alarmTile: AlarmTile
+ @Mock private lateinit var quickAccessWalletTile: QuickAccessWalletTile
+ @Mock private lateinit var qrCodeScannerTile: QRCodeScannerTile
+ @Mock private lateinit var oneHandedModeTile: OneHandedModeTile
+ @Mock private lateinit var fgsManagerTile: FgsManagerTile
+
+ private lateinit var factory: QSFactoryImpl
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ whenever(qsHost.context).thenReturn(mContext)
+ whenever(qsHost.userContext).thenReturn(mContext)
+ whenever(customTileBuilder.build()).thenReturn(customTile)
+
+ factory = QSFactoryImpl(
+ { qsHost },
+ { customTileBuilder },
+ { wifiTile },
+ { internetTile },
+ { bluetoothTile },
+ { cellularTile },
+ { dndTile },
+ { colorInversionTile },
+ { airplaneTile },
+ { workTile },
+ { rotationTile },
+ { flashlightTile },
+ { locationTile },
+ { castTile },
+ { hotspotTile },
+ { userTile },
+ { batterySaverTile },
+ { dataSaverTile },
+ { nightDisplayTile },
+ { nfcTile },
+ { memoryTile },
+ { darkModeTile },
+ { screenRecordTile },
+ { reduceBrightColorsTile },
+ { cameraToggleTile },
+ { microphoneToggleTile },
+ { deviceControlsTile },
+ { alarmTile },
+ { quickAccessWalletTile },
+ { qrCodeScannerTile },
+ { oneHandedModeTile },
+ { fgsManagerTile }
+ )
+ // When adding/removing tiles, fix also [specMap]
+ }
+
+ @Test
+ fun testCorrectTileClassStock() {
+ specMap.forEach { spec, klazz ->
+ assertThat(factory.createTile(spec)).isInstanceOf(klazz)
+ }
+ }
+
+ @Test
+ fun testCustomTileClass() {
+ val customSpec = CustomTile.toSpec(ComponentName("test", "test"))
+ assertThat(factory.createTile(customSpec)).isInstanceOf(CustomTile::class.java)
+ }
+
+ @Test
+ fun testBadTileNull() {
+ assertThat(factory.createTile("-432~")).isNull()
+ }
+
+ @Test
+ fun testTileInitializedAndStale() {
+ specMap.forEach { spec, _ ->
+ val tile = factory.createTile(spec) as QSTileImpl<*>
+ val inOrder = inOrder(tile)
+ inOrder.verify(tile).initialize()
+ inOrder.verify(tile).postStale()
+ }
+
+ val customSpec = CustomTile.toSpec(ComponentName("test", "test"))
+ val tile = factory.createTile(customSpec) as QSTileImpl<*>
+ val inOrder = inOrder(tile)
+ inOrder.verify(tile).initialize()
+ inOrder.verify(tile).postStale()
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
index 77946cf791aa..d3bb241baad4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
@@ -1,5 +1,7 @@
package com.android.systemui.qs.tiles.dialog;
+import static com.android.systemui.qs.tiles.dialog.InternetDialogController.MAX_WIFI_ENTRY_COUNT;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -83,7 +85,7 @@ public class InternetAdapterTest extends SysuiTestCase {
@Test
public void getItemCount_returnWifiEntriesCount() {
- for (int i = 0; i < InternetDialogController.MAX_WIFI_ENTRY_COUNT; i++) {
+ for (int i = 0; i < MAX_WIFI_ENTRY_COUNT; i++) {
mInternetAdapter.setWifiEntries(mWifiEntries, i /* wifiEntriesCount */);
assertThat(mInternetAdapter.getItemCount()).isEqualTo(i);
@@ -141,6 +143,60 @@ public class InternetAdapterTest extends SysuiTestCase {
}
@Test
+ public void setWifiEntries_wifiCountLessThenMaxCount_setWifiCount() {
+ final int wifiCount = MAX_WIFI_ENTRY_COUNT - 1;
+ mInternetAdapter.mMaxEntriesCount = MAX_WIFI_ENTRY_COUNT;
+
+ mInternetAdapter.setWifiEntries(mWifiEntries, wifiCount);
+
+ assertThat(mInternetAdapter.mWifiEntriesCount).isEqualTo(wifiCount);
+ }
+
+ @Test
+ public void setWifiEntries_wifiCountGreaterThenMaxCount_setMaxCount() {
+ final int wifiCount = MAX_WIFI_ENTRY_COUNT;
+ mInternetAdapter.mMaxEntriesCount = MAX_WIFI_ENTRY_COUNT - 1;
+
+ mInternetAdapter.setWifiEntries(mWifiEntries, wifiCount);
+
+ assertThat(mInternetAdapter.mWifiEntriesCount).isEqualTo(mInternetAdapter.mMaxEntriesCount);
+ }
+
+ @Test
+ public void setMaxEntriesCount_maxCountLessThenZero_doNothing() {
+ mInternetAdapter.mMaxEntriesCount = MAX_WIFI_ENTRY_COUNT;
+ final int maxCount = -1;
+
+ mInternetAdapter.setMaxEntriesCount(maxCount);
+
+ assertThat(mInternetAdapter.mMaxEntriesCount).isEqualTo(MAX_WIFI_ENTRY_COUNT);
+ }
+
+ @Test
+ public void setMaxEntriesCount_maxCountGreaterThenWifiCount_updateMaxCount() {
+ mInternetAdapter.mWifiEntriesCount = MAX_WIFI_ENTRY_COUNT - 2;
+ mInternetAdapter.mMaxEntriesCount = MAX_WIFI_ENTRY_COUNT;
+ final int maxCount = MAX_WIFI_ENTRY_COUNT - 1;
+
+ mInternetAdapter.setMaxEntriesCount(maxCount);
+
+ assertThat(mInternetAdapter.mWifiEntriesCount).isEqualTo(MAX_WIFI_ENTRY_COUNT - 2);
+ assertThat(mInternetAdapter.mMaxEntriesCount).isEqualTo(maxCount);
+ }
+
+ @Test
+ public void setMaxEntriesCount_maxCountLessThenWifiCount_updateBothCount() {
+ mInternetAdapter.mWifiEntriesCount = MAX_WIFI_ENTRY_COUNT;
+ mInternetAdapter.mMaxEntriesCount = MAX_WIFI_ENTRY_COUNT;
+ final int maxCount = MAX_WIFI_ENTRY_COUNT - 1;
+
+ mInternetAdapter.setMaxEntriesCount(maxCount);
+
+ assertThat(mInternetAdapter.mWifiEntriesCount).isEqualTo(maxCount);
+ assertThat(mInternetAdapter.mMaxEntriesCount).isEqualTo(maxCount);
+ }
+
+ @Test
public void viewHolderUpdateEndIcon_wifiConnected_updateGearIcon() {
mTestableResources.addOverride(GEAR_ICON_RES_ID, mGearIcon);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
index 47f6e5c420f9..0d6554103dac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
@@ -434,7 +434,6 @@ public class InternetDialogControllerTest extends SysuiTestCase {
@Test
public void onAccessPointsChanged_oneConnectedEntry_callbackConnectedEntryOnly() {
reset(mInternetDialogCallback);
- fakeAirplaneModeEnabled(true);
mAccessPoints.clear();
mAccessPoints.add(mConnectedEntry);
@@ -448,7 +447,6 @@ public class InternetDialogControllerTest extends SysuiTestCase {
@Test
public void onAccessPointsChanged_noConnectedEntryAndOneOther_callbackWifiEntriesOnly() {
reset(mInternetDialogCallback);
- fakeAirplaneModeEnabled(true);
mAccessPoints.clear();
mAccessPoints.add(mWifiEntry1);
@@ -463,7 +461,6 @@ public class InternetDialogControllerTest extends SysuiTestCase {
@Test
public void onAccessPointsChanged_oneConnectedEntryAndOneOther_callbackCorrectly() {
reset(mInternetDialogCallback);
- fakeAirplaneModeEnabled(true);
mAccessPoints.clear();
mAccessPoints.add(mConnectedEntry);
mAccessPoints.add(mWifiEntry1);
@@ -479,7 +476,6 @@ public class InternetDialogControllerTest extends SysuiTestCase {
@Test
public void onAccessPointsChanged_oneConnectedEntryAndTwoOthers_callbackCorrectly() {
reset(mInternetDialogCallback);
- fakeAirplaneModeEnabled(true);
mAccessPoints.clear();
mAccessPoints.add(mConnectedEntry);
mAccessPoints.add(mWifiEntry1);
@@ -497,7 +493,6 @@ public class InternetDialogControllerTest extends SysuiTestCase {
@Test
public void onAccessPointsChanged_oneConnectedEntryAndThreeOthers_callbackCutMore() {
reset(mInternetDialogCallback);
- fakeAirplaneModeEnabled(true);
mAccessPoints.clear();
mAccessPoints.add(mConnectedEntry);
mAccessPoints.add(mWifiEntry1);
@@ -509,27 +504,14 @@ public class InternetDialogControllerTest extends SysuiTestCase {
mWifiEntries.clear();
mWifiEntries.add(mWifiEntry1);
mWifiEntries.add(mWifiEntry2);
- mWifiEntries.add(mWifiEntry3);
- verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry,
- false /* hasMoreEntry */);
-
- // Turn off airplane mode to has carrier network, then Wi-Fi entries will cut last one.
- reset(mInternetDialogCallback);
- fakeAirplaneModeEnabled(false);
-
- mInternetDialogController.onAccessPointsChanged(mAccessPoints);
-
- mWifiEntries.remove(mWifiEntry3);
verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry,
true /* hasMoreEntry */);
}
@Test
- public void onAccessPointsChanged_oneConnectedEntryAndFourOthers_callbackCutMore() {
+ public void onAccessPointsChanged_fourWifiEntries_callbackCutMore() {
reset(mInternetDialogCallback);
- fakeAirplaneModeEnabled(true);
mAccessPoints.clear();
- mAccessPoints.add(mConnectedEntry);
mAccessPoints.add(mWifiEntry1);
mAccessPoints.add(mWifiEntry2);
mAccessPoints.add(mWifiEntry3);
@@ -541,106 +523,56 @@ public class InternetDialogControllerTest extends SysuiTestCase {
mWifiEntries.add(mWifiEntry1);
mWifiEntries.add(mWifiEntry2);
mWifiEntries.add(mWifiEntry3);
- verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry,
- true /* hasMoreEntry */);
-
- // Turn off airplane mode to has carrier network, then Wi-Fi entries will cut last one.
- reset(mInternetDialogCallback);
- fakeAirplaneModeEnabled(false);
-
- mInternetDialogController.onAccessPointsChanged(mAccessPoints);
-
- mWifiEntries.remove(mWifiEntry3);
- verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry,
- true /* hasMoreEntry */);
+ verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries,
+ null /* connectedEntry */, true /* hasMoreEntry */);
}
@Test
- public void onAccessPointsChanged_oneCarrierWifiAndFourOthers_callbackCutMore() {
+ public void onAccessPointsChanged_wifiIsDefaultButNoInternetAccess_putIntoWifiEntries() {
reset(mInternetDialogCallback);
- fakeAirplaneModeEnabled(true);
- when(mMergedCarrierEntry.isDefaultNetwork()).thenReturn(true);
mAccessPoints.clear();
+ when(mWifiEntry1.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_CONNECTED);
+ when(mWifiEntry1.isDefaultNetwork()).thenReturn(true);
+ when(mWifiEntry1.hasInternetAccess()).thenReturn(false);
mAccessPoints.add(mWifiEntry1);
- mAccessPoints.add(mWifiEntry2);
- mAccessPoints.add(mWifiEntry3);
- mAccessPoints.add(mWifiEntry4);
mInternetDialogController.onAccessPointsChanged(mAccessPoints);
mWifiEntries.clear();
mWifiEntries.add(mWifiEntry1);
- mWifiEntries.add(mWifiEntry2);
- mWifiEntries.add(mWifiEntry3);
verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries,
- null /* connectedEntry */, true /* hasMoreEntry */);
-
- // Turn off airplane mode to has carrier WiFi, then Wi-Fi entries will keep the same.
- reset(mInternetDialogCallback);
- fakeAirplaneModeEnabled(false);
-
- mInternetDialogController.onAccessPointsChanged(mAccessPoints);
-
- verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries,
- null /* connectedEntry */, true /* hasMoreEntry */);
+ null /* connectedEntry */, false /* hasMoreEntry */);
}
@Test
- public void onAccessPointsChanged_fourWifiEntries_callbackCutMore() {
- reset(mInternetDialogCallback);
- fakeAirplaneModeEnabled(true);
+ public void onAccessPointsChanged_connectedWifiNoInternetAccess_shouldSetListener() {
+ reset(mWifiEntry1);
mAccessPoints.clear();
+ when(mWifiEntry1.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_CONNECTED);
+ when(mWifiEntry1.isDefaultNetwork()).thenReturn(true);
+ when(mWifiEntry1.hasInternetAccess()).thenReturn(false);
mAccessPoints.add(mWifiEntry1);
- mAccessPoints.add(mWifiEntry2);
- mAccessPoints.add(mWifiEntry3);
- mAccessPoints.add(mWifiEntry4);
-
- mInternetDialogController.onAccessPointsChanged(mAccessPoints);
-
- mWifiEntries.clear();
- mWifiEntries.add(mWifiEntry1);
- mWifiEntries.add(mWifiEntry2);
- mWifiEntries.add(mWifiEntry3);
- mWifiEntries.add(mWifiEntry4);
- verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries,
- null /* connectedEntry */, false /* hasMoreEntry */);
-
- // If the Ethernet exists, then Wi-Fi entries will cut last one.
- reset(mInternetDialogCallback);
- mInternetDialogController.mHasEthernet = true;
-
- mInternetDialogController.onAccessPointsChanged(mAccessPoints);
-
- mWifiEntries.remove(mWifiEntry4);
- verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries,
- null /* connectedEntry */, true /* hasMoreEntry */);
-
- // Turn off airplane mode to has carrier network, then Wi-Fi entries will cut last one.
- reset(mInternetDialogCallback);
- fakeAirplaneModeEnabled(false);
mInternetDialogController.onAccessPointsChanged(mAccessPoints);
- mWifiEntries.remove(mWifiEntry3);
- verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries,
- null /* connectedEntry */, true /* hasMoreEntry */);
+ verify(mWifiEntry1).setListener(mInternetDialogController.mConnectedWifiInternetMonitor);
}
@Test
- public void onAccessPointsChanged_wifiIsDefaultButNoInternetAccess_putIntoWifiEntries() {
- reset(mInternetDialogCallback);
- mAccessPoints.clear();
+ public void onUpdated_connectedWifiHasInternetAccess_shouldScanWifiAccessPoints() {
+ reset(mAccessPointController);
when(mWifiEntry1.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_CONNECTED);
when(mWifiEntry1.isDefaultNetwork()).thenReturn(true);
when(mWifiEntry1.hasInternetAccess()).thenReturn(false);
- mAccessPoints.add(mWifiEntry1);
+ InternetDialogController.ConnectedWifiInternetMonitor mConnectedWifiInternetMonitor =
+ mInternetDialogController.mConnectedWifiInternetMonitor;
+ mConnectedWifiInternetMonitor.registerCallbackIfNeed(mWifiEntry1);
- mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+ // When the hasInternetAccess() changed to true, and call back the onUpdated() function.
+ when(mWifiEntry1.hasInternetAccess()).thenReturn(true);
+ mConnectedWifiInternetMonitor.onUpdated();
- mWifiEntries.clear();
- mWifiEntries.add(mWifiEntry1);
- verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries,
- null /* connectedEntry */, false /* hasMoreEntry */);
+ verify(mAccessPointController).scanForAccessPoints();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
index 89537884903a..c20e88708f40 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
@@ -312,6 +312,7 @@ public class InternetDialogTest extends SysuiTestCase {
assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
// Show a blank block to fix the dialog height even if there is no WiFi list
assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
+ verify(mInternetAdapter).setMaxEntriesCount(3);
assertThat(mSeeAll.getVisibility()).isEqualTo(View.INVISIBLE);
}
@@ -326,6 +327,7 @@ public class InternetDialogTest extends SysuiTestCase {
assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
// Show a blank block to fix the dialog height even if there is no WiFi list
assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
+ verify(mInternetAdapter).setMaxEntriesCount(3);
assertThat(mSeeAll.getVisibility()).isEqualTo(View.INVISIBLE);
}
@@ -339,6 +341,7 @@ public class InternetDialogTest extends SysuiTestCase {
assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE);
// Show a blank block to fix the dialog height even if there is no WiFi list
assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
+ verify(mInternetAdapter).setMaxEntriesCount(2);
assertThat(mSeeAll.getVisibility()).isEqualTo(View.INVISIBLE);
}
@@ -347,12 +350,13 @@ public class InternetDialogTest extends SysuiTestCase {
// The preconditions WiFi ON and WiFi entries are already in setUp()
mInternetDialog.mConnectedWifiEntry = null;
mInternetDialog.mWifiEntriesCount = MAX_WIFI_ENTRY_COUNT;
- mInternetDialog.mHasMoreEntry = true;
+ mInternetDialog.mHasMoreWifiEntries = true;
mInternetDialog.updateDialog(false);
assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
+ verify(mInternetAdapter).setMaxEntriesCount(3);
assertThat(mSeeAll.getVisibility()).isEqualTo(View.VISIBLE);
}
@@ -360,12 +364,13 @@ public class InternetDialogTest extends SysuiTestCase {
public void updateDialog_wifiOnAndHasBothWifiEntry_showBothWifiEntryAndSeeAll() {
// The preconditions WiFi ON and WiFi entries are already in setUp()
mInternetDialog.mWifiEntriesCount = MAX_WIFI_ENTRY_COUNT - 1;
- mInternetDialog.mHasMoreEntry = true;
+ mInternetDialog.mHasMoreWifiEntries = true;
mInternetDialog.updateDialog(false);
assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
+ verify(mInternetAdapter).setMaxEntriesCount(2);
assertThat(mSeeAll.getVisibility()).isEqualTo(View.VISIBLE);
}
@@ -518,45 +523,46 @@ public class InternetDialogTest extends SysuiTestCase {
@Test
public void getWifiListMaxCount_returnCountCorrectly() {
- // Ethernet, MobileData, ConnectedWiFi are all hidden.
+ // Both of the Ethernet, MobileData is hidden.
// Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT.
setNetworkVisible(false, false, false);
assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT);
- // Only one of Ethernet, MobileData, ConnectedWiFi is displayed.
- // Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT - 1.
- setNetworkVisible(true, false, false);
-
- assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
-
- setNetworkVisible(false, true, false);
+ // If the Connected Wi-Fi is displayed then reduce one of the Wi-Fi list max count.
+ setNetworkVisible(false, false, true);
assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
- setNetworkVisible(false, false, true);
+ // Only one of Ethernet, MobileData is displayed.
+ // Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT.
+ setNetworkVisible(true, false, false);
- assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
+ assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT);
- // Only one of Ethernet, MobileData, ConnectedWiFi is hidden.
- // Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT - 2.
- setNetworkVisible(true, true, false);
+ setNetworkVisible(false, true, false);
- assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 2);
+ assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT);
+ // If the Connected Wi-Fi is displayed then reduce one of the Wi-Fi list max count.
setNetworkVisible(true, false, true);
- assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 2);
+ assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
setNetworkVisible(false, true, true);
- assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 2);
+ assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
+
+ // Both of Ethernet, MobileData, ConnectedWiFi is displayed.
+ // Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT - 1.
+ setNetworkVisible(true, true, false);
- // Ethernet, MobileData, ConnectedWiFi are all displayed.
- // Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT - 3.
+ assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
+
+ // If the Connected Wi-Fi is displayed then reduce one of the Wi-Fi list max count.
setNetworkVisible(true, true, true);
- assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 3);
+ assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 2);
}
private void setNetworkVisible(boolean ethernetVisible, boolean mobileDataVisible,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
index de627de2e1d0..1961ab269267 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
@@ -214,6 +214,8 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
// THEN the session is created
verify(smartspaceManager).createSmartspaceSession(any())
+ // THEN an event notifier is registered
+ verify(plugin).registerSmartspaceEventNotifier(any())
}
@Test
@@ -241,7 +243,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
}
@Test
- fun testEmptyListIsEmittedAfterDisconnect() {
+ fun testEmptyListIsEmittedAndNotifierRemovedAfterDisconnect() {
// GIVEN a registered listener on an active session
connectSession()
clearInvocations(plugin)
@@ -250,8 +252,9 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
controller.stateChangeListener.onViewDetachedFromWindow(smartspaceView as View)
controller.disconnect()
- // THEN the listener receives an empty list of targets
+ // THEN the listener receives an empty list of targets and unregisters the notifier
verify(plugin).onTargetsAvailable(emptyList())
+ verify(plugin).registerSmartspaceEventNotifier(null)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 9be283716058..eda0e83d7eb3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -22,6 +22,7 @@ import static android.view.View.GONE;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
+import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static junit.framework.Assert.assertEquals;
@@ -102,6 +103,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
@Mock private NotificationSwipeHelper mNotificationSwipeHelper;
@Mock private NotificationStackScrollLayoutController mStackScrollLayoutController;
@Mock private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
+ @Mock private NotificationShelf mNotificationShelf;
@Before
@UiThreadTest
@@ -123,13 +125,13 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
mDependency.injectTestDependency(GroupMembershipManager.class, mGroupMembershipManger);
mDependency.injectTestDependency(GroupExpansionManager.class, mGroupExpansionManager);
mDependency.injectTestDependency(AmbientState.class, mAmbientState);
+ mDependency.injectTestDependency(NotificationShelf.class, mNotificationShelf);
mDependency.injectTestDependency(
UnlockedScreenOffAnimationController.class, mUnlockedScreenOffAnimationController);
NotificationShelfController notificationShelfController =
mock(NotificationShelfController.class);
- NotificationShelf notificationShelf = mock(NotificationShelf.class);
- when(notificationShelfController.getView()).thenReturn(notificationShelf);
+ when(notificationShelfController.getView()).thenReturn(mNotificationShelf);
when(mNotificationSectionsManager.createSectionsForBuckets()).thenReturn(
new NotificationSection[]{
mNotificationSection
@@ -159,7 +161,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
anyBoolean());
doNothing().when(mGroupExpansionManager).collapseGroups();
doNothing().when(mExpandHelper).cancelImmediately();
- doNothing().when(notificationShelf).setAnimationsEnabled(anyBoolean());
+ doNothing().when(mNotificationShelf).setAnimationsEnabled(anyBoolean());
}
@Test
@@ -202,21 +204,43 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
@Test
@UiThreadTest
- public void testSetExpandedHeight_blockingHelperManagerReceivedCallbacks() {
- final float[] expectedHeight = {0f};
- final float[] expectedAppear = {0f};
+ public void testSetExpandedHeight_listenerReceivedCallbacks() {
+ final float expectedHeight = 0f;
mStackScroller.addOnExpandedHeightChangedListener((height, appear) -> {
- Assert.assertEquals(expectedHeight[0], height, 0);
- Assert.assertEquals(expectedAppear[0], appear, .1);
+ Assert.assertEquals(expectedHeight, height, 0);
});
- expectedHeight[0] = 1f;
- expectedAppear[0] = 1f;
- mStackScroller.setExpandedHeight(expectedHeight[0]);
+ mStackScroller.setExpandedHeight(expectedHeight);
+ }
- expectedHeight[0] = 100f;
- expectedAppear[0] = 0f;
- mStackScroller.setExpandedHeight(expectedHeight[0]);
+ @Test
+ public void testAppearFractionCalculation() {
+ // appear start position
+ when(mNotificationShelf.getIntrinsicHeight()).thenReturn(100);
+ // because it's the same as shelf height, appear start position equals shelf height
+ mStackScroller.mStatusBarHeight = 100;
+ // appear end position
+ when(mEmptyShadeView.getHeight()).thenReturn(200);
+
+ assertEquals(0f, mStackScroller.calculateAppearFraction(100));
+ assertEquals(1f, mStackScroller.calculateAppearFraction(200));
+ assertEquals(0.5f, mStackScroller.calculateAppearFraction(150));
+ }
+
+ @Test
+ public void testAppearFractionCalculationIsNotNegativeWhenShelfBecomesSmaller() {
+ // this situation might occur if status bar height is defined in pixels while shelf height
+ // in dp and screen density changes - appear start position
+ // (calculated in NSSL#getMinExpansionHeight) that is adjusting for status bar might
+ // increase and become bigger that end position, which should be prevented
+
+ // appear start position
+ when(mNotificationShelf.getIntrinsicHeight()).thenReturn(80);
+ mStackScroller.mStatusBarHeight = 100;
+ // appear end position
+ when(mEmptyShadeView.getHeight()).thenReturn(90);
+
+ assertThat(mStackScroller.calculateAppearFraction(100)).isAtLeast(0);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
index 8d05e6693e33..37cf7485b8ab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
@@ -278,11 +278,11 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
}
@Test
- public void updateViewState_qsExpansionOne_viewHidden() {
+ public void updateViewState_dragProgressOne_viewHidden() {
mController.onViewAttached();
updateStateToKeyguard();
- mNotificationPanelViewStateProvider.setQsExpansionFraction(1f);
+ mNotificationPanelViewStateProvider.setLockscreenShadeDragProgress(1f);
mController.updateViewState();
@@ -356,6 +356,7 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
private float mPanelViewExpandedHeight = 100f;
private float mQsExpansionFraction = 0f;
private boolean mShouldHeadsUpBeVisible = false;
+ private float mLockscreenShadeDragProgress = 0f;
@Override
public float getPanelViewExpandedHeight() {
@@ -372,6 +373,11 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
return mShouldHeadsUpBeVisible;
}
+ @Override
+ public float getLockscreenShadeDragProgress() {
+ return mLockscreenShadeDragProgress;
+ }
+
public void setPanelViewExpandedHeight(float panelViewExpandedHeight) {
this.mPanelViewExpandedHeight = panelViewExpandedHeight;
}
@@ -383,5 +389,9 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
public void setShouldHeadsUpBeVisible(boolean shouldHeadsUpBeVisible) {
this.mShouldHeadsUpBeVisible = shouldHeadsUpBeVisible;
}
+
+ public void setLockscreenShadeDragProgress(float lockscreenShadeDragProgress) {
+ this.mLockscreenShadeDragProgress = lockscreenShadeDragProgress;
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
index 9bcdcc96767a..1cd9b9e77423 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
@@ -425,6 +425,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
.thenReturn(mKeyguardUserSwitcherComponent);
when(mKeyguardUserSwitcherComponent.getKeyguardUserSwitcherController())
.thenReturn(mKeyguardUserSwitcherController);
+ when(mScreenOffAnimationController.shouldAnimateClockChange()).thenReturn(true);
doAnswer((Answer<Void>) invocation -> {
mTouchHandler = invocation.getArgument(0);
@@ -880,11 +881,11 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
triggerPositionClockAndNotifications();
- verify(mKeyguardStatusViewController).displayClock(LARGE);
+ verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true);
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
mNotificationPanelViewController.closeQs();
- verify(mKeyguardStatusViewController).displayClock(SMALL);
+ verify(mKeyguardStatusViewController).displayClock(SMALL, /* animate */ true);
}
@Test
@@ -894,12 +895,14 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
triggerPositionClockAndNotifications();
- verify(mKeyguardStatusViewController).displayClock(LARGE);
+ verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true);
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
triggerPositionClockAndNotifications();
- verify(mKeyguardStatusViewController, times(2)).displayClock(LARGE);
- verify(mKeyguardStatusViewController, never()).displayClock(SMALL);
+ verify(mKeyguardStatusViewController, times(2))
+ .displayClock(LARGE, /* animate */ true);
+ verify(mKeyguardStatusViewController, never())
+ .displayClock(SMALL, /* animate */ true);
}
@Test
@@ -911,7 +914,20 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mNotificationPanelViewController.setDozing(true, false, null);
- verify(mKeyguardStatusViewController).displayClock(LARGE);
+ verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true);
+ }
+
+ @Test
+ public void testSwitchesToBigClockInSplitShadeOnAodAnimateDisabled() {
+ when(mScreenOffAnimationController.shouldAnimateClockChange()).thenReturn(false);
+ mStatusBarStateController.setState(KEYGUARD);
+ enableSplitShade(/* enabled= */ true);
+ when(mMediaDataManager.hasActiveMedia()).thenReturn(true);
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+
+ mNotificationPanelViewController.setDozing(true, false, null);
+
+ verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ false);
}
@Test
@@ -923,13 +939,13 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
// one notification + media player visible
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
triggerPositionClockAndNotifications();
- verify(mKeyguardStatusViewController).displayClock(SMALL);
+ verify(mKeyguardStatusViewController).displayClock(SMALL, /* animate */ true);
// only media player visible
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
triggerPositionClockAndNotifications();
- verify(mKeyguardStatusViewController, times(2)).displayClock(SMALL);
- verify(mKeyguardStatusViewController, never()).displayClock(LARGE);
+ verify(mKeyguardStatusViewController, times(2)).displayClock(SMALL, true);
+ verify(mKeyguardStatusViewController, never()).displayClock(LARGE, /* animate */ true);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt
index 2d548e96e598..2b1826eab5aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt
@@ -1,13 +1,14 @@
package com.android.systemui.statusbar.phone
-import android.test.suitebuilder.annotation.SmallTest
import android.testing.AndroidTestingRunner
import android.view.View
+import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
+import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.qs.HeaderPrivacyIconsController
@@ -37,11 +38,13 @@ class SplitShadeHeaderControllerTest : SysuiTestCase() {
@Mock private lateinit var batteryMeterView: BatteryMeterView
@Mock private lateinit var batteryMeterViewController: BatteryMeterViewController
@Mock private lateinit var privacyIconsController: HeaderPrivacyIconsController
+ @Mock private lateinit var dumpManager: DumpManager
@JvmField @Rule val mockitoRule = MockitoJUnit.rule()
var viewVisibility = View.GONE
private lateinit var splitShadeHeaderController: SplitShadeHeaderController
+ private lateinit var carrierIconSlots: List<String>
@Before
fun setup() {
@@ -65,14 +68,16 @@ class SplitShadeHeaderControllerTest : SysuiTestCase() {
privacyIconsController,
qsCarrierGroupControllerBuilder,
featureFlags,
- batteryMeterViewController
+ batteryMeterViewController,
+ dumpManager
)
+ carrierIconSlots = listOf(
+ context.getString(com.android.internal.R.string.status_bar_mobile))
}
@Test
fun setVisible_onlyInSplitShade() {
- splitShadeHeaderController.splitShadeMode = true
- splitShadeHeaderController.shadeExpanded = true
+ makeShadeVisible()
assertThat(viewVisibility).isEqualTo(View.VISIBLE)
splitShadeHeaderController.splitShadeMode = false
@@ -81,17 +86,38 @@ class SplitShadeHeaderControllerTest : SysuiTestCase() {
@Test
fun updateListeners_registersWhenVisible() {
- splitShadeHeaderController.splitShadeMode = true
- splitShadeHeaderController.shadeExpanded = true
+ makeShadeVisible()
verify(qsCarrierGroupController).setListening(true)
verify(statusBarIconController).addIconGroup(any())
}
@Test
fun shadeExpandedFraction_updatesAlpha() {
- splitShadeHeaderController.splitShadeMode = true
- splitShadeHeaderController.shadeExpanded = true
+ makeShadeVisible()
splitShadeHeaderController.shadeExpandedFraction = 0.5f
verify(view).setAlpha(ShadeInterpolation.getContentAlpha(0.5f))
}
-} \ No newline at end of file
+
+ @Test
+ fun singleCarrier_enablesCarrierIconsInStatusIcons() {
+ whenever(qsCarrierGroupController.isSingleCarrier).thenReturn(true)
+
+ makeShadeVisible()
+
+ verify(statusIcons).removeIgnoredSlots(carrierIconSlots)
+ }
+
+ @Test
+ fun dualCarrier_disablesCarrierIconsInStatusIcons() {
+ whenever(qsCarrierGroupController.isSingleCarrier).thenReturn(false)
+
+ makeShadeVisible()
+
+ verify(statusIcons).addIgnoredSlots(carrierIconSlots)
+ }
+
+ private fun makeShadeVisible() {
+ splitShadeHeaderController.splitShadeMode = true
+ splitShadeHeaderController.shadeExpanded = true
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index c5bdfed6082b..5d80bca03e03 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -43,9 +43,6 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dock.DockManager;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.keyguard.DismissCallbackRegistry;
-import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -79,9 +76,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
@Mock
private NotificationPanelViewController mNotificationPanelView;
@Mock
- private BiometricUnlockController mBiometrucUnlockController;
- @Mock
- private DismissCallbackRegistry mDismissCallbackRegistry;
+ private BiometricUnlockController mBiometricUnlockController;
@Mock
private SysuiStatusBarStateController mStatusBarStateController;
@Mock
@@ -97,15 +92,12 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
@Mock
private KeyguardBouncer mBouncer;
@Mock
- private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
- @Mock
private StatusBarKeyguardViewManager.AlternateAuthInterceptor mAlternateAuthInterceptor;
@Mock
private KeyguardMessageArea mKeyguardMessageArea;
@Mock
private ShadeController mShadeController;
- private WakefulnessLifecycle mWakefulnessLifecycle;
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@Before
@@ -117,10 +109,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
.thenReturn(mBouncer);
when(mStatusBar.getBouncerContainer()).thenReturn(mContainer);
when(mContainer.findViewById(anyInt())).thenReturn(mKeyguardMessageArea);
- mWakefulnessLifecycle = new WakefulnessLifecycle(
- getContext(),
- null,
- mock(DumpManager.class));
mStatusBarKeyguardViewManager = new StatusBarKeyguardViewManager(
getContext(),
mViewMediatorCallback,
@@ -134,15 +122,13 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
mKeyguardStateController,
mock(NotificationMediaManager.class),
mKeyguardBouncerFactory,
- mWakefulnessLifecycle,
- mUnlockedScreenOffAnimationController,
mKeyguardMessageAreaFactory,
() -> mShadeController);
mStatusBarKeyguardViewManager.registerStatusBar(
mStatusBar,
mNotificationPanelView,
new PanelExpansionStateManager(),
- mBiometrucUnlockController,
+ mBiometricUnlockController,
mNotificationContainer,
mBypassController);
mStatusBarKeyguardViewManager.show(null);
@@ -261,7 +247,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
@Test
public void onPanelExpansionChanged_neverTranslatesBouncerWhenWakeAndUnlock() {
- when(mBiometrucUnlockController.getMode())
+ when(mBiometricUnlockController.getMode())
.thenReturn(BiometricUnlockController.MODE_WAKE_AND_UNLOCK);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(
/* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE,
@@ -389,6 +375,39 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
}
@Test
+ public void testShowAltAuth_unlockingWithBiometricNotAllowed() {
+ // GIVEN alt auth exists, unlocking with biometric isn't allowed
+ mStatusBarKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
+ when(mBouncer.isShowing()).thenReturn(false);
+ when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean()))
+ .thenReturn(false);
+
+ // WHEN showGenericBouncer is called
+ final boolean scrimmed = true;
+ mStatusBarKeyguardViewManager.showGenericBouncer(scrimmed);
+
+ // THEN regular bouncer is shown
+ verify(mBouncer).show(anyBoolean(), eq(scrimmed));
+ verify(mAlternateAuthInterceptor, never()).showAlternateAuthBouncer();
+ }
+
+ @Test
+ public void testShowAltAuth_unlockingWithBiometricAllowed() {
+ // GIVEN alt auth exists, unlocking with biometric is allowed
+ mStatusBarKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
+ when(mBouncer.isShowing()).thenReturn(false);
+ when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean()))
+ .thenReturn(true);
+
+ // WHEN showGenericBouncer is called
+ mStatusBarKeyguardViewManager.showGenericBouncer(true);
+
+ // THEN alt auth bouncer is shown
+ verify(mAlternateAuthInterceptor).showAlternateAuthBouncer();
+ verify(mBouncer, never()).show(anyBoolean(), anyBoolean());
+ }
+
+ @Test
public void testUpdateResources_delegatesToBouncer() {
mStatusBarKeyguardViewManager.updateResources();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
new file mode 100644
index 000000000000..f600b12993b9
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.unfold
+
+import android.hardware.devicestate.DeviceStateManager
+import android.hardware.devicestate.DeviceStateManager.FoldStateListener
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.internal.util.LatencyTracker
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyguard.ScreenLifecycle
+import com.android.systemui.unfold.util.FoldableDeviceStates
+import com.android.systemui.unfold.util.FoldableTestUtils
+import com.android.systemui.util.mockito.any
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class UnfoldLatencyTrackerTest : SysuiTestCase() {
+
+ @Mock
+ lateinit var latencyTracker: LatencyTracker
+
+ @Mock
+ lateinit var deviceStateManager: DeviceStateManager
+
+ @Mock
+ lateinit var screenLifecycle: ScreenLifecycle
+
+ @Captor
+ private lateinit var foldStateListenerCaptor: ArgumentCaptor<FoldStateListener>
+
+ @Captor
+ private lateinit var screenLifecycleCaptor: ArgumentCaptor<ScreenLifecycle.Observer>
+
+ private lateinit var deviceStates: FoldableDeviceStates
+
+ private lateinit var unfoldLatencyTracker: UnfoldLatencyTracker
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ unfoldLatencyTracker = UnfoldLatencyTracker(
+ latencyTracker,
+ deviceStateManager,
+ context.mainExecutor,
+ context,
+ screenLifecycle
+ )
+ deviceStates = FoldableTestUtils.findDeviceStates(context)
+
+ verify(deviceStateManager).registerCallback(any(), foldStateListenerCaptor.capture())
+ verify(screenLifecycle).addObserver(screenLifecycleCaptor.capture())
+ }
+
+ @Test
+ fun unfold_eventPropagated() {
+ sendFoldEvent(folded = false)
+ sendScreenTurnedOnEvent()
+
+ verify(latencyTracker).onActionStart(any())
+ verify(latencyTracker).onActionEnd(any())
+ }
+
+ @Test
+ fun fold_eventNotPropagated() {
+ sendFoldEvent(folded = true)
+ sendScreenTurnedOnEvent() // outer display on.
+
+ verifyNoMoreInteractions(latencyTracker)
+ }
+
+ @Test
+ fun onScreenTurnedOn_stateNeverSet_eventNotPropagated() {
+ sendScreenTurnedOnEvent()
+
+ verifyNoMoreInteractions(latencyTracker)
+ }
+
+ private fun sendFoldEvent(folded: Boolean) {
+ val state = if (folded) deviceStates.folded else deviceStates.unfolded
+ foldStateListenerCaptor.value.onStateChanged(state)
+ }
+
+ private fun sendScreenTurnedOnEvent() {
+ screenLifecycleCaptor.value.onScreenTurnedOn()
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
index be1720d07d11..46e0b1238013 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
@@ -26,9 +26,10 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener
+import com.android.systemui.unfold.util.FoldableDeviceStates
+import com.android.systemui.unfold.util.FoldableTestUtils
import com.android.systemui.util.mockito.any
import com.google.common.truth.Truth.assertThat
-import org.junit.Assume.assumeTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -70,8 +71,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
private val foldUpdates: MutableList<Int> = arrayListOf()
private val hingeAngleUpdates: MutableList<Float> = arrayListOf()
- private var foldedDeviceState: Int = 0
- private var unfoldedDeviceState: Int = 0
+ private lateinit var deviceStates: FoldableDeviceStates
private var scheduledRunnable: Runnable? = null
private var scheduledRunnableDelay: Long? = null
@@ -79,13 +79,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- val foldedDeviceStates: IntArray = context.resources.getIntArray(
- com.android.internal.R.array.config_foldedDeviceStates)
- assumeTrue("Test should be launched on a foldable device",
- foldedDeviceStates.isNotEmpty())
-
- foldedDeviceState = foldedDeviceStates.maxOrNull()!!
- unfoldedDeviceState = foldedDeviceState + 1
+ deviceStates = FoldableTestUtils.findDeviceStates(context)
foldStateProvider = DeviceFoldStateProvider(
context,
@@ -282,7 +276,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
}
private fun setFoldState(folded: Boolean) {
- val state = if (folded) foldedDeviceState else unfoldedDeviceState
+ val state = if (folded) deviceStates.folded else deviceStates.unfolded
foldStateListenerCaptor.value.onStateChanged(state)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt
new file mode 100644
index 000000000000..01b535964ba6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.unfold.util
+
+import android.content.Context
+import org.junit.Assume.assumeTrue
+
+object FoldableTestUtils {
+
+ /** Finds device state for folded and unfolded. */
+ fun findDeviceStates(context: Context): FoldableDeviceStates {
+ val foldedDeviceStates: IntArray = context.resources.getIntArray(
+ com.android.internal.R.array.config_foldedDeviceStates)
+ assumeTrue("Test should be launched on a foldable device",
+ foldedDeviceStates.isNotEmpty())
+
+ val folded = foldedDeviceStates.maxOrNull()!!
+ val unfolded = folded + 1
+ return FoldableDeviceStates(folded = folded, unfolded = unfolded)
+ }
+}
+
+data class FoldableDeviceStates(val folded: Int, val unfolded: Int) \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java
index 878bdeac43c9..d6454490e62a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java
@@ -58,7 +58,7 @@ public class ConditionMonitorTest extends SysuiTestCase {
mCondition3 = spy(new FakeCondition());
mConditions = new HashSet<>(Arrays.asList(mCondition1, mCondition2, mCondition3));
- mConditionMonitor = new Monitor(mConditions);
+ mConditionMonitor = new Monitor(mConditions, null /*callbacks*/);
}
@Test
@@ -98,7 +98,7 @@ public class ConditionMonitorTest extends SysuiTestCase {
@Test
public void addCallback_noConditions_reportAllConditionsMet() {
- final Monitor monitor = new Monitor(new HashSet<>());
+ final Monitor monitor = new Monitor(new HashSet<>(), null /*callbacks*/);
final Monitor.Callback callback = mock(Monitor.Callback.class);
monitor.addCallback(callback);
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 71ae5eb028a7..d27a57077c9c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -116,7 +116,6 @@ import com.android.wm.shell.bubbles.BubbleDataRepository;
import com.android.wm.shell.bubbles.BubbleEntry;
import com.android.wm.shell.bubbles.BubbleIconFactory;
import com.android.wm.shell.bubbles.BubbleLogger;
-import com.android.wm.shell.bubbles.BubbleOverflow;
import com.android.wm.shell.bubbles.BubbleStackView;
import com.android.wm.shell.bubbles.BubbleViewInfoTask;
import com.android.wm.shell.bubbles.Bubbles;
@@ -125,6 +124,7 @@ import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.onehanded.OneHandedController;
import com.google.common.collect.ImmutableList;
@@ -138,6 +138,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.List;
+import java.util.Optional;
/**
* Tests the NotificationEntryManager setup with BubbleController.
@@ -249,6 +250,8 @@ public class BubblesTest extends SysuiTestCase {
private AuthController mAuthController;
@Mock
private TaskViewTransitions mTaskViewTransitions;
+ @Mock
+ private Optional<OneHandedController> mOneHandedOptional;
private TestableBubblePositioner mPositioner;
@@ -345,6 +348,7 @@ public class BubblesTest extends SysuiTestCase {
mShellTaskOrganizer,
mPositioner,
mock(DisplayController.class),
+ mOneHandedOptional,
syncExecutor,
mock(Handler.class),
mTaskViewTransitions,
@@ -631,7 +635,7 @@ public class BubblesTest extends SysuiTestCase {
}
@Test
- public void testRemoveLastExpanded_selectsOverflow() {
+ public void testRemoveLastExpanded_collapses() {
// Mark it as a bubble and add it explicitly
mEntryListener.onPendingEntryAdded(mRow);
mEntryListener.onPendingEntryAdded(mRow2);
@@ -670,11 +674,10 @@ public class BubblesTest extends SysuiTestCase {
stackView.getExpandedBubble().getKey()).getKey(),
Bubbles.DISMISS_USER_GESTURE);
- // Overflow should be selected
- assertEquals(mBubbleData.getSelectedBubble().getKey(), BubbleOverflow.KEY);
- verify(mBubbleExpandListener).onBubbleExpandChanged(true, BubbleOverflow.KEY);
- assertTrue(mBubbleController.hasBubbles());
- assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
+ // We should be collapsed
+ verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getKey());
+ assertFalse(mBubbleController.hasBubbles());
+ assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
}
@Test
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 7b9e6a9efb3b..ad26f0112a8e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
@@ -19,8 +19,11 @@ package com.android.systemui.wmshell;
import static android.app.Notification.FLAG_BUBBLE;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
+
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -97,7 +100,6 @@ import com.android.wm.shell.bubbles.BubbleData;
import com.android.wm.shell.bubbles.BubbleDataRepository;
import com.android.wm.shell.bubbles.BubbleEntry;
import com.android.wm.shell.bubbles.BubbleLogger;
-import com.android.wm.shell.bubbles.BubbleOverflow;
import com.android.wm.shell.bubbles.BubbleStackView;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.common.DisplayController;
@@ -105,6 +107,7 @@ import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.onehanded.OneHandedController;
import org.junit.Before;
import org.junit.Ignore;
@@ -116,6 +119,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.List;
+import java.util.Optional;
/**
* Tests the NotifPipeline setup with BubbleController.
@@ -220,6 +224,8 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase {
private ScreenOffAnimationController mScreenOffAnimationController;
@Mock
private TaskViewTransitions mTaskViewTransitions;
+ @Mock
+ private Optional<OneHandedController> mOneHandedOptional;
private TestableBubblePositioner mPositioner;
@@ -307,6 +313,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase {
mShellTaskOrganizer,
mPositioner,
mock(DisplayController.class),
+ mOneHandedOptional,
syncExecutor,
mock(Handler.class),
mTaskViewTransitions,
@@ -572,7 +579,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase {
}
@Test
- public void testRemoveLastExpanded_selectsOverflow() {
+ public void testRemoveLastExpanded_collapses() {
// Mark it as a bubble and add it explicitly
mEntryListener.onEntryAdded(mRow);
mEntryListener.onEntryAdded(mRow2);
@@ -611,11 +618,10 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase {
stackView.getExpandedBubble().getKey()).getKey(),
Bubbles.DISMISS_USER_GESTURE);
- // Overflow should be selected
- assertEquals(mBubbleData.getSelectedBubble().getKey(), BubbleOverflow.KEY);
- verify(mBubbleExpandListener).onBubbleExpandChanged(true, BubbleOverflow.KEY);
- assertTrue(mBubbleController.hasBubbles());
- assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
+ // We should be collapsed
+ verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getKey());
+ assertFalse(mBubbleController.hasBubbles());
+ assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
index 80834c8130e6..83f5987ac962 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
@@ -35,6 +35,9 @@ import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.onehanded.OneHandedController;
+
+import java.util.Optional;
/**
* Testable BubbleController subclass that immediately synchronizes surfaces.
@@ -55,6 +58,7 @@ public class TestableBubbleController extends BubbleController {
ShellTaskOrganizer shellTaskOrganizer,
BubblePositioner positioner,
DisplayController displayController,
+ Optional<OneHandedController> oneHandedOptional,
ShellExecutor shellMainExecutor,
Handler shellMainHandler,
TaskViewTransitions taskViewTransitions,
@@ -62,7 +66,8 @@ public class TestableBubbleController extends BubbleController {
super(context, data, Runnable::run, floatingContentCoordinator, dataRepository,
statusBarService, windowManager, windowManagerShellWrapper, launcherApps,
bubbleLogger, taskStackListener, shellTaskOrganizer, positioner, displayController,
- shellMainExecutor, shellMainHandler, taskViewTransitions, syncQueue);
+ oneHandedOptional, shellMainExecutor, shellMainHandler, taskViewTransitions,
+ syncQueue);
setInflateSynchronously(true);
initialize();
}
diff --git a/services/Android.bp b/services/Android.bp
index dcbcff0aa539..c830c226cb66 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -90,6 +90,7 @@ filegroup {
":services.profcollect-sources",
":services.restrictions-sources",
":services.searchui-sources",
+ ":services.selectiontoolbar-sources",
":services.smartspace-sources",
":services.speech-sources",
":services.startop.iorap-sources",
@@ -144,6 +145,7 @@ java_library {
"services.profcollect",
"services.restrictions",
"services.searchui",
+ "services.selectiontoolbar",
"services.smartspace",
"services.speech",
"services.startop",
diff --git a/services/accessibility/Android.bp b/services/accessibility/Android.bp
index 1698e9a70245..bf8a9af99076 100644
--- a/services/accessibility/Android.bp
+++ b/services/accessibility/Android.bp
@@ -16,7 +16,15 @@ filegroup {
java_library_static {
name: "services.accessibility",
- defaults: ["platform_service_defaults"],
- srcs: [":services.accessibility-sources"],
- libs: ["services.core"],
+ defaults: [
+ "platform_service_defaults",
+ ],
+ srcs: [
+ ":services.accessibility-sources",
+ "//frameworks/base/packages/SettingsLib/RestrictedLockUtils:SettingsLibRestrictedLockUtilsSrc",
+ ],
+ libs: [
+ "services.core",
+ "androidx.annotation_annotation",
+ ],
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 881c910b399b..f050b6622a5d 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -1027,8 +1027,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
mSystemSupport.getMagnificationProcessor();
final long identity = Binder.clearCallingIdentity();
try {
- magnificationProcessor.getMagnificationRegion(displayId, region,
- mSecurityPolicy.canControlMagnification(this));
+ magnificationProcessor.getFullscreenMagnificationRegion(displayId,
+ region, mSecurityPolicy.canControlMagnification(this));
return region;
} finally {
Binder.restoreCallingIdentity(identity);
@@ -1095,7 +1095,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
try {
MagnificationProcessor magnificationProcessor =
mSystemSupport.getMagnificationProcessor();
- return (magnificationProcessor.reset(displayId, animate)
+ return (magnificationProcessor.resetFullscreenMagnification(displayId, animate)
|| !magnificationProcessor.isMagnifying(displayId));
} finally {
Binder.restoreCallingIdentity(identity);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
index 0ab0c89ec9ae..e251bcc39f32 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
@@ -16,13 +16,18 @@
package com.android.server.accessibility;
+import static android.accessibilityservice.AccessibilityService.SoftKeyboardController.ENABLE_IME_FAIL_BY_ADMIN;
+import static android.accessibilityservice.AccessibilityService.SoftKeyboardController.ENABLE_IME_SUCCESS;
import static android.content.pm.PackageManagerInternal.PACKAGE_INSTALLER;
import android.Manifest;
+import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.AppOpsManager;
+import android.app.admin.DevicePolicyManager;
import android.appwidget.AppWidgetManagerInternal;
import android.content.ComponentName;
import android.content.Context;
@@ -41,13 +46,17 @@ import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Slog;
import android.view.accessibility.AccessibilityEvent;
+import android.view.inputmethod.InputMethodInfo;
import com.android.internal.util.ArrayUtils;
import com.android.server.LocalServices;
+import com.android.server.inputmethod.InputMethodManagerInternal;
+import com.android.settingslib.RestrictedLockUtils;
import libcore.util.EmptyArray;
import java.util.ArrayList;
+import java.util.List;
import java.util.Set;
/**
@@ -364,6 +373,115 @@ public class AccessibilitySecurityPolicy {
}
/**
+ * Check whether the input method can be enabled or disabled by the accessibility service.
+ *
+ * @param imeId The id of the input method.
+ * @param service The accessibility service connection.
+ * @return Whether the input method can be enabled/disabled or the reason why it can't be
+ * enabled/disabled.
+ * @throws SecurityException if the input method is not in the same package as the service.
+ */
+ @AccessibilityService.SoftKeyboardController.EnableImeResult
+ int canEnableDisableInputMethod(String imeId, AbstractAccessibilityServiceConnection service)
+ throws SecurityException {
+ final String servicePackageName = service.getComponentName().getPackageName();
+ final int callingUserId = UserHandle.getCallingUserId();
+
+ InputMethodInfo inputMethodInfo = null;
+ List<InputMethodInfo> inputMethodInfoList =
+ InputMethodManagerInternal.get().getInputMethodListAsUser(callingUserId);
+ if (inputMethodInfoList != null) {
+ for (InputMethodInfo info : inputMethodInfoList) {
+ if (info.getId().equals(imeId)) {
+ inputMethodInfo = info;
+ break;
+ }
+ }
+ }
+
+ if (inputMethodInfo == null
+ || !inputMethodInfo.getPackageName().equals(servicePackageName)) {
+ throw new SecurityException("The input method is in a different package with the "
+ + "accessibility service");
+ }
+
+ // TODO(b/207697949, b/208872785): Add cts test for managed device.
+ // Use RestrictedLockUtilsInternal in AccessibilitySecurityPolicy
+ if (checkIfInputMethodDisallowed(
+ mContext, inputMethodInfo.getPackageName(), callingUserId) != null) {
+ return ENABLE_IME_FAIL_BY_ADMIN;
+ }
+
+ return ENABLE_IME_SUCCESS;
+ }
+
+ /**
+ * @return the UserHandle for a userId. Return null for USER_NULL
+ */
+ private static UserHandle getUserHandleOf(@UserIdInt int userId) {
+ if (userId == UserHandle.USER_NULL) {
+ return null;
+ } else {
+ return UserHandle.of(userId);
+ }
+ }
+
+ private static int getManagedProfileId(Context context, int userId) {
+ UserManager um = context.getSystemService(UserManager.class);
+ List<UserInfo> userProfiles = um.getProfiles(userId);
+ for (UserInfo uInfo : userProfiles) {
+ if (uInfo.id == userId) {
+ continue;
+ }
+ if (uInfo.isManagedProfile()) {
+ return uInfo.id;
+ }
+ }
+ return UserHandle.USER_NULL;
+ }
+
+ private static RestrictedLockUtils.EnforcedAdmin checkIfInputMethodDisallowed(Context context,
+ String packageName, int userId) {
+ DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+ if (dpm == null) {
+ return null;
+ }
+ RestrictedLockUtils.EnforcedAdmin admin =
+ RestrictedLockUtils.getProfileOrDeviceOwner(context, getUserHandleOf(userId));
+ boolean permitted = true;
+ if (admin != null) {
+ permitted = dpm.isInputMethodPermittedByAdmin(admin.component,
+ packageName, userId);
+ }
+
+ boolean permittedByParentAdmin = true;
+ RestrictedLockUtils.EnforcedAdmin profileAdmin = null;
+ int managedProfileId = getManagedProfileId(context, userId);
+ if (managedProfileId != UserHandle.USER_NULL) {
+ profileAdmin = RestrictedLockUtils.getProfileOrDeviceOwner(
+ context, getUserHandleOf(managedProfileId));
+ // If the device is an organization-owned device with a managed profile, the
+ // managedProfileId will be used instead of the affected userId. This is because
+ // isInputMethodPermittedByAdmin is called on the parent DPM instance, which will
+ // return results affecting the personal profile.
+ if (profileAdmin != null && dpm.isOrganizationOwnedDeviceWithManagedProfile()) {
+ DevicePolicyManager parentDpm = dpm.getParentProfileInstance(
+ UserManager.get(context).getUserInfo(managedProfileId));
+ permittedByParentAdmin = parentDpm.isInputMethodPermittedByAdmin(
+ profileAdmin.component, packageName, managedProfileId);
+ }
+ }
+ if (!permitted && !permittedByParentAdmin) {
+ return RestrictedLockUtils.EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
+ } else if (!permitted) {
+ return admin;
+ } else if (!permittedByParentAdmin) {
+ return profileAdmin;
+ }
+ return null;
+ }
+
+ /**
* Returns the parent userId of the profile according to the specified userId.
*
* @param userId The userId to check
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index bcb34139056d..8f7260f0df83 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -16,9 +16,13 @@
package com.android.server.accessibility;
+import static android.accessibilityservice.AccessibilityService.SoftKeyboardController.ENABLE_IME_FAIL_UNKNOWN;
+import static android.accessibilityservice.AccessibilityService.SoftKeyboardController.ENABLE_IME_SUCCESS;
+
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import android.Manifest;
+import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.IAccessibilityServiceClient;
@@ -310,6 +314,41 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
}
@Override
+ @AccessibilityService.SoftKeyboardController.EnableImeResult
+ public int setInputMethodEnabled(String imeId, boolean enabled) throws SecurityException {
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("switchToInputMethod", "imeId=" + imeId);
+ }
+ synchronized (mLock) {
+ if (!hasRightsToCurrentUserLocked()) {
+ return ENABLE_IME_FAIL_UNKNOWN;
+ }
+ }
+
+ final int callingUserId = UserHandle.getCallingUserId();
+ final InputMethodManagerInternal inputMethodManagerInternal =
+ InputMethodManagerInternal.get();
+
+ final @AccessibilityService.SoftKeyboardController.EnableImeResult int checkResult;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ checkResult = mSecurityPolicy.canEnableDisableInputMethod(imeId, this);
+ }
+ if (checkResult != ENABLE_IME_SUCCESS) {
+ return checkResult;
+ }
+ if (inputMethodManagerInternal.setInputMethodEnabled(imeId,
+ enabled, callingUserId)) {
+ return ENABLE_IME_SUCCESS;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ return ENABLE_IME_FAIL_UNKNOWN;
+ }
+
+ @Override
public boolean isAccessibilityButtonAvailable() {
if (svcConnTracingEnabled()) {
logTraceSvcConn("isAccessibilityButtonAvailable", "");
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index 7ee0690265f3..6cc22143d6d6 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -16,6 +16,7 @@
package com.android.server.accessibility;
+import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.IAccessibilityServiceClient;
@@ -333,6 +334,11 @@ class UiAutomationManager {
}
@Override
+ public int setInputMethodEnabled(String imeId, boolean enabled) {
+ return AccessibilityService.SoftKeyboardController.ENABLE_IME_FAIL_UNKNOWN;
+ }
+
+ @Override
public boolean isAccessibilityButtonAvailable() {
return false;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index 51e34178e80c..38615feeba48 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -63,7 +63,8 @@ import java.util.Locale;
* magnification region. If a value is out of bounds, it will be adjusted to guarantee these
* constraints.
*/
-public class FullScreenMagnificationController {
+public class FullScreenMagnificationController implements
+ WindowManagerInternal.AccessibilityControllerInternal.UiChangesForAccessibilityCallbacks {
private static final boolean DEBUG = false;
private static final String LOG_TAG = "FullScreenMagnificationController";
@@ -85,6 +86,8 @@ public class FullScreenMagnificationController {
@GuardedBy("mLock")
private final SparseArray<DisplayMagnification> mDisplays = new SparseArray<>(0);
+ private final Rect mTempRect = new Rect();
+
/**
* This class implements {@link WindowManagerInternal.MagnificationCallbacks} and holds
* magnification information per display.
@@ -727,6 +730,26 @@ public class FullScreenMagnificationController {
}
}
+ @Override
+ public void onRectangleOnScreenRequested(int displayId, int left, int top, int right,
+ int bottom) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return;
+ }
+ if (!display.isMagnifying()) {
+ return;
+ }
+ final Rect magnifiedRegionBounds = mTempRect;
+ display.getMagnifiedFrameInContentCoordsLocked(magnifiedRegionBounds);
+ if (magnifiedRegionBounds.contains(left, top, right, bottom)) {
+ return;
+ }
+ display.onRectangleOnScreenRequested(left, top, right, bottom);
+ }
+ }
+
/**
* Remove the display magnification with given id.
*
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index d430f6b032c1..791da694c811 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -40,7 +40,9 @@ import android.view.accessibility.MagnificationAnimationCallback;
import com.android.internal.accessibility.util.AccessibilityStatsLogUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
import com.android.server.accessibility.AccessibilityManagerService;
+import com.android.server.wm.WindowManagerInternal;
/**
* Handles all magnification controllers initialization, generic interactions,
@@ -68,7 +70,8 @@ import com.android.server.accessibility.AccessibilityManagerService;
*/
public class MagnificationController implements WindowMagnificationManager.Callback,
MagnificationGestureHandler.Callback,
- FullScreenMagnificationController.MagnificationInfoChangedCallback {
+ FullScreenMagnificationController.MagnificationInfoChangedCallback,
+ WindowManagerInternal.AccessibilityControllerInternal.UiChangesForAccessibilityCallbacks {
private static final boolean DEBUG = false;
private static final String TAG = "MagnificationController";
@@ -96,6 +99,11 @@ public class MagnificationController implements WindowMagnificationManager.Callb
private long mWindowModeEnabledTime = 0;
private long mFullScreenModeEnabledTime = 0;
+ @GuardedBy("mLock")
+ @Nullable
+ private WindowManagerInternal.AccessibilityControllerInternal.UiChangesForAccessibilityCallbacks
+ mAccessibilityCallbacksDelegate;
+
/**
* A callback to inform the magnification transition result on the given display.
*/
@@ -115,6 +123,8 @@ public class MagnificationController implements WindowMagnificationManager.Callb
mLock = lock;
mContext = context;
mScaleProvider = scaleProvider;
+ LocalServices.getService(WindowManagerInternal.class)
+ .getAccessibilityController().setUiChangesForAccessibilityCallbacks(this);
}
@VisibleForTesting
@@ -292,6 +302,37 @@ public class MagnificationController implements WindowMagnificationManager.Callb
return false;
}
+ @GuardedBy("mLock")
+ private void setActivatedModeAndSwitchDelegate(int mode) {
+ mActivatedMode = mode;
+ assignMagnificationWindowManagerDelegateByMode(mode);
+ }
+
+ private void assignMagnificationWindowManagerDelegateByMode(int mode) {
+ synchronized (mLock) {
+ if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN) {
+ mAccessibilityCallbacksDelegate = getFullScreenMagnificationController();
+ } else if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
+ mAccessibilityCallbacksDelegate = getWindowMagnificationMgr();
+ } else {
+ mAccessibilityCallbacksDelegate = null;
+ }
+ }
+ }
+
+ @Override
+ public void onRectangleOnScreenRequested(int displayId, int left, int top, int right,
+ int bottom) {
+ WindowManagerInternal.AccessibilityControllerInternal.UiChangesForAccessibilityCallbacks
+ delegate;
+ synchronized (mLock) {
+ delegate = mAccessibilityCallbacksDelegate;
+ }
+ if (delegate != null) {
+ delegate.onRectangleOnScreenRequested(displayId, left, top, right, bottom);
+ }
+ }
+
@Override
public void onRequestMagnificationSpec(int displayId, int serviceId) {
final WindowMagnificationManager windowMagnificationManager;
@@ -314,7 +355,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
mWindowModeEnabledTime = SystemClock.uptimeMillis();
synchronized (mLock) {
- mActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
+ setActivatedModeAndSwitchDelegate(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
mLastActivatedMode = mActivatedMode;
}
logMagnificationModeWithImeOnIfNeeded();
@@ -324,7 +365,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
SystemClock.uptimeMillis() - mWindowModeEnabledTime);
synchronized (mLock) {
- mActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
+ setActivatedModeAndSwitchDelegate(ACCESSIBILITY_MAGNIFICATION_MODE_NONE);
}
}
updateMagnificationButton(displayId, ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
@@ -353,7 +394,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
mFullScreenModeEnabledTime = SystemClock.uptimeMillis();
synchronized (mLock) {
- mActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+ setActivatedModeAndSwitchDelegate(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
mLastActivatedMode = mActivatedMode;
}
logMagnificationModeWithImeOnIfNeeded();
@@ -363,7 +404,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
SystemClock.uptimeMillis() - mFullScreenModeEnabledTime);
synchronized (mLock) {
- mActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
+ setActivatedModeAndSwitchDelegate(ACCESSIBILITY_MAGNIFICATION_MODE_NONE);
}
}
updateMagnificationButton(displayId, ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
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 d0b989582ebe..7a525ee23d27 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
@@ -119,6 +119,18 @@ public class MagnificationProcessor {
return false;
}
+ private boolean setScaleAndCenterForFullScreenMagnification(int displayId, float scale,
+ float centerX, float centerY,
+ boolean animate, int id) {
+ if (!isRegistered(displayId)) {
+ register(displayId);
+ }
+ return mController.getFullScreenMagnificationController().setScaleAndCenter(
+ displayId,
+ scale,
+ centerX, centerY, animate, id);
+ }
+
/**
* Returns {@code true} if transition magnification mode needed. And it is no need to transition
* mode when the controlling mode is unchanged or the controlling magnifier is not activated.
@@ -135,24 +147,18 @@ public class MagnificationProcessor {
}
/**
- * Returns the magnification scale. If an animation is in progress,
- * this reflects the end state of the animation.
+ * Returns the magnification scale of full-screen magnification on the display.
+ * If an animation is in progress, this reflects the end state of the animation.
*
* @param displayId The logical display id.
* @return the scale
*/
public float getScale(int displayId) {
- int mode = getControllingMode(displayId);
- if (mode == MAGNIFICATION_MODE_FULLSCREEN) {
- return mController.getFullScreenMagnificationController().getScale(displayId);
- } else if (mode == MAGNIFICATION_MODE_WINDOW) {
- return mController.getWindowMagnificationMgr().getScale(displayId);
- }
- return 0;
+ return mController.getFullScreenMagnificationController().getScale(displayId);
}
/**
- * Returns the magnification center in X coordinate of the controlling magnification mode.
+ * Returns the magnification center in X coordinate of full-screen magnification.
* If the service can control magnification but fullscreen magnifier is not registered, it will
* register the magnifier for this call then unregister the magnifier finally to make the
* magnification center correct.
@@ -162,25 +168,19 @@ public class MagnificationProcessor {
* @return the X coordinate
*/
public float getCenterX(int displayId, boolean canControlMagnification) {
- int mode = getControllingMode(displayId);
- if (mode == MAGNIFICATION_MODE_FULLSCREEN) {
- boolean registeredJustForThisCall = registerDisplayMagnificationIfNeeded(displayId,
- canControlMagnification);
- try {
- return mController.getFullScreenMagnificationController().getCenterX(displayId);
- } finally {
- if (registeredJustForThisCall) {
- unregister(displayId);
- }
+ boolean registeredJustForThisCall = registerDisplayMagnificationIfNeeded(displayId,
+ canControlMagnification);
+ try {
+ return mController.getFullScreenMagnificationController().getCenterX(displayId);
+ } finally {
+ if (registeredJustForThisCall) {
+ unregister(displayId);
}
- } else if (mode == MAGNIFICATION_MODE_WINDOW) {
- return mController.getWindowMagnificationMgr().getCenterX(displayId);
}
- return 0;
}
/**
- * Returns the magnification center in Y coordinate of the controlling magnification mode.
+ * Returns the magnification center in Y coordinate of full-screen magnification.
* If the service can control magnification but fullscreen magnifier is not registered, it will
* register the magnifier for this call then unregister the magnifier finally to make the
* magnification center correct.
@@ -190,49 +190,25 @@ public class MagnificationProcessor {
* @return the Y coordinate
*/
public float getCenterY(int displayId, boolean canControlMagnification) {
- int mode = getControllingMode(displayId);
- if (mode == MAGNIFICATION_MODE_FULLSCREEN) {
- boolean registeredJustForThisCall = registerDisplayMagnificationIfNeeded(displayId,
- canControlMagnification);
- try {
- return mController.getFullScreenMagnificationController().getCenterY(displayId);
- } finally {
- if (registeredJustForThisCall) {
- unregister(displayId);
- }
+ boolean registeredJustForThisCall = registerDisplayMagnificationIfNeeded(displayId,
+ canControlMagnification);
+ try {
+ return mController.getFullScreenMagnificationController().getCenterY(displayId);
+ } finally {
+ if (registeredJustForThisCall) {
+ unregister(displayId);
}
- } else if (mode == MAGNIFICATION_MODE_WINDOW) {
- return mController.getWindowMagnificationMgr().getCenterY(displayId);
}
- return 0;
}
/**
- * Return the magnification bounds of the current controlling magnification on the given
- * display. If the magnifier is not enabled, it returns an empty region.
- * If the service can control magnification but fullscreen magnifier is not registered, it will
- * register the magnifier for this call then unregister the magnifier finally to make
- * the magnification region correct.
+ * Returns the magnification bounds of full-screen magnification on the given display.
*
* @param displayId The logical display id
* @param outRegion the region to populate
* @param canControlMagnification Whether the service can control magnification
- * @return outRegion the magnification bounds of full-screen magnifier or the magnification
- * source bounds of window magnifier
*/
- public Region getMagnificationRegion(int displayId, @NonNull Region outRegion,
- boolean canControlMagnification) {
- int mode = getControllingMode(displayId);
- if (mode == MAGNIFICATION_MODE_FULLSCREEN) {
- getFullscreenMagnificationRegion(displayId, outRegion, canControlMagnification);
- } else if (mode == MAGNIFICATION_MODE_WINDOW) {
- mController.getWindowMagnificationMgr().getMagnificationSourceBounds(displayId,
- outRegion);
- }
- return outRegion;
- }
-
- private void getFullscreenMagnificationRegion(int displayId, @NonNull Region outRegion,
+ public void getFullscreenMagnificationRegion(int displayId, @NonNull Region outRegion,
boolean canControlMagnification) {
boolean registeredJustForThisCall = registerDisplayMagnificationIfNeeded(displayId,
canControlMagnification);
@@ -246,21 +222,9 @@ public class MagnificationProcessor {
}
}
- private boolean setScaleAndCenterForFullScreenMagnification(int displayId, float scale,
- float centerX, float centerY,
- boolean animate, int id) {
- if (!isRegistered(displayId)) {
- register(displayId);
- }
- return mController.getFullScreenMagnificationController().setScaleAndCenter(
- displayId,
- scale,
- centerX, centerY, animate, id);
- }
-
/**
- * Resets the magnification on the given display. The reset mode could be full-screen or
- * window if it is activated.
+ * Resets the current magnification on the given display. The reset mode could be
+ * full-screen or window if it is activated.
*
* @param displayId The logical display id.
* @param animate {@code true} to animate the transition, {@code false}
@@ -268,7 +232,7 @@ public class MagnificationProcessor {
* @return {@code true} if the magnification spec changed, {@code false} if
* the spec did not change
*/
- public boolean reset(int displayId, boolean animate) {
+ public boolean resetCurrentMagnification(int displayId, boolean animate) {
int mode = getControllingMode(displayId);
if (mode == MAGNIFICATION_MODE_FULLSCREEN) {
return mController.getFullScreenMagnificationController().reset(displayId, animate);
@@ -279,6 +243,19 @@ public class MagnificationProcessor {
}
/**
+ * Resets the full-screen magnification on the given display.
+ *
+ * @param displayId The logical display id.
+ * @param animate {@code true} to animate the transition, {@code false}
+ * to transition immediately
+ * @return {@code true} if the magnification spec changed, {@code false} if
+ * the spec did not change
+ */
+ public boolean resetFullscreenMagnification(int displayId, boolean animate) {
+ return mController.getFullScreenMagnificationController().reset(displayId, animate);
+ }
+
+ /**
* {@link FullScreenMagnificationController#resetIfNeeded(int, boolean)}
*/
// TODO: support window magnification
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 9162064780ca..3f6ff25dafe5 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -45,6 +45,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.server.wm.WindowManagerInternal;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -57,7 +58,8 @@ import java.lang.annotation.RetentionPolicy;
* {@link MagnificationScaleProvider#constrainScale(float)}
*/
public class WindowMagnificationManager implements
- PanningScalingHandler.MagnificationDelegate {
+ PanningScalingHandler.MagnificationDelegate,
+ WindowManagerInternal.AccessibilityControllerInternal.UiChangesForAccessibilityCallbacks {
private static final boolean DBG = false;
@@ -264,6 +266,13 @@ public class WindowMagnificationManager implements
}
@Override
+ public void onRectangleOnScreenRequested(int displayId, int left, int top, int right,
+ int bottom) {
+ // TODO(b/194668976): We will implement following typing focus in window mode after
+ // our refactor.
+ }
+
+ @Override
public boolean processScroll(int displayId, float distanceX, float distanceY) {
moveWindowMagnification(displayId, -distanceX, -distanceY);
return /* event consumed: */ true;
diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
index 0855b9d8f675..0fe90b14c9bb 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
@@ -6,7 +6,6 @@ import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME;
-import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP_WAIT;
import android.app.ApplicationThreadConstants;
import android.app.IBackupAgent;
@@ -22,6 +21,7 @@ import android.os.RemoteException;
import android.os.SELinux;
import android.util.Slog;
+import com.android.server.backup.OperationStorage.OpType;
import com.android.server.backup.fullbackup.AppMetadataBackupWriter;
import com.android.server.backup.remote.ServiceBackupCallback;
import com.android.server.backup.utils.FullBackupUtils;
@@ -162,7 +162,7 @@ public class KeyValueAdbBackupEngine {
long kvBackupAgentTimeoutMillis = mAgentTimeoutParameters.getKvBackupAgentTimeoutMillis();
try {
mBackupManagerService.prepareOperationTimeout(token, kvBackupAgentTimeoutMillis, null,
- OP_TYPE_BACKUP_WAIT);
+ OpType.BACKUP_WAIT);
IBackupCallback callback =
new ServiceBackupCallback(
@@ -262,7 +262,7 @@ public class KeyValueAdbBackupEngine {
pipes = ParcelFileDescriptor.createPipe();
mBackupManagerService.prepareOperationTimeout(token, kvBackupAgentTimeoutMillis, null,
- OP_TYPE_BACKUP_WAIT);
+ OpType.BACKUP_WAIT);
// We will have to create a runnable that will read the manifest and backup data we
// created, such that we can pipe the data into mOutput. The reason we do this is that
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 98ea03e35296..f8da03539ae6 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -107,12 +107,14 @@ import com.android.internal.util.Preconditions;
import com.android.server.AppWidgetBackupBridge;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
+import com.android.server.backup.OperationStorage.OpState;
+import com.android.server.backup.OperationStorage.OpType;
import com.android.server.backup.fullbackup.FullBackupEntry;
import com.android.server.backup.fullbackup.PerformFullTransportBackupTask;
import com.android.server.backup.internal.BackupHandler;
import com.android.server.backup.internal.ClearDataObserver;
+import com.android.server.backup.internal.LifecycleOperationStorage;
import com.android.server.backup.internal.OnTaskFinishedListener;
-import com.android.server.backup.internal.Operation;
import com.android.server.backup.internal.PerformInitializeTask;
import com.android.server.backup.internal.RunInitializeReceiver;
import com.android.server.backup.internal.SetupObserver;
@@ -287,21 +289,6 @@ public class UserBackupManagerService {
private static final String BACKUP_FINISHED_ACTION = "android.intent.action.BACKUP_FINISHED";
private static final String BACKUP_FINISHED_PACKAGE_EXTRA = "packageName";
- // Bookkeeping of in-flight operations. The operation token is the index of the entry in the
- // pending operations list.
- public static final int OP_PENDING = 0;
- private static final int OP_ACKNOWLEDGED = 1;
- private static final int OP_TIMEOUT = -1;
-
- // Waiting for backup agent to respond during backup operation.
- public static final int OP_TYPE_BACKUP_WAIT = 0;
-
- // Waiting for backup agent to respond during restore operation.
- public static final int OP_TYPE_RESTORE_WAIT = 1;
-
- // An entire backup operation spanning multiple packages.
- public static final int OP_TYPE_BACKUP = 2;
-
// Time delay for initialization operations that can be delayed so as not to consume too much
// CPU on bring-up and increase time-to-UI.
private static final long INITIALIZATION_DELAY_MILLIS = 3000;
@@ -400,30 +387,8 @@ public class UserBackupManagerService {
private ActiveRestoreSession mActiveRestoreSession;
- /**
- * mCurrentOperations contains the list of currently active operations.
- *
- * If type of operation is OP_TYPE_WAIT, it are waiting for an ack or timeout.
- * An operation wraps a BackupRestoreTask within it.
- * It's the responsibility of this task to remove the operation from this array.
- *
- * A BackupRestore task gets notified of ack/timeout for the operation via
- * BackupRestoreTask#handleCancel, BackupRestoreTask#operationComplete and notifyAll called
- * on the mCurrentOpLock.
- * {@link UserBackupManagerService#waitUntilOperationComplete(int)} is
- * used in various places to 'wait' for notifyAll and detect change of pending state of an
- * operation. So typically, an operation will be removed from this array by:
- * - BackupRestoreTask#handleCancel and
- * - BackupRestoreTask#operationComplete OR waitUntilOperationComplete. Do not remove at both
- * these places because waitUntilOperationComplete relies on the operation being present to
- * determine its completion status.
- *
- * If type of operation is OP_BACKUP, it is a task running backups. It provides a handle to
- * cancel backup tasks.
- */
- @GuardedBy("mCurrentOpLock")
- private final SparseArray<Operation> mCurrentOperations = new SparseArray<>();
- private final Object mCurrentOpLock = new Object();
+ private final LifecycleOperationStorage mOperationStorage;
+
private final Random mTokenGenerator = new Random();
private final AtomicInteger mNextToken = new AtomicInteger();
@@ -542,12 +507,14 @@ public class UserBackupManagerService {
}
@VisibleForTesting
- UserBackupManagerService(Context context, PackageManager packageManager) {
+ UserBackupManagerService(Context context, PackageManager packageManager,
+ LifecycleOperationStorage operationStorage) {
mContext = context;
mUserId = 0;
mRegisterTransportsRequestedTime = 0;
mPackageManager = packageManager;
+ mOperationStorage = operationStorage;
mBaseStateDir = null;
mDataDir = null;
@@ -600,8 +567,10 @@ public class UserBackupManagerService {
BackupAgentTimeoutParameters(Handler.getMain(), mContext.getContentResolver());
mAgentTimeoutParameters.start();
+ mOperationStorage = new LifecycleOperationStorage(mUserId);
+
Objects.requireNonNull(userBackupThread, "userBackupThread cannot be null");
- mBackupHandler = new BackupHandler(this, userBackupThread);
+ mBackupHandler = new BackupHandler(this, mOperationStorage, userBackupThread);
// Set up our bookkeeping
final ContentResolver resolver = context.getContentResolver();
@@ -756,6 +725,10 @@ public class UserBackupManagerService {
return mTransportManager;
}
+ public OperationStorage getOperationStorage() {
+ return mOperationStorage;
+ }
+
public boolean isEnabled() {
return mEnabled;
}
@@ -838,14 +811,6 @@ public class UserBackupManagerService {
return mActiveRestoreSession;
}
- public SparseArray<Operation> getCurrentOperations() {
- return mCurrentOperations;
- }
-
- public Object getCurrentOpLock() {
- return mCurrentOpLock;
- }
-
public SparseArray<AdbParams> getAdbBackupRestoreConfirmations() {
return mAdbBackupRestoreConfirmations;
}
@@ -1987,18 +1952,12 @@ public class UserBackupManagerService {
}
final long oldToken = Binder.clearCallingIdentity();
try {
- List<Integer> operationsToCancel = new ArrayList<>();
- synchronized (mCurrentOpLock) {
- for (int i = 0; i < mCurrentOperations.size(); i++) {
- Operation op = mCurrentOperations.valueAt(i);
- int token = mCurrentOperations.keyAt(i);
- if (op.type == OP_TYPE_BACKUP) {
- operationsToCancel.add(token);
- }
- }
- }
+ Set<Integer> operationsToCancel =
+ mOperationStorage.operationTokensForOpType(OpType.BACKUP);
+
for (Integer token : operationsToCancel) {
- handleCancel(token, true /* cancelAll */);
+ mOperationStorage.cancelOperation(token, /* cancelAll */ true,
+ operationType -> { /* no callback needed here */ });
}
// We don't want the backup jobs to kick in any time soon.
// Reschedules them to run in the distant future.
@@ -2012,7 +1971,7 @@ public class UserBackupManagerService {
/** Schedule a timeout message for the operation identified by {@code token}. */
public void prepareOperationTimeout(int token, long interval, BackupRestoreTask callback,
int operationType) {
- if (operationType != OP_TYPE_BACKUP_WAIT && operationType != OP_TYPE_RESTORE_WAIT) {
+ if (operationType != OpType.BACKUP_WAIT && operationType != OpType.RESTORE_WAIT) {
Slog.wtf(
TAG,
addUserIdToLogMessage(
@@ -2036,19 +1995,17 @@ public class UserBackupManagerService {
+ callback));
}
- synchronized (mCurrentOpLock) {
- mCurrentOperations.put(token, new Operation(OP_PENDING, callback, operationType));
- Message msg = mBackupHandler.obtainMessage(getMessageIdForOperationType(operationType),
- token, 0, callback);
- mBackupHandler.sendMessageDelayed(msg, interval);
- }
+ mOperationStorage.registerOperation(token, OpState.PENDING, callback, operationType);
+ Message msg = mBackupHandler.obtainMessage(getMessageIdForOperationType(operationType),
+ token, 0, callback);
+ mBackupHandler.sendMessageDelayed(msg, interval);
}
private int getMessageIdForOperationType(int operationType) {
switch (operationType) {
- case OP_TYPE_BACKUP_WAIT:
+ case OpType.BACKUP_WAIT:
return MSG_BACKUP_OPERATION_TIMEOUT;
- case OP_TYPE_RESTORE_WAIT:
+ case OpType.RESTORE_WAIT:
return MSG_RESTORE_OPERATION_TIMEOUT;
default:
Slog.wtf(
@@ -2061,162 +2018,28 @@ public class UserBackupManagerService {
}
}
- /**
- * Add an operation to the list of currently running operations. Used for cancellation,
- * completion and timeout callbacks that act on the operation via the {@code token}.
- */
- public void putOperation(int token, Operation operation) {
- if (MORE_DEBUG) {
- Slog.d(
- TAG,
- addUserIdToLogMessage(
- mUserId,
- "Adding operation token="
- + Integer.toHexString(token)
- + ", operation type="
- + operation.type));
- }
- synchronized (mCurrentOpLock) {
- mCurrentOperations.put(token, operation);
- }
- }
-
- /**
- * Remove an operation from the list of currently running operations. An operation is removed
- * when it is completed, cancelled, or timed out, and thus no longer running.
- */
- public void removeOperation(int token) {
- if (MORE_DEBUG) {
- Slog.d(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Removing operation token=" + Integer.toHexString(token)));
- }
- synchronized (mCurrentOpLock) {
- if (mCurrentOperations.get(token) == null) {
- Slog.w(TAG, addUserIdToLogMessage(mUserId, "Duplicate remove for operation. token="
- + Integer.toHexString(token)));
- }
- mCurrentOperations.remove(token);
- }
- }
-
/** Block until we received an operation complete message (from the agent or cancellation). */
public boolean waitUntilOperationComplete(int token) {
- if (MORE_DEBUG) {
- Slog.i(TAG, addUserIdToLogMessage(mUserId, "Blocking until operation complete for "
- + Integer.toHexString(token)));
- }
- int finalState = OP_PENDING;
- Operation op = null;
- synchronized (mCurrentOpLock) {
- while (true) {
- op = mCurrentOperations.get(token);
- if (op == null) {
- // mysterious disappearance: treat as success with no callback
- break;
- } else {
- if (op.state == OP_PENDING) {
- try {
- mCurrentOpLock.wait();
- } catch (InterruptedException e) {
- }
- // When the wait is notified we loop around and recheck the current state
- } else {
- if (MORE_DEBUG) {
- Slog.d(
- TAG,
- addUserIdToLogMessage(
- mUserId,
- "Unblocked waiting for operation token="
- + Integer.toHexString(token)));
- }
- // No longer pending; we're done
- finalState = op.state;
- break;
- }
- }
- }
- }
-
- removeOperation(token);
- if (op != null) {
- mBackupHandler.removeMessages(getMessageIdForOperationType(op.type));
- }
- if (MORE_DEBUG) {
- Slog.v(TAG, addUserIdToLogMessage(mUserId, "operation " + Integer.toHexString(token)
- + " complete: finalState=" + finalState));
- }
- return finalState == OP_ACKNOWLEDGED;
+ return mOperationStorage.waitUntilOperationComplete(token, operationType -> {
+ mBackupHandler.removeMessages(getMessageIdForOperationType(operationType));
+ });
}
/** Cancel the operation associated with {@code token}. */
public void handleCancel(int token, boolean cancelAll) {
- // Notify any synchronous waiters
- Operation op = null;
- synchronized (mCurrentOpLock) {
- op = mCurrentOperations.get(token);
- if (MORE_DEBUG) {
- if (op == null) {
- Slog.w(
- TAG,
- addUserIdToLogMessage(
- mUserId,
- "Cancel of token "
- + Integer.toHexString(token)
- + " but no op found"));
- }
- }
- int state = (op != null) ? op.state : OP_TIMEOUT;
- if (state == OP_ACKNOWLEDGED) {
- // The operation finished cleanly, so we have nothing more to do.
- if (DEBUG) {
- Slog.w(TAG, addUserIdToLogMessage(mUserId, "Operation already got an ack."
- + "Should have been removed from mCurrentOperations."));
- }
- op = null;
- mCurrentOperations.delete(token);
- } else if (state == OP_PENDING) {
- if (DEBUG) {
- Slog.v(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Cancel: token=" + Integer.toHexString(token)));
- }
- op.state = OP_TIMEOUT;
- // Can't delete op from mCurrentOperations here. waitUntilOperationComplete may be
- // called after we receive cancel here. We need this op's state there.
-
- // Remove all pending timeout messages of types OP_TYPE_BACKUP_WAIT and
- // OP_TYPE_RESTORE_WAIT. On the other hand, OP_TYPE_BACKUP cannot time out and
- // doesn't require cancellation.
- if (op.type == OP_TYPE_BACKUP_WAIT || op.type == OP_TYPE_RESTORE_WAIT) {
- mBackupHandler.removeMessages(getMessageIdForOperationType(op.type));
- }
- }
- mCurrentOpLock.notifyAll();
- }
-
- // If there's a TimeoutHandler for this event, call it
- if (op != null && op.callback != null) {
- if (MORE_DEBUG) {
- Slog.v(TAG, addUserIdToLogMessage(mUserId, " Invoking cancel on " + op.callback));
+ // Remove all pending timeout messages of types OpType.BACKUP_WAIT and
+ // OpType.RESTORE_WAIT. On the other hand, OP_TYPE_BACKUP cannot time out and
+ // doesn't require cancellation.
+ mOperationStorage.cancelOperation(token, cancelAll, operationType -> {
+ if (operationType == OpType.BACKUP_WAIT || operationType == OpType.RESTORE_WAIT) {
+ mBackupHandler.removeMessages(getMessageIdForOperationType(operationType));
}
- op.callback.handleCancel(cancelAll);
- }
+ });
}
/** Returns {@code true} if a backup is currently running, else returns {@code false}. */
public boolean isBackupOperationInProgress() {
- synchronized (mCurrentOpLock) {
- for (int i = 0; i < mCurrentOperations.size(); i++) {
- Operation op = mCurrentOperations.valueAt(i);
- if (op.type == OP_TYPE_BACKUP && op.state == OP_PENDING) {
- return true;
- }
- }
- }
- return false;
+ return mOperationStorage.isBackupOperationInProgress();
}
/** Unbind the backup agent and kill the app if it's a non-system app. */
@@ -2578,6 +2401,7 @@ public class UserBackupManagerService {
String[] pkg = new String[]{entry.packageName};
mRunningFullBackupTask = PerformFullTransportBackupTask.newWithCurrentTransport(
this,
+ mOperationStorage,
/* observer */ null,
pkg,
/* updateSchedule */ true,
@@ -3107,6 +2931,7 @@ public class UserBackupManagerService {
CountDownLatch latch = new CountDownLatch(1);
Runnable task = PerformFullTransportBackupTask.newWithCurrentTransport(
this,
+ mOperationStorage,
/* observer */ null,
pkgNames,
/* updateSchedule */ false,
@@ -3893,7 +3718,6 @@ public class UserBackupManagerService {
* only be called from the {@link ActivityManager}.
*/
public void agentDisconnected(String packageName) {
- // TODO: handle backup being interrupted
synchronized (mAgentConnectLock) {
if (Binder.getCallingUid() == Process.SYSTEM_UID) {
mConnectedAgent = null;
@@ -3907,6 +3731,20 @@ public class UserBackupManagerService {
+ Binder.getCallingUid()
+ " claiming agent disconnected"));
}
+ Slog.w(TAG, "agentDisconnected: the backup agent for " + packageName
+ + " died: cancel current operations");
+
+ // handleCancel() causes the PerformFullTransportBackupTask to go on to
+ // tearDownAgentAndKill: that will unbindBackupAgent in the Activity Manager, so
+ // that the package being backed up doesn't get stuck in restricted mode until the
+ // backup time-out elapses.
+ for (int token : mOperationStorage.operationTokensForPackage(packageName)) {
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "agentDisconnected: will handleCancel(all) for token:"
+ + Integer.toHexString(token));
+ }
+ handleCancel(token, true /* cancelAll */);
+ }
mAgentConnectLock.notifyAll();
}
}
@@ -4126,48 +3964,11 @@ public class UserBackupManagerService {
* outstanding asynchronous backup/restore operation.
*/
public void opComplete(int token, long result) {
- if (MORE_DEBUG) {
- Slog.v(
- TAG,
- addUserIdToLogMessage(
- mUserId,
- "opComplete: " + Integer.toHexString(token) + " result=" + result));
- }
- Operation op = null;
- synchronized (mCurrentOpLock) {
- op = mCurrentOperations.get(token);
- if (op != null) {
- if (op.state == OP_TIMEOUT) {
- // The operation already timed out, and this is a late response. Tidy up
- // and ignore it; we've already dealt with the timeout.
- op = null;
- mCurrentOperations.delete(token);
- } else if (op.state == OP_ACKNOWLEDGED) {
- if (DEBUG) {
- Slog.w(
- TAG,
- addUserIdToLogMessage(
- mUserId,
- "Received duplicate ack for token="
- + Integer.toHexString(token)));
- }
- op = null;
- mCurrentOperations.remove(token);
- } else if (op.state == OP_PENDING) {
- // Can't delete op from mCurrentOperations. waitUntilOperationComplete can be
- // called after we we receive this call.
- op.state = OP_ACKNOWLEDGED;
- }
- }
- mCurrentOpLock.notifyAll();
- }
-
- // The completion callback, if any, is invoked on the handler
- if (op != null && op.callback != null) {
- Pair<BackupRestoreTask, Long> callbackAndResult = Pair.create(op.callback, result);
+ mOperationStorage.onOperationComplete(token, result, callback -> {
+ Pair<BackupRestoreTask, Long> callbackAndResult = Pair.create(callback, result);
Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, callbackAndResult);
mBackupHandler.sendMessage(msg);
- }
+ });
}
/** Checks if the package is eligible for backup. */
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
index fe5497f3eb94..1e1ca95d69db 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
@@ -21,7 +21,6 @@ import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME;
import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_FILENAME;
-import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP_WAIT;
import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import android.annotation.UserIdInt;
@@ -39,6 +38,7 @@ import android.util.Slog;
import com.android.server.AppWidgetBackupBridge;
import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupRestoreTask;
+import com.android.server.backup.OperationStorage.OpType;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.remote.RemoteCall;
import com.android.server.backup.utils.BackupEligibilityRules;
@@ -147,7 +147,7 @@ public class FullBackupEngine {
mToken,
timeout,
mTimeoutMonitor /* in parent class */,
- OP_TYPE_BACKUP_WAIT);
+ OpType.BACKUP_WAIT);
mAgent.doFullBackup(
mPipe,
mQuota,
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
index aaf1f0a65dc8..be6ac26ba92a 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
@@ -18,7 +18,6 @@ package com.android.server.backup.fullbackup;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
-import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP_WAIT;
import android.app.backup.IBackupManager;
import android.content.ComponentName;
@@ -33,6 +32,7 @@ import android.util.Slog;
import com.android.internal.backup.IObbBackupService;
import com.android.server.backup.BackupAgentTimeoutParameters;
+import com.android.server.backup.OperationStorage.OpType;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.utils.FullBackupUtils;
@@ -83,7 +83,7 @@ public class FullBackupObbConnection implements ServiceConnection {
long fullBackupAgentTimeoutMillis =
mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
backupManagerService.prepareOperationTimeout(
- token, fullBackupAgentTimeoutMillis, null, OP_TYPE_BACKUP_WAIT);
+ token, fullBackupAgentTimeoutMillis, null, OpType.BACKUP_WAIT);
mService.backupObbs(pkg.packageName, pipes[1], token,
backupManagerService.getBackupManagerBinder());
FullBackupUtils.routeSocketDataToOutput(pipes[0], out);
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
index 448e0860b88d..7ee307e30dce 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
@@ -37,6 +37,7 @@ import android.util.Slog;
import com.android.server.AppWidgetBackupBridge;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.KeyValueAdbBackupEngine;
+import com.android.server.backup.OperationStorage;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.utils.BackupEligibilityRules;
import com.android.server.backup.utils.PasswordUtils;
@@ -67,6 +68,7 @@ import javax.crypto.spec.SecretKeySpec;
public class PerformAdbBackupTask extends FullBackupTask implements BackupRestoreTask {
private final UserBackupManagerService mUserBackupManagerService;
+ private final OperationStorage mOperationStorage;
private final AtomicBoolean mLatch;
private final ParcelFileDescriptor mOutputFile;
@@ -85,7 +87,8 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor
private final int mCurrentOpToken;
private final BackupEligibilityRules mBackupEligibilityRules;
- public PerformAdbBackupTask(UserBackupManagerService backupManagerService,
+ public PerformAdbBackupTask(
+ UserBackupManagerService backupManagerService, OperationStorage operationStorage,
ParcelFileDescriptor fd, IFullBackupRestoreObserver observer,
boolean includeApks, boolean includeObbs, boolean includeShared, boolean doWidgets,
String curPassword, String encryptPassword, boolean doAllApps, boolean doSystem,
@@ -93,6 +96,7 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor
BackupEligibilityRules backupEligibilityRules) {
super(observer);
mUserBackupManagerService = backupManagerService;
+ mOperationStorage = operationStorage;
mCurrentOpToken = backupManagerService.generateRandomIntegerToken();
mLatch = latch;
@@ -505,6 +509,6 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor
if (target != null) {
mUserBackupManagerService.tearDownAgentAndKill(mCurrentTarget.applicationInfo);
}
- mUserBackupManagerService.removeOperation(mCurrentOpToken);
+ mOperationStorage.removeOperation(mCurrentOpToken);
}
}
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index 9ce4eabb0fa1..e74a3b97edd1 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -19,9 +19,6 @@ package com.android.server.backup.fullbackup;
import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.DEBUG_SCHEDULING;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.UserBackupManagerService.OP_PENDING;
-import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP;
-import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP_WAIT;
import android.annotation.Nullable;
import android.app.IBackupAgent;
@@ -45,10 +42,12 @@ import com.android.server.EventLogTags;
import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.FullBackupJob;
+import com.android.server.backup.OperationStorage;
+import com.android.server.backup.OperationStorage.OpState;
+import com.android.server.backup.OperationStorage.OpType;
import com.android.server.backup.TransportManager;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.internal.OnTaskFinishedListener;
-import com.android.server.backup.internal.Operation;
import com.android.server.backup.remote.RemoteCall;
import com.android.server.backup.transport.BackupTransportClient;
import com.android.server.backup.transport.TransportConnection;
@@ -57,12 +56,15 @@ import com.android.server.backup.utils.BackupEligibilityRules;
import com.android.server.backup.utils.BackupManagerMonitorUtils;
import com.android.server.backup.utils.BackupObserverUtils;
+import com.google.android.collect.Sets;
+
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
@@ -99,6 +101,7 @@ import java.util.concurrent.atomic.AtomicLong;
public class PerformFullTransportBackupTask extends FullBackupTask implements BackupRestoreTask {
public static PerformFullTransportBackupTask newWithCurrentTransport(
UserBackupManagerService backupManagerService,
+ OperationStorage operationStorage,
IFullBackupRestoreObserver observer,
String[] whichPackages,
boolean updateSchedule,
@@ -118,6 +121,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
listenerCaller);
return new PerformFullTransportBackupTask(
backupManagerService,
+ operationStorage,
transportConnection,
observer,
whichPackages,
@@ -136,6 +140,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
private UserBackupManagerService mUserBackupManagerService;
private final Object mCancelLock = new Object();
+ OperationStorage mOperationStorage;
List<PackageInfo> mPackages;
PackageInfo mCurrentPackage;
boolean mUpdateSchedule;
@@ -158,6 +163,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
private final BackupEligibilityRules mBackupEligibilityRules;
public PerformFullTransportBackupTask(UserBackupManagerService backupManagerService,
+ OperationStorage operationStorage,
TransportConnection transportConnection,
IFullBackupRestoreObserver observer,
String[] whichPackages, boolean updateSchedule,
@@ -165,7 +171,8 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
@Nullable IBackupManagerMonitor monitor, @Nullable OnTaskFinishedListener listener,
boolean userInitiated, BackupEligibilityRules backupEligibilityRules) {
super(observer);
- this.mUserBackupManagerService = backupManagerService;
+ mUserBackupManagerService = backupManagerService;
+ mOperationStorage = operationStorage;
mTransportConnection = transportConnection;
mUpdateSchedule = updateSchedule;
mLatch = latch;
@@ -191,8 +198,6 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
return;
}
- registerTask();
-
for (String pkg : whichPackages) {
try {
PackageManager pm = backupManagerService.getPackageManager();
@@ -258,19 +263,20 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
}
mPackages = backupManagerService.filterUserFacingPackages(mPackages);
- }
- private void registerTask() {
- synchronized (mUserBackupManagerService.getCurrentOpLock()) {
- Slog.d(TAG, "backupmanager pftbt token=" + Integer.toHexString(mCurrentOpToken));
- mUserBackupManagerService.getCurrentOperations().put(
- mCurrentOpToken,
- new Operation(OP_PENDING, this, OP_TYPE_BACKUP));
+ Set<String> packageNames = Sets.newHashSet();
+ for (PackageInfo pkgInfo : mPackages) {
+ packageNames.add(pkgInfo.packageName);
}
+
+ Slog.d(TAG, "backupmanager pftbt token=" + Integer.toHexString(mCurrentOpToken));
+ mOperationStorage.registerOperationForPackages(mCurrentOpToken, OpState.PENDING,
+ packageNames, this, OpType.BACKUP);
}
+ // public, because called from KeyValueBackupTask.finishTask.
public void unregisterTask() {
- mUserBackupManagerService.removeOperation(mCurrentOpToken);
+ mOperationStorage.removeOperation(mCurrentOpToken);
}
@Override
@@ -722,7 +728,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
try {
mUserBackupManagerService.prepareOperationTimeout(
- mCurrentOpToken, fullBackupAgentTimeoutMillis, this, OP_TYPE_BACKUP_WAIT);
+ mCurrentOpToken, fullBackupAgentTimeoutMillis, this, OpType.BACKUP_WAIT);
if (MORE_DEBUG) {
Slog.d(TAG, "Preflighting full payload of " + pkg.packageName);
}
@@ -777,7 +783,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
}
mResult.set(result);
mLatch.countDown();
- mUserBackupManagerService.removeOperation(mCurrentOpToken);
+ mOperationStorage.removeOperation(mCurrentOpToken);
}
@Override
@@ -787,7 +793,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
}
mResult.set(BackupTransport.AGENT_ERROR);
mLatch.countDown();
- mUserBackupManagerService.removeOperation(mCurrentOpToken);
+ mOperationStorage.removeOperation(mCurrentOpToken);
}
@Override
@@ -833,20 +839,17 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
mBackupResult = BackupTransport.AGENT_ERROR;
mQuota = quota;
mTransportFlags = transportFlags;
- registerTask();
+ registerTask(target.packageName);
}
- void registerTask() {
- synchronized (mUserBackupManagerService.getCurrentOpLock()) {
- mUserBackupManagerService.getCurrentOperations().put(
- mCurrentOpToken, new Operation(OP_PENDING, this, OP_TYPE_BACKUP_WAIT));
- }
+ void registerTask(String packageName) {
+ Set<String> packages = Sets.newHashSet(packageName);
+ mOperationStorage.registerOperationForPackages(mCurrentOpToken, OpState.PENDING,
+ packages, this, OpType.BACKUP_WAIT);
}
void unregisterTask() {
- synchronized (mUserBackupManagerService.getCurrentOpLock()) {
- mUserBackupManagerService.getCurrentOperations().remove(mCurrentOpToken);
- }
+ mOperationStorage.removeOperation(mCurrentOpToken);
}
@Override
@@ -956,7 +959,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba
mPreflightLatch.countDown();
mBackupLatch.countDown();
// We are done with this operation.
- mUserBackupManagerService.removeOperation(mCurrentOpToken);
+ mOperationStorage.removeOperation(mCurrentOpToken);
}
}
}
diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
index 5c2485905814..03796eae3b8e 100644
--- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java
+++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
@@ -35,6 +35,7 @@ import com.android.server.EventLogTags;
import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.DataChangedJournal;
+import com.android.server.backup.OperationStorage;
import com.android.server.backup.TransportManager;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.fullbackup.PerformAdbBackupTask;
@@ -84,6 +85,7 @@ public class BackupHandler extends Handler {
public static final int MSG_STOP = 22;
private final UserBackupManagerService backupManagerService;
+ private final OperationStorage mOperationStorage;
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
private final HandlerThread mBackupThread;
@@ -92,10 +94,12 @@ public class BackupHandler extends Handler {
volatile boolean mIsStopping = false;
public BackupHandler(
- UserBackupManagerService backupManagerService, HandlerThread backupThread) {
+ UserBackupManagerService backupManagerService, OperationStorage operationStorage,
+ HandlerThread backupThread) {
super(backupThread.getLooper());
mBackupThread = backupThread;
this.backupManagerService = backupManagerService;
+ mOperationStorage = operationStorage;
mAgentTimeoutParameters = Objects.requireNonNull(
backupManagerService.getAgentTimeoutParameters(),
"Timeout parameters cannot be null");
@@ -215,6 +219,7 @@ public class BackupHandler extends Handler {
caller);
KeyValueBackupTask.start(
backupManagerService,
+ mOperationStorage,
transportConnection,
transport.transportDirName(),
queue,
@@ -278,8 +283,8 @@ public class BackupHandler extends Handler {
// TODO: refactor full backup to be a looper-based state machine
// similar to normal backup/restore.
AdbBackupParams params = (AdbBackupParams) msg.obj;
- PerformAdbBackupTask task = new PerformAdbBackupTask(backupManagerService,
- params.fd,
+ PerformAdbBackupTask task = new PerformAdbBackupTask(
+ backupManagerService, mOperationStorage, params.fd,
params.observer, params.includeApks, params.includeObbs,
params.includeShared, params.doWidgets, params.curPassword,
params.encryptPassword, params.allApps, params.includeSystem,
@@ -296,6 +301,7 @@ public class BackupHandler extends Handler {
PerformUnifiedRestoreTask task =
new PerformUnifiedRestoreTask(
backupManagerService,
+ mOperationStorage,
params.mTransportConnection,
params.observer,
params.monitor,
@@ -332,7 +338,7 @@ public class BackupHandler extends Handler {
// similar to normal backup/restore.
AdbRestoreParams params = (AdbRestoreParams) msg.obj;
PerformAdbRestoreTask task = new PerformAdbRestoreTask(backupManagerService,
- params.fd,
+ mOperationStorage, params.fd,
params.curPassword, params.encryptPassword,
params.observer, params.latch);
(new Thread(task, "adb-restore")).start();
@@ -459,6 +465,7 @@ public class BackupHandler extends Handler {
KeyValueBackupTask.start(
backupManagerService,
+ mOperationStorage,
params.mTransportConnection,
params.dirName,
params.kvPackages,
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
index 30da8c1d5e76..16aa4ebb0656 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
@@ -23,8 +23,6 @@ import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
import static com.android.server.backup.UserBackupManagerService.KEY_WIDGET_STATE;
-import static com.android.server.backup.UserBackupManagerService.OP_PENDING;
-import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP;
import android.annotation.IntDef;
import android.annotation.Nullable;
@@ -57,10 +55,12 @@ import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.DataChangedJournal;
import com.android.server.backup.KeyValueBackupJob;
+import com.android.server.backup.OperationStorage;
+import com.android.server.backup.OperationStorage.OpState;
+import com.android.server.backup.OperationStorage.OpType;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.fullbackup.PerformFullTransportBackupTask;
import com.android.server.backup.internal.OnTaskFinishedListener;
-import com.android.server.backup.internal.Operation;
import com.android.server.backup.remote.RemoteCall;
import com.android.server.backup.remote.RemoteCallable;
import com.android.server.backup.remote.RemoteResult;
@@ -211,6 +211,7 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
*/
public static KeyValueBackupTask start(
UserBackupManagerService backupManagerService,
+ OperationStorage operationStorage,
TransportConnection transportConnection,
String transportDirName,
List<String> queue,
@@ -227,6 +228,7 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
KeyValueBackupTask task =
new KeyValueBackupTask(
backupManagerService,
+ operationStorage,
transportConnection,
transportDirName,
queue,
@@ -244,6 +246,7 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
}
private final UserBackupManagerService mBackupManagerService;
+ private final OperationStorage mOperationStorage;
private final PackageManager mPackageManager;
private final TransportConnection mTransportConnection;
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
@@ -302,6 +305,7 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
@VisibleForTesting
public KeyValueBackupTask(
UserBackupManagerService backupManagerService,
+ OperationStorage operationStorage,
TransportConnection transportConnection,
String transportDirName,
List<String> queue,
@@ -313,6 +317,7 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
boolean nonIncremental,
BackupEligibilityRules backupEligibilityRules) {
mBackupManagerService = backupManagerService;
+ mOperationStorage = operationStorage;
mPackageManager = backupManagerService.getPackageManager();
mTransportConnection = transportConnection;
mOriginalQueue = queue;
@@ -338,12 +343,11 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
}
private void registerTask() {
- mBackupManagerService.putOperation(
- mCurrentOpToken, new Operation(OP_PENDING, this, OP_TYPE_BACKUP));
+ mOperationStorage.registerOperation(mCurrentOpToken, OpState.PENDING, this, OpType.BACKUP);
}
private void unregisterTask() {
- mBackupManagerService.removeOperation(mCurrentOpToken);
+ mOperationStorage.removeOperation(mCurrentOpToken);
}
@Override
@@ -639,6 +643,7 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable {
private PerformFullTransportBackupTask createFullBackupTask(List<String> packages) {
return new PerformFullTransportBackupTask(
mBackupManagerService,
+ mOperationStorage,
mTransportConnection,
/* fullBackupRestoreObserver */ null,
packages.toArray(new String[packages.size()]),
diff --git a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
index 376b618935c5..cfc0f203e6ec 100644
--- a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
+++ b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
@@ -23,6 +23,7 @@ import android.util.Slog;
import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupRestoreTask;
+import com.android.server.backup.OperationStorage;
import com.android.server.backup.UserBackupManagerService;
import java.util.Objects;
@@ -36,13 +37,16 @@ public class AdbRestoreFinishedLatch implements BackupRestoreTask {
private static final String TAG = "AdbRestoreFinishedLatch";
private UserBackupManagerService backupManagerService;
+ private final OperationStorage mOperationStorage;
final CountDownLatch mLatch;
private final int mCurrentOpToken;
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
public AdbRestoreFinishedLatch(UserBackupManagerService backupManagerService,
+ OperationStorage operationStorage,
int currentOpToken) {
this.backupManagerService = backupManagerService;
+ mOperationStorage = operationStorage;
mLatch = new CountDownLatch(1);
mCurrentOpToken = currentOpToken;
mAgentTimeoutParameters = Objects.requireNonNull(
@@ -72,7 +76,7 @@ public class AdbRestoreFinishedLatch implements BackupRestoreTask {
Slog.w(TAG, "adb onRestoreFinished() complete");
}
mLatch.countDown();
- backupManagerService.removeOperation(mCurrentOpToken);
+ mOperationStorage.removeOperation(mCurrentOpToken);
}
@Override
@@ -81,6 +85,6 @@ public class AdbRestoreFinishedLatch implements BackupRestoreTask {
Slog.w(TAG, "adb onRestoreFinished() timed out");
}
mLatch.countDown();
- backupManagerService.removeOperation(mCurrentOpToken);
+ mOperationStorage.removeOperation(mCurrentOpToken);
}
}
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index 5718bdfc4971..76df8b9f84e8 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -21,7 +21,6 @@ import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME;
import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_FILENAME;
-import static com.android.server.backup.UserBackupManagerService.OP_TYPE_RESTORE_WAIT;
import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
@@ -48,6 +47,8 @@ import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.FileMetadata;
import com.android.server.backup.KeyValueAdbRestoreEngine;
+import com.android.server.backup.OperationStorage;
+import com.android.server.backup.OperationStorage.OpType;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.fullbackup.FullBackupObbConnection;
import com.android.server.backup.utils.BackupEligibilityRules;
@@ -71,6 +72,7 @@ import java.util.Objects;
public class FullRestoreEngine extends RestoreEngine {
private final UserBackupManagerService mBackupManagerService;
+ private final OperationStorage mOperationStorage;
private final int mUserId;
// Task in charge of monitoring timeouts
@@ -133,12 +135,14 @@ public class FullRestoreEngine extends RestoreEngine {
private boolean mPipesClosed;
private final BackupEligibilityRules mBackupEligibilityRules;
- public FullRestoreEngine(UserBackupManagerService backupManagerService,
+ public FullRestoreEngine(
+ UserBackupManagerService backupManagerService, OperationStorage operationStorage,
BackupRestoreTask monitorTask, IFullBackupRestoreObserver observer,
IBackupManagerMonitor monitor, PackageInfo onlyPackage, boolean allowApks,
int ephemeralOpToken, boolean isAdbRestore,
BackupEligibilityRules backupEligibilityRules) {
mBackupManagerService = backupManagerService;
+ mOperationStorage = operationStorage;
mEphemeralOpToken = ephemeralOpToken;
mMonitorTask = monitorTask;
mObserver = observer;
@@ -409,7 +413,7 @@ public class FullRestoreEngine extends RestoreEngine {
mBackupManagerService.prepareOperationTimeout(token,
timeout,
mMonitorTask,
- OP_TYPE_RESTORE_WAIT);
+ OpType.RESTORE_WAIT);
if (FullBackup.OBB_TREE_TOKEN.equals(info.domain)) {
if (DEBUG) {
@@ -603,9 +607,9 @@ public class FullRestoreEngine extends RestoreEngine {
long fullBackupAgentTimeoutMillis =
mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
final AdbRestoreFinishedLatch latch = new AdbRestoreFinishedLatch(
- mBackupManagerService, token);
+ mBackupManagerService, mOperationStorage, token);
mBackupManagerService.prepareOperationTimeout(
- token, fullBackupAgentTimeoutMillis, latch, OP_TYPE_RESTORE_WAIT);
+ token, fullBackupAgentTimeoutMillis, latch, OpType.RESTORE_WAIT);
if (mTargetApp.processName.equals("system")) {
if (MORE_DEBUG) {
Slog.d(TAG, "system agent - restoreFinished on thread");
diff --git a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
index e03150eb824f..22af19e8d2cb 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
@@ -32,6 +32,7 @@ import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
+import com.android.server.backup.OperationStorage;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.fullbackup.FullBackupObbConnection;
import com.android.server.backup.utils.BackupEligibilityRules;
@@ -60,6 +61,7 @@ import javax.crypto.spec.SecretKeySpec;
public class PerformAdbRestoreTask implements Runnable {
private final UserBackupManagerService mBackupManagerService;
+ private final OperationStorage mOperationStorage;
private final ParcelFileDescriptor mInputFile;
private final String mCurrentPassword;
private final String mDecryptPassword;
@@ -68,10 +70,12 @@ public class PerformAdbRestoreTask implements Runnable {
private IFullBackupRestoreObserver mObserver;
- public PerformAdbRestoreTask(UserBackupManagerService backupManagerService,
+ public PerformAdbRestoreTask(
+ UserBackupManagerService backupManagerService, OperationStorage operationStorage,
ParcelFileDescriptor fd, String curPassword, String decryptPassword,
IFullBackupRestoreObserver observer, AtomicBoolean latch) {
this.mBackupManagerService = backupManagerService;
+ mOperationStorage = operationStorage;
mInputFile = fd;
mCurrentPassword = curPassword;
mDecryptPassword = decryptPassword;
@@ -109,9 +113,9 @@ public class PerformAdbRestoreTask implements Runnable {
mBackupManagerService.getPackageManager(),
LocalServices.getService(PackageManagerInternal.class),
mBackupManagerService.getUserId(), BackupManager.OperationType.ADB_BACKUP);
- FullRestoreEngine mEngine = new FullRestoreEngine(mBackupManagerService, null,
- mObserver, null, null, true, 0 /*unused*/, true,
- eligibilityRules);
+ FullRestoreEngine mEngine = new FullRestoreEngine(mBackupManagerService,
+ mOperationStorage, null, mObserver, null, null,
+ true, 0 /*unused*/, true, eligibilityRules);
FullRestoreEngineThread mEngineThread = new FullRestoreEngineThread(mEngine,
tarInputStream);
mEngineThread.run();
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index ac831af7612b..b48367db17c2 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -20,7 +20,6 @@ import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
import static com.android.server.backup.UserBackupManagerService.KEY_WIDGET_STATE;
-import static com.android.server.backup.UserBackupManagerService.OP_TYPE_RESTORE_WAIT;
import static com.android.server.backup.UserBackupManagerService.PACKAGE_MANAGER_SENTINEL;
import static com.android.server.backup.UserBackupManagerService.SETTINGS_PACKAGE;
import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_RESTORE_STEP;
@@ -60,6 +59,8 @@ import com.android.server.LocalServices;
import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.BackupUtils;
+import com.android.server.backup.OperationStorage;
+import com.android.server.backup.OperationStorage.OpType;
import com.android.server.backup.PackageManagerBackupAgent;
import com.android.server.backup.PackageManagerBackupAgent.Metadata;
import com.android.server.backup.TransportManager;
@@ -84,6 +85,7 @@ import java.util.Set;
public class PerformUnifiedRestoreTask implements BackupRestoreTask {
private UserBackupManagerService backupManagerService;
+ private final OperationStorage mOperationStorage;
private final int mUserId;
private final TransportManager mTransportManager;
// Transport client we're working with to do the restore
@@ -169,6 +171,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
PerformUnifiedRestoreTask(UserBackupManagerService backupManagerService) {
mListener = null;
mAgentTimeoutParameters = null;
+ mOperationStorage = null;
mTransportConnection = null;
mTransportManager = null;
mEphemeralOpToken = 0;
@@ -181,6 +184,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
// about releasing it.
public PerformUnifiedRestoreTask(
UserBackupManagerService backupManagerService,
+ OperationStorage operationStorage,
TransportConnection transportConnection,
IRestoreObserver observer,
IBackupManagerMonitor monitor,
@@ -192,6 +196,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
OnTaskFinishedListener listener,
BackupEligibilityRules backupEligibilityRules) {
this.backupManagerService = backupManagerService;
+ mOperationStorage = operationStorage;
mUserId = backupManagerService.getUserId();
mTransportManager = backupManagerService.getTransportManager();
mEphemeralOpToken = backupManagerService.generateRandomIntegerToken();
@@ -767,7 +772,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
long restoreAgentTimeoutMillis = mAgentTimeoutParameters.getRestoreAgentTimeoutMillis(
app.applicationInfo.uid);
backupManagerService.prepareOperationTimeout(
- mEphemeralOpToken, restoreAgentTimeoutMillis, this, OP_TYPE_RESTORE_WAIT);
+ mEphemeralOpToken, restoreAgentTimeoutMillis, this, OpType.RESTORE_WAIT);
startedAgentRestore = true;
mAgent.doRestoreWithExcludedKeys(mBackupData, appVersionCode, mNewState,
mEphemeralOpToken, backupManagerService.getBackupManagerBinder(),
@@ -877,7 +882,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
backupManagerService
.prepareOperationTimeout(mEphemeralOpToken,
restoreAgentFinishedTimeoutMillis, this,
- OP_TYPE_RESTORE_WAIT);
+ OpType.RESTORE_WAIT);
mAgent.doRestoreFinished(mEphemeralOpToken,
backupManagerService.getBackupManagerBinder());
// If we get this far, the callback or timeout will schedule the
@@ -921,7 +926,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
EventLog.writeEvent(EventLogTags.FULL_RESTORE_PACKAGE,
mCurrentPackage.packageName);
- mEngine = new FullRestoreEngine(backupManagerService, this, null,
+ mEngine = new FullRestoreEngine(backupManagerService, mOperationStorage, this, null,
mMonitor, mCurrentPackage, false, mEphemeralOpToken, false,
mBackupEligibilityRules);
mEngineThread = new FullRestoreEngineThread(mEngine, mEnginePipes[0]);
@@ -1071,7 +1076,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
// The app has timed out handling a restoring file
@Override
public void handleCancel(boolean cancelAll) {
- backupManagerService.removeOperation(mEphemeralOpToken);
+ mOperationStorage.removeOperation(mEphemeralOpToken);
if (DEBUG) {
Slog.w(TAG, "Full-data restore target timed out; shutting down");
}
@@ -1268,7 +1273,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
@Override
public void operationComplete(long unusedResult) {
- backupManagerService.removeOperation(mEphemeralOpToken);
+ mOperationStorage.removeOperation(mEphemeralOpToken);
if (MORE_DEBUG) {
Slog.i(TAG, "operationComplete() during restore: target="
+ mCurrentPackage.packageName
@@ -1331,7 +1336,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
// A call to agent.doRestore() or agent.doRestoreFinished() has timed out
@Override
public void handleCancel(boolean cancelAll) {
- backupManagerService.removeOperation(mEphemeralOpToken);
+ mOperationStorage.removeOperation(mEphemeralOpToken);
Slog.e(TAG, "Timeout restoring application " + mCurrentPackage.packageName);
mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
BackupManagerMonitor.LOG_EVENT_ID_KEY_VALUE_RESTORE_TIMEOUT,
diff --git a/services/cloudsearch/OWNERS b/services/cloudsearch/OWNERS
new file mode 100644
index 000000000000..aa4da3b4bee0
--- /dev/null
+++ b/services/cloudsearch/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 758286
+
+huiwu@google.com
+srazdan@google.com
diff --git a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
index bcc345ff98e4..637994f6d73d 100644
--- a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
+++ b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
@@ -16,8 +16,13 @@
package com.android.server.companion;
+import static android.app.PendingIntent.FLAG_CANCEL_CURRENT;
+import static android.app.PendingIntent.FLAG_IMMUTABLE;
+import static android.app.PendingIntent.FLAG_ONE_SHOT;
+import static android.companion.CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME;
+import static android.content.ComponentName.createRelative;
+
import static com.android.internal.util.CollectionUtils.filter;
-import static com.android.internal.util.FunctionalUtils.uncheckExceptions;
import static com.android.server.companion.CompanionDeviceManagerService.DEBUG;
import static com.android.server.companion.CompanionDeviceManagerService.LOG_TAG;
import static com.android.server.companion.PermissionsUtils.enforcePermissionsForAssociation;
@@ -28,73 +33,114 @@ import static java.util.Objects.requireNonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.PendingIntent;
import android.companion.AssociationInfo;
import android.companion.AssociationRequest;
-import android.companion.CompanionDeviceManager;
import android.companion.IAssociationRequestCallback;
-import android.companion.ICompanionDeviceDiscoveryService;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.Signature;
+import android.net.MacAddress;
import android.os.Binder;
-import android.os.IBinder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Parcel;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.util.PackageUtils;
import android.util.Slog;
-import com.android.internal.infra.AndroidFuture;
-import com.android.internal.infra.PerUser;
-import com.android.internal.infra.ServiceConnector;
import com.android.internal.util.ArrayUtils;
-import com.android.server.FgThread;
-import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashSet;
-import java.util.Objects;
import java.util.Set;
+/**
+ * Class responsible for handling incoming {@link AssociationRequest}s.
+ * The main responsibilities of an {@link AssociationRequestsProcessor} are:
+ * <ul>
+ * <li> Requests validation and checking if the package that would own the association holds all
+ * necessary permissions.
+ * <li> Communication with the requester via a provided
+ * {@link android.companion.CompanionDeviceManager.Callback}.
+ * <li> Constructing an {@link Intent} for collecting user's approval (if needed), and handling the
+ * approval.
+ * <li> Calling to {@link CompanionDeviceManagerService} to create an association when/if the
+ * request was found valid and was approved by user.
+ * </ul>
+ *
+ * The class supports two variants of the "Association Flow": the full variant, and the shortened
+ * (a.k.a. No-UI) variant.
+ * Both flows start similarly: in
+ * {@link #processNewAssociationRequest(AssociationRequest, String, int, IAssociationRequestCallback)}
+ * invoked from
+ * {@link CompanionDeviceManagerService.CompanionDeviceManagerImpl#associate(AssociationRequest, IAssociationRequestCallback, String, int)}
+ * method call.
+ * Then an {@link AssociationRequestsProcessor} makes a decision whether user's confirmation is
+ * required.
+ *
+ * If the user's approval is NOT required: an {@link AssociationRequestsProcessor} invokes
+ * {@link #createAssociationAndNotifyApplication(AssociationRequest, String, int, MacAddress, IAssociationRequestCallback)}
+ * which after calling to {@link CompanionDeviceManagerService} to create an association, notifies
+ * the requester via
+ * {@link android.companion.CompanionDeviceManager.Callback#onAssociationCreated(AssociationInfo)}.
+ *
+ * If the user's approval is required: an {@link AssociationRequestsProcessor} constructs a
+ * {@link PendingIntent} for the approval UI and sends it back to the requester via
+ * {@link android.companion.CompanionDeviceManager.Callback#onAssociationPending(IntentSender)}.
+ * When/if user approves the request, {@link AssociationRequestsProcessor} receives a "callback"
+ * from the Approval UI in via {@link #mOnRequestConfirmationReceiver} and invokes
+ * {@link #processAssociationRequestApproval(AssociationRequest, IAssociationRequestCallback, ResultReceiver, MacAddress)}
+ * which one more time checks that the packages holds all necessary permissions before proceeding to
+ * {@link #createAssociationAndNotifyApplication(AssociationRequest, String, int, MacAddress, IAssociationRequestCallback)}.
+ *
+ * @see #processNewAssociationRequest(AssociationRequest, String, int, IAssociationRequestCallback)
+ * @see #processAssociationRequestApproval(AssociationRequest, IAssociationRequestCallback,
+ * ResultReceiver, MacAddress)
+ */
class AssociationRequestsProcessor {
private static final String TAG = LOG_TAG + ".AssociationRequestsProcessor";
- private static final ComponentName SERVICE_TO_BIND_TO = ComponentName.createRelative(
- CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME,
- ".CompanionDeviceDiscoveryService");
+ private static final ComponentName ASSOCIATION_REQUEST_APPROVAL_ACTIVITY =
+ createRelative(COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME, ".CompanionDeviceActivity");
+
+ // AssociationRequestsProcessor <-> UI
+ private static final String EXTRA_APPLICATION_CALLBACK = "application_callback";
+ private static final String EXTRA_ASSOCIATION_REQUEST = "association_request";
+ private static final String EXTRA_RESULT_RECEIVER = "result_receiver";
+
+ // AssociationRequestsProcessor -> UI
+ private static final int RESULT_CODE_ASSOCIATION_CREATED = 0;
+ private static final String EXTRA_ASSOCIATION = "association";
+
+ // UI -> AssociationRequestsProcessor
+ private static final int RESULT_CODE_ASSOCIATION_APPROVED = 0;
+ private static final String EXTRA_MAC_ADDRESS = "mac_address";
private static final int ASSOCIATE_WITHOUT_PROMPT_MAX_PER_TIME_WINDOW = 5;
private static final long ASSOCIATE_WITHOUT_PROMPT_WINDOW_MS = 60 * 60 * 1000; // 60 min;
private final Context mContext;
private final CompanionDeviceManagerService mService;
- private final PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>> mServiceConnectors;
-
- private AssociationRequest mRequest;
- private IAssociationRequestCallback mAppCallback;
- private AndroidFuture<?> mOngoingDeviceDiscovery;
+ private final PackageManagerInternal mPackageManager;
AssociationRequestsProcessor(CompanionDeviceManagerService service) {
mContext = service.getContext();
mService = service;
-
- final Intent serviceIntent = new Intent().setComponent(SERVICE_TO_BIND_TO);
- mServiceConnectors = new PerUser<>() {
- @Override
- protected ServiceConnector<ICompanionDeviceDiscoveryService> create(int userId) {
- return new ServiceConnector.Impl<>(
- mContext,
- serviceIntent, 0/* bindingFlags */, userId,
- ICompanionDeviceDiscoveryService.Stub::asInterface);
- }
- };
+ mPackageManager = service.mPackageManagerInternal;
}
/**
* Handle incoming {@link AssociationRequest}s, sent via
* {@link android.companion.ICompanionDeviceManager#associate(AssociationRequest, IAssociationRequestCallback, String, int)}
*/
- void process(@NonNull AssociationRequest request, @NonNull String packageName,
- @UserIdInt int userId, @NonNull IAssociationRequestCallback callback) {
+ void processNewAssociationRequest(@NonNull AssociationRequest request,
+ @NonNull String packageName, @UserIdInt int userId,
+ @NonNull IAssociationRequestCallback callback) {
requireNonNull(request, "Request MUST NOT be null");
if (request.isSelfManaged()) {
requireNonNull(request.getDisplayName(), "AssociationRequest.displayName "
@@ -103,14 +149,15 @@ class AssociationRequestsProcessor {
requireNonNull(packageName, "Package name MUST NOT be null");
requireNonNull(callback, "Callback MUST NOT be null");
+ final int packageUid = mPackageManager.getPackageUid(packageName, 0, userId);
if (DEBUG) {
- Slog.d(TAG, "process() "
+ Slog.d(TAG, "processNewAssociationRequest() "
+ "request=" + request + ", "
- + "package=u" + userId + "/" + packageName);
+ + "package=u" + userId + "/" + packageName + " (uid=" + packageUid + ")");
}
// 1. Enforce permissions and other requirements.
- enforcePermissionsForAssociation(mContext, request, packageName, userId);
+ enforcePermissionsForAssociation(mContext, request, packageUid);
mService.checkUsesFeature(packageName, userId);
// 2. Check if association can be created without launching UI (i.e. CDM needs NEITHER
@@ -118,71 +165,99 @@ class AssociationRequestsProcessor {
if (request.isSelfManaged() && !request.isForceConfirmation()
&& !willAddRoleHolder(request, packageName, userId)) {
// 2a. Create association right away.
- final AssociationInfo association = mService.createAssociation(userId, packageName,
- /* macAddress */ null, request.getDisplayName(), request.getDeviceProfile(),
- /* selfManaged */true);
- withCatchingRemoteException(() -> callback.onAssociationCreated(association));
+ createAssociationAndNotifyApplication(request, packageName, userId,
+ /*macAddress*/ null, callback);
return;
}
- // 2b. Launch the UI.
- synchronized (mService.mLock) {
- if (mRequest != null) {
- Slog.w(TAG, "CDM is already processing another AssociationRequest.");
+ // 2b. Build a PendingIntent for launching the confirmation UI, and send it back to the app:
- withCatchingRemoteException(() -> callback.onFailure("Busy."));
- }
+ // 2b.1. Populate the request with required info.
+ request.setPackageName(packageName);
+ request.setUserId(userId);
+ request.setSkipPrompt(mayAssociateWithoutPrompt(request, packageName, userId));
- final boolean linked = withCatchingRemoteException(
- () -> callback.asBinder().linkToDeath(mBinderDeathRecipient, 0));
- if (!linked) {
- // The process has died by now: do not proceed.
- return;
- }
+ // 2b.2. Prepare extras and create an Intent.
+ final Bundle extras = new Bundle();
+ extras.putParcelable(EXTRA_ASSOCIATION_REQUEST, request);
+ extras.putBinder(EXTRA_APPLICATION_CALLBACK, callback.asBinder());
+ extras.putParcelable(EXTRA_RESULT_RECEIVER, prepareForIpc(mOnRequestConfirmationReceiver));
- mRequest = request;
+ final Intent intent = new Intent();
+ intent.setComponent(ASSOCIATION_REQUEST_APPROVAL_ACTIVITY);
+ intent.putExtras(extras);
+
+ // 2b.3. Create a PendingIntent.
+ final PendingIntent pendingIntent;
+ final long token = Binder.clearCallingIdentity();
+ try {
+ // Using uid of the application that will own the association (usually the same
+ // application that sent the request) allows us to have multiple "pending" association
+ // requests at the same time.
+ // If the application already has a pending association request, that PendingIntent
+ // will be cancelled.
+ pendingIntent = PendingIntent.getActivity(mContext, /*requestCode */ packageUid, intent,
+ FLAG_ONE_SHOT | FLAG_CANCEL_CURRENT | FLAG_IMMUTABLE);
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
- mAppCallback = callback;
- request.setCallingPackage(packageName);
+ // 2b.4. Send the PendingIntent back to the app.
+ try {
+ callback.onAssociationPending(pendingIntent);
+ } catch (RemoteException ignore) { }
+ }
- if (mayAssociateWithoutPrompt(packageName, userId)) {
- Slog.i(TAG, "setSkipPrompt(true)");
- request.setSkipPrompt(true);
+ private void processAssociationRequestApproval(@NonNull AssociationRequest request,
+ @NonNull IAssociationRequestCallback callback,
+ @NonNull ResultReceiver resultReceiver, @Nullable MacAddress macAddress) {
+ final String packageName = request.getPackageName();
+ final int userId = request.getUserId();
+ final int packageUid = mPackageManager.getPackageUid(packageName, 0, userId);
+
+ if (DEBUG) {
+ Slog.d(TAG, "processAssociationRequestApproval()\n"
+ + " package=u" + userId + "/" + packageName + " (uid=" + packageUid + ")\n"
+ + " request=" + request + "\n"
+ + " macAddress=" + macAddress + "\n");
}
- final String deviceProfile = request.getDeviceProfile();
- mOngoingDeviceDiscovery = getDeviceProfilePermissionDescription(deviceProfile)
- .thenComposeAsync(description -> {
- if (DEBUG) {
- Slog.d(TAG, "fetchProfileDescription done: " + description);
- }
-
- request.setDeviceProfilePrivilegesDescription(description);
-
- return mServiceConnectors.forUser(userId).postAsync(service -> {
- if (DEBUG) {
- Slog.d(TAG, "Connected to CDM service -> "
- + "Starting discovery for " + request);
- }
-
- AndroidFuture<String> future = new AndroidFuture<>();
- service.startDiscovery(request, packageName, callback, future);
- return future;
- }).cancelTimeout();
-
- }, FgThread.getExecutor()).whenComplete(uncheckExceptions((deviceAddress, err) -> {
- if (err == null) {
- mService.legacyCreateAssociation(
- userId, deviceAddress, packageName, deviceProfile);
- mServiceConnectors.forUser(userId).post(
- ICompanionDeviceDiscoveryService::onAssociationCreated);
- } else {
- Slog.e(TAG, "Failed to discover device(s)", err);
- callback.onFailure("No devices found: " + err.getMessage());
- }
- cleanup();
- }));
+ // 1. Need to check permissions again in case something changed, since we first received
+ // this request.
+ try {
+ enforcePermissionsForAssociation(mContext, request, packageUid);
+ } catch (SecurityException e) {
+ // Since, at this point the caller is our own UI, we need to catch the exception on
+ // forward it back to the application via the callback.
+ try {
+ callback.onFailure(e.getMessage());
+ } catch (RemoteException ignore) { }
+ return;
+ }
+
+ // 2. Create association and notify the application.
+ final AssociationInfo association = createAssociationAndNotifyApplication(
+ request, packageName, userId, macAddress, callback);
+
+ // 3. Send the association back the Approval Activity, so that it can report back to the app
+ // via Activity.setResult().
+ final Bundle data = new Bundle();
+ data.putParcelable(EXTRA_ASSOCIATION, association);
+ resultReceiver.send(RESULT_CODE_ASSOCIATION_CREATED, data);
+ }
+
+ 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());
+
+ try {
+ callback.onAssociationCreated(association);
+ } catch (RemoteException ignore) { }
+
+ return association;
}
private boolean willAddRoleHolder(@NonNull AssociationRequest request,
@@ -197,26 +272,44 @@ class AssociationRequestsProcessor {
return !isRoleHolder;
}
- private void cleanup() {
- if (DEBUG) {
- Slog.d(TAG, "cleanup(); discovery = "
- + mOngoingDeviceDiscovery + ", request = " + mRequest);
- }
- synchronized (mService.mLock) {
- AndroidFuture<?> ongoingDeviceDiscovery = mOngoingDeviceDiscovery;
- if (ongoingDeviceDiscovery != null && !ongoingDeviceDiscovery.isDone()) {
- ongoingDeviceDiscovery.cancel(true);
+ private final ResultReceiver mOnRequestConfirmationReceiver =
+ new ResultReceiver(Handler.getMain()) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle data) {
+ if (DEBUG) {
+ Slog.d(TAG, "mOnRequestConfirmationReceiver.onReceiveResult() "
+ + "code=" + resultCode + ", " + "data=" + data);
}
- if (mAppCallback != null) {
- mAppCallback.asBinder().unlinkToDeath(mBinderDeathRecipient, 0);
- mAppCallback = null;
+
+ if (resultCode != RESULT_CODE_ASSOCIATION_APPROVED) {
+ Slog.w(TAG, "Unknown result code:" + resultCode);
+ return;
}
- mRequest = null;
+
+ final AssociationRequest request = data.getParcelable(EXTRA_ASSOCIATION_REQUEST);
+ final IAssociationRequestCallback callback = IAssociationRequestCallback.Stub
+ .asInterface(data.getBinder(EXTRA_APPLICATION_CALLBACK));
+ final ResultReceiver resultReceiver = data.getParcelable(EXTRA_RESULT_RECEIVER);
+
+ requireNonNull(request);
+ requireNonNull(callback);
+ requireNonNull(resultReceiver);
+
+ final MacAddress macAddress;
+ if (request.isSelfManaged()) {
+ macAddress = null;
+ } else {
+ macAddress = data.getParcelable(EXTRA_MAC_ADDRESS);
+ requireNonNull(macAddress);
+ }
+
+ processAssociationRequestApproval(request, callback, resultReceiver, macAddress);
}
- }
+ };
- private boolean mayAssociateWithoutPrompt(String packageName, int userId) {
- final String deviceProfile = mRequest.getDeviceProfile();
+ private boolean mayAssociateWithoutPrompt(@NonNull AssociationRequest request,
+ @NonNull String packageName, @UserIdInt int userId) {
+ final String deviceProfile = request.getDeviceProfile();
if (deviceProfile != null) {
final boolean isRoleHolder = Binder.withCleanCallingIdentity(
() -> isRoleHolder(mContext, userId, packageName, deviceProfile));
@@ -252,8 +345,8 @@ class AssociationRequestsProcessor {
String[] sameOemCerts = mContext.getResources()
.getStringArray(com.android.internal.R.array.config_companionDeviceCerts);
- Signature[] signatures = mService.mPackageManagerInternal
- .getPackage(packageName).getSigningDetails().getSignatures();
+ Signature[] signatures = mPackageManager.getPackage(packageName).getSigningDetails()
+ .getSignatures();
String[] apkCerts = PackageUtils.computeSignaturesSha256Digests(signatures);
Set<String> sameOemPackageCerts =
@@ -274,47 +367,6 @@ class AssociationRequestsProcessor {
return false;
}
- @NonNull
- private AndroidFuture<String> getDeviceProfilePermissionDescription(
- @Nullable String deviceProfile) {
- if (deviceProfile == null) {
- return AndroidFuture.completedFuture(null);
- }
-
- final AndroidFuture<String> result = new AndroidFuture<>();
- mService.mPermissionControllerManager.getPrivilegesDescriptionStringForProfile(
- deviceProfile, FgThread.getExecutor(), desc -> {
- try {
- result.complete(String.valueOf(desc));
- } catch (Exception e) {
- result.completeExceptionally(e);
- }
- });
- return result;
- }
-
-
- void dump(@NonNull PrintWriter pw) {
- pw.append("Discovery Service State:").append('\n');
- for (int i = 0, size = mServiceConnectors.size(); i < size; i++) {
- int userId = mServiceConnectors.keyAt(i);
- pw.append(" ")
- .append("u").append(Integer.toString(userId)).append(": ")
- .append(Objects.toString(mServiceConnectors.valueAt(i)))
- .append('\n');
- }
- }
-
- private final IBinder.DeathRecipient mBinderDeathRecipient = new IBinder.DeathRecipient() {
- @Override
- public void binderDied() {
- if (DEBUG) {
- Slog.d(TAG, "binderDied()");
- }
- mService.mMainHandler.post(AssociationRequestsProcessor.this::cleanup);
- }
- };
-
private static Set<String> getSameOemPackageCerts(
String packageName, String[] oemPackages, String[] sameOemCerts) {
Set<String> sameOemPackageCerts = new HashSet<>();
@@ -330,16 +382,19 @@ class AssociationRequestsProcessor {
return sameOemPackageCerts;
}
- private static boolean withCatchingRemoteException(ThrowingRunnable runnable) {
- try {
- runnable.run();
- } catch (RemoteException e) {
- return false;
- }
- return true;
- }
+ /**
+ * Convert an instance of a "locally-defined" ResultReceiver to an instance of
+ * {@link android.os.ResultReceiver} itself, which the receiving process will be able to
+ * unmarshall.
+ */
+ private static <T extends ResultReceiver> ResultReceiver prepareForIpc(T resultReceiver) {
+ final Parcel parcel = Parcel.obtain();
+ resultReceiver.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ final ResultReceiver ipcFriendly = ResultReceiver.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
- private interface ThrowingRunnable {
- void run() throws RemoteException;
+ return ipcFriendly;
}
}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 9c996f452cf6..626128aeba2a 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -406,7 +406,8 @@ public class CompanionDeviceManagerService extends SystemService {
enforceCallerCanManageAssociationsForPackage(getContext(), userId, packageName,
"create associations");
- mAssociationRequestsProcessor.process(request, packageName, userId, callback);
+ mAssociationRequestsProcessor.processNewAssociationRequest(
+ request, packageName, userId, callback);
}
@Override
@@ -717,8 +718,6 @@ public class CompanionDeviceManagerService extends SystemService {
.append(sDateFormat.format(time)).append('\n');
}
- mAssociationRequestsProcessor.dump(fout);
-
fout.append("Device Listener Services State:").append('\n');
for (int i = 0, size = mCompanionDevicePresenceController.mBoundServices.size();
i < size; i++) {
diff --git a/services/companion/java/com/android/server/companion/PermissionsUtils.java b/services/companion/java/com/android/server/companion/PermissionsUtils.java
index ea570895c18f..b981ff1d43c9 100644
--- a/services/companion/java/com/android/server/companion/PermissionsUtils.java
+++ b/services/companion/java/com/android/server/companion/PermissionsUtils.java
@@ -38,13 +38,11 @@ import android.annotation.UserIdInt;
import android.companion.AssociationRequest;
import android.companion.CompanionDeviceManager;
import android.content.Context;
-import android.content.pm.PackageManagerInternal;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.ArrayMap;
import com.android.internal.app.IAppOpsService;
-import com.android.server.LocalServices;
import java.util.Map;
@@ -69,9 +67,7 @@ final class PermissionsUtils {
}
static void enforcePermissionsForAssociation(@NonNull Context context,
- @NonNull AssociationRequest request, @NonNull String packageName,
- @UserIdInt int userId) {
- final int packageUid = getPackageUid(userId, packageName);
+ @NonNull AssociationRequest request, int packageUid) {
enforceRequestDeviceProfilePermissions(context, request.getDeviceProfile(), packageUid);
if (request.isSelfManaged()) {
@@ -88,18 +84,6 @@ final class PermissionsUtils {
throw new IllegalArgumentException("Unsupported device profile: " + deviceProfile);
}
- if (DEVICE_PROFILE_APP_STREAMING.equals(deviceProfile)) {
- // TODO: remove, when properly supporting this profile.
- throw new UnsupportedOperationException(
- "DEVICE_PROFILE_APP_STREAMING is not fully supported yet.");
- }
-
- if (DEVICE_PROFILE_AUTOMOTIVE_PROJECTION.equals(deviceProfile)) {
- // TODO: remove, when properly supporting this profile.
- throw new UnsupportedOperationException(
- "DEVICE_PROFILE_AUTOMOTIVE_PROJECTION is not fully supported yet.");
- }
-
final String permission = DEVICE_PROFILE_TO_PERMISSION.get(deviceProfile);
if (context.checkPermission(permission, getCallingPid(), packageUid)
!= PERMISSION_GRANTED) {
@@ -207,11 +191,6 @@ final class PermissionsUtils {
}
}
- private static int getPackageUid(@UserIdInt int userId, @NonNull String packageName) {
- return LocalServices.getService(PackageManagerInternal.class)
- .getPackageUid(packageName, 0, userId);
- }
-
private static IAppOpsService getAppOpsService() {
if (sAppOpsService == null) {
synchronized (PermissionsUtils.class) {
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
index a6a87936616b..e98b63ecd4b5 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -29,6 +29,7 @@ import android.os.Build;
import android.os.UserHandle;
import android.window.DisplayWindowPolicyController;
+import java.util.HashSet;
import java.util.List;
@@ -45,6 +46,8 @@ class GenericWindowPolicyController extends DisplayWindowPolicyController {
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
public static final long ALLOW_SECURE_ACTIVITY_DISPLAY_ON_REMOTE_DEVICE = 201712607L;
+ @NonNull final HashSet<Integer> mRunningUids = new HashSet<>();
+
GenericWindowPolicyController(int windowFlags, int systemWindowFlags) {
setInterestedWindowFlags(windowFlags, systemWindowFlags);
}
@@ -89,6 +92,17 @@ class GenericWindowPolicyController extends DisplayWindowPolicyController {
@Override
public void onRunningAppsChanged(int[] runningUids) {
+ mRunningUids.clear();
+ for (int i = 0; i < runningUids.length; i++) {
+ mRunningUids.add(runningUids[i]);
+ }
+ }
+ /**
+ * Returns true if an app with the given UID has an activity running on the virtual display for
+ * this controller.
+ */
+ boolean containsUid(int uid) {
+ return mRunningUids.contains(uid);
}
}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 022da4361be4..36edf4fe9787 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -32,6 +32,7 @@ import android.hardware.input.VirtualTouchEvent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.SparseArray;
import android.window.DisplayWindowPolicyController;
import com.android.internal.annotations.VisibleForTesting;
@@ -50,12 +51,18 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
private final Context mContext;
private final AssociationInfo mAssociationInfo;
private final int mOwnerUid;
- private final GenericWindowPolicyController mGenericWindowPolicyController;
private final InputController mInputController;
@VisibleForTesting
final List<Integer> mVirtualDisplayIds = new ArrayList<>();
private final OnDeviceCloseListener mListener;
+ /**
+ * A mapping from the virtual display ID to its corresponding
+ * {@link GenericWindowPolicyController}.
+ */
+ private final SparseArray<GenericWindowPolicyController> mWindowPolicyControllers =
+ new SparseArray<>();
+
VirtualDeviceImpl(Context context, AssociationInfo associationInfo,
IBinder token, int ownerUid, OnDeviceCloseListener listener) {
this(context, associationInfo, token, ownerUid, /* inputController= */ null, listener);
@@ -66,8 +73,6 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
int ownerUid, InputController inputController, OnDeviceCloseListener listener) {
mContext = context;
mAssociationInfo = associationInfo;
- mGenericWindowPolicyController = new GenericWindowPolicyController(FLAG_SECURE,
- SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
mOwnerUid = ownerUid;
if (inputController == null) {
mInputController = new InputController(mVirtualDeviceLock);
@@ -257,7 +262,11 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
"Virtual device already have a virtual display with ID " + displayId);
}
mVirtualDisplayIds.add(displayId);
- return mGenericWindowPolicyController;
+ final GenericWindowPolicyController dwpc =
+ new GenericWindowPolicyController(FLAG_SECURE,
+ SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+ mWindowPolicyControllers.put(displayId, dwpc);
+ return dwpc;
}
void onVirtualDisplayRemovedLocked(int displayId) {
@@ -266,12 +275,27 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
"Virtual device doesn't have a virtual display with ID " + displayId);
}
mVirtualDisplayIds.remove(displayId);
+ mWindowPolicyControllers.remove(displayId);
}
int getOwnerUid() {
return mOwnerUid;
}
+ /**
+ * Returns true if an app with the given {@code uid} is currently running on this virtual
+ * device.
+ */
+ boolean isAppRunningOnVirtualDevice(int uid) {
+ final int size = mWindowPolicyControllers.size();
+ for (int i = 0; i < size; i++) {
+ if (mWindowPolicyControllers.valueAt(i).containsUid(uid)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
interface OnDeviceCloseListener {
void onClose(int associationId);
}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 46e75f75ea9f..0db670e46909 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -252,7 +252,14 @@ public class VirtualDeviceManagerService extends SystemService {
@Override
public boolean isAppRunningOnAnyVirtualDevice(int uid) {
- // TODO(yukl): Implement this using DWPC.onRunningAppsChanged
+ synchronized (mVirtualDeviceManagerLock) {
+ int size = mVirtualDevices.size();
+ for (int i = 0; i < size; i++) {
+ if (mVirtualDevices.valueAt(i).isAppRunningOnVirtualDevice(uid)) {
+ return true;
+ }
+ }
+ }
return false;
}
}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 9641eb51a9cd..826b16a438f3 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -129,8 +129,6 @@ java_library_static {
"android.hardware.common-V2-java",
"android.hardware.light-V2.0-java",
"android.hardware.gnss-V2-java",
- "android.hardware.power-V1-java",
- "android.hardware.power-V1.0-java",
"android.hardware.vibrator-V2-java",
"app-compat-annotations",
"framework-tethering.stubs.module_lib",
@@ -168,6 +166,7 @@ java_library_static {
"android.hardware.rebootescrow-V1-java",
"android.hardware.soundtrigger-V2.3-java",
"android.hardware.power.stats-V1-java",
+ "android.hardware.power-V3-java",
"android.hidl.manager-V1.2-java",
"capture_state_listener-aidl-java",
"icu4j_calendar_astronomer",
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 91cd2f6ad676..811f2f5e5283 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -91,6 +91,7 @@ import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
+import com.android.internal.telephony.ICarrierPrivilegesListener;
import com.android.internal.telephony.IOnSubscriptionsChangedListener;
import com.android.internal.telephony.IPhoneStateListener;
import com.android.internal.telephony.ITelephonyRegistry;
@@ -106,6 +107,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -149,6 +151,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
IPhoneStateListener callback;
IOnSubscriptionsChangedListener onSubscriptionsChangedListenerCallback;
IOnSubscriptionsChangedListener onOpportunisticSubscriptionsChangedListenerCallback;
+ ICarrierPrivilegesListener carrierPrivilegesListener;
int callerUid;
int callerPid;
@@ -173,6 +176,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
return (onOpportunisticSubscriptionsChangedListenerCallback != null);
}
+ boolean matchCarrierPrivilegesListener() {
+ return carrierPrivilegesListener != null;
+ }
+
boolean canReadCallLog() {
try {
return TelephonyPermissions.checkReadCallLog(
@@ -189,8 +196,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
+ " onSubscriptionsChangedListenererCallback="
+ onSubscriptionsChangedListenerCallback
+ " onOpportunisticSubscriptionsChangedListenererCallback="
- + onOpportunisticSubscriptionsChangedListenerCallback + " subId=" + subId
- + " phoneId=" + phoneId + " events=" + eventList + "}";
+ + onOpportunisticSubscriptionsChangedListenerCallback
+ + " carrierPrivilegesListener=" + carrierPrivilegesListener
+ + " subId=" + subId + " phoneId=" + phoneId + " events=" + eventList + "}";
}
}
@@ -366,7 +374,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
private List<BarringInfo> mBarringInfo = null;
- private boolean mCarrierNetworkChangeState = false;
+ private boolean[] mCarrierNetworkChangeState = null;
private PhoneCapability mPhoneCapability = null;
@@ -402,6 +410,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
*/
private List<Map<Pair<Integer, ApnSetting>, PreciseDataConnectionState>>
mPreciseDataConnectionStates;
+
+ /** Per-phoneId snapshot of privileged packages (names + UIDs). */
+ private List<Pair<List<String>, int[]>> mCarrierPrivilegeStates;
+
/**
* Support backward compatibility for {@link android.telephony.TelephonyDisplayInfo}.
*/
@@ -675,6 +687,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mOutgoingCallEmergencyNumber = copyOf(mOutgoingCallEmergencyNumber, mNumPhones);
mOutgoingSmsEmergencyNumber = copyOf(mOutgoingSmsEmergencyNumber, mNumPhones);
mTelephonyDisplayInfos = copyOf(mTelephonyDisplayInfos, mNumPhones);
+ mCarrierNetworkChangeState = copyOf(mCarrierNetworkChangeState, mNumPhones);
mIsDataEnabled= copyOf(mIsDataEnabled, mNumPhones);
mDataEnabledReason = copyOf(mDataEnabledReason, mNumPhones);
mAllowedNetworkTypeReason = copyOf(mAllowedNetworkTypeReason, mNumPhones);
@@ -688,6 +701,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
cutListToSize(mBarringInfo, mNumPhones);
cutListToSize(mPhysicalChannelConfigs, mNumPhones);
cutListToSize(mLinkCapacityEstimateLists, mNumPhones);
+ cutListToSize(mCarrierPrivilegeStates, mNumPhones);
return;
}
@@ -720,6 +734,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mBackgroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
mPreciseDataConnectionStates.add(new ArrayMap<>());
mBarringInfo.add(i, new BarringInfo());
+ mCarrierNetworkChangeState[i] = false;
mTelephonyDisplayInfos[i] = null;
mIsDataEnabled[i] = false;
mDataEnabledReason[i] = TelephonyManager.DATA_ENABLED_REASON_USER;
@@ -727,6 +742,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mAllowedNetworkTypeReason[i] = -1;
mAllowedNetworkTypeValue[i] = -1;
mLinkCapacityEstimateLists.add(i, INVALID_LCE_LIST);
+ mCarrierPrivilegeStates.add(i, new Pair<>(Collections.emptyList(), new int[0]));
}
}
@@ -784,6 +800,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mOutgoingCallEmergencyNumber = new EmergencyNumber[numPhones];
mOutgoingSmsEmergencyNumber = new EmergencyNumber[numPhones];
mBarringInfo = new ArrayList<>();
+ mCarrierNetworkChangeState = new boolean[numPhones];
mTelephonyDisplayInfos = new TelephonyDisplayInfo[numPhones];
mPhysicalChannelConfigs = new ArrayList<>();
mAllowedNetworkTypeReason = new int[numPhones];
@@ -791,6 +808,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mIsDataEnabled = new boolean[numPhones];
mDataEnabledReason = new int[numPhones];
mLinkCapacityEstimateLists = new ArrayList<>();
+ mCarrierPrivilegeStates = new ArrayList<>();
for (int i = 0; i < numPhones; i++) {
mCallState[i] = TelephonyManager.CALL_STATE_IDLE;
@@ -820,6 +838,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mBackgroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
mPreciseDataConnectionStates.add(new ArrayMap<>());
mBarringInfo.add(i, new BarringInfo());
+ mCarrierNetworkChangeState[i] = false;
mTelephonyDisplayInfos[i] = null;
mIsDataEnabled[i] = false;
mDataEnabledReason[i] = TelephonyManager.DATA_ENABLED_REASON_USER;
@@ -827,6 +846,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
mAllowedNetworkTypeReason[i] = -1;
mAllowedNetworkTypeValue[i] = -1;
mLinkCapacityEstimateLists.add(i, INVALID_LCE_LIST);
+ mCarrierPrivilegeStates.add(i, new Pair<>(Collections.emptyList(), new int[0]));
}
mAppOps = mContext.getSystemService(AppOpsManager.class);
@@ -1230,7 +1250,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
if (events.contains(TelephonyCallback.EVENT_CARRIER_NETWORK_CHANGED)) {
try {
- r.callback.onCarrierNetworkChange(mCarrierNetworkChangeState);
+ r.callback.onCarrierNetworkChange(mCarrierNetworkChangeState[r.phoneId]);
} catch (RemoteException ex) {
remove(r.binder);
}
@@ -1724,23 +1744,37 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
throw new SecurityException("notifyCarrierNetworkChange without carrier privilege");
}
+ for (int subId : subIds) {
+ notifyCarrierNetworkChangeWithPermission(subId, active);
+ }
+ }
+
+ @Override
+ public void notifyCarrierNetworkChangeWithSubId(int subId, boolean active) {
+ if (!TelephonyPermissions.checkCarrierPrivilegeForSubId(mContext, subId)) {
+ throw new SecurityException(
+ "notifyCarrierNetworkChange without carrier privilege on subId " + subId);
+ }
+
+ notifyCarrierNetworkChangeWithPermission(subId, active);
+ }
+
+ private void notifyCarrierNetworkChangeWithPermission(int subId, boolean active) {
synchronized (mRecords) {
- mCarrierNetworkChangeState = active;
- for (int subId : subIds) {
- int phoneId = getPhoneIdFromSubId(subId);
+ int phoneId = getPhoneIdFromSubId(subId);
+ mCarrierNetworkChangeState[phoneId] = active;
- if (VDBG) {
- log("notifyCarrierNetworkChange: active=" + active + "subId: " + subId);
- }
- for (Record r : mRecords) {
- if (r.matchTelephonyCallbackEvent(
- TelephonyCallback.EVENT_CARRIER_NETWORK_CHANGED)
- && idMatch(r, subId, phoneId)) {
- try {
- r.callback.onCarrierNetworkChange(active);
- } catch (RemoteException ex) {
- mRemoveList.add(r.binder);
- }
+ if (VDBG) {
+ log("notifyCarrierNetworkChange: active=" + active + "subId: " + subId);
+ }
+ for (Record r : mRecords) {
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_CARRIER_NETWORK_CHANGED)
+ && idMatch(r, subId, phoneId)) {
+ try {
+ r.callback.onCarrierNetworkChange(active);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
}
}
}
@@ -2748,6 +2782,104 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
@Override
+ public void addCarrierPrivilegesListener(
+ int phoneId,
+ ICarrierPrivilegesListener callback,
+ String callingPackage,
+ String callingFeatureId) {
+ int callerUserId = UserHandle.getCallingUserId();
+ mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ "addCarrierPrivilegesListener");
+ if (VDBG) {
+ log(
+ "listen carrier privs: E pkg=" + pii(callingPackage) + " phoneId=" + phoneId
+ + " uid=" + Binder.getCallingUid()
+ + " myUserId=" + UserHandle.myUserId() + " callerUserId=" + callerUserId
+ + " callback=" + callback
+ + " callback.asBinder=" + callback.asBinder());
+ }
+ if (!validatePhoneId(phoneId)) {
+ throw new IllegalArgumentException("Invalid slot index: " + phoneId);
+ }
+
+ synchronized (mRecords) {
+ Record r = add(
+ callback.asBinder(), Binder.getCallingUid(), Binder.getCallingPid(), false);
+
+ if (r == null) return;
+
+ r.context = mContext;
+ r.carrierPrivilegesListener = callback;
+ r.callingPackage = callingPackage;
+ r.callingFeatureId = callingFeatureId;
+ r.callerUid = Binder.getCallingUid();
+ r.callerPid = Binder.getCallingPid();
+ r.phoneId = phoneId;
+ r.eventList = new ArraySet<>();
+ if (DBG) {
+ log("listen carrier privs: Register r=" + r);
+ }
+
+ Pair<List<String>, int[]> state = mCarrierPrivilegeStates.get(phoneId);
+ try {
+ r.carrierPrivilegesListener.onCarrierPrivilegesChanged(
+ Collections.unmodifiableList(state.first),
+ Arrays.copyOf(state.second, state.second.length));
+ } catch (RemoteException ex) {
+ remove(r.binder);
+ }
+ }
+ }
+
+ @Override
+ public void removeCarrierPrivilegesListener(
+ ICarrierPrivilegesListener callback, String callingPackage) {
+ mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ "removeCarrierPrivilegesListener");
+ remove(callback.asBinder());
+ }
+
+ @Override
+ public void notifyCarrierPrivilegesChanged(
+ int phoneId, List<String> privilegedPackageNames, int[] privilegedUids) {
+ if (!checkNotifyPermission("notifyCarrierPrivilegesChanged")) {
+ return;
+ }
+ if (!validatePhoneId(phoneId)) return;
+ if (VDBG) {
+ log(
+ "notifyCarrierPrivilegesChanged: phoneId=" + phoneId
+ + ", <packages=" + pii(privilegedPackageNames)
+ + ", uids=" + Arrays.toString(privilegedUids) + ">");
+ }
+ synchronized (mRecords) {
+ mCarrierPrivilegeStates.set(
+ phoneId, new Pair<>(privilegedPackageNames, privilegedUids));
+ for (Record r : mRecords) {
+ // Listeners are per-slot, not per-subscription. This is to provide a stable
+ // view across SIM profile switches.
+ if (!r.matchCarrierPrivilegesListener()
+ || !idMatch(r, SubscriptionManager.INVALID_SUBSCRIPTION_ID, phoneId)) {
+ continue;
+ }
+ try {
+ // Make sure even in-process listeners can't modify the values.
+ r.carrierPrivilegesListener.onCarrierPrivilegesChanged(
+ Collections.unmodifiableList(privilegedPackageNames),
+ Arrays.copyOf(privilegedUids, privilegedUids.length));
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+ handleRemoveListLocked();
+ }
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
@@ -2788,6 +2920,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
pw.println("mOutgoingCallEmergencyNumber=" + mOutgoingCallEmergencyNumber[i]);
pw.println("mOutgoingSmsEmergencyNumber=" + mOutgoingSmsEmergencyNumber[i]);
pw.println("mBarringInfo=" + mBarringInfo.get(i));
+ pw.println("mCarrierNetworkChangeState=" + mCarrierNetworkChangeState[i]);
pw.println("mTelephonyDisplayInfo=" + mTelephonyDisplayInfos[i]);
pw.println("mIsDataEnabled=" + mIsDataEnabled);
pw.println("mDataEnabledReason=" + mDataEnabledReason);
@@ -2795,9 +2928,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
pw.println("mAllowedNetworkTypeValue=" + mAllowedNetworkTypeValue[i]);
pw.println("mPhysicalChannelConfigs=" + mPhysicalChannelConfigs.get(i));
pw.println("mLinkCapacityEstimateList=" + mLinkCapacityEstimateLists.get(i));
+ // We need to obfuscate package names, and primitive arrays' native toString is ugly
+ Pair<List<String>, int[]> carrierPrivilegeState = mCarrierPrivilegeStates.get(i);
+ pw.println(
+ "mCarrierPrivilegeState=<packages=" + pii(carrierPrivilegeState.first)
+ + ", uids=" + Arrays.toString(carrierPrivilegeState.second) + ">");
pw.decreaseIndent();
}
- pw.println("mCarrierNetworkChangeState=" + mCarrierNetworkChangeState);
+
pw.println("mPhoneCapability=" + mPhoneCapability);
pw.println("mActiveDataSubId=" + mActiveDataSubId);
pw.println("mRadioPowerState=" + mRadioPowerState);
@@ -3521,4 +3659,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
private static String pii(String packageName) {
return Build.IS_DEBUGGABLE ? packageName : "***";
}
+
+ /** Redacts an entire list of package names if necessary. */
+ private static String pii(List<String> packageNames) {
+ if (packageNames.isEmpty() || Build.IS_DEBUGGABLE) return packageNames.toString();
+ return "[***, size=" + packageNames.size() + "]";
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d6f911c965b0..2593e512084d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -93,6 +93,7 @@ import static android.provider.Settings.Global.DEBUG_APP;
import static android.provider.Settings.Global.NETWORK_ACCESS_TIMEOUT_MS;
import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
+import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
@@ -307,6 +308,7 @@ import android.text.style.SuggestionSpan;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
+import android.util.FeatureFlagUtils;
import android.util.IntArray;
import android.util.Log;
import android.util.Pair;
@@ -8969,8 +8971,8 @@ public class ActivityManagerService extends IActivityManager.Stub
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
+ mProcessList.mAppExitInfoTracker.dumpHistoryProcessExitInfo(pw, dumpPackage);
}
- mProcessList.mAppExitInfoTracker.dumpHistoryProcessExitInfo(pw, dumpPackage);
if (dumpPackage == null) {
pw.println();
if (dumpAll) {
@@ -13376,12 +13378,7 @@ public class ActivityManagerService extends IActivityManager.Stub
!intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, false);
final boolean fullUninstall = removed && !replacing;
if (removed) {
- if (killProcess) {
- forceStopPackageLocked(ssp, UserHandle.getAppId(
- intent.getIntExtra(Intent.EXTRA_UID, -1)),
- false, true, true, false, fullUninstall, userId,
- removed ? "pkg removed" : "pkg changed");
- } else {
+ if (!killProcess) {
// Kill any app zygotes always, since they can't fork new
// processes with references to the old code
forceStopAppZygoteLocked(ssp, UserHandle.getAppId(
@@ -14602,6 +14599,8 @@ public class ActivityManagerService extends IActivityManager.Stub
private void checkExcessivePowerUsage() {
updateCpuStatsNow();
+ final boolean monitorPhantomProcs = mSystemReady && FeatureFlagUtils.isEnabled(mContext,
+ SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS);
synchronized (mProcLock) {
final boolean doCpuKills = mLastPowerCheckUptime != 0;
final long curUptime = SystemClock.uptimeMillis();
@@ -14627,9 +14626,11 @@ public class ActivityManagerService extends IActivityManager.Stub
updateAppProcessCpuTimeLPr(uptimeSince, doCpuKills, checkDur, cpuLimit, app);
- // Also check the phantom processes if there is any
- updatePhantomProcessCpuTimeLPr(
- uptimeSince, doCpuKills, checkDur, cpuLimit, app);
+ if (monitorPhantomProcs) {
+ // Also check the phantom processes if there is any
+ updatePhantomProcessCpuTimeLPr(
+ uptimeSince, doCpuKills, checkDur, cpuLimit, app);
+ }
}
});
}
@@ -15663,6 +15664,11 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
+ @Override
+ public void enableBinderTracing() {
+ Binder.enableTracingForUid(Binder.getCallingUid());
+ }
+
@VisibleForTesting
public final class LocalService extends ActivityManagerInternal
implements ActivityManagerLocal {
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index 221de8d003e5..d6a4cf650cba 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -19,6 +19,7 @@ package com.android.server.am;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
import static android.os.Process.FIRST_APPLICATION_UID;
+import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS;
import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_CRITICAL;
import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_LOW;
@@ -77,6 +78,7 @@ import android.provider.DeviceConfig.Properties;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.DebugUtils;
+import android.util.FeatureFlagUtils;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -1805,6 +1807,8 @@ public class AppProfiler {
}
void updateCpuStatsNow() {
+ final boolean monitorPhantomProcs = mService.mSystemReady && FeatureFlagUtils.isEnabled(
+ mService.mContext, SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS);
synchronized (mProcessCpuTracker) {
mProcessCpuMutexFree.set(false);
final long now = SystemClock.uptimeMillis();
@@ -1843,7 +1847,7 @@ public class AppProfiler {
}
}
- if (haveNewCpuStats) {
+ if (monitorPhantomProcs && haveNewCpuStats) {
mService.mPhantomProcessList.updateProcessCpuStatesLocked(mProcessCpuTracker);
}
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 9180ef80ae71..336572f44be3 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -130,6 +130,9 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
@GuardedBy("this")
private Future<?> mBatteryLevelSync;
+ @GuardedBy("this")
+ private Future<?> mProcessStateSync;
+
// If both mStats and mWorkerLock need to be synchronized, mWorkerLock must be acquired first.
private final Object mWorkerLock = new Object();
@@ -316,6 +319,25 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
}
@Override
+ public Future<?> scheduleSyncDueToProcessStateChange(long delayMillis) {
+ synchronized (BatteryExternalStatsWorker.this) {
+ mProcessStateSync = scheduleDelayedSyncLocked(mProcessStateSync,
+ () -> scheduleSync("procstate-change", UPDATE_ON_PROC_STATE_CHANGE),
+ delayMillis);
+ return mProcessStateSync;
+ }
+ }
+
+ public void cancelSyncDueToProcessStateChange() {
+ synchronized (BatteryExternalStatsWorker.this) {
+ if (mProcessStateSync != null) {
+ mProcessStateSync.cancel(false);
+ mProcessStateSync = null;
+ }
+ }
+ }
+
+ @Override
public Future<?> scheduleCleanupDueToRemovedUser(int userId) {
synchronized (BatteryExternalStatsWorker.this) {
return mExecutorService.schedule(() -> {
@@ -434,6 +456,9 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
if ((updateFlags & UPDATE_CPU) != 0) {
cancelCpuSyncDueToWakelockChange();
}
+ if ((updateFlags & UPDATE_ON_PROC_STATE_CHANGE) == UPDATE_ON_PROC_STATE_CHANGE) {
+ cancelSyncDueToProcessStateChange();
+ }
}
try {
diff --git a/services/core/java/com/android/server/am/PhantomProcessList.java b/services/core/java/com/android/server/am/PhantomProcessList.java
index b07684c9a004..2ec1aedd18f9 100644
--- a/services/core/java/com/android/server/am/PhantomProcessList.java
+++ b/services/core/java/com/android/server/am/PhantomProcessList.java
@@ -18,6 +18,7 @@ package com.android.server.am;
import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR;
import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
+import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
@@ -28,6 +29,7 @@ import android.app.ApplicationExitInfo.SubReason;
import android.os.Handler;
import android.os.Process;
import android.os.StrictMode;
+import android.util.FeatureFlagUtils;
import android.util.Slog;
import android.util.SparseArray;
@@ -419,6 +421,10 @@ public final class PhantomProcessList {
* order of the oom adjs of their parent process.
*/
void trimPhantomProcessesIfNecessary() {
+ if (!mService.mSystemReady || !FeatureFlagUtils.isEnabled(mService.mContext,
+ SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS)) {
+ return;
+ }
synchronized (mService.mProcLock) {
synchronized (mLock) {
mTrimPhantomProcessScheduled = false;
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index 20606f0e9f4d..fc48cd5507a8 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -42,6 +42,7 @@ import android.annotation.RequiresPermission;
import android.app.ActivityManager;
import android.app.GameManager;
import android.app.GameManager.GameMode;
+import android.app.GameState;
import android.app.IGameManagerService;
import android.app.compat.PackageOverride;
import android.content.BroadcastReceiver;
@@ -51,12 +52,15 @@ import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.hardware.power.Mode;
import android.net.Uri;
import android.os.Binder;
+import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.PowerManagerInternal;
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -72,6 +76,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.compat.CompatibilityOverrideConfig;
import com.android.internal.compat.IPlatformCompat;
+import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.SystemService;
import com.android.server.SystemService.TargetUser;
@@ -95,11 +100,14 @@ public final class GameManagerService extends IGameManagerService.Stub {
static final int WRITE_SETTINGS = 1;
static final int REMOVE_SETTINGS = 2;
static final int POPULATE_GAME_MODE_SETTINGS = 3;
+ static final int SET_GAME_STATE = 4;
static final int WRITE_SETTINGS_DELAY = 10 * 1000; // 10 seconds
static final PackageOverride COMPAT_ENABLED = new PackageOverride.Builder().setEnabled(true)
.build();
static final PackageOverride COMPAT_DISABLED = new PackageOverride.Builder().setEnabled(false)
.build();
+ private static final String PACKAGE_NAME_MSG_KEY = "packageName";
+ private static final String USER_ID_MSG_KEY = "userId";
private final Context mContext;
private final Object mLock = new Object();
@@ -107,6 +115,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
private final Handler mHandler;
private final PackageManager mPackageManager;
private final IPlatformCompat mPlatformCompat;
+ private final PowerManagerInternal mPowerManagerInternal;
private DeviceConfigListener mDeviceConfigListener;
@GuardedBy("mLock")
private final ArrayMap<Integer, GameManagerSettings> mSettings = new ArrayMap<>();
@@ -123,6 +132,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
mPackageManager = mContext.getPackageManager();
mPlatformCompat = IPlatformCompat.Stub.asInterface(
ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
+ mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
}
@Override
@@ -200,6 +210,19 @@ public final class GameManagerService extends IGameManagerService.Stub {
updateConfigsForUser(userId, packageNames);
break;
}
+ case SET_GAME_STATE: {
+ if (mPowerManagerInternal == null) {
+ final Bundle data = msg.getData();
+ Slog.d(TAG, "Error setting loading mode for package "
+ + data.getString(PACKAGE_NAME_MSG_KEY)
+ + " and userId " + data.getInt(USER_ID_MSG_KEY));
+ break;
+ }
+ final GameState gameState = (GameState) msg.obj;
+ final boolean isLoading = gameState.isLoading();
+ mPowerManagerInternal.setPowerMode(Mode.GAME_LOADING, isLoading);
+ break;
+ }
}
}
}
@@ -258,6 +281,32 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
/**
+ * 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) {
+ if (!isPackageGame(packageName, userId)) {
+ // Restrict to games only.
+ return;
+ }
+
+ if (getGameMode(packageName, userId) != GameManager.GAME_MODE_PERFORMANCE) {
+ // Requires performance mode to be enabled.
+ return;
+ }
+
+ final Message msg = mHandler.obtainMessage(SET_GAME_STATE);
+ final Bundle data = new Bundle();
+ data.putString(PACKAGE_NAME_MSG_KEY, packageName);
+ data.putInt(USER_ID_MSG_KEY, userId);
+ msg.setData(data);
+ msg.obj = gameState;
+ mHandler.sendMessage(msg);
+ }
+
+ /**
* GamePackageConfiguration manages all game mode config details for its associated package.
*/
@VisibleForTesting
@@ -385,9 +434,9 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
public boolean isValid() {
- return (mGameMode == GameManager.GAME_MODE_PERFORMANCE
- || mGameMode == GameManager.GAME_MODE_BATTERY)
- && (!mAllowDownscale || getCompatChangeId() != 0);
+ return mGameMode == GameManager.GAME_MODE_STANDARD
+ || mGameMode == GameManager.GAME_MODE_PERFORMANCE
+ || mGameMode == GameManager.GAME_MODE_BATTERY;
}
/**
@@ -639,6 +688,16 @@ 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;
+ }
+ }
+
/**
* Sets the Game Mode for the package name.
* Verifies that the calling process has {@link android.Manifest.permission#MANAGE_GAME_MODE}.
@@ -649,16 +708,8 @@ public final class GameManagerService extends IGameManagerService.Stub {
throws SecurityException {
checkPermission(Manifest.permission.MANAGE_GAME_MODE);
- // Restrict to games only.
- try {
- final ApplicationInfo applicationInfo = mPackageManager
- .getApplicationInfoAsUser(packageName, PackageManager.MATCH_ALL, userId);
- if (applicationInfo.category != ApplicationInfo.CATEGORY_GAME) {
- // Ignore attempt to set the game mode for applications that are not identified
- // as game. See {@link PackageManager#setApplicationCategoryHint(String, int)}
- return;
- }
- } catch (PackageManager.NameNotFoundException e) {
+ if (!isPackageGame(packageName, userId)) {
+ // Restrict to games only.
return;
}
@@ -839,7 +890,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
long scaleId = modeConfig.getCompatChangeId();
if (scaleId == 0) {
- Slog.w(TAG, "Invalid downscaling change id " + scaleId + " for "
+ Slog.i(TAG, "Invalid downscaling change id " + scaleId + " for "
+ packageName);
return;
}
diff --git a/services/core/java/com/android/server/app/GameManagerShellCommand.java b/services/core/java/com/android/server/app/GameManagerShellCommand.java
index a0a83b1031b7..f07d207e7d06 100644
--- a/services/core/java/com/android/server/app/GameManagerShellCommand.java
+++ b/services/core/java/com/android/server/app/GameManagerShellCommand.java
@@ -167,15 +167,9 @@ public class GameManagerShellCommand extends ShellCommand {
switch (gameMode.toLowerCase()) {
case "1":
case "standard":
- // Standard should only be available if other game modes are.
- if (batteryModeSupported || perfModeSupported) {
- service.setGameMode(packageName, GameManager.GAME_MODE_STANDARD,
- userId);
- } else {
- pw.println("Game mode: " + gameMode + " not supported by "
- + packageName);
- return -1;
- }
+ // Standard mode can be used to specify loading ANGLE as the default OpenGL ES
+ // driver, so it should always be available.
+ service.setGameMode(packageName, GameManager.GAME_MODE_STANDARD, userId);
break;
case "2":
case "performance":
diff --git a/services/core/java/com/android/server/audio/AudioManagerShellCommand.java b/services/core/java/com/android/server/audio/AudioManagerShellCommand.java
new file mode 100644
index 000000000000..241abafe6179
--- /dev/null
+++ b/services/core/java/com/android/server/audio/AudioManagerShellCommand.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 com.android.server.audio;
+
+import android.content.Context;
+import android.media.AudioManager;
+import android.os.ShellCommand;
+
+import java.io.PrintWriter;
+
+
+class AudioManagerShellCommand extends ShellCommand {
+ private static final String TAG = "AudioManagerShellCommand";
+
+ private final AudioService mService;
+
+ AudioManagerShellCommand(AudioService service) {
+ mService = service;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+ final PrintWriter pw = getOutPrintWriter();
+ switch(cmd) {
+ case "set-surround-format-enabled":
+ return setSurroundFormatEnabled();
+ case "get-is-surround-format-enabled":
+ return getIsSurroundFormatEnabled();
+ case "set-encoded-surround-mode":
+ return setEncodedSurroundMode();
+ case "get-encoded-surround-mode":
+ return getEncodedSurroundMode();
+ }
+ return 0;
+ }
+
+ @Override
+ public void onHelp() {
+ final PrintWriter pw = getOutPrintWriter();
+ pw.println("Audio manager commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println();
+ pw.println(" set-surround-format-enabled SURROUND_FORMAT IS_ENABLED");
+ pw.println(" Enables/disabled the SURROUND_FORMAT based on IS_ENABLED");
+ pw.println(" get-is-surround-format-enabled SURROUND_FORMAT");
+ pw.println(" Returns if the SURROUND_FORMAT is enabled");
+ pw.println(" set-encoded-surround-mode SURROUND_SOUND_MODE");
+ pw.println(" Sets the encoded surround sound mode to SURROUND_SOUND_MODE");
+ pw.println(" get-encoded-surround-mode");
+ pw.println(" Returns the encoded surround sound mode");
+ }
+
+ private int setSurroundFormatEnabled() {
+ String surroundFormatText = getNextArg();
+ String isSurroundFormatEnabledText = getNextArg();
+
+ if (surroundFormatText == null) {
+ getErrPrintWriter().println("Error: no surroundFormat specified");
+ return 1;
+ }
+
+ if (isSurroundFormatEnabledText == null) {
+ getErrPrintWriter().println("Error: no enabled value for surroundFormat specified");
+ return 1;
+ }
+
+ int surroundFormat = -1;
+ boolean isSurroundFormatEnabled = false;
+ try {
+ surroundFormat = Integer.parseInt(surroundFormatText);
+ isSurroundFormatEnabled = Boolean.parseBoolean(isSurroundFormatEnabledText);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: wrong format specified for surroundFormat");
+ return 1;
+ }
+ if (surroundFormat < 0) {
+ getErrPrintWriter().println("Error: invalid value of surroundFormat");
+ return 1;
+ }
+
+ final Context context = mService.mContext;
+ final AudioManager am = context.getSystemService(AudioManager.class);
+ am.setSurroundFormatEnabled(surroundFormat, isSurroundFormatEnabled);
+ return 0;
+ }
+
+ private int getIsSurroundFormatEnabled() {
+ String surroundFormatText = getNextArg();
+
+ if (surroundFormatText == null) {
+ getErrPrintWriter().println("Error: no surroundFormat specified");
+ return 1;
+ }
+
+ int surroundFormat = -1;
+ try {
+ surroundFormat = Integer.parseInt(surroundFormatText);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: wrong format specified for surroundFormat");
+ return 1;
+ }
+
+ if (surroundFormat < 0) {
+ getErrPrintWriter().println("Error: invalid value of surroundFormat");
+ return 1;
+ }
+ final Context context = mService.mContext;
+ final AudioManager am = context.getSystemService(AudioManager.class);
+ getOutPrintWriter().println("Value of enabled for " + surroundFormat + " is: "
+ + am.isSurroundFormatEnabled(surroundFormat));
+ return 0;
+ }
+
+ private int setEncodedSurroundMode() {
+ String encodedSurroundModeText = getNextArg();
+
+ if (encodedSurroundModeText == null) {
+ getErrPrintWriter().println("Error: no encodedSurroundMode specified");
+ return 1;
+ }
+
+ int encodedSurroundMode = -1;
+ try {
+ encodedSurroundMode = Integer.parseInt(encodedSurroundModeText);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: wrong format specified for encoded surround mode");
+ return 1;
+ }
+
+ if (encodedSurroundMode < 0) {
+ getErrPrintWriter().println("Error: invalid value of encodedSurroundMode");
+ return 1;
+ }
+
+ final Context context = mService.mContext;
+ final AudioManager am = context.getSystemService(AudioManager.class);
+ am.setEncodedSurroundMode(encodedSurroundMode);
+ return 0;
+ }
+
+ private int getEncodedSurroundMode() {
+ final Context context = mService.mContext;
+ final AudioManager am = context.getSystemService(AudioManager.class);
+ getOutPrintWriter().println("Encoded surround mode: " + am.getEncodedSurroundMode());
+ return 0;
+ }
+}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 6238198e9488..8615393dbfec 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -130,7 +130,9 @@ import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.os.ServiceManager;
+import android.os.ShellCallback;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -1996,6 +1998,18 @@ public class AudioService extends IAudioService.Stub
}
}
+ @Override // Binder call
+ public void onShellCommand(FileDescriptor in, FileDescriptor out,
+ FileDescriptor err, String[] args, ShellCallback callback,
+ ResultReceiver resultReceiver) {
+ if (mContext.checkCallingOrSelfPermission(Manifest.permission.MANAGE_AUDIO_POLICY)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Missing MANAGE_AUDIO_POLICY permission");
+ }
+ new AudioManagerShellCommand(AudioService.this).exec(this, in, out, err,
+ args, callback, resultReceiver);
+ }
+
/** @see AudioManager#getSurroundFormats() */
@Override
public Map<Integer, Boolean> getSurroundFormats() {
@@ -10142,6 +10156,27 @@ public class AudioService extends IAudioService.Stub
return AudioManager.SUCCESS;
}
+ /** @see AudioPolicy#getFocusStack() */
+ public List<AudioFocusInfo> getFocusStack() {
+ enforceModifyAudioRoutingPermission();
+ return mMediaFocusControl.getFocusStack();
+ }
+
+ /** @see AudioPolicy#sendFocusLoss */
+ public boolean sendFocusLoss(@NonNull AudioFocusInfo focusLoser,
+ @NonNull IAudioPolicyCallback apcb) {
+ Objects.requireNonNull(focusLoser);
+ Objects.requireNonNull(apcb);
+ enforceModifyAudioRoutingPermission();
+ if (!mAudioPolicies.containsKey(apcb.asBinder())) {
+ throw new IllegalStateException("Only registered AudioPolicy can change focus");
+ }
+ if (!mAudioPolicies.get(apcb.asBinder()).mHasFocusListener) {
+ throw new IllegalStateException("AudioPolicy must have focus listener to change focus");
+ }
+ return mMediaFocusControl.sendFocusLoss(focusLoser);
+ }
+
/** see AudioManager.hasRegisteredDynamicPolicy */
public boolean hasRegisteredDynamicPolicy() {
synchronized (mAudioPolicies) {
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index 66f62355c7f0..69a4c23cf867 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -27,6 +27,7 @@ import android.media.AudioManager;
import android.media.AudioSystem;
import android.media.IAudioFocusDispatcher;
import android.media.MediaMetrics;
+import android.media.audiopolicy.AudioPolicy;
import android.media.audiopolicy.IAudioPolicyCallback;
import android.os.Binder;
import android.os.Build;
@@ -221,6 +222,51 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
}
}
+ /**
+ * Return a copy of the focus stack for external consumption (composed of AudioFocusInfo
+ * instead of FocusRequester instances)
+ * @return a SystemApi-friendly version of the focus stack, in the same order (last entry
+ * is top of focus stack, i.e. latest focus owner)
+ * @see AudioPolicy#getFocusStack()
+ */
+ @NonNull List<AudioFocusInfo> getFocusStack() {
+ synchronized (mAudioFocusLock) {
+ final ArrayList<AudioFocusInfo> stack = new ArrayList<>(mFocusStack.size());
+ for (FocusRequester fr : mFocusStack) {
+ stack.add(fr.toAudioFocusInfo());
+ }
+ return stack;
+ }
+ }
+
+ /**
+ * Send AUDIOFOCUS_LOSS to a specific stack entry.
+ * Note this method is supporting an external API, and is restricted to LOSS in order to
+ * prevent allowing the stack to be in an invalid state (e.g. entry inside stack has focus)
+ * @param focusLoser the stack entry that is exiting the stack through a focus loss
+ * @return false if the focusLoser wasn't found in the stack, true otherwise
+ * @see AudioPolicy#sendFocusLoss(AudioFocusInfo)
+ */
+ boolean sendFocusLoss(@NonNull AudioFocusInfo focusLoser) {
+ synchronized (mAudioFocusLock) {
+ FocusRequester loserToRemove = null;
+ for (FocusRequester fr : mFocusStack) {
+ if (fr.getClientId().equals(focusLoser.getClientId())) {
+ fr.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS, null,
+ false /*forceDuck*/);
+ loserToRemove = fr;
+ break;
+ }
+ }
+ if (loserToRemove != null) {
+ mFocusStack.remove(loserToRemove);
+ loserToRemove.release();
+ return true;
+ }
+ }
+ return false;
+ }
+
@GuardedBy("mAudioFocusLock")
private void notifyTopOfAudioFocusStack() {
// notify the top of the stack it gained focus
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 093ecd57124f..e12034333554 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -50,6 +50,8 @@ import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.IUserManager;
+import android.os.Looper;
+import android.os.Message;
import android.os.Parcel;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
@@ -74,6 +76,8 @@ import android.widget.Toast;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.UiThread;
@@ -99,6 +103,22 @@ public class ClipboardService extends SystemService {
private static final boolean IS_EMULATOR =
SystemProperties.getBoolean("ro.boot.qemu", false);
+ @VisibleForTesting
+ public static final long DEFAULT_CLIPBOARD_TIMEOUT_MILLIS = 3600000;
+
+ /**
+ * Device config property for whether clipboard auto clear is enabled on the device
+ **/
+ public static final String PROPERTY_AUTO_CLEAR_ENABLED =
+ "auto_clear_enabled";
+
+ /**
+ * Device config property for time period in milliseconds after which clipboard is auto
+ * cleared
+ **/
+ public static final String PROPERTY_AUTO_CLEAR_TIMEOUT =
+ "auto_clear_timeout";
+
// DeviceConfig properties
private static final String PROPERTY_MAX_CLASSIFICATION_LENGTH = "max_classification_length";
private static final int DEFAULT_MAX_CLASSIFICATION_LENGTH = 400;
@@ -312,6 +332,10 @@ public class ClipboardService extends SystemService {
* 'intendingUserId' and the uid is called 'intendingUid'.
*/
private class ClipboardImpl extends IClipboard.Stub {
+
+ private final Handler mClipboardClearHandler = new ClipboardClearHandler(
+ mWorkerHandler.getLooper());
+
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
@@ -352,10 +376,34 @@ public class ClipboardService extends SystemService {
}
checkDataOwner(clip, intendingUid);
synchronized (mLock) {
+ scheduleAutoClear(userId);
setPrimaryClipInternalLocked(clip, intendingUid, sourcePackage);
}
}
+ private void scheduleAutoClear(@UserIdInt int userId) {
+ final long oldIdentity = Binder.clearCallingIdentity();
+ try {
+ if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_CLIPBOARD,
+ PROPERTY_AUTO_CLEAR_ENABLED, false)) {
+ mClipboardClearHandler.removeEqualMessages(ClipboardClearHandler.MSG_CLEAR,
+ userId);
+ Message clearMessage = Message.obtain(mClipboardClearHandler,
+ ClipboardClearHandler.MSG_CLEAR, userId, 0, userId);
+ mClipboardClearHandler.sendMessageDelayed(clearMessage,
+ getTimeoutForAutoClear());
+ }
+ } finally {
+ Binder.restoreCallingIdentity(oldIdentity);
+ }
+ }
+
+ private long getTimeoutForAutoClear() {
+ return DeviceConfig.getLong(DeviceConfig.NAMESPACE_CLIPBOARD,
+ PROPERTY_AUTO_CLEAR_TIMEOUT,
+ DEFAULT_CLIPBOARD_TIMEOUT_MILLIS);
+ }
+
@Override
public void clearPrimaryClip(String callingPackage, @UserIdInt int userId) {
final int intendingUid = getIntendingUid(callingPackage, userId);
@@ -365,6 +413,8 @@ public class ClipboardService extends SystemService {
return;
}
synchronized (mLock) {
+ mClipboardClearHandler.removeEqualMessages(ClipboardClearHandler.MSG_CLEAR,
+ userId);
setPrimaryClipInternalLocked(null, intendingUid, callingPackage);
}
}
@@ -391,6 +441,9 @@ public class ClipboardService extends SystemService {
PerUserClipboard clipboard = getClipboardLocked(intendingUserId);
showAccessNotificationLocked(pkg, intendingUid, intendingUserId, clipboard);
notifyTextClassifierLocked(clipboard, pkg, intendingUid);
+ if (clipboard.primaryClip != null) {
+ scheduleAutoClear(userId);
+ }
return clipboard.primaryClip;
}
}
@@ -484,6 +537,32 @@ public class ClipboardService extends SystemService {
return null;
}
}
+
+ private class ClipboardClearHandler extends Handler {
+
+ public static final int MSG_CLEAR = 101;
+
+ ClipboardClearHandler(Looper looper) {
+ super(looper);
+ }
+
+ public void handleMessage(@NonNull Message msg) {
+ switch (msg.what) {
+ case MSG_CLEAR:
+ final int userId = msg.arg1;
+ synchronized (mLock) {
+ if (getClipboardLocked(userId).primaryClip != null) {
+ FrameworkStatsLog.write(FrameworkStatsLog.CLIPBOARD_CLEARED,
+ FrameworkStatsLog.CLIPBOARD_CLEARED__SOURCE__AUTO_CLEAR);
+ setPrimaryClipInternalLocked(null, Binder.getCallingUid(), null);
+ }
+ }
+ break;
+ default:
+ Slog.wtf(TAG, "ClipboardClearHandler received unknown message " + msg.what);
+ }
+ }
+ }
};
@GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/communal/CommunalManagerService.java b/services/core/java/com/android/server/communal/CommunalManagerService.java
index 3bf6ca2dac0d..df95bf597dc0 100644
--- a/services/core/java/com/android/server/communal/CommunalManagerService.java
+++ b/services/core/java/com/android/server/communal/CommunalManagerService.java
@@ -87,7 +87,7 @@ public final class CommunalManagerService extends SystemService {
new ActivityInterceptorCallback() {
@Nullable
@Override
- public Intent intercept(ActivityInterceptorInfo info) {
+ public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
if (!shouldIntercept(info.aInfo)) {
if (DEBUG) {
Slog.d(TAG, "Activity allowed, not intercepting: "
@@ -110,8 +110,10 @@ public final class CommunalManagerService extends SystemService {
PendingIntent.FLAG_IMMUTABLE,
/* bOptions= */ null);
- return LaunchAfterAuthenticationActivity.createLaunchAfterAuthenticationIntent(
- new IntentSender(target));
+ return new ActivityInterceptResult(
+ LaunchAfterAuthenticationActivity.createLaunchAfterAuthenticationIntent(
+ new IntentSender(target)),
+ info.checkedOptions);
}
};
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 3f55848e26a8..c0a6abf3a121 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -45,7 +45,10 @@ import android.app.compat.CompatChanges;
import android.companion.virtual.IVirtualDevice;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.res.Resources;
@@ -421,6 +424,32 @@ public final class DisplayManagerService extends SystemService {
// Receives notifications about changes to Settings.
private SettingsObserver mSettingsObserver;
+ // Keeps note of what state the device is in, used for idle screen brightness mode.
+ private boolean mIsDocked;
+ private boolean mIsDreaming;
+
+ private final BroadcastReceiver mIdleModeReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final DisplayManagerInternal dmi =
+ LocalServices.getService(DisplayManagerInternal.class);
+ if (Intent.ACTION_DOCK_EVENT.equals(intent.getAction())) {
+ int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
+ Intent.EXTRA_DOCK_STATE_UNDOCKED);
+ mIsDocked = dockState == Intent.EXTRA_DOCK_STATE_DESK
+ || dockState == Intent.EXTRA_DOCK_STATE_LE_DESK
+ || dockState == Intent.EXTRA_DOCK_STATE_HE_DESK;
+ }
+ if (Intent.ACTION_DREAMING_STARTED.equals(intent.getAction())) {
+ mIsDreaming = true;
+ } else if (Intent.ACTION_DREAMING_STOPPED.equals(intent.getAction())) {
+ mIsDreaming = false;
+ }
+ setDockedAndIdleEnabled(/* enabled= */(mIsDocked && mIsDreaming),
+ Display.DEFAULT_DISPLAY);
+ }
+ };
+
private final boolean mAllowNonNativeRefreshRateOverride;
private final BrightnessSynchronizer mBrightnessSynchronizer;
@@ -616,6 +645,13 @@ public final class DisplayManagerService extends SystemService {
mSettingsObserver = new SettingsObserver();
mBrightnessSynchronizer.startSynchronizing();
+
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_DREAMING_STARTED);
+ filter.addAction(Intent.ACTION_DREAMING_STOPPED);
+ filter.addAction(Intent.ACTION_DOCK_EVENT);
+
+ mContext.registerReceiver(mIdleModeReceiver, filter);
}
@VisibleForTesting
@@ -1072,6 +1108,13 @@ public final class DisplayManagerService extends SystemService {
+ "setUserDisabledHdrTypesInternal");
return;
}
+
+ // Verify if userDisabledHdrTypes contains expected HDR types
+ if (!isSubsetOf(Display.HdrCapabilities.HDR_TYPES, userDisabledHdrTypes)) {
+ Slog.e(TAG, "userDisabledHdrTypes contains unexpected types");
+ return;
+ }
+
Arrays.sort(userDisabledHdrTypes);
if (Arrays.equals(mUserDisabledHdrTypes, userDisabledHdrTypes)) {
return;
@@ -1094,6 +1137,15 @@ public final class DisplayManagerService extends SystemService {
}
}
+ private boolean isSubsetOf(int[] sortedSuperset, int[] subset) {
+ for (int i : subset) {
+ if (Arrays.binarySearch(sortedSuperset, i) < 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
private void setAreUserDisabledHdrTypesAllowedInternal(
boolean areUserDisabledHdrTypesAllowed) {
synchronized (mSyncRoot) {
@@ -2080,6 +2132,16 @@ public final class DisplayManagerService extends SystemService {
}
}
+ void setDockedAndIdleEnabled(boolean enabled, int displayId) {
+ synchronized (mSyncRoot) {
+ final DisplayPowerController displayPowerController = mDisplayPowerControllers.get(
+ displayId);
+ if (displayPowerController != null) {
+ displayPowerController.setAutomaticScreenBrightnessMode(enabled);
+ }
+ }
+ }
+
private void clearViewportsLocked() {
mViewports.clear();
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index 9412c938f934..9a7ddcb2ff91 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -20,9 +20,11 @@ import android.content.Context;
import android.content.Intent;
import android.hardware.display.DisplayManager;
import android.os.ShellCommand;
+import android.util.Slog;
import android.view.Display;
import java.io.PrintWriter;
+import java.util.Arrays;
class DisplayManagerShellCommand extends ShellCommand {
private static final String TAG = "DisplayManagerShellCommand";
@@ -60,6 +62,24 @@ class DisplayManagerShellCommand extends ShellCommand {
return setAmbientColorTemperatureOverride();
case "constrain-launcher-metrics":
return setConstrainLauncherMetrics();
+ case "set-user-preferred-display-mode":
+ return setUserPreferredDisplayMode();
+ case "clear-user-preferred-display-mode":
+ return clearUserPreferredDisplayMode();
+ case "get-user-preferred-display-mode":
+ return getUserPreferredDisplayMode();
+ case "set-match-content-frame-rate-pref":
+ return setMatchContentFrameRateUserPreference();
+ case "get-match-content-frame-rate-pref":
+ return getMatchContentFrameRateUserPreference();
+ case "set-user-disabled-hdr-types":
+ return setUserDisabledHdrTypes();
+ case "get-user-disabled-hdr-types":
+ return getUserDisabledHdrTypes();
+ case "dock":
+ return setDockedAndIdle();
+ case "undock":
+ return unsetDockedAndIdle();
default:
return handleDefaultCommands(cmd);
}
@@ -93,6 +113,25 @@ class DisplayManagerShellCommand extends ShellCommand {
pw.println(" constrain-launcher-metrics [true|false]");
pw.println(" Sets if Display#getRealSize and getRealMetrics should be constrained for ");
pw.println(" Launcher.");
+ pw.println(" set-user-preferred-display-mode WIDTH HEIGHT REFRESH-RATE");
+ pw.println(" Sets the user preferred display mode which has fields WIDTH, HEIGHT and "
+ + "REFRESH-RATE");
+ pw.println(" clear-user-preferred-display-mode");
+ pw.println(" Clears the user preferred display mode");
+ pw.println(" get-user-preferred-display-mode");
+ pw.println(" Returns the user preferred display mode or null id no mode is set by user");
+ pw.println(" set-match-content-frame-rate-pref PREFERENCE");
+ pw.println(" Sets the match content frame rate preference as PREFERENCE ");
+ pw.println(" get-match-content-frame-rate-pref");
+ pw.println(" Returns the match content frame rate preference");
+ pw.println(" set-user-disabled-hdr-types TYPES...");
+ pw.println(" Sets the user disabled HDR types as TYPES");
+ pw.println(" get-user-disabled-hdr-types");
+ pw.println(" Returns the user disabled HDR types");
+ pw.println(" dock");
+ pw.println(" Sets brightness to docked + idle screen brightness mode");
+ pw.println(" undock");
+ pw.println(" Sets brightness to active (normal) screen brightness mode");
pw.println();
Intent.printIntentArgsHelp(pw , "");
}
@@ -166,4 +205,162 @@ class DisplayManagerShellCommand extends ShellCommand {
mService.setShouldConstrainMetricsForLauncher(constrain);
return 0;
}
+
+ private int setUserPreferredDisplayMode() {
+ final String widthText = getNextArg();
+ if (widthText == null) {
+ getErrPrintWriter().println("Error: no width specified");
+ return 1;
+ }
+
+ final String heightText = getNextArg();
+ if (heightText == null) {
+ getErrPrintWriter().println("Error: no height specified");
+ return 1;
+ }
+
+ final String refreshRateText = getNextArg();
+ if (refreshRateText == null) {
+ getErrPrintWriter().println("Error: no refresh-rate specified");
+ return 1;
+ }
+
+ final int width, height;
+ final float refreshRate;
+ try {
+ width = Integer.parseInt(widthText);
+ height = Integer.parseInt(heightText);
+ refreshRate = Float.parseFloat(refreshRateText);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: invalid format of width, height or refresh rate");
+ return 1;
+ }
+ if (width < 0 || height < 0 || refreshRate <= 0.0f) {
+ getErrPrintWriter().println("Error: invalid value of width, height or refresh rate");
+ return 1;
+ }
+
+ final Context context = mService.getContext();
+ final DisplayManager dm = context.getSystemService(DisplayManager.class);
+ dm.setUserPreferredDisplayMode(new Display.Mode(width, height, refreshRate));
+ return 0;
+ }
+
+ private int clearUserPreferredDisplayMode() {
+ final Context context = mService.getContext();
+ final DisplayManager dm = context.getSystemService(DisplayManager.class);
+ dm.clearUserPreferredDisplayMode();
+ return 0;
+ }
+
+ private int getUserPreferredDisplayMode() {
+ final Context context = mService.getContext();
+ final DisplayManager dm = context.getSystemService(DisplayManager.class);
+ final Display.Mode mode = dm.getUserPreferredDisplayMode();
+ if (mode == null) {
+ getOutPrintWriter().println("User preferred display mode: null");
+ return 0;
+ }
+
+ getOutPrintWriter().println("User preferred display mode: " + mode.getPhysicalWidth() + " "
+ + mode.getPhysicalHeight() + " " + mode.getRefreshRate());
+ return 0;
+ }
+
+ private int setMatchContentFrameRateUserPreference() {
+ final String matchContentFrameRatePrefText = getNextArg();
+ if (matchContentFrameRatePrefText == null) {
+ getErrPrintWriter().println("Error: no matchContentFrameRatePref specified");
+ return 1;
+ }
+
+ final int matchContentFrameRatePreference;
+ try {
+ matchContentFrameRatePreference = Integer.parseInt(matchContentFrameRatePrefText);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: invalid format of matchContentFrameRatePreference");
+ return 1;
+ }
+ if (matchContentFrameRatePreference < 0) {
+ getErrPrintWriter().println("Error: invalid value of matchContentFrameRatePreference");
+ return 1;
+ }
+
+ final Context context = mService.getContext();
+ final DisplayManager dm = context.getSystemService(DisplayManager.class);
+
+ final int refreshRateSwitchingType =
+ toRefreshRateSwitchingType(matchContentFrameRatePreference);
+ dm.setRefreshRateSwitchingType(refreshRateSwitchingType);
+ return 0;
+ }
+
+ private int getMatchContentFrameRateUserPreference() {
+ final Context context = mService.getContext();
+ final DisplayManager dm = context.getSystemService(DisplayManager.class);
+ getOutPrintWriter().println("Match content frame rate type: "
+ + dm.getMatchContentFrameRateUserPreference());
+ return 0;
+ }
+
+ private int setUserDisabledHdrTypes() {
+ final String[] userDisabledHdrTypesText = getAllArgs();
+ if (userDisabledHdrTypesText == null) {
+ getErrPrintWriter().println("Error: no userDisabledHdrTypes specified");
+ return 1;
+ }
+
+ int[] userDisabledHdrTypes = new int[userDisabledHdrTypesText.length];
+ try {
+ int index = 0;
+ for (String userDisabledHdrType : userDisabledHdrTypesText) {
+ userDisabledHdrTypes[index++] = Integer.parseInt(userDisabledHdrType);
+ }
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: invalid format of userDisabledHdrTypes");
+ return 1;
+ }
+
+ final Context context = mService.getContext();
+ final DisplayManager dm = context.getSystemService(DisplayManager.class);
+ dm.setUserDisabledHdrTypes(userDisabledHdrTypes);
+ return 0;
+ }
+
+ private int getUserDisabledHdrTypes() {
+ final Context context = mService.getContext();
+ final DisplayManager dm = context.getSystemService(DisplayManager.class);
+ final int[] userDisabledHdrTypes = dm.getUserDisabledHdrTypes();
+ getOutPrintWriter().println("User disabled HDR types: "
+ + Arrays.toString(userDisabledHdrTypes));
+ return 0;
+ }
+
+ @DisplayManager.SwitchingType
+ private int toRefreshRateSwitchingType(
+ @DisplayManager.MatchContentFrameRateType int matchContentFrameRateType) {
+ switch (matchContentFrameRateType) {
+ case DisplayManager.MATCH_CONTENT_FRAMERATE_NEVER:
+ return DisplayManager.SWITCHING_TYPE_NONE;
+ case DisplayManager.MATCH_CONTENT_FRAMERATE_SEAMLESSS_ONLY:
+ return DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS;
+ case DisplayManager.MATCH_CONTENT_FRAMERATE_ALWAYS:
+ return DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS;
+ case DisplayManager.MATCH_CONTENT_FRAMERATE_UNKNOWN:
+ default:
+ Slog.e(TAG, matchContentFrameRateType + " is not a valid value of "
+ + "matchContentFrameRate type.");
+ return -1;
+ }
+ }
+
+ private int setDockedAndIdle() {
+ mService.setDockedAndIdleEnabled(true, Display.DEFAULT_DISPLAY);
+ return 0;
+ }
+
+ private int unsetDockedAndIdle() {
+ mService.setDockedAndIdleEnabled(false, Display.DEFAULT_DISPLAY);
+ return 0;
+ }
}
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index c82b16d77772..efd2f6f1bfe1 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -41,7 +41,6 @@ import android.os.IThermalEventListener;
import android.os.IThermalService;
import android.os.Looper;
import android.os.Message;
-import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -619,10 +618,11 @@ public class DisplayModeDirector {
mAppRequestObserver.dumpLocked(pw);
mBrightnessObserver.dumpLocked(pw);
mUdfpsObserver.dumpLocked(pw);
- mSensorObserver.dumpLocked(pw);
mHbmObserver.dumpLocked(pw);
mSkinThermalStatusObserver.dumpLocked(pw);
}
+
+ mSensorObserver.dump(pw);
}
private void updateVoteLocked(int priority, Vote vote) {
@@ -2244,7 +2244,7 @@ public class DisplayModeDirector {
}
}
- void dumpLocked(PrintWriter pw) {
+ void dump(PrintWriter pw) {
pw.println(" SensorObserver");
synchronized (mSensorObserverLock) {
pw.println(" mIsProxActive=" + mIsProxActive);
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index b46ee27d30cb..2dfaa8bad380 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -395,11 +395,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
@Nullable
private BrightnessMappingStrategy mIdleModeBrightnessMapper;
- // If these are both true, and mIdleModeBrightnessMapper != null,
- // then we are in idle screen brightness mode.
- private boolean mIsDreaming;
- private boolean mIsDocked;
-
// The current brightness configuration.
@Nullable
private BrightnessConfiguration mBrightnessConfiguration;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index 05e1bdd11db6..c5dc23e30822 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -39,6 +39,7 @@ import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.ArrayMap;
+import android.util.EventLog;
import android.util.Slog;
import android.view.IWindowManager;
import android.view.WindowManager;
@@ -49,6 +50,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.InputBindResult;
import com.android.internal.inputmethod.UnbindReason;
import com.android.internal.view.IInputMethod;
+import com.android.server.EventLogTags;
import com.android.server.wm.WindowManagerInternal;
/**
@@ -58,6 +60,9 @@ final class InputMethodBindingController {
static final boolean DEBUG = false;
private static final String TAG = InputMethodBindingController.class.getSimpleName();
+ /** Time in milliseconds that the IME service has to bind before it is reconnected. */
+ static final long TIME_TO_RECONNECT = 3 * 1000;
+
@NonNull private final InputMethodManagerService mService;
@NonNull private final Context mContext;
@NonNull private final ArrayMap<String, InputMethodInfo> mMethodMap;
@@ -77,6 +82,7 @@ final class InputMethodBindingController {
private IBinder mCurToken;
private int mCurSeq;
private boolean mVisibleBound;
+ private boolean mSupportsStylusHw;
/**
* Binding flags for establishing connection to the {@link InputMethodService}.
@@ -239,7 +245,7 @@ final class InputMethodBindingController {
}
/**
- * Indicates whether {@link #getVisibleConnection} is currently in use.
+ * Indicates whether {@link #mVisibleConnection} is currently in use.
*/
boolean isVisibleBound() {
return mVisibleBound;
@@ -248,11 +254,6 @@ final class InputMethodBindingController {
/**
* Used to bring IME service up to visible adjustment while it is being shown.
*/
- @NonNull
- ServiceConnection getVisibleConnection() {
- return mVisibleConnection;
- }
-
private final ServiceConnection mVisibleConnection = new ServiceConnection() {
@Override public void onBindingDied(ComponentName name) {
synchronized (mMethodMap) {
@@ -295,6 +296,10 @@ final class InputMethodBindingController {
mService.scheduleNotifyImeUidToAudioService(mCurMethodUid);
mService.reRequestCurrentClientSessionLocked();
}
+ mSupportsStylusHw = mMethodMap.get(mSelectedMethodId).supportsStylusHandwriting();
+ if (mSupportsStylusHw) {
+ // TODO init Handwriting spy.
+ }
}
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
@@ -334,7 +339,7 @@ final class InputMethodBindingController {
// We consider this to be a new bind attempt, since the system
// should now try to restart the service for us.
mLastBindTime = SystemClock.uptimeMillis();
- mService.clearClientSessionsLocked();
+ clearCurMethodAndSessionsLocked();
mService.clearInputShowRequestLocked();
mService.unbindCurrentClientLocked(UnbindReason.DISCONNECT_IME);
}
@@ -358,11 +363,12 @@ final class InputMethodBindingController {
}
mCurId = null;
- mService.clearClientSessionsLocked();
+ clearCurMethodAndSessionsLocked();
}
@GuardedBy("mMethodMap")
- void clearCurMethodLocked() {
+ private void clearCurMethodAndSessionsLocked() {
+ mService.clearClientSessionsLocked();
mCurMethod = null;
mCurMethodUid = Process.INVALID_UID;
}
@@ -382,7 +388,7 @@ final class InputMethodBindingController {
@GuardedBy("mMethodMap")
@NonNull
- InputBindResult bindCurrentMethodLocked(int displayIdToShowIme) {
+ InputBindResult bindCurrentMethodLocked() {
InputMethodInfo info = mMethodMap.get(mSelectedMethodId);
if (info == null) {
throw new IllegalArgumentException("Unknown id: " + mSelectedMethodId);
@@ -394,7 +400,7 @@ final class InputMethodBindingController {
mCurId = info.getId();
mLastBindTime = SystemClock.uptimeMillis();
- addFreshWindowTokenLocked(displayIdToShowIme);
+ addFreshWindowTokenLocked();
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
null, null, mCurId, mCurSeq, false);
@@ -419,7 +425,8 @@ final class InputMethodBindingController {
}
@GuardedBy("mMethodMap")
- private void addFreshWindowTokenLocked(int displayIdToShowIme) {
+ private void addFreshWindowTokenLocked() {
+ int displayIdToShowIme = mService.getDisplayIdToShowIme();
mCurToken = new Binder();
mService.setCurTokenDisplayId(displayIdToShowIme);
@@ -438,7 +445,7 @@ final class InputMethodBindingController {
}
@GuardedBy("mMethodMap")
- void unbindMainConnectionLocked() {
+ private void unbindMainConnectionLocked() {
mContext.unbindService(mMainConnection);
mHasConnection = false;
}
@@ -460,17 +467,61 @@ final class InputMethodBindingController {
}
@GuardedBy("mMethodMap")
- boolean bindCurrentInputMethodServiceVisibleConnectionLocked() {
+ private boolean bindCurrentInputMethodServiceVisibleConnectionLocked() {
mVisibleBound = bindCurrentInputMethodServiceLocked(mVisibleConnection,
IME_VISIBLE_BIND_FLAGS);
return mVisibleBound;
}
@GuardedBy("mMethodMap")
- boolean bindCurrentInputMethodServiceMainConnectionLocked() {
+ private boolean bindCurrentInputMethodServiceMainConnectionLocked() {
mHasConnection = bindCurrentInputMethodServiceLocked(mMainConnection,
mImeConnectionBindFlags);
return mHasConnection;
}
+ /**
+ * Bind the IME so that it can be shown.
+ *
+ * <p>
+ * Performs a rebind if no binding is achieved in {@link #TIME_TO_RECONNECT} milliseconds.
+ */
+ @GuardedBy("mMethodMap")
+ void setCurrentMethodVisibleLocked() {
+ if (mCurMethod != null) {
+ if (DEBUG) Slog.d(TAG, "setCurrentMethodVisibleLocked: mCurToken=" + mCurToken);
+ if (mHasConnection && !mVisibleBound) {
+ bindCurrentInputMethodServiceVisibleConnectionLocked();
+ }
+ return;
+ }
+
+ long bindingDuration = SystemClock.uptimeMillis() - mLastBindTime;
+ if (mHasConnection && bindingDuration >= TIME_TO_RECONNECT) {
+ // The client has asked to have the input method shown, but
+ // we have been sitting here too long with a connection to the
+ // service and no interface received, so let's disconnect/connect
+ // to try to prod things along.
+ EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME, getSelectedMethodId(),
+ bindingDuration, 1);
+ Slog.w(TAG, "Force disconnect/connect to the IME in setCurrentMethodVisibleLocked()");
+ unbindMainConnectionLocked();
+ bindCurrentInputMethodServiceMainConnectionLocked();
+ } else {
+ if (DEBUG) {
+ Slog.d(TAG, "Can't show input: connection = " + mHasConnection + ", time = "
+ + (TIME_TO_RECONNECT - bindingDuration));
+ }
+ }
+ }
+
+ /**
+ * Remove the binding needed for the IME to be shown.
+ */
+ @GuardedBy("mMethodMap")
+ void setCurrentMethodNotVisibleLocked() {
+ if (mVisibleBound) {
+ unbindVisibleConnectionLocked();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index 2328dfc8b781..9a53d837f25b 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -95,6 +95,21 @@ public abstract class InputMethodManagerInternal {
public abstract boolean switchToInputMethod(String imeId, @UserIdInt int userId);
/**
+ * Force enable or disable the input method associated with {@code imeId} for given user. If
+ * the input method associated with {@code imeId} is not installed, do nothing.
+ *
+ * @param imeId The input method ID to be enabled or disabled.
+ * @param enabled {@code true} if the input method associated with {@code imeId} should be
+ * enabled.
+ * @param userId The user ID to be queried.
+ * @return {@code true} if the input method associated with {@code imeId} was successfully
+ * enabled or disabled, {@code false} if the input method specified is not installed
+ * or was unable to be enabled/disabled for some other reason.
+ */
+ public abstract boolean setInputMethodEnabled(String imeId, boolean enabled,
+ @UserIdInt int userId);
+
+ /**
* Registers a new {@link InputMethodListListener}.
*/
public abstract void registerInputMethodListListener(InputMethodListListener listener);
@@ -168,6 +183,11 @@ public abstract class InputMethodManagerInternal {
}
@Override
+ public boolean setInputMethodEnabled(String imeId, boolean enabled, int userId) {
+ return false;
+ }
+
+ @Override
public void registerInputMethodListListener(InputMethodListListener listener) {
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 3c6b0966dfc3..da3729d7498f 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -49,6 +49,8 @@ import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.DISPLAY_IME_POLICY_HIDE;
import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
+import static com.android.server.inputmethod.InputMethodBindingController.TIME_TO_RECONNECT;
+
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.Manifest;
@@ -111,12 +113,10 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
-import android.text.style.SuggestionSpan;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
import android.util.IndentingPrintWriter;
-import android.util.LruCache;
import android.util.Pair;
import android.util.PrintWriterPrinter;
import android.util.Printer;
@@ -250,10 +250,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
static final int MSG_NOTIFY_IME_UID_TO_AUDIO_SERVICE = 7000;
- static final long TIME_TO_RECONNECT = 3 * 1000;
-
- static final int SECURE_SUGGESTION_SPANS_MAX_SIZE = 20;
-
private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID;
private static final String TAG_TRY_SUPPRESSING_IME_SWITCHER = "TrySuppressingImeSwitcher";
private static final String HANDLER_THREAD_NAME = "android.imms";
@@ -301,8 +297,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// lock for this class.
final ArrayList<InputMethodInfo> mMethodList = new ArrayList<>();
final ArrayMap<String, InputMethodInfo> mMethodMap = new ArrayMap<>();
- private final LruCache<SuggestionSpan, InputMethodInfo> mSecureSuggestionSpans =
- new LruCache<>(SECURE_SUGGESTION_SPANS_MAX_SIZE);
final InputMethodSubtypeSwitchingController mSwitchingController;
/**
@@ -312,13 +306,16 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
private int mMethodMapUpdateCount = 0;
/**
- * Indicates whether {@link InputMethodBindingController#getVisibleConnection} is currently
- * in use.
+ * The display id for which the latest startInput was called.
*/
- private boolean isVisibleBound() {
- return mBindingController.isVisibleBound();
+ @GuardedBy("mMethodMap")
+ int getDisplayIdToShowIme() {
+ return mDisplayIdToShowIme;
}
+ @GuardedBy("mMethodMap")
+ private int mDisplayIdToShowIme = INVALID_DISPLAY;
+
// Ongoing notification
private NotificationManager mNotificationManager;
KeyguardManager mKeyguardManager;
@@ -1937,13 +1934,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
if (userId == mSettings.getCurrentUserId()) {
return mSettings.getEnabledInputMethodListLocked();
}
- final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
- final ArrayList<InputMethodInfo> methodList = new ArrayList<>();
- final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
- new ArrayMap<>();
- AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
- queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap,
- methodList);
+ final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId);
final InputMethodSettings settings = new InputMethodSettings(mContext.getResources(),
mContext.getContentResolver(), methodMap, userId, true);
return settings.getEnabledInputMethodListLocked();
@@ -2108,13 +2099,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
return mSettings.getEnabledInputMethodSubtypeListLocked(
mContext, imi, allowsImplicitlySelectedSubtypes);
}
- final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
- final ArrayList<InputMethodInfo> methodList = new ArrayList<>();
- final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
- new ArrayMap<>();
- AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
- queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap,
- methodList);
+ final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId);
final InputMethodInfo imi = methodMap.get(imiId);
if (imi == null) {
return Collections.emptyList();
@@ -2354,10 +2339,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
// Compute the final shown display ID with validated cs.selfReportedDisplayId for this
// session & other conditions.
- final int displayIdToShowIme = computeImeDisplayIdForTarget(cs.selfReportedDisplayId,
+ mDisplayIdToShowIme = computeImeDisplayIdForTarget(cs.selfReportedDisplayId,
mImeDisplayValidator);
- if (displayIdToShowIme == INVALID_DISPLAY) {
+ if (mDisplayIdToShowIme == INVALID_DISPLAY) {
mImeHiddenByDisplayPolicy = true;
hideCurrentInputLocked(mCurFocusedWindow, 0, null,
SoftInputShowHideReason.HIDE_DISPLAY_IME_POLICY_HIDE);
@@ -2378,7 +2363,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// Check if the input method is changing.
// We expect the caller has already verified that the client is allowed to access this
// display ID.
- if (isSelectedMethodBound(displayIdToShowIme)) {
+ if (isSelectedMethodBound()) {
if (cs.curSession != null) {
// Fast case: if we are already connected to the input method,
// then just return it.
@@ -2394,13 +2379,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mBindingController.unbindCurrentMethodLocked();
- return mBindingController.bindCurrentMethodLocked(displayIdToShowIme);
+ return mBindingController.bindCurrentMethodLocked();
}
- private boolean isSelectedMethodBound(int displayIdToShowIme) {
+ private boolean isSelectedMethodBound() {
String curId = getCurId();
return curId != null && curId.equals(getSelectedMethodId())
- && displayIdToShowIme == mCurTokenDisplayId;
+ && mDisplayIdToShowIme == mCurTokenDisplayId;
}
@GuardedBy("mMethodMap")
@@ -2590,7 +2575,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
finishSessionLocked(mEnabledSession);
mEnabledSession = null;
- mBindingController.clearCurMethodLocked();
scheduleNotifyImeUidToAudioService(Process.INVALID_UID);
}
hideStatusBarIconLocked();
@@ -3047,42 +3031,20 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
return false;
}
- boolean res = false;
- IInputMethod curMethod = getCurMethod();
- if (curMethod != null) {
- if (DEBUG) Slog.d(TAG, "showCurrentInputLocked: mCurToken=" + getCurToken());
+ mBindingController.setCurrentMethodVisibleLocked();
+ if (getCurMethod() != 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 = getCurMethod();
executeOrSendMessage(curMethod, mCaller.obtainMessageIIOOO(MSG_SHOW_SOFT_INPUT,
getImeShowFlagsLocked(), reason, curMethod, resultReceiver,
showInputToken));
mInputShown = true;
- if (hasConnection() && !isVisibleBound()) {
- mBindingController.bindCurrentInputMethodServiceVisibleConnectionLocked();
- }
- res = true;
- } else {
- long bindingDuration = SystemClock.uptimeMillis() - getLastBindTime();
- if (hasConnection() && bindingDuration >= TIME_TO_RECONNECT) {
- // The client has asked to have the input method shown, but
- // we have been sitting here too long with a connection to the
- // service and no interface received, so let's disconnect/connect
- // to try to prod things along.
- EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME, getSelectedMethodId(),
- bindingDuration, 1);
- Slog.w(TAG, "Force disconnect/connect to the IME in showCurrentInputLocked()");
- mBindingController.unbindMainConnectionLocked();
- mBindingController.bindCurrentInputMethodServiceMainConnectionLocked();
- } else {
- if (DEBUG) {
- Slog.d(TAG, "Can't show input: connection = " + hasConnection() + ", time = "
- + (TIME_TO_RECONNECT - bindingDuration));
- }
- }
+ return true;
}
- return res;
+ return false;
}
@Override
@@ -3166,9 +3128,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
} else {
res = false;
}
- if (hasConnection() && isVisibleBound()) {
- mBindingController.unbindVisibleConnectionLocked();
- }
+ mBindingController.setCurrentMethodNotVisibleLocked();
mInputShown = false;
mShowRequested = false;
mShowExplicitlyRequested = false;
@@ -3522,10 +3482,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
return mWindowManagerInternal.shouldRestoreImeVisibility(windowToken);
}
- private boolean isImeVisible() {
- return (mImeWindowVis & InputMethodService.IME_VISIBLE) != 0;
- }
-
@GuardedBy("mMethodMap")
private boolean canShowInputMethodPickerLocked(IInputMethodClient client) {
// TODO(yukawa): multi-display support.
@@ -4814,31 +4770,36 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
+ private ArrayMap<String, InputMethodInfo> queryMethodMapForUser(@UserIdInt int userId) {
+ final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
+ final ArrayList<InputMethodInfo> methodList = new ArrayList<>();
+ final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
+ new ArrayMap<>();
+ AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
+ queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap,
+ methodMap, methodList);
+ return methodMap;
+ }
+
private boolean switchToInputMethod(String imeId, @UserIdInt int userId) {
synchronized (mMethodMap) {
if (userId == mSettings.getCurrentUserId()) {
if (!mMethodMap.containsKey(imeId)
|| !mSettings.getEnabledInputMethodListLocked()
.contains(mMethodMap.get(imeId))) {
- return false; // IME is not is found or not enabled.
+ return false; // IME is not found or not enabled.
}
setInputMethodLocked(imeId, NOT_A_SUBTYPE_ID);
return true;
}
- final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
- final ArrayList<InputMethodInfo> methodList = new ArrayList<>();
- final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
- new ArrayMap<>();
- AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
- queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap,
- methodMap, methodList);
+ final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId);
final InputMethodSettings settings = new InputMethodSettings(
mContext.getResources(), mContext.getContentResolver(), methodMap,
userId, false);
if (!methodMap.containsKey(imeId)
|| !settings.getEnabledInputMethodListLocked()
.contains(methodMap.get(imeId))) {
- return false; // IME is not is found or not enabled.
+ return false; // IME is not found or not enabled.
}
settings.putSelectedInputMethod(imeId);
settings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
@@ -4846,6 +4807,35 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
+ private boolean setInputMethodEnabled(String imeId, boolean enabled, @UserIdInt int userId) {
+ synchronized (mMethodMap) {
+ if (userId == mSettings.getCurrentUserId()) {
+ if (!mMethodMap.containsKey(imeId)) {
+ return false; // IME is not found.
+ }
+ setInputMethodEnabledLocked(imeId, enabled);
+ return true;
+ }
+ final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId);
+ final InputMethodSettings settings = new InputMethodSettings(
+ mContext.getResources(), mContext.getContentResolver(), methodMap,
+ userId, false);
+ if (!methodMap.containsKey(imeId)) {
+ return false; // IME is not found.
+ }
+ if (enabled) {
+ if (!settings.getEnabledInputMethodListLocked().contains(methodMap.get(imeId))) {
+ settings.appendAndPutEnabledInputMethodLocked(imeId, false);
+ }
+ } else {
+ settings.buildAndPutEnabledInputMethodsStrRemovingIdLocked(
+ new StringBuilder(),
+ settings.getEnabledInputMethodsAndSubtypeListLocked(), imeId);
+ }
+ return true;
+ }
+ }
+
private boolean transferTouchFocusToImeWindow(@NonNull IBinder sourceInputToken,
int displayId) {
//TODO(b/150843766): Check if Input Token is valid.
@@ -4918,6 +4908,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
@Override
+ public boolean setInputMethodEnabled(String imeId, boolean enabled, int userId) {
+ return mService.setInputMethodEnabled(imeId, enabled, userId);
+ }
+
+ @Override
public void registerInputMethodListListener(InputMethodListListener listener) {
mService.mInputMethodListListeners.addIfAbsent(listener);
}
@@ -5114,7 +5109,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
+ " client=" + mCurFocusedWindowClient);
focusedWindowClient = mCurFocusedWindowClient;
p.println(" mCurId=" + getCurId() + " mHaveConnection=" + hasConnection()
- + " mBoundToMethod=" + mBoundToMethod + " mVisibleBound=" + isVisibleBound());
+ + " mBoundToMethod=" + mBoundToMethod + " mVisibleBound="
+ + mBindingController.isVisibleBound());
p.println(" mCurToken=" + getCurToken());
p.println(" mCurTokenDisplayId=" + mCurTokenDisplayId);
p.println(" mCurHostInputToken=" + mCurHostInputToken);
@@ -5513,13 +5509,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
previouslyEnabled = setInputMethodEnabledLocked(imeId, enabled);
}
} else {
- final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
- final ArrayList<InputMethodInfo> methodList = new ArrayList<>();
- final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
- new ArrayMap<>();
- AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
- queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap,
- methodMap, methodList);
+ final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId);
final InputMethodSettings settings = new InputMethodSettings(mContext.getResources(),
mContext.getContentResolver(), methodMap, userId, false);
if (enabled) {
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 2550e3a9aede..ffef80356a66 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -112,7 +112,6 @@ import com.android.server.location.injector.DeviceIdleHelper;
import com.android.server.location.injector.DeviceStationaryHelper;
import com.android.server.location.injector.EmergencyHelper;
import com.android.server.location.injector.Injector;
-import com.android.server.location.injector.LocationAttributionHelper;
import com.android.server.location.injector.LocationPermissionsHelper;
import com.android.server.location.injector.LocationPowerSaveModeHelper;
import com.android.server.location.injector.LocationUsageLogger;
@@ -1705,7 +1704,6 @@ public class LocationManagerService extends ILocationManager.Stub implements
private final SystemScreenInteractiveHelper mScreenInteractiveHelper;
private final SystemDeviceStationaryHelper mDeviceStationaryHelper;
private final SystemDeviceIdleHelper mDeviceIdleHelper;
- private final LocationAttributionHelper mLocationAttributionHelper;
private final LocationUsageLogger mLocationUsageLogger;
// lazily instantiated since they may not always be used
@@ -1731,7 +1729,6 @@ public class LocationManagerService extends ILocationManager.Stub implements
mScreenInteractiveHelper = new SystemScreenInteractiveHelper(context);
mDeviceStationaryHelper = new SystemDeviceStationaryHelper();
mDeviceIdleHelper = new SystemDeviceIdleHelper(context);
- mLocationAttributionHelper = new LocationAttributionHelper(mAppOpsHelper);
mLocationUsageLogger = new LocationUsageLogger();
}
@@ -1808,11 +1805,6 @@ public class LocationManagerService extends ILocationManager.Stub implements
}
@Override
- public LocationAttributionHelper getLocationAttributionHelper() {
- return mLocationAttributionHelper;
- }
-
- @Override
public synchronized EmergencyHelper getEmergencyHelper() {
if (mEmergencyCallHelper == null) {
mEmergencyCallHelper = new SystemEmergencyHelper(mContext);
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
index 2cdf75d98659..8fdde2486401 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
@@ -29,6 +29,7 @@ import android.hardware.location.ContextHubInfo;
import android.hardware.location.ContextHubTransaction;
import android.hardware.location.NanoAppBinary;
import android.hardware.location.NanoAppMessage;
+import android.hardware.location.NanoAppRpcService;
import android.hardware.location.NanoAppState;
import android.util.Log;
@@ -211,9 +212,14 @@ import java.util.List;
android.hardware.contexthub.NanoappInfo[] nanoAppInfoList) {
ArrayList<NanoAppState> nanoAppStateList = new ArrayList<>();
for (android.hardware.contexthub.NanoappInfo appInfo : nanoAppInfoList) {
+ ArrayList<NanoAppRpcService> rpcServiceList = new ArrayList<>();
+ for (android.hardware.contexthub.NanoappRpcService service : appInfo.rpcServices) {
+ rpcServiceList.add(new NanoAppRpcService(service.id, service.version));
+ }
nanoAppStateList.add(
new NanoAppState(appInfo.nanoappId, appInfo.nanoappVersion,
- appInfo.enabled, new ArrayList<>(Arrays.asList(appInfo.permissions))));
+ appInfo.enabled, new ArrayList<>(Arrays.asList(appInfo.permissions)),
+ rpcServiceList));
}
return nanoAppStateList;
diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
index 1781588b0ba2..699f1439538e 100644
--- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
@@ -16,6 +16,8 @@
package com.android.server.location.gnss;
+import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
+
import static com.android.server.location.gnss.GnssManagerService.D;
import static com.android.server.location.gnss.GnssManagerService.TAG;
@@ -32,7 +34,6 @@ import android.util.Log;
import com.android.server.location.gnss.hal.GnssNative;
import com.android.server.location.injector.AppOpsHelper;
import com.android.server.location.injector.Injector;
-import com.android.server.location.injector.LocationAttributionHelper;
import com.android.server.location.injector.LocationUsageLogger;
import com.android.server.location.injector.SettingsHelper;
@@ -68,25 +69,23 @@ public final class GnssMeasurementsProvider extends
@Nullable
@Override
protected void onActive() {
- mLocationAttributionHelper.reportHighPowerLocationStart(getIdentity());
+ mAppOpsHelper.startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, getIdentity());
}
@Nullable
@Override
protected void onInactive() {
- mLocationAttributionHelper.reportHighPowerLocationStop(getIdentity());
+ mAppOpsHelper.finishOp(OP_MONITOR_HIGH_POWER_LOCATION, getIdentity());
}
}
private final AppOpsHelper mAppOpsHelper;
- private final LocationAttributionHelper mLocationAttributionHelper;
private final LocationUsageLogger mLogger;
private final GnssNative mGnssNative;
public GnssMeasurementsProvider(Injector injector, GnssNative gnssNative) {
super(injector);
mAppOpsHelper = injector.getAppOpsHelper();
- mLocationAttributionHelper = injector.getLocationAttributionHelper();
mLogger = injector.getLocationUsageLogger();
mGnssNative = gnssNative;
diff --git a/services/core/java/com/android/server/location/injector/Injector.java b/services/core/java/com/android/server/location/injector/Injector.java
index 173fd13c11a1..c0ce3a6853cf 100644
--- a/services/core/java/com/android/server/location/injector/Injector.java
+++ b/services/core/java/com/android/server/location/injector/Injector.java
@@ -58,9 +58,6 @@ public interface Injector {
/** Returns a DeviceIdleHelper. */
DeviceIdleHelper getDeviceIdleHelper();
- /** Returns a LocationAttributionHelper. */
- LocationAttributionHelper getLocationAttributionHelper();
-
/** Returns an EmergencyHelper. */
EmergencyHelper getEmergencyHelper();
diff --git a/services/core/java/com/android/server/location/injector/LocationAttributionHelper.java b/services/core/java/com/android/server/location/injector/LocationAttributionHelper.java
deleted file mode 100644
index 483875230ac4..000000000000
--- a/services/core/java/com/android/server/location/injector/LocationAttributionHelper.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.location.injector;
-
-import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
-import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
-
-import static com.android.server.location.LocationManagerService.D;
-import static com.android.server.location.LocationManagerService.TAG;
-
-import android.location.util.identity.CallerIdentity;
-import android.util.ArrayMap;
-import android.util.Log;
-
-import com.android.internal.annotations.GuardedBy;
-
-import java.util.Map;
-
-/**
- * Helps manage appop monitoring for multiple location clients.
- */
-public class LocationAttributionHelper {
-
- private final AppOpsHelper mAppOpsHelper;
-
- @GuardedBy("this")
- private final Map<CallerIdentity, Integer> mAttributions;
- @GuardedBy("this")
- private final Map<CallerIdentity, Integer> mHighPowerAttributions;
-
- public LocationAttributionHelper(AppOpsHelper appOpsHelper) {
- mAppOpsHelper = appOpsHelper;
-
- mAttributions = new ArrayMap<>();
- mHighPowerAttributions = new ArrayMap<>();
- }
-
- /**
- * Report normal location usage for the given caller in the given bucket, with a unique key.
- */
- public synchronized void reportLocationStart(CallerIdentity identity) {
- identity = CallerIdentity.forAggregation(identity);
-
- int count = mAttributions.getOrDefault(identity, 0);
- if (count == 0) {
- if (mAppOpsHelper.startOpNoThrow(OP_MONITOR_LOCATION, identity)) {
- mAttributions.put(identity, 1);
- }
- } else {
- mAttributions.put(identity, count + 1);
- }
- }
-
- /**
- * Report normal location usage has stopped for the given caller in the given bucket, with a
- * unique key.
- */
- public synchronized void reportLocationStop(CallerIdentity identity) {
- identity = CallerIdentity.forAggregation(identity);
-
- int count = mAttributions.getOrDefault(identity, 0);
- if (count == 1) {
- mAttributions.remove(identity);
- mAppOpsHelper.finishOp(OP_MONITOR_LOCATION, identity);
- } else if (count > 1) {
- mAttributions.put(identity, count - 1);
- }
- }
-
- /**
- * Report high power location usage for the given caller in the given bucket, with a unique
- * key.
- */
- public synchronized void reportHighPowerLocationStart(CallerIdentity identity) {
- identity = CallerIdentity.forAggregation(identity);
-
- int count = mHighPowerAttributions.getOrDefault(identity, 0);
- if (count == 0) {
- if (D) {
- Log.v(TAG, "starting high power location attribution for " + identity);
- }
- if (mAppOpsHelper.startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, identity)) {
- mHighPowerAttributions.put(identity, 1);
- }
- } else {
- mHighPowerAttributions.put(identity, count + 1);
- }
- }
-
- /**
- * Report high power location usage has stopped for the given caller in the given bucket,
- * with a unique key.
- */
- public synchronized void reportHighPowerLocationStop(CallerIdentity identity) {
- identity = CallerIdentity.forAggregation(identity);
-
- int count = mHighPowerAttributions.getOrDefault(identity, 0);
- if (count == 1) {
- if (D) {
- Log.v(TAG, "stopping high power location attribution for " + identity);
- }
- mHighPowerAttributions.remove(identity);
- mAppOpsHelper.finishOp(OP_MONITOR_HIGH_POWER_LOCATION, identity);
- } else if (count > 1) {
- mHighPowerAttributions.put(identity, count - 1);
- }
- }
-}
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 0ce24dda666e..e7cc7b6e09e9 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -16,6 +16,8 @@
package com.android.server.location.provider;
+import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
+import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
import static android.app.compat.CompatChanges.isChangeEnabled;
import static android.location.LocationManager.DELIVER_HISTORICAL_LOCATIONS;
import static android.location.LocationManager.GPS_PROVIDER;
@@ -32,6 +34,7 @@ import static android.os.PowerManager.LOCATION_MODE_FOREGROUND_ONLY;
import static android.os.PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF;
import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
+import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
import static com.android.server.location.LocationManagerService.D;
import static com.android.server.location.LocationManagerService.TAG;
import static com.android.server.location.LocationPermissions.PERMISSION_COARSE;
@@ -98,7 +101,6 @@ import com.android.server.location.injector.AppForegroundHelper;
import com.android.server.location.injector.AppForegroundHelper.AppForegroundListener;
import com.android.server.location.injector.AppOpsHelper;
import com.android.server.location.injector.Injector;
-import com.android.server.location.injector.LocationAttributionHelper;
import com.android.server.location.injector.LocationPermissionsHelper;
import com.android.server.location.injector.LocationPermissionsHelper.LocationPermissionsListener;
import com.android.server.location.injector.LocationPowerSaveModeHelper;
@@ -191,7 +193,7 @@ public class LocationProviderManager extends
private final ILocationListener mListener;
- protected LocationListenerTransport(ILocationListener listener) {
+ LocationListenerTransport(ILocationListener listener) {
mListener = Objects.requireNonNull(listener);
}
@@ -287,7 +289,7 @@ public class LocationProviderManager extends
private final ILocationCallback mCallback;
- protected GetCurrentLocationTransport(ILocationCallback callback) {
+ GetCurrentLocationTransport(ILocationCallback callback) {
mCallback = Objects.requireNonNull(callback);
}
@@ -398,7 +400,7 @@ public class LocationProviderManager extends
EVENT_LOG.logProviderClientActive(mName, getIdentity());
if (!getRequest().isHiddenFromAppOps()) {
- mLocationAttributionHelper.reportLocationStart(getIdentity());
+ mAppOpsHelper.startOpNoThrow(OP_MONITOR_LOCATION, getIdentity());
}
onHighPowerUsageChanged();
@@ -413,7 +415,7 @@ public class LocationProviderManager extends
onHighPowerUsageChanged();
if (!getRequest().isHiddenFromAppOps()) {
- mLocationAttributionHelper.reportLocationStop(getIdentity());
+ mAppOpsHelper.finishOp(OP_MONITOR_LOCATION, getIdentity());
}
onProviderListenerInactive();
@@ -487,11 +489,9 @@ public class LocationProviderManager extends
if (!getRequest().isHiddenFromAppOps()) {
if (mIsUsingHighPower) {
- mLocationAttributionHelper.reportHighPowerLocationStart(
- getIdentity());
+ mAppOpsHelper.startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, getIdentity());
} else {
- mLocationAttributionHelper.reportHighPowerLocationStop(
- getIdentity());
+ mAppOpsHelper.finishOp(OP_MONITOR_HIGH_POWER_LOCATION, getIdentity());
}
}
}
@@ -994,7 +994,7 @@ public class LocationProviderManager extends
protected final class LocationListenerRegistration extends LocationRegistration implements
IBinder.DeathRecipient {
- protected LocationListenerRegistration(LocationRequest request, CallerIdentity identity,
+ LocationListenerRegistration(LocationRequest request, CallerIdentity identity,
LocationListenerTransport transport, @PermissionLevel int permissionLevel) {
super(request, identity, transport, permissionLevel);
}
@@ -1059,7 +1059,7 @@ public class LocationProviderManager extends
protected final class LocationPendingIntentRegistration extends LocationRegistration implements
PendingIntent.CancelListener {
- protected LocationPendingIntentRegistration(LocationRequest request,
+ LocationPendingIntentRegistration(LocationRequest request,
CallerIdentity identity, LocationPendingIntentTransport transport,
@PermissionLevel int permissionLevel) {
super(request, identity, transport, permissionLevel);
@@ -1068,13 +1068,15 @@ public class LocationProviderManager extends
@GuardedBy("mLock")
@Override
protected void onLocationListenerRegister() {
- ((PendingIntent) getKey()).registerCancelListener(this);
+ if (!((PendingIntent) getKey()).addCancelListener(DIRECT_EXECUTOR, this)) {
+ remove();
+ }
}
@GuardedBy("mLock")
@Override
protected void onLocationListenerUnregister() {
- ((PendingIntent) getKey()).unregisterCancelListener(this);
+ ((PendingIntent) getKey()).removeCancelListener(this);
}
@Override
@@ -1117,7 +1119,7 @@ public class LocationProviderManager extends
private long mExpirationRealtimeMs = Long.MAX_VALUE;
- protected GetCurrentLocationListenerRegistration(LocationRequest request,
+ GetCurrentLocationListenerRegistration(LocationRequest request,
CallerIdentity identity, LocationTransport transport, int permissionLevel) {
super(request, identity, transport, permissionLevel);
}
@@ -1326,7 +1328,6 @@ public class LocationProviderManager extends
protected final AppForegroundHelper mAppForegroundHelper;
protected final LocationPowerSaveModeHelper mLocationPowerSaveModeHelper;
protected final ScreenInteractiveHelper mScreenInteractiveHelper;
- protected final LocationAttributionHelper mLocationAttributionHelper;
protected final LocationUsageLogger mLocationUsageLogger;
protected final LocationFudger mLocationFudger;
@@ -1394,7 +1395,6 @@ public class LocationProviderManager extends
mAppForegroundHelper = injector.getAppForegroundHelper();
mLocationPowerSaveModeHelper = injector.getLocationPowerSaveModeHelper();
mScreenInteractiveHelper = injector.getScreenInteractiveHelper();
- mLocationAttributionHelper = injector.getLocationAttributionHelper();
mLocationUsageLogger = injector.getLocationUsageLogger();
mLocationFudger = new LocationFudger(mSettingsHelper.getCoarseLocationAccuracyM());
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 7d4877cdc7ee..258ae8c1c849 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -2151,6 +2151,10 @@ public class PreferencesHelper implements RankingConfig {
final PackagePreferences r = mPackagePreferences.valueAt(i);
event.writeInt(r.uid);
event.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
+
+ // collect whether this package's importance info was user-set for later, if needed
+ // before the migration is enabled, this will simply default to false in all cases.
+ boolean importanceIsUserSet = false;
if (mPermissionHelper.isMigrationEnabled()) {
// Even if this package's data is not present, we need to write something;
// so default to IMPORTANCE_NONE, since if PM doesn't know about the package
@@ -2158,8 +2162,12 @@ public class PreferencesHelper implements RankingConfig {
int importance = IMPORTANCE_NONE;
Pair<Integer, String> key = new Pair<>(r.uid, r.pkg);
if (pkgPermissions != null && pkgsWithPermissionsToHandle.contains(key)) {
- importance = pkgPermissions.get(key).first
+ Pair<Boolean, Boolean> permissionPair = pkgPermissions.get(key);
+ importance = permissionPair.first
? IMPORTANCE_DEFAULT : IMPORTANCE_NONE;
+ // cache the second value for writing later
+ importanceIsUserSet = permissionPair.second;
+
pkgsWithPermissionsToHandle.remove(key);
}
event.writeInt(importance);
@@ -2168,6 +2176,7 @@ public class PreferencesHelper implements RankingConfig {
}
event.writeInt(r.visibility);
event.writeInt(r.lockedAppFields);
+ event.writeBoolean(importanceIsUserSet); // optional bool user_set_importance = 5;
events.add(event.build());
}
}
@@ -2189,6 +2198,7 @@ public class PreferencesHelper implements RankingConfig {
// builder
event.writeInt(DEFAULT_VISIBILITY);
event.writeInt(DEFAULT_LOCKED_APP_FIELDS);
+ event.writeBoolean(pkgPermissions.get(p).second); // user_set_importance field
events.add(event.build());
}
}
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index fb77d101f910..05567480e6bf 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -34,6 +34,7 @@ import android.content.pm.PackageManager;
import android.content.pm.SigningDetails;
import android.content.pm.parsing.PackageInfoWithoutStateUtils;
import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.component.ParsedApexSystemService;
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
import android.os.Binder;
@@ -53,6 +54,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
+import com.android.modules.utils.build.UnboundedSdkLevel;
import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.ParsedPackage;
@@ -411,6 +413,11 @@ public abstract class ApexManager {
throws PackageManagerException;
/**
+ * Get a map of system services defined in an apex mapped to the jar files they reside in.
+ */
+ public abstract Map<String, String> getApexSystemServices();
+
+ /**
* Dumps various state information to the provided {@link PrintWriter} object.
*
* @param pw the {@link PrintWriter} object to send information to.
@@ -438,6 +445,12 @@ public abstract class ApexManager {
private Set<ActiveApexInfo> mActiveApexInfosCache;
/**
+ * Map of all apex system services to the jar files they are contained in.
+ */
+ @GuardedBy("mLock")
+ private Map<String, String> mApexSystemServices = new ArrayMap<>();
+
+ /**
* Contains the list of {@code packageName}s of apks-in-apex for given
* {@code apexModuleName}. See {@link #mPackageNameToApexModuleName} to understand the
* difference between {@code packageName} and {@code apexModuleName}.
@@ -573,6 +586,32 @@ public abstract class ApexManager {
+ ai.modulePath);
}
mAllPackagesCache.add(packageInfo);
+ for (ParsedApexSystemService service :
+ parseResult.parsedPackage.getApexSystemServices()) {
+ String minSdkVersion = service.getMinSdkVersion();
+ if (minSdkVersion != null && !UnboundedSdkLevel.isAtLeast(minSdkVersion)) {
+ Slog.d(TAG, String.format(
+ "ApexSystemService %s with min_sdk_version=%s is skipped",
+ service.getName(), service.getMinSdkVersion()));
+ continue;
+ }
+ String maxSdkVersion = service.getMaxSdkVersion();
+ if (maxSdkVersion != null && !UnboundedSdkLevel.isAtMost(maxSdkVersion)) {
+ Slog.d(TAG, String.format(
+ "ApexSystemService %s with max_sdk_version=%s is skipped",
+ service.getName(), service.getMaxSdkVersion()));
+ continue;
+ }
+
+ String name = service.getName();
+ if (mApexSystemServices.containsKey(name)) {
+ throw new IllegalStateException(String.format(
+ "Duplicate apex-system-service %s from %s, %s",
+ name, mApexSystemServices.get(name), service.getJarPath()));
+ }
+
+ mApexSystemServices.put(name, service.getJarPath());
+ }
mPackageNameToApexModuleName.put(packageInfo.packageName, ai.moduleName);
if (ai.isActive) {
if (activePackagesSet.contains(packageInfo.packageName)) {
@@ -1092,6 +1131,15 @@ public abstract class ApexManager {
}
}
+ @Override
+ public Map<String, String> getApexSystemServices() {
+ synchronized (mLock) {
+ Preconditions.checkState(mApexSystemServices != null,
+ "APEX packages have not been scanned");
+ return mApexSystemServices;
+ }
+ }
+
/**
* Dump information about the packages contained in a particular cache
* @param packagesCache the cache to print information about.
@@ -1370,6 +1418,13 @@ public abstract class ApexManager {
}
@Override
+ public Map<String, String> getApexSystemServices() {
+ // TODO(satayev): we can't really support flattened apex use case, and need to migrate
+ // the manifest entries into system's manifest asap.
+ return Collections.emptyMap();
+ }
+
+ @Override
void dump(PrintWriter pw, String packageName) {
// No-op
}
diff --git a/services/core/java/com/android/server/pm/AppDataHelper.java b/services/core/java/com/android/server/pm/AppDataHelper.java
index e99512d3b9fa..bedb8b9f7a18 100644
--- a/services/core/java/com/android/server/pm/AppDataHelper.java
+++ b/services/core/java/com/android/server/pm/AppDataHelper.java
@@ -74,13 +74,6 @@ final class AppDataHelper {
mArtManagerService = mInjector.getArtManagerService();
}
- AppDataHelper(PackageManagerService pm, PackageManagerServiceInjector injector) {
- mPm = pm;
- mInjector = injector;
- mInstaller = injector.getInstaller();
- mArtManagerService = injector.getArtManagerService();
- }
-
/**
* Prepare app data for the given app just after it was installed or
* upgraded. This method carefully only touches users that it's installed
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 627970e7cb11..1945ed054e21 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -3156,7 +3156,7 @@ public class ComputerEngine implements Computer {
try {
mDomainVerificationManager.printState(writer, packageName,
UserHandle.USER_ALL, mSettings::getPackage);
- } catch (PackageManager.NameNotFoundException e) {
+ } catch (Exception e) {
pw.println("Failure printing domain verification information");
Slog.e(TAG, "Failure printing domain verification information", e);
}
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index e8922ebbfd13..e84d990b5d21 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -236,7 +236,9 @@ final class DeletePackageHelper {
if (res) {
final boolean killApp = (deleteFlags & PackageManager.DELETE_DONT_KILL_APP) == 0;
info.sendPackageRemovedBroadcasts(killApp, removedBySystem);
- info.sendSystemPackageUpdatedBroadcasts();
+ if (disabledSystemPs != null) {
+ info.sendSystemPackageUpdatedBroadcasts(disabledSystemPs.getAppId());
+ }
}
// Force a gc here.
Runtime.getRuntime().gc();
@@ -431,10 +433,9 @@ final class DeletePackageHelper {
if (DEBUG_REMOVE) Slog.d(TAG, "Removing system package: " + ps.getPackageName());
// When an updated system application is deleted we delete the existing resources
// as well and fall back to existing code in system partition
- PackageSetting disabledPs = deleteInstalledSystemPackage(action, ps, allUserHandles,
- flags, outInfo, writeSettings);
+ deleteInstalledSystemPackage(action, allUserHandles, writeSettings);
new InstallPackageHelper(mPm).restoreDisabledSystemPackageLIF(
- action, ps, allUserHandles, outInfo, writeSettings, disabledPs);
+ action, allUserHandles, writeSettings);
} else {
if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package: " + ps.getPackageName());
deleteInstalledPackageLIF(ps, deleteCodeAndResources, flags, allUserHandles,
@@ -561,10 +562,11 @@ final class DeletePackageHelper {
mPm.mSettings.writeKernelMappingLPr(ps);
}
- private PackageSetting deleteInstalledSystemPackage(DeletePackageAction action,
- PackageSetting deletedPs,
- @NonNull int[] allUserHandles, int flags, @Nullable PackageRemovedInfo outInfo,
- boolean writeSettings) {
+ private void deleteInstalledSystemPackage(DeletePackageAction action,
+ @NonNull int[] allUserHandles, boolean writeSettings) {
+ int flags = action.mFlags;
+ final PackageSetting deletedPs = action.mDeletingPs;
+ final PackageRemovedInfo outInfo = action.mRemovedInfo;
final boolean applyUserRestrictions = outInfo != null && (outInfo.mOrigUsers != null);
final AndroidPackage deletedPkg = deletedPs.getPkg();
// Confirm if the system package has been updated
@@ -591,20 +593,19 @@ final class DeletePackageHelper {
if (outInfo != null) {
// Delete the updated package
outInfo.mIsRemovedPackageSystemUpdate = true;
+ outInfo.mAppIdChanging = disabledPs.getAppId() != deletedPs.getAppId();
}
- if (disabledPs.getVersionCode() < deletedPs.getVersionCode()) {
- // Delete data for downgrades
+ if (disabledPs.getVersionCode() < deletedPs.getVersionCode()
+ || disabledPs.getAppId() != deletedPs.getAppId()) {
+ // Delete data for downgrades, or when the system app changed appId
flags &= ~PackageManager.DELETE_KEEP_DATA;
} else {
// Preserve data by setting flag
flags |= PackageManager.DELETE_KEEP_DATA;
}
- deleteInstalledPackageLIF(deletedPs, true, flags, allUserHandles,
- outInfo, writeSettings);
-
- return disabledPs;
+ deleteInstalledPackageLIF(deletedPs, true, flags, allUserHandles, outInfo, writeSettings);
}
public void deletePackageVersionedInternal(VersionedPackage versionedPackage,
diff --git a/services/core/java/com/android/server/pm/DomainVerificationConnection.java b/services/core/java/com/android/server/pm/DomainVerificationConnection.java
index 4ddf2ff51dd1..d24435e12593 100644
--- a/services/core/java/com/android/server/pm/DomainVerificationConnection.java
+++ b/services/core/java/com/android/server/pm/DomainVerificationConnection.java
@@ -76,7 +76,7 @@ public final class DomainVerificationConnection implements DomainVerificationSer
@Override
public long getPowerSaveTempWhitelistAppDuration() {
- return VerificationUtils.getVerificationTimeout(mPm.mContext);
+ return VerificationUtils.getDefaultVerificationTimeout(mPm.mContext);
}
@Override
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index fce37a9f2345..8e6746dca7fc 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -32,8 +32,6 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_SESSION_INVALID;
import static android.content.pm.PackageManager.INSTALL_FAILED_TEST_ONLY;
import static android.content.pm.PackageManager.INSTALL_FAILED_UID_CHANGED;
import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
-import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_RESTORE;
import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_SETUP;
import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
@@ -47,7 +45,6 @@ import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
-import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
@@ -56,6 +53,7 @@ import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION;
import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING;
import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE;
+import static com.android.server.pm.PackageManagerService.DEBUG_UPGRADE;
import static com.android.server.pm.PackageManagerService.DEBUG_VERIFY;
import static com.android.server.pm.PackageManagerService.EMPTY_INT_ARRAY;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
@@ -74,13 +72,16 @@ import static com.android.server.pm.PackageManagerService.SCAN_AS_VENDOR;
import static com.android.server.pm.PackageManagerService.SCAN_AS_VIRTUAL_PRELOAD;
import static com.android.server.pm.PackageManagerService.SCAN_BOOTING;
import static com.android.server.pm.PackageManagerService.SCAN_DONT_KILL_APP;
+import static com.android.server.pm.PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE;
import static com.android.server.pm.PackageManagerService.SCAN_IGNORE_FROZEN;
import static com.android.server.pm.PackageManagerService.SCAN_INITIAL;
import static com.android.server.pm.PackageManagerService.SCAN_MOVE;
import static com.android.server.pm.PackageManagerService.SCAN_NEW_INSTALL;
import static com.android.server.pm.PackageManagerService.SCAN_NO_DEX;
+import static com.android.server.pm.PackageManagerService.SCAN_REQUIRE_KNOWN;
import static com.android.server.pm.PackageManagerService.SCAN_UPDATE_SIGNATURE;
import static com.android.server.pm.PackageManagerService.TAG;
+import static com.android.server.pm.PackageManagerServiceUtils.comparePackageSignatures;
import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
import static com.android.server.pm.PackageManagerServiceUtils.compressedFileExists;
import static com.android.server.pm.PackageManagerServiceUtils.deriveAbiOverride;
@@ -127,7 +128,6 @@ import android.os.Environment;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
@@ -147,14 +147,15 @@ import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.F2fsUtils;
-import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.content.PackageHelper;
import com.android.internal.security.VerityUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.EventLogTags;
-import com.android.server.Watchdog;
+import com.android.server.pm.dex.ArtManagerService;
+import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DexoptOptions;
+import com.android.server.pm.dex.ViewCompiler;
import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
@@ -190,278 +191,47 @@ import java.util.Set;
import java.util.concurrent.ExecutorService;
final class InstallPackageHelper {
- /**
- * Whether verification is enabled by default.
- */
- private static final boolean DEFAULT_VERIFY_ENABLE = true;
-
private final PackageManagerService mPm;
private final AppDataHelper mAppDataHelper;
- private final PackageManagerServiceInjector mInjector;
private final BroadcastHelper mBroadcastHelper;
private final RemovePackageHelper mRemovePackageHelper;
- private final ScanPackageHelper mScanPackageHelper;
+ private final StorageManager mStorageManager;
+ private final RollbackManagerInternal mRollbackManager;
+ private final IncrementalManager mIncrementalManager;
+ private final ApexManager mApexManager;
+ private final DexManager mDexManager;
+ private final ArtManagerService mArtManagerService;
+ private final AppOpsManager mAppOpsManager;
+ private final Context mContext;
+ private final PackageDexOptimizer mPackageDexOptimizer;
+ private final PackageAbiHelper mPackageAbiHelper;
+ private final ViewCompiler mViewCompiler;
+ private final IBackupManager mIBackupManager;
// TODO(b/198166813): remove PMS dependency
InstallPackageHelper(PackageManagerService pm, AppDataHelper appDataHelper) {
mPm = pm;
- mInjector = pm.mInjector;
mAppDataHelper = appDataHelper;
- mBroadcastHelper = new BroadcastHelper(mInjector);
+ mBroadcastHelper = new BroadcastHelper(pm.mInjector);
mRemovePackageHelper = new RemovePackageHelper(pm);
- mScanPackageHelper = new ScanPackageHelper(pm);
+ mStorageManager = pm.mInjector.getSystemService(StorageManager.class);
+ mRollbackManager = pm.mInjector.getLocalService(RollbackManagerInternal.class);
+ mIncrementalManager = pm.mInjector.getIncrementalManager();
+ mApexManager = pm.mInjector.getApexManager();
+ mDexManager = pm.mInjector.getDexManager();
+ mArtManagerService = pm.mInjector.getArtManagerService();
+ mAppOpsManager = pm.mInjector.getSystemService(AppOpsManager.class);
+ mContext = pm.mInjector.getContext();
+ mPackageDexOptimizer = pm.mInjector.getPackageDexOptimizer();
+ mPackageAbiHelper = pm.mInjector.getAbiHelper();
+ mViewCompiler = pm.mInjector.getViewCompiler();
+ mIBackupManager = pm.mInjector.getIBackupManager();
}
InstallPackageHelper(PackageManagerService pm) {
this(pm, new AppDataHelper(pm));
}
- InstallPackageHelper(PackageManagerService pm, PackageManagerServiceInjector injector) {
- mPm = pm;
- mInjector = injector;
- mAppDataHelper = new AppDataHelper(pm, mInjector);
- mBroadcastHelper = new BroadcastHelper(injector);
- mRemovePackageHelper = new RemovePackageHelper(pm);
- mScanPackageHelper = new ScanPackageHelper(pm);
- }
-
- @GuardedBy("mPm.mLock")
- public Map<String, ReconciledPackage> reconcilePackagesLocked(
- final ReconcileRequest request, KeySetManagerService ksms,
- PackageManagerServiceInjector injector)
- throws ReconcileFailure {
- final Map<String, ScanResult> scannedPackages = request.mScannedPackages;
-
- final Map<String, ReconciledPackage> result = new ArrayMap<>(scannedPackages.size());
-
- // make a copy of the existing set of packages so we can combine them with incoming packages
- final ArrayMap<String, AndroidPackage> combinedPackages =
- new ArrayMap<>(request.mAllPackages.size() + scannedPackages.size());
-
- combinedPackages.putAll(request.mAllPackages);
-
- final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> incomingSharedLibraries =
- new ArrayMap<>();
-
- for (String installPackageName : scannedPackages.keySet()) {
- final ScanResult scanResult = scannedPackages.get(installPackageName);
-
- // add / replace existing with incoming packages
- combinedPackages.put(scanResult.mPkgSetting.getPackageName(),
- scanResult.mRequest.mParsedPackage);
-
- // in the first pass, we'll build up the set of incoming shared libraries
- final List<SharedLibraryInfo> allowedSharedLibInfos =
- SharedLibraryHelper.getAllowedSharedLibInfos(scanResult,
- request.mSharedLibrarySource);
- if (allowedSharedLibInfos != null) {
- for (SharedLibraryInfo info : allowedSharedLibInfos) {
- if (!SharedLibraryHelper.addSharedLibraryToPackageVersionMap(
- incomingSharedLibraries, info)) {
- throw new ReconcileFailure("Shared Library " + info.getName()
- + " is being installed twice in this set!");
- }
- }
- }
-
- // the following may be null if we're just reconciling on boot (and not during install)
- final InstallArgs installArgs = request.mInstallArgs.get(installPackageName);
- final PackageInstalledInfo res = request.mInstallResults.get(installPackageName);
- final PrepareResult prepareResult = request.mPreparedPackages.get(installPackageName);
- final boolean isInstall = installArgs != null;
- if (isInstall && (res == null || prepareResult == null)) {
- throw new ReconcileFailure("Reconcile arguments are not balanced for "
- + installPackageName + "!");
- }
-
- final DeletePackageAction deletePackageAction;
- // we only want to try to delete for non system apps
- if (isInstall && prepareResult.mReplace && !prepareResult.mSystem) {
- final boolean killApp = (scanResult.mRequest.mScanFlags & SCAN_DONT_KILL_APP) == 0;
- final int deleteFlags = PackageManager.DELETE_KEEP_DATA
- | (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP);
- deletePackageAction = DeletePackageHelper.mayDeletePackageLocked(res.mRemovedInfo,
- prepareResult.mOriginalPs, prepareResult.mDisabledPs,
- deleteFlags, null /* all users */);
- if (deletePackageAction == null) {
- throw new ReconcileFailure(
- PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE,
- "May not delete " + installPackageName + " to replace");
- }
- } else {
- deletePackageAction = null;
- }
-
- final int scanFlags = scanResult.mRequest.mScanFlags;
- final int parseFlags = scanResult.mRequest.mParseFlags;
- final ParsedPackage parsedPackage = scanResult.mRequest.mParsedPackage;
-
- final PackageSetting disabledPkgSetting = scanResult.mRequest.mDisabledPkgSetting;
- final PackageSetting lastStaticSharedLibSetting =
- request.mLastStaticSharedLibSettings.get(installPackageName);
- final PackageSetting signatureCheckPs =
- (prepareResult != null && lastStaticSharedLibSetting != null)
- ? lastStaticSharedLibSetting
- : scanResult.mPkgSetting;
- boolean removeAppKeySetData = false;
- boolean sharedUserSignaturesChanged = false;
- SigningDetails signingDetails = null;
- if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
- if (ksms.checkUpgradeKeySetLocked(signatureCheckPs, parsedPackage)) {
- // We just determined the app is signed correctly, so bring
- // over the latest parsed certs.
- } else {
- if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
- throw new ReconcileFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
- "Package " + parsedPackage.getPackageName()
- + " upgrade keys do not match the previously installed"
- + " version");
- } else {
- String msg = "System package " + parsedPackage.getPackageName()
- + " signature changed; retaining data.";
- PackageManagerService.reportSettingsProblem(Log.WARN, msg);
- }
- }
- signingDetails = parsedPackage.getSigningDetails();
- } else {
- try {
- final Settings.VersionInfo versionInfo =
- request.mVersionInfos.get(installPackageName);
- final boolean compareCompat = isCompatSignatureUpdateNeeded(versionInfo);
- final boolean compareRecover = isRecoverSignatureUpdateNeeded(versionInfo);
- final boolean isRollback = installArgs != null
- && installArgs.mInstallReason == PackageManager.INSTALL_REASON_ROLLBACK;
- final boolean compatMatch = verifySignatures(signatureCheckPs,
- disabledPkgSetting, parsedPackage.getSigningDetails(), compareCompat,
- compareRecover, isRollback);
- // The new KeySets will be re-added later in the scanning process.
- if (compatMatch) {
- removeAppKeySetData = true;
- }
- // We just determined the app is signed correctly, so bring
- // over the latest parsed certs.
- signingDetails = parsedPackage.getSigningDetails();
-
- // if this is is a sharedUser, check to see if the new package is signed by a
- // newer
- // signing certificate than the existing one, and if so, copy over the new
- // details
- if (signatureCheckPs.getSharedUser() != null) {
- // Attempt to merge the existing lineage for the shared SigningDetails with
- // the lineage of the new package; if the shared SigningDetails are not
- // returned this indicates the new package added new signers to the lineage
- // and/or changed the capabilities of existing signers in the lineage.
- SigningDetails sharedSigningDetails =
- signatureCheckPs.getSharedUser().signatures.mSigningDetails;
- SigningDetails mergedDetails = sharedSigningDetails.mergeLineageWith(
- signingDetails);
- if (mergedDetails != sharedSigningDetails) {
- signatureCheckPs.getSharedUser().signatures.mSigningDetails =
- mergedDetails;
- }
- if (signatureCheckPs.getSharedUser().signaturesChanged == null) {
- signatureCheckPs.getSharedUser().signaturesChanged = Boolean.FALSE;
- }
- }
- } catch (PackageManagerException e) {
- if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
- throw new ReconcileFailure(e);
- }
- signingDetails = parsedPackage.getSigningDetails();
-
- // If the system app is part of a shared user we allow that shared user to
- // change
- // signatures as well as part of an OTA. We still need to verify that the
- // signatures
- // are consistent within the shared user for a given boot, so only allow
- // updating
- // the signatures on the first package scanned for the shared user (i.e. if the
- // signaturesChanged state hasn't been initialized yet in SharedUserSetting).
- if (signatureCheckPs.getSharedUser() != null) {
- final Signature[] sharedUserSignatures = signatureCheckPs.getSharedUser()
- .signatures.mSigningDetails.getSignatures();
- if (signatureCheckPs.getSharedUser().signaturesChanged != null
- && compareSignatures(sharedUserSignatures,
- parsedPackage.getSigningDetails().getSignatures())
- != PackageManager.SIGNATURE_MATCH) {
- if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 29) {
- // Mismatched signatures is an error and silently skipping system
- // packages will likely break the device in unforeseen ways.
- // However, we allow the device to boot anyway because, prior to Q,
- // vendors were not expecting the platform to crash in this
- // situation.
- // This WILL be a hard failure on any new API levels after Q.
- throw new ReconcileFailure(
- INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
- "Signature mismatch for shared user: "
- + scanResult.mPkgSetting.getSharedUser());
- } else {
- // Treat mismatched signatures on system packages using a shared
- // UID as
- // fatal for the system overall, rather than just failing to install
- // whichever package happened to be scanned later.
- throw new IllegalStateException(
- "Signature mismatch on system package "
- + parsedPackage.getPackageName()
- + " for shared user "
- + scanResult.mPkgSetting.getSharedUser());
- }
- }
-
- sharedUserSignaturesChanged = true;
- signatureCheckPs.getSharedUser().signatures.mSigningDetails =
- parsedPackage.getSigningDetails();
- signatureCheckPs.getSharedUser().signaturesChanged = Boolean.TRUE;
- }
- // File a report about this.
- String msg = "System package " + parsedPackage.getPackageName()
- + " signature changed; retaining data.";
- PackageManagerService.reportSettingsProblem(Log.WARN, msg);
- } catch (IllegalArgumentException e) {
- // should never happen: certs matched when checking, but not when comparing
- // old to new for sharedUser
- throw new RuntimeException(
- "Signing certificates comparison made on incomparable signing details"
- + " but somehow passed verifySignatures!", e);
- }
- }
-
- result.put(installPackageName,
- new ReconciledPackage(request, installArgs, scanResult.mPkgSetting,
- res, request.mPreparedPackages.get(installPackageName), scanResult,
- deletePackageAction, allowedSharedLibInfos, signingDetails,
- sharedUserSignaturesChanged, removeAppKeySetData));
- }
-
- for (String installPackageName : scannedPackages.keySet()) {
- // Check all shared libraries and map to their actual file path.
- // We only do this here for apps not on a system dir, because those
- // are the only ones that can fail an install due to this. We
- // will take care of the system apps by updating all of their
- // library paths after the scan is done. Also during the initial
- // scan don't update any libs as we do this wholesale after all
- // apps are scanned to avoid dependency based scanning.
- final ScanResult scanResult = scannedPackages.get(installPackageName);
- if ((scanResult.mRequest.mScanFlags & SCAN_BOOTING) != 0
- || (scanResult.mRequest.mParseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR)
- != 0) {
- continue;
- }
- try {
- result.get(installPackageName).mCollectedSharedLibraryInfos =
- SharedLibraryHelper.collectSharedLibraryInfos(
- scanResult.mRequest.mParsedPackage,
- combinedPackages, request.mSharedLibrarySource,
- incomingSharedLibraries, injector.getCompatibility());
-
- } catch (PackageManagerException e) {
- throw new ReconcileFailure(e.error, e.getMessage());
- }
- }
-
- return result;
- }
-
/**
* Commits the package scan and modifies system state.
* <p><em>WARNING:</em> The method may throw an exception in the middle
@@ -494,7 +264,13 @@ final class InstallPackageHelper {
if (request.mPkgSetting != null && request.mPkgSetting.getSharedUser() != null
&& request.mPkgSetting.getSharedUser() != result.mPkgSetting.getSharedUser()) {
// shared user changed, remove from old shared user
- request.mPkgSetting.getSharedUser().removePackage(request.mPkgSetting);
+ final SharedUserSetting sus = request.mPkgSetting.getSharedUser();
+ sus.removePackage(request.mPkgSetting);
+ // Prune unused SharedUserSetting
+ if (mPm.mSettings.checkAndPruneSharedUserLPw(sus, false)) {
+ // Set the app ID in removed info for UID_REMOVED broadcasts
+ reconciledPkg.mInstallResult.mRemovedInfo.mRemovedAppId = sus.userId;
+ }
}
if (result.mExistingSettingCopied) {
pkgSetting = request.mPkgSetting;
@@ -672,7 +448,7 @@ final class InstallPackageHelper {
// Add the new setting to mPackages
mPm.mPackages.put(pkg.getPackageName(), pkg);
if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0) {
- mPm.mApexManager.registerApkInApex(pkg);
+ mApexManager.registerApkInApex(pkg);
}
// Add the package's KeySets to the global KeySetManagerService
@@ -723,27 +499,6 @@ final class InstallPackageHelper {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
- /**
- * If the database version for this type of package (internal storage or
- * external storage) is less than the version where package signatures
- * were updated, return true.
- */
- public boolean isCompatSignatureUpdateNeeded(AndroidPackage pkg) {
- return isCompatSignatureUpdateNeeded(mPm.getSettingsVersionForPackage(pkg));
- }
-
- public static boolean isCompatSignatureUpdateNeeded(Settings.VersionInfo ver) {
- return ver.databaseVersion < Settings.DatabaseVersion.SIGNATURE_END_ENTITY;
- }
-
- public boolean isRecoverSignatureUpdateNeeded(AndroidPackage pkg) {
- return isRecoverSignatureUpdateNeeded(mPm.getSettingsVersionForPackage(pkg));
- }
-
- public static boolean isRecoverSignatureUpdateNeeded(Settings.VersionInfo ver) {
- return ver.databaseVersion < Settings.DatabaseVersion.SIGNATURE_MALFORMED_RECOVER;
- }
-
public int installExistingPackageAsUser(@Nullable String packageName, @UserIdInt int userId,
@PackageManager.InstallFlags int installFlags,
@PackageManager.InstallReason int installReason,
@@ -756,9 +511,9 @@ final class InstallPackageHelper {
}
final int callingUid = Binder.getCallingUid();
- if (mPm.mContext.checkCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES)
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES)
!= PackageManager.PERMISSION_GRANTED
- && mPm.mContext.checkCallingOrSelfPermission(
+ && mContext.checkCallingOrSelfPermission(
android.Manifest.permission.INSTALL_EXISTING_PACKAGES)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Neither user " + callingUid + " nor current process has "
@@ -811,7 +566,8 @@ final class InstallPackageHelper {
// upgrade app from instant to full; we don't allow app downgrade
installed = true;
}
- mPm.setInstantAppForUser(mInjector, pkgSetting, userId, instantApp, fullApp);
+ ScanPackageUtils.setInstantAppForUser(mPm.mInjector, pkgSetting, userId, instantApp,
+ fullApp);
}
if (installed) {
@@ -848,7 +604,7 @@ final class InstallPackageHelper {
mPm.restorePermissionsAndUpdateRolesForNewUserInstall(packageName,
userId);
if (intentSender != null) {
- onRestoreComplete(res.mReturnCode, mPm.mContext, intentSender);
+ onRestoreComplete(res.mReturnCode, mContext, intentSender);
}
});
restoreAndPostInstall(userId, res, postInstallData);
@@ -934,9 +690,7 @@ final class InstallPackageHelper {
* Returns whether the restore successfully completed.
*/
private boolean performBackupManagerRestore(int userId, int token, PackageInstalledInfo res) {
- IBackupManager bm = IBackupManager.Stub.asInterface(
- ServiceManager.getService(Context.BACKUP_SERVICE));
- if (bm != null) {
+ if (mIBackupManager != null) {
// For backwards compatibility as USER_ALL previously routed directly to USER_SYSTEM
// in the BackupManager. USER_ALL is used in compatibility tests.
if (userId == UserHandle.USER_ALL) {
@@ -947,8 +701,8 @@ final class InstallPackageHelper {
}
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "restore", token);
try {
- if (bm.isUserReadyForBackup(userId)) {
- bm.restoreAtInstallForUser(
+ if (mIBackupManager.isUserReadyForBackup(userId)) {
+ mIBackupManager.restoreAtInstallForUser(
userId, res.mPkg.getPackageName(), token);
} else {
Slog.w(TAG, "User " + userId + " is not ready. Restore at install "
@@ -974,8 +728,6 @@ final class InstallPackageHelper {
*/
private boolean performRollbackManagerRestore(int userId, int token, PackageInstalledInfo res,
PostInstallData data) {
- RollbackManagerInternal rm = mInjector.getLocalService(RollbackManagerInternal.class);
-
final String packageName = res.mPkg.getPackageName();
final int[] allUsers = mPm.mUserManager.getUserIds();
final int[] installedUsers;
@@ -1001,8 +753,8 @@ final class InstallPackageHelper {
if (ps != null && doSnapshotOrRestore) {
final String seInfo = AndroidPackageUtils.getSeInfo(res.mPkg, ps);
- rm.snapshotAndRestoreUserData(packageName, UserHandle.toUserHandles(installedUsers),
- appId, ceDataInode, seInfo, token);
+ mRollbackManager.snapshotAndRestoreUserData(packageName,
+ UserHandle.toUserHandles(installedUsers), appId, ceDataInode, seInfo, token);
return true;
}
return false;
@@ -1094,7 +846,7 @@ final class InstallPackageHelper {
+ " got: " + apexes.length);
}
try (PackageParser2 packageParser = mPm.mInjector.getScanningPackageParser()) {
- mPm.mApexManager.installPackage(apexes[0], packageParser);
+ mApexManager.installPackage(apexes[0], packageParser);
}
} catch (PackageManagerException e) {
request.mInstallResult.setError("APEX installation failed", e);
@@ -1171,7 +923,7 @@ final class InstallPackageHelper {
installResults.put(packageName, request.mInstallResult);
installArgs.put(packageName, request.mArgs);
try {
- final ScanResult result = mScanPackageHelper.scanPackageTracedLI(
+ final ScanResult result = scanPackageTracedLI(
prepareResult.mPackageToScan, prepareResult.mParseFlags,
prepareResult.mScanFlags, System.currentTimeMillis(),
request.mArgs.mUser, request.mArgs.mAbiOverride);
@@ -1184,6 +936,9 @@ final class InstallPackageHelper {
+ " in multi-package install request.");
return;
}
+ if (result.needsNewAppId()) {
+ request.mInstallResult.mRemovedInfo.mAppIdChanging = true;
+ }
createdAppId.put(packageName, optimisticallyRegisterAppId(result));
versionInfos.put(result.mPkgSetting.getPkg().getPackageName(),
mPm.getSettingsVersionForPackage(result.mPkgSetting.getPkg()));
@@ -1214,7 +969,7 @@ final class InstallPackageHelper {
Map<String, ReconciledPackage> reconciledPackages;
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");
- reconciledPackages = reconcilePackagesLocked(
+ reconciledPackages = ReconcilePackageUtils.reconcilePackages(
reconcileRequest, mPm.mSettings.getKeySetManagerService(),
mPm.mInjector);
} catch (ReconcileFailure e) {
@@ -1257,7 +1012,7 @@ final class InstallPackageHelper {
.buildVerificationRootHashString(baseCodePath, splitCodePaths);
VerificationUtils.broadcastPackageVerified(verificationId, originUri,
PackageManager.VERIFICATION_ALLOW, rootHashString,
- args.mDataLoaderType, args.getUser(), mPm.mContext);
+ args.mDataLoaderType, args.getUser(), mContext);
}
} else {
for (ScanResult result : preparedScans.values()) {
@@ -1473,9 +1228,11 @@ final class InstallPackageHelper {
} else {
try {
final boolean compareCompat =
- isCompatSignatureUpdateNeeded(parsedPackage);
+ ReconcilePackageUtils.isCompatSignatureUpdateNeeded(
+ mPm.getSettingsVersionForPackage(parsedPackage));
final boolean compareRecover =
- isRecoverSignatureUpdateNeeded(parsedPackage);
+ ReconcilePackageUtils.isRecoverSignatureUpdateNeeded(
+ mPm.getSettingsVersionForPackage(parsedPackage));
// We don't care about disabledPkgSetting on install for now.
final boolean compatMatch = verifySignatures(signatureCheckPs, null,
parsedPackage.getSigningDetails(), compareCompat, compareRecover,
@@ -1677,9 +1434,9 @@ final class InstallPackageHelper {
final String abiOverride = deriveAbiOverride(args.mAbiOverride);
boolean isUpdatedSystemAppInferred = oldPackage != null && oldPackage.isSystem();
final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths>
- derivedAbi = mPm.mInjector.getAbiHelper().derivePackageAbi(parsedPackage,
+ derivedAbi = mPackageAbiHelper.derivePackageAbi(parsedPackage,
isUpdatedSystemAppFromExistingSetting || isUpdatedSystemAppInferred,
- abiOverride, mPm.mAppLib32InstallDir);
+ abiOverride, ScanPackageUtils.getAppLib32InstallDir());
derivedAbi.first.applyTo(parsedPackage);
derivedAbi.second.applyTo(parsedPackage);
} catch (PackageManagerException pme) {
@@ -2377,8 +2134,8 @@ final class InstallPackageHelper {
// that can be used for all the packages.
final String codePath = ps.getPathString();
if (IncrementalManager.isIncrementalPath(codePath)
- && mPm.mIncrementalManager != null) {
- mPm.mIncrementalManager.registerLoadingProgressCallback(codePath,
+ && mIncrementalManager != null) {
+ mIncrementalManager.registerLoadingProgressCallback(codePath,
new IncrementalProgressListener(ps.getPackageName(), mPm));
}
@@ -2439,17 +2196,16 @@ final class InstallPackageHelper {
*/
private void executePostCommitSteps(CommitRequest commitRequest) {
final ArraySet<IncrementalStorage> incrementalStorages = new ArraySet<>();
- final AppDataHelper appDataHelper = new AppDataHelper(mPm);
for (ReconciledPackage reconciledPkg : commitRequest.mReconciledPackages.values()) {
final boolean instantApp = ((reconciledPkg.mScanResult.mRequest.mScanFlags
& SCAN_AS_INSTANT_APP) != 0);
final AndroidPackage pkg = reconciledPkg.mPkgSetting.getPkg();
final String packageName = pkg.getPackageName();
final String codePath = pkg.getPath();
- final boolean onIncremental = mPm.mIncrementalManager != null
+ final boolean onIncremental = mIncrementalManager != null
&& isIncrementalPath(codePath);
if (onIncremental) {
- IncrementalStorage storage = mPm.mIncrementalManager.openStorage(codePath);
+ IncrementalStorage storage = mIncrementalManager.openStorage(codePath);
if (storage == null) {
throw new IllegalArgumentException(
"Install: null storage for incremental package " + packageName);
@@ -2461,28 +2217,28 @@ final class InstallPackageHelper {
// Only set previousAppId if the app is migrating out of shared UID
previousAppId = reconciledPkg.mScanResult.mPreviousAppId;
}
- appDataHelper.prepareAppDataPostCommitLIF(pkg, previousAppId);
+ mAppDataHelper.prepareAppDataPostCommitLIF(pkg, previousAppId);
if (reconciledPkg.mPrepareResult.mClearCodeCache) {
- appDataHelper.clearAppDataLIF(pkg, UserHandle.USER_ALL,
+ mAppDataHelper.clearAppDataLIF(pkg, UserHandle.USER_ALL,
FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL
| Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
}
if (reconciledPkg.mPrepareResult.mReplace) {
- mPm.getDexManager().notifyPackageUpdated(pkg.getPackageName(),
+ mDexManager.notifyPackageUpdated(pkg.getPackageName(),
pkg.getBaseApkPath(), pkg.getSplitCodePaths());
}
// Prepare the application profiles for the new code paths.
// This needs to be done before invoking dexopt so that any install-time profile
// can be used for optimizations.
- mPm.mArtManagerService.prepareAppProfiles(
+ mArtManagerService.prepareAppProfiles(
pkg,
mPm.resolveUserIds(reconciledPkg.mInstallArgs.mUser.getIdentifier()),
/* updateReferenceProfileContent= */ true);
// Compute the compilation reason from the installation scenario.
final int compilationReason =
- mPm.getDexManager().getCompilationReasonForInstallScenario(
+ mDexManager.getCompilationReasonForInstallScenario(
reconciledPkg.mInstallArgs.mInstallScenario);
// Construct the DexoptOptions early to see if we should skip running dexopt.
@@ -2530,7 +2286,7 @@ final class InstallPackageHelper {
// path moved to SCENARIO_FAST.
final boolean performDexopt =
(!instantApp || android.provider.Settings.Global.getInt(
- mPm.mContext.getContentResolver(),
+ mContext.getContentResolver(),
android.provider.Settings.Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
&& !pkg.isDebuggable()
&& (!onIncremental)
@@ -2540,7 +2296,7 @@ final class InstallPackageHelper {
// Compile the layout resources.
if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "compileLayouts");
- mPm.mViewCompiler.compileLayouts(pkg);
+ mViewCompiler.compileLayouts(pkg);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -2565,10 +2321,10 @@ final class InstallPackageHelper {
realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp);
- mPm.mPackageDexOptimizer.performDexOpt(pkg, realPkgSetting,
+ mPackageDexOptimizer.performDexOpt(pkg, realPkgSetting,
null /* instructionSets */,
mPm.getOrCreateCompilerPackageStats(pkg),
- mPm.getDexManager().getPackageUseInfoOrDefault(packageName),
+ mDexManager.getPackageUseInfoOrDefault(packageName),
dexoptOptions);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -2581,7 +2337,8 @@ final class InstallPackageHelper {
notifyPackageChangeObserversOnUpdate(reconciledPkg);
}
- waitForNativeBinariesExtraction(incrementalStorages);
+ PackageManagerServiceUtils.waitForNativeBinariesExtractionForIncremental(
+ incrementalStorages);
}
private void notifyPackageChangeObserversOnUpdate(ReconciledPackage reconciledPkg) {
@@ -2600,27 +2357,6 @@ final class InstallPackageHelper {
mPm.notifyPackageChangeObservers(pkgChangeEvent);
}
- private static void waitForNativeBinariesExtraction(
- ArraySet<IncrementalStorage> incrementalStorages) {
- if (incrementalStorages.isEmpty()) {
- return;
- }
- try {
- // Native library extraction may take very long time: each page could potentially
- // wait for either 10s or 100ms (adb vs non-adb data loader), and that easily adds
- // up to a full watchdog timeout of 1 min, killing the system after that. It doesn't
- // make much sense as blocking here doesn't lock up the framework, but only blocks
- // the installation session and the following ones.
- Watchdog.getInstance().pauseWatchingCurrentThread("native_lib_extract");
- for (int i = 0; i < incrementalStorages.size(); ++i) {
- IncrementalStorage storage = incrementalStorages.valueAtUnchecked(i);
- storage.waitForNativeBinariesExtraction();
- }
- } finally {
- Watchdog.getInstance().resumeWatchingCurrentThread("native_lib_extract");
- }
- }
-
public int installLocationPolicy(PackageInfoLite pkgLite, int installFlags) {
String packageName = pkgLite.packageName;
int installLocation = pkgLite.installLocation;
@@ -2706,7 +2442,7 @@ final class InstallPackageHelper {
if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags,
dataOwnerPkg.isDebuggable())) {
try {
- checkDowngrade(dataOwnerPkg, pkgLite);
+ PackageManagerServiceUtils.checkDowngrade(dataOwnerPkg, pkgLite);
} catch (PackageManagerException e) {
String errorMsg = "Downgrade detected: " + e.getMessage();
Slog.w(TAG, errorMsg);
@@ -2723,7 +2459,7 @@ final class InstallPackageHelper {
long requiredInstalledVersionCode, int installFlags) {
String packageName = pkgLite.packageName;
- final PackageInfo activePackage = mPm.mApexManager.getPackageInfo(packageName,
+ final PackageInfo activePackage = mApexManager.getPackageInfo(packageName,
ApexManager.MATCH_ACTIVE_PACKAGE);
if (activePackage == null) {
String errorMsg = "Attempting to install new APEX package " + packageName;
@@ -2756,41 +2492,6 @@ final class InstallPackageHelper {
return Pair.create(PackageManager.INSTALL_SUCCEEDED, null);
}
- /**
- * Check and throw if the given before/after packages would be considered a
- * downgrade.
- */
- private static void checkDowngrade(AndroidPackage before, PackageInfoLite after)
- throws PackageManagerException {
- if (after.getLongVersionCode() < before.getLongVersionCode()) {
- throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
- "Update version code " + after.versionCode + " is older than current "
- + before.getLongVersionCode());
- } else if (after.getLongVersionCode() == before.getLongVersionCode()) {
- if (after.baseRevisionCode < before.getBaseRevisionCode()) {
- throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
- "Update base revision code " + after.baseRevisionCode
- + " is older than current " + before.getBaseRevisionCode());
- }
-
- if (!ArrayUtils.isEmpty(after.splitNames)) {
- for (int i = 0; i < after.splitNames.length; i++) {
- final String splitName = after.splitNames[i];
- final int j = ArrayUtils.indexOf(before.getSplitNames(), splitName);
- if (j != -1) {
- if (after.splitRevisionCodes[i] < before.getSplitRevisionCodes()[j]) {
- throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
- "Update split " + splitName + " revision code "
- + after.splitRevisionCodes[i]
- + " is older than current "
- + before.getSplitRevisionCodes()[j]);
- }
- }
- }
- }
- }
- }
-
int getUidForVerifier(VerifierInfo verifierInfo) {
synchronized (mPm.mLock) {
final AndroidPackage pkg = mPm.mPackages.get(verifierInfo.packageName);
@@ -2829,56 +2530,6 @@ final class InstallPackageHelper {
}
}
- /**
- * Check whether or not package verification has been enabled.
- *
- * @return true if verification should be performed
- */
- boolean isVerificationEnabled(PackageInfoLite pkgInfoLite, int userId, int installFlags,
- int installerUid) {
- if (!DEFAULT_VERIFY_ENABLE) {
- return false;
- }
-
- // Check if installing from ADB
- if ((installFlags & PackageManager.INSTALL_FROM_ADB) != 0) {
- if (mPm.isUserRestricted(userId, UserManager.ENSURE_VERIFY_APPS)) {
- return true;
- }
- // Check if the developer wants to skip verification for ADB installs
- if ((installFlags & PackageManager.INSTALL_DISABLE_VERIFICATION) != 0) {
- synchronized (mPm.mLock) {
- if (mPm.mSettings.getPackageLPr(pkgInfoLite.packageName) == null) {
- // Always verify fresh install
- return true;
- }
- }
- // Only skip when apk is debuggable
- return !pkgInfoLite.debuggable;
- }
- return android.provider.Settings.Global.getInt(mPm.mContext.getContentResolver(),
- android.provider.Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1) != 0;
- }
-
- // only when not installed from ADB, skip verification for instant apps when
- // the installer and verifier are the same.
- if ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
- if (mPm.mInstantAppInstallerActivity != null
- && mPm.mInstantAppInstallerActivity.packageName.equals(
- mPm.mRequiredVerifierPackage)) {
- try {
- mPm.mInjector.getSystemService(AppOpsManager.class)
- .checkPackage(installerUid, mPm.mRequiredVerifierPackage);
- if (DEBUG_VERIFY) {
- Slog.i(TAG, "disable verification for instant app");
- }
- return false;
- } catch (SecurityException ignore) { }
- }
- }
- return true;
- }
-
public void sendPendingBroadcasts() {
String[] packages;
ArrayList<String>[] components;
@@ -2934,6 +2585,8 @@ final class InstallPackageHelper {
final int dataLoaderType = installArgs.mDataLoaderType;
final boolean succeeded = res.mReturnCode == PackageManager.INSTALL_SUCCEEDED;
final boolean update = res.mRemovedInfo != null && res.mRemovedInfo.mRemovedPackage != null;
+ final int previousAppId = (res.mRemovedInfo != null && res.mRemovedInfo.mAppIdChanging)
+ ? res.mRemovedInfo.mUid : Process.INVALID_UID;
final String packageName = res.mName;
final PackageStateInternal pkgSetting =
succeeded ? mPm.getPackageStateInternal(packageName) : null;
@@ -3030,9 +2683,12 @@ final class InstallPackageHelper {
dataLoaderType);
// Send added for users that don't see the package for the first time
- Bundle extras = new Bundle(1);
+ Bundle extras = new Bundle();
extras.putInt(Intent.EXTRA_UID, res.mUid);
- if (update) {
+ if (previousAppId != Process.INVALID_UID) {
+ extras.putBoolean(Intent.EXTRA_UID_CHANGING, true);
+ extras.putInt(Intent.EXTRA_PREVIOUS_UID, previousAppId);
+ } else if (update) {
extras.putBoolean(Intent.EXTRA_REPLACING, true);
}
extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);
@@ -3075,22 +2731,27 @@ final class InstallPackageHelper {
// Send replaced for users that don't see the package for the first time
if (update) {
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
- packageName, extras, 0 /*flags*/,
- null /*targetPackage*/, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, res.mRemovedInfo.mBroadcastAllowList,
- null);
- if (installerPackageName != null) {
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
- extras, 0 /*flags*/,
- installerPackageName, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, null /*broadcastAllowList*/, null);
- }
- if (notifyVerifier) {
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
- extras, 0 /*flags*/,
- mPm.mRequiredVerifierPackage, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, null /*broadcastAllowList*/, null);
+ // Only send PACKAGE_REPLACED if appId has not changed
+ if (previousAppId == Process.INVALID_UID) {
+ mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
+ packageName, extras, 0 /*flags*/,
+ null /*targetPackage*/, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds, res.mRemovedInfo.mBroadcastAllowList,
+ null);
+ if (installerPackageName != null) {
+ mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
+ extras, 0 /*flags*/,
+ installerPackageName, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds, null /*broadcastAllowList*/,
+ null);
+ }
+ if (notifyVerifier) {
+ mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
+ extras, 0 /*flags*/,
+ mPm.mRequiredVerifierPackage, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds, null /*broadcastAllowList*/,
+ null);
+ }
}
mPm.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
null /*package*/, null /*extras*/, 0 /*flags*/,
@@ -3113,10 +2774,8 @@ final class InstallPackageHelper {
// Send broadcast package appeared if external for all users
if (res.mPkg.isExternalStorage()) {
if (!update) {
- final StorageManager storage = mPm.mInjector.getSystemService(
- StorageManager.class);
VolumeInfo volume =
- storage.findVolumeByUuid(
+ mStorageManager.findVolumeByUuid(
StorageManager.convert(
res.mPkg.getVolumeUuid()).toString());
int packageExternalStorageType =
@@ -3198,7 +2857,7 @@ final class InstallPackageHelper {
// There's a race currently where some install events may interleave with an
// uninstall. This can lead to package info being null (b/36642664).
if (info != null) {
- mPm.getDexManager().notifyPackageInstalled(info, userId);
+ mDexManager.notifyPackageInstalled(info, userId);
}
}
}
@@ -3226,7 +2885,7 @@ final class InstallPackageHelper {
* @return the current "allow unknown sources" setting
*/
private int getUnknownSourcesSettings() {
- return android.provider.Settings.Secure.getIntForUser(mPm.mContext.getContentResolver(),
+ return android.provider.Settings.Secure.getIntForUser(mContext.getContentResolver(),
android.provider.Settings.Secure.INSTALL_NON_MARKET_APPS,
-1, UserHandle.USER_SYSTEM);
}
@@ -3355,7 +3014,7 @@ final class InstallPackageHelper {
mAppDataHelper.clearAppDataLIF(pkg, UserHandle.USER_ALL,
FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL
| Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
- mPm.getDexManager().notifyPackageUpdated(pkg.getPackageName(),
+ mDexManager.notifyPackageUpdated(pkg.getPackageName(),
pkg.getBaseApkPath(), pkg.getSplitCodePaths());
}
return true;
@@ -3409,7 +3068,7 @@ final class InstallPackageHelper {
packageName);
int ret = PackageManagerServiceUtils.decompressFiles(codePath, dstCodePath, packageName);
if (ret == PackageManager.INSTALL_SUCCEEDED) {
- ret = extractNativeBinaries(dstCodePath, packageName);
+ ret = PackageManagerServiceUtils.extractNativeBinaries(dstCodePath, packageName);
}
if (ret == PackageManager.INSTALL_SUCCEEDED) {
// NOTE: During boot, we have to delay releasing cblocks for no other reason than
@@ -3422,7 +3081,7 @@ final class InstallPackageHelper {
}
mPm.mReleaseOnSystemReady.add(dstCodePath);
} else {
- final ContentResolver resolver = mPm.mContext.getContentResolver();
+ final ContentResolver resolver = mContext.getContentResolver();
F2fsUtils.releaseCompressedBlocks(resolver, dstCodePath);
}
} else {
@@ -3436,33 +3095,15 @@ final class InstallPackageHelper {
return dstCodePath;
}
- private static int extractNativeBinaries(File dstCodePath, String packageName) {
- final File libraryRoot = new File(dstCodePath, LIB_DIR_NAME);
- NativeLibraryHelper.Handle handle = null;
- try {
- handle = NativeLibraryHelper.Handle.create(dstCodePath);
- return NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
- null /*abiOverride*/, false /*isIncremental*/);
- } catch (IOException e) {
- logCriticalInfo(Log.ERROR, "Failed to extract native libraries"
- + "; pkg: " + packageName);
- return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
- } finally {
- IoUtils.closeQuietly(handle);
- }
- }
-
/**
* Tries to restore the disabled system package after an update has been deleted.
*/
- @GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
public void restoreDisabledSystemPackageLIF(DeletePackageAction action,
- PackageSetting deletedPs, @NonNull int[] allUserHandles,
- @Nullable PackageRemovedInfo outInfo,
- boolean writeSettings,
- PackageSetting disabledPs)
- throws SystemDeleteException {
- // writer
+ @NonNull int[] allUserHandles, boolean writeSettings) throws SystemDeleteException {
+ final PackageSetting deletedPs = action.mDeletingPs;
+ final PackageRemovedInfo outInfo = action.mRemovedInfo;
+ final PackageSetting disabledPs = action.mDisabledPs;
+
synchronized (mPm.mLock) {
// NOTE: The system package always needs to be enabled; even if it's for
// a compressed stub. If we don't, installing the system package fails
@@ -3471,25 +3112,27 @@ final class InstallPackageHelper {
// Reinstate the old system package
mPm.mSettings.enableSystemPackageLPw(disabledPs.getPkg().getPackageName());
// Remove any native libraries from the upgraded package.
- removeNativeBinariesLI(deletedPs);
- }
+ PackageManagerServiceUtils.removeNativeBinariesLI(deletedPs);
- // Install the system package
- if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs);
- try {
- installPackageFromSystemLIF(disabledPs.getPathString(), allUserHandles,
- outInfo == null ? null : outInfo.mOrigUsers, writeSettings);
- } catch (PackageManagerException e) {
- Slog.w(TAG, "Failed to restore system package:" + deletedPs.getPackageName() + ": "
- + e.getMessage());
- // TODO(b/194319951): can we avoid this; throw would come from scan...
- throw new SystemDeleteException(e);
- } finally {
- if (disabledPs.getPkg().isStub()) {
- // We've re-installed the stub; make sure it's disabled here. If package was
- // originally enabled, we'll install the compressed version of the application
- // and re-enable it afterward.
- disableStubPackage(action, deletedPs, allUserHandles);
+ // Install the system package
+ if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs);
+ try {
+ synchronized (mPm.mInstallLock) {
+ installPackageFromSystemLIF(disabledPs.getPathString(), allUserHandles,
+ outInfo == null ? null : outInfo.mOrigUsers, writeSettings);
+ }
+ } catch (PackageManagerException e) {
+ Slog.w(TAG, "Failed to restore system package:" + deletedPs.getPackageName() + ": "
+ + e.getMessage());
+ // TODO(b/194319951): can we avoid this; throw would come from scan...
+ throw new SystemDeleteException(e);
+ } finally {
+ if (disabledPs.getPkg().isStub()) {
+ // We've re-installed the stub; make sure it's disabled here. If package was
+ // originally enabled, we'll install the compressed version of the application
+ // and re-enable it afterward.
+ disableStubPackage(action, deletedPs, allUserHandles);
+ }
}
}
}
@@ -3512,12 +3155,6 @@ final class InstallPackageHelper {
}
}
- private static void removeNativeBinariesLI(PackageSetting ps) {
- if (ps != null) {
- NativeLibraryHelper.removeNativeBinariesLI(ps.getLegacyNativeLibraryPath());
- }
- }
-
/**
* Installs a package that's already on the system partition.
*/
@@ -3788,7 +3425,7 @@ final class InstallPackageHelper {
}
if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0 && errorCode != INSTALL_SUCCEEDED) {
- mPm.mApexManager.reportErrorWithApkInApex(scanDir.getAbsolutePath(), errorMsg);
+ mApexManager.reportErrorWithApkInApex(scanDir.getAbsolutePath(), errorMsg);
}
// Delete invalid userdata apps
@@ -3871,7 +3508,7 @@ final class InstallPackageHelper {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
final ParsedPackage parsedPackage;
- try (PackageParser2 pp = mInjector.getScanningPackageParser()) {
+ try (PackageParser2 pp = mPm.mInjector.getScanningPackageParser()) {
parsedPackage = pp.parsePackage(scanFile, parseFlags, false);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -3904,7 +3541,7 @@ final class InstallPackageHelper {
@PackageManagerService.ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user) throws PackageManagerException {
- final Pair<ScanResult, Boolean> scanResultPair = mScanPackageHelper.scanSystemPackageLI(
+ final Pair<ScanResult, Boolean> scanResultPair = scanSystemPackageLI(
parsedPackage, parseFlags, scanFlags, currentTime, user);
final ScanResult scanResult = scanResultPair.first;
boolean shouldHideSystemApp = scanResultPair.second;
@@ -3914,7 +3551,7 @@ final class InstallPackageHelper {
try {
final String pkgName = scanResult.mPkgSetting.getPackageName();
final Map<String, ReconciledPackage> reconcileResult =
- reconcilePackagesLocked(
+ ReconcilePackageUtils.reconcilePackages(
new ReconcileRequest(
Collections.singletonMap(pkgName, scanResult),
mPm.mSharedLibraries,
@@ -3926,7 +3563,7 @@ final class InstallPackageHelper {
Collections.singletonMap(pkgName,
mPm.getSharedLibLatestVersionSetting(
scanResult))),
- mPm.mSettings.getKeySetManagerService(), mInjector);
+ mPm.mSettings.getKeySetManagerService(), mPm.mInjector);
appIdCreated = optimisticallyRegisterAppId(scanResult);
commitReconciledScanResultLocked(
reconcileResult.get(pkgName), mPm.mUserManager.getUserIds());
@@ -3944,10 +3581,10 @@ final class InstallPackageHelper {
mPm.mSettings.disableSystemPackageLPw(parsedPackage.getPackageName(), true);
}
}
- if (mPm.mIncrementalManager != null && isIncrementalPath(parsedPackage.getPath())) {
+ if (mIncrementalManager != null && isIncrementalPath(parsedPackage.getPath())) {
if (scanResult.mPkgSetting != null && scanResult.mPkgSetting.isLoading()) {
// Continue monitoring loading progress of active incremental packages
- mPm.mIncrementalManager.registerLoadingProgressCallback(parsedPackage.getPath(),
+ mIncrementalManager.registerLoadingProgressCallback(parsedPackage.getPath(),
new IncrementalProgressListener(parsedPackage.getPackageName(), mPm));
}
}
@@ -3985,5 +3622,743 @@ final class InstallPackageHelper {
}
}
+ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+ private ScanResult scanPackageTracedLI(ParsedPackage parsedPackage,
+ final @ParsingPackageUtils.ParseFlags int parseFlags,
+ @PackageManagerService.ScanFlags int scanFlags, long currentTime,
+ @Nullable UserHandle user, String cpuAbiOverride) throws PackageManagerException {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage");
+ try {
+ return scanPackageNewLI(parsedPackage, parseFlags, scanFlags, currentTime, user,
+ cpuAbiOverride);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+
+ private ScanRequest prepareInitialScanRequest(@NonNull ParsedPackage parsedPackage,
+ @ParsingPackageUtils.ParseFlags int parseFlags,
+ @PackageManagerService.ScanFlags int scanFlags,
+ @Nullable UserHandle user, String cpuAbiOverride)
+ throws PackageManagerException {
+ final AndroidPackage platformPackage;
+ final String realPkgName;
+ final PackageSetting disabledPkgSetting;
+ final PackageSetting installedPkgSetting;
+ final PackageSetting originalPkgSetting;
+ final SharedUserSetting sharedUserSetting;
+
+ synchronized (mPm.mLock) {
+ platformPackage = mPm.getPlatformPackage();
+ final String renamedPkgName = mPm.mSettings.getRenamedPackageLPr(
+ AndroidPackageUtils.getRealPackageOrNull(parsedPackage));
+ realPkgName = ScanPackageUtils.getRealPackageName(parsedPackage, renamedPkgName);
+ if (realPkgName != null) {
+ ScanPackageUtils.ensurePackageRenamed(parsedPackage, renamedPkgName);
+ }
+ originalPkgSetting = getOriginalPackageLocked(parsedPackage, renamedPkgName);
+ installedPkgSetting = mPm.mSettings.getPackageLPr(parsedPackage.getPackageName());
+ if (mPm.mTransferredPackages.contains(parsedPackage.getPackageName())) {
+ Slog.w(TAG, "Package " + parsedPackage.getPackageName()
+ + " was transferred to another, but its .apk remains");
+ }
+ disabledPkgSetting = mPm.mSettings.getDisabledSystemPkgLPr(
+ parsedPackage.getPackageName());
+ sharedUserSetting = (parsedPackage.getSharedUserId() != null)
+ ? mPm.mSettings.getSharedUserLPw(parsedPackage.getSharedUserId(),
+ 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true)
+ : null;
+ if (DEBUG_PACKAGE_SCANNING
+ && (parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0
+ && sharedUserSetting != null) {
+ Log.d(TAG, "Shared UserID " + parsedPackage.getSharedUserId()
+ + " (uid=" + sharedUserSetting.userId + "):"
+ + " packages=" + sharedUserSetting.packages);
+ }
+ }
+
+ final boolean isPlatformPackage = platformPackage != null
+ && platformPackage.getPackageName().equals(parsedPackage.getPackageName());
+
+ return new ScanRequest(parsedPackage, sharedUserSetting,
+ installedPkgSetting == null ? null : installedPkgSetting.getPkg() /* oldPkg */,
+ installedPkgSetting /* packageSetting */,
+ disabledPkgSetting /* disabledPackageSetting */,
+ originalPkgSetting /* originalPkgSetting */,
+ realPkgName, parseFlags, scanFlags, isPlatformPackage, user, cpuAbiOverride);
+ }
+
+ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+ private ScanResult scanPackageNewLI(@NonNull ParsedPackage parsedPackage,
+ final @ParsingPackageUtils.ParseFlags int parseFlags,
+ @PackageManagerService.ScanFlags int scanFlags, long currentTime,
+ @Nullable UserHandle user, String cpuAbiOverride) throws PackageManagerException {
+
+ final ScanRequest initialScanRequest = prepareInitialScanRequest(parsedPackage, parseFlags,
+ scanFlags, user, cpuAbiOverride);
+ final PackageSetting installedPkgSetting = initialScanRequest.mPkgSetting;
+ final PackageSetting disabledPkgSetting = initialScanRequest.mDisabledPkgSetting;
+
+ boolean isUpdatedSystemApp;
+ if (installedPkgSetting != null) {
+ isUpdatedSystemApp = installedPkgSetting.getPkgState().isUpdatedSystemApp();
+ } else {
+ isUpdatedSystemApp = disabledPkgSetting != null;
+ }
+
+ final int newScanFlags = adjustScanFlags(scanFlags, installedPkgSetting, disabledPkgSetting,
+ user, parsedPackage);
+ ScanPackageUtils.applyPolicy(parsedPackage, newScanFlags,
+ mPm.getPlatformPackage(), isUpdatedSystemApp);
+
+ synchronized (mPm.mLock) {
+ assertPackageIsValid(parsedPackage, parseFlags, newScanFlags);
+ final ScanRequest request = new ScanRequest(parsedPackage,
+ initialScanRequest.mSharedUserSetting,
+ initialScanRequest.mOldPkg, installedPkgSetting, disabledPkgSetting,
+ initialScanRequest.mOriginalPkgSetting, initialScanRequest.mRealPkgName,
+ parseFlags, scanFlags, initialScanRequest.mIsPlatformPackage, user,
+ cpuAbiOverride);
+ return ScanPackageUtils.scanPackageOnlyLI(request, mPm.mInjector, mPm.mFactoryTest,
+ currentTime);
+ }
+ }
+
+ private Pair<ScanResult, Boolean> scanSystemPackageLI(ParsedPackage parsedPackage,
+ @ParsingPackageUtils.ParseFlags int parseFlags,
+ @PackageManagerService.ScanFlags int scanFlags, long currentTime,
+ @Nullable UserHandle user) throws PackageManagerException {
+ final boolean scanSystemPartition =
+ (parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0;
+ final ScanRequest initialScanRequest = prepareInitialScanRequest(parsedPackage, parseFlags,
+ scanFlags, user, null);
+ final PackageSetting installedPkgSetting = initialScanRequest.mPkgSetting;
+ final PackageSetting originalPkgSetting = initialScanRequest.mOriginalPkgSetting;
+ final PackageSetting pkgSetting =
+ originalPkgSetting == null ? installedPkgSetting : originalPkgSetting;
+ final boolean pkgAlreadyExists = pkgSetting != null;
+ final String disabledPkgName = pkgAlreadyExists
+ ? pkgSetting.getPackageName() : parsedPackage.getPackageName();
+ final boolean isSystemPkgUpdated;
+ final boolean isUpgrade;
+ synchronized (mPm.mLock) {
+ isUpgrade = mPm.isDeviceUpgrading();
+ if (scanSystemPartition && !pkgAlreadyExists
+ && mPm.mSettings.getDisabledSystemPkgLPr(disabledPkgName) != null) {
+ // The updated-package data for /system apk remains inconsistently
+ // after the package data for /data apk is lost accidentally.
+ // To recover it, enable /system apk and install it as non-updated system app.
+ Slog.w(TAG, "Inconsistent package setting of updated system app for "
+ + disabledPkgName + ". To recover it, enable the system app "
+ + "and install it as non-updated system app.");
+ mPm.mSettings.removeDisabledSystemPackageLPw(disabledPkgName);
+ }
+ final PackageSetting disabledPkgSetting =
+ mPm.mSettings.getDisabledSystemPkgLPr(disabledPkgName);
+ isSystemPkgUpdated = disabledPkgSetting != null;
+
+ if (DEBUG_INSTALL && isSystemPkgUpdated) {
+ Slog.d(TAG, "updatedPkg = " + disabledPkgSetting);
+ }
+
+ if (scanSystemPartition && isSystemPkgUpdated) {
+ // we're updating the disabled package, so, scan it as the package setting
+ final ScanRequest request = new ScanRequest(parsedPackage,
+ initialScanRequest.mSharedUserSetting,
+ null, disabledPkgSetting /* pkgSetting */,
+ null /* disabledPkgSetting */, null /* originalPkgSetting */,
+ null, parseFlags, scanFlags,
+ initialScanRequest.mIsPlatformPackage, user, null);
+ ScanPackageUtils.applyPolicy(parsedPackage, scanFlags,
+ mPm.getPlatformPackage(), true);
+ final ScanResult scanResult =
+ ScanPackageUtils.scanPackageOnlyLI(request, mPm.mInjector,
+ mPm.mFactoryTest, -1L);
+ if (scanResult.mExistingSettingCopied
+ && scanResult.mRequest.mPkgSetting != null) {
+ scanResult.mRequest.mPkgSetting.updateFrom(scanResult.mPkgSetting);
+ }
+ }
+ } // End of mLock
+
+ final boolean newPkgChangedPaths = pkgAlreadyExists
+ && !pkgSetting.getPathString().equals(parsedPackage.getPath());
+ final boolean newPkgVersionGreater = pkgAlreadyExists
+ && parsedPackage.getLongVersionCode() > pkgSetting.getVersionCode();
+ final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated
+ && newPkgChangedPaths && newPkgVersionGreater;
+ if (isSystemPkgBetter) {
+ // The version of the application on /system is greater than the version on
+ // /data. Switch back to the application on /system.
+ // It's safe to assume the application on /system will correctly scan. If not,
+ // there won't be a working copy of the application.
+ synchronized (mPm.mLock) {
+ // just remove the loaded entries from package lists
+ mPm.mPackages.remove(pkgSetting.getPackageName());
+ }
+
+ logCriticalInfo(Log.WARN,
+ "System package updated;"
+ + " name: " + pkgSetting.getPackageName()
+ + "; " + pkgSetting.getVersionCode() + " --> "
+ + parsedPackage.getLongVersionCode()
+ + "; " + pkgSetting.getPathString()
+ + " --> " + parsedPackage.getPath());
+
+ final InstallArgs args = new FileInstallArgs(
+ pkgSetting.getPathString(), getAppDexInstructionSets(
+ pkgSetting.getPrimaryCpuAbi(), pkgSetting.getSecondaryCpuAbi()), mPm);
+ args.cleanUpResourcesLI();
+ synchronized (mPm.mLock) {
+ mPm.mSettings.enableSystemPackageLPw(pkgSetting.getPackageName());
+ }
+ }
+
+ // The version of the application on the /system partition is less than or
+ // equal to the version on the /data partition. Throw an exception and use
+ // the application already installed on the /data partition.
+ if (scanSystemPartition && isSystemPkgUpdated && !isSystemPkgBetter) {
+ // In the case of a skipped package, commitReconciledScanResultLocked is not called to
+ // add the object to the "live" data structures, so this is the final mutation step
+ // for the package. Which means it needs to be finalized here to cache derived fields.
+ // This is relevant for cases where the disabled system package is used for flags or
+ // other metadata.
+ parsedPackage.hideAsFinal();
+ throw new PackageManagerException(Log.WARN, "Package " + parsedPackage.getPackageName()
+ + " at " + parsedPackage.getPath() + " ignored: updated version "
+ + (pkgAlreadyExists ? String.valueOf(pkgSetting.getVersionCode()) : "unknown")
+ + " better than this " + parsedPackage.getLongVersionCode());
+ }
+
+ // Verify certificates against what was last scanned. Force re-collecting certificate in two
+ // special cases:
+ // 1) when scanning system, force re-collect only if system is upgrading.
+ // 2) when scanning /data, force re-collect only if the app is privileged (updated from
+ // preinstall, or treated as privileged, e.g. due to shared user ID).
+ final boolean forceCollect = scanSystemPartition ? isUpgrade
+ : PackageManagerServiceUtils.isApkVerificationForced(pkgSetting);
+ if (DEBUG_VERIFY && forceCollect) {
+ Slog.d(TAG, "Force collect certificate of " + parsedPackage.getPackageName());
+ }
+
+ // Full APK verification can be skipped during certificate collection, only if the file is
+ // in verified partition, or can be verified on access (when apk verity is enabled). In both
+ // cases, only data in Signing Block is verified instead of the whole file.
+ final boolean skipVerify = scanSystemPartition
+ || (forceCollect && canSkipForcedPackageVerification(parsedPackage));
+ ScanPackageUtils.collectCertificatesLI(pkgSetting, parsedPackage,
+ mPm.getSettingsVersionForPackage(parsedPackage), forceCollect, skipVerify,
+ mPm.isPreNMR1Upgrade());
+
+ // Reset profile if the application version is changed
+ maybeClearProfilesForUpgradesLI(pkgSetting, parsedPackage);
+
+ /*
+ * A new system app appeared, but we already had a non-system one of the
+ * same name installed earlier.
+ */
+ boolean shouldHideSystemApp = false;
+ // A new application appeared on /system, but, we already have a copy of
+ // the application installed on /data.
+ if (scanSystemPartition && !isSystemPkgUpdated && pkgAlreadyExists
+ && !pkgSetting.isSystem()) {
+
+ if (!parsedPackage.getSigningDetails()
+ .checkCapability(pkgSetting.getSigningDetails(),
+ SigningDetails.CertCapabilities.INSTALLED_DATA)
+ && !pkgSetting.getSigningDetails().checkCapability(
+ parsedPackage.getSigningDetails(),
+ SigningDetails.CertCapabilities.ROLLBACK)) {
+ logCriticalInfo(Log.WARN,
+ "System package signature mismatch;"
+ + " name: " + pkgSetting.getPackageName());
+ try (@SuppressWarnings("unused") PackageFreezer freezer = mPm.freezePackage(
+ parsedPackage.getPackageName(),
+ "scanPackageInternalLI")) {
+ DeletePackageHelper deletePackageHelper = new DeletePackageHelper(mPm);
+ deletePackageHelper.deletePackageLIF(parsedPackage.getPackageName(), null, true,
+ mPm.mUserManager.getUserIds(), 0, null, false);
+ }
+ } else if (newPkgVersionGreater) {
+ // The application on /system is newer than the application on /data.
+ // Simply remove the application on /data [keeping application data]
+ // and replace it with the version on /system.
+ logCriticalInfo(Log.WARN,
+ "System package enabled;"
+ + " name: " + pkgSetting.getPackageName()
+ + "; " + pkgSetting.getVersionCode() + " --> "
+ + parsedPackage.getLongVersionCode()
+ + "; " + pkgSetting.getPathString() + " --> "
+ + parsedPackage.getPath());
+ InstallArgs args = new FileInstallArgs(
+ pkgSetting.getPathString(), getAppDexInstructionSets(
+ pkgSetting.getPrimaryCpuAbi(), pkgSetting.getSecondaryCpuAbi()),
+ mPm);
+ synchronized (mPm.mInstallLock) {
+ args.cleanUpResourcesLI();
+ }
+ } else {
+ // The application on /system is older than the application on /data. Hide
+ // the application on /system and the version on /data will be scanned later
+ // and re-added like an update.
+ shouldHideSystemApp = true;
+ logCriticalInfo(Log.INFO,
+ "System package disabled;"
+ + " name: " + pkgSetting.getPackageName()
+ + "; old: " + pkgSetting.getPathString() + " @ "
+ + pkgSetting.getVersionCode()
+ + "; new: " + parsedPackage.getPath() + " @ "
+ + parsedPackage.getPath());
+ }
+ }
+
+ final ScanResult scanResult = scanPackageNewLI(parsedPackage, parseFlags,
+ scanFlags | SCAN_UPDATE_SIGNATURE, currentTime, user, null);
+ return new Pair<>(scanResult, shouldHideSystemApp);
+ }
+
+ /**
+ * Returns if forced apk verification can be skipped for the whole package, including splits.
+ */
+ private boolean canSkipForcedPackageVerification(AndroidPackage pkg) {
+ final String packageName = pkg.getPackageName();
+ if (!canSkipForcedApkVerification(packageName, pkg.getBaseApkPath())) {
+ return false;
+ }
+ // TODO: Allow base and splits to be verified individually.
+ String[] splitCodePaths = pkg.getSplitCodePaths();
+ if (!ArrayUtils.isEmpty(splitCodePaths)) {
+ for (int i = 0; i < splitCodePaths.length; i++) {
+ if (!canSkipForcedApkVerification(packageName, splitCodePaths[i])) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns if forced apk verification can be skipped, depending on current FSVerity setup and
+ * whether the apk contains signed root hash. Note that the signer's certificate still needs to
+ * match one in a trusted source, and should be done separately.
+ */
+ private boolean canSkipForcedApkVerification(String packageName, String apkPath) {
+ if (!PackageManagerServiceUtils.isLegacyApkVerityEnabled()) {
+ return VerityUtils.hasFsverity(apkPath);
+ }
+
+ try {
+ final byte[] rootHashObserved = VerityUtils.generateApkVerityRootHash(apkPath);
+ if (rootHashObserved == null) {
+ return false; // APK does not contain Merkle tree root hash.
+ }
+ synchronized (mPm.mInstallLock) {
+ // Returns whether the observed root hash matches what kernel has.
+ mPm.mInstaller.assertFsverityRootHashMatches(packageName, apkPath,
+ rootHashObserved);
+ return true;
+ }
+ } catch (Installer.InstallerException | IOException | DigestException
+ | NoSuchAlgorithmException e) {
+ Slog.w(TAG, "Error in fsverity check. Fallback to full apk verification.", e);
+ }
+ return false;
+ }
+
+ /**
+ * Clear the package profile if this was an upgrade and the package
+ * version was updated.
+ */
+ private void maybeClearProfilesForUpgradesLI(
+ @Nullable PackageSetting originalPkgSetting,
+ @NonNull AndroidPackage pkg) {
+ if (originalPkgSetting == null || !mPm.isDeviceUpgrading()) {
+ return;
+ }
+ if (originalPkgSetting.getVersionCode() == pkg.getLongVersionCode()) {
+ return;
+ }
+
+ mAppDataHelper.clearAppProfilesLIF(pkg);
+ if (DEBUG_INSTALL) {
+ Slog.d(TAG, originalPkgSetting.getPackageName()
+ + " clear profile due to version change "
+ + originalPkgSetting.getVersionCode() + " != "
+ + pkg.getLongVersionCode());
+ }
+ }
+
+ /**
+ * Returns the original package setting.
+ * <p>A package can migrate its name during an update. In this scenario, a package
+ * designates a set of names that it considers as one of its original names.
+ * <p>An original package must be signed identically and it must have the same
+ * shared user [if any].
+ */
+ @GuardedBy("mPm.mLock")
+ @Nullable
+ private PackageSetting getOriginalPackageLocked(@NonNull AndroidPackage pkg,
+ @Nullable String renamedPkgName) {
+ if (ScanPackageUtils.isPackageRenamed(pkg, renamedPkgName)) {
+ return null;
+ }
+ for (int i = ArrayUtils.size(pkg.getOriginalPackages()) - 1; i >= 0; --i) {
+ final PackageSetting originalPs =
+ mPm.mSettings.getPackageLPr(pkg.getOriginalPackages().get(i));
+ if (originalPs != null) {
+ // the package is already installed under its original name...
+ // but, should we use it?
+ if (!verifyPackageUpdateLPr(originalPs, pkg)) {
+ // the new package is incompatible with the original
+ continue;
+ } else if (originalPs.getSharedUser() != null) {
+ if (!originalPs.getSharedUser().name.equals(pkg.getSharedUserId())) {
+ // the shared user id is incompatible with the original
+ Slog.w(TAG, "Unable to migrate data from " + originalPs.getPackageName()
+ + " to " + pkg.getPackageName() + ": old uid "
+ + originalPs.getSharedUser().name
+ + " differs from " + pkg.getSharedUserId());
+ continue;
+ }
+ // TODO: Add case when shared user id is added [b/28144775]
+ } else {
+ if (DEBUG_UPGRADE) {
+ Log.v(TAG, "Renaming new package "
+ + pkg.getPackageName() + " to old name "
+ + originalPs.getPackageName());
+ }
+ }
+ return originalPs;
+ }
+ }
+ return null;
+ }
+
+ @GuardedBy("mPm.mLock")
+ private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, AndroidPackage newPkg) {
+ if ((oldPkg.getFlags() & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ Slog.w(TAG, "Unable to update from " + oldPkg.getPackageName()
+ + " to " + newPkg.getPackageName()
+ + ": old package not in system partition");
+ return false;
+ } else if (mPm.mPackages.get(oldPkg.getPackageName()) != null) {
+ Slog.w(TAG, "Unable to update from " + oldPkg.getPackageName()
+ + " to " + newPkg.getPackageName()
+ + ": old package still exists");
+ return false;
+ }
+ return true;
+ }
+ /**
+ * Asserts the parsed package is valid according to the given policy. If the
+ * package is invalid, for whatever reason, throws {@link PackageManagerException}.
+ * <p>
+ * Implementation detail: This method must NOT have any side effects. It would
+ * ideally be static, but, it requires locks to read system state.
+ *
+ * @throws PackageManagerException If the package fails any of the validation checks
+ */
+ private void assertPackageIsValid(AndroidPackage pkg,
+ final @ParsingPackageUtils.ParseFlags int parseFlags,
+ final @PackageManagerService.ScanFlags int scanFlags)
+ throws PackageManagerException {
+ if ((parseFlags & ParsingPackageUtils.PARSE_ENFORCE_CODE) != 0) {
+ ScanPackageUtils.assertCodePolicy(pkg);
+ }
+
+ if (pkg.getPath() == null) {
+ // Bail out. The resource and code paths haven't been set.
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+ "Code and resource paths haven't been set correctly");
+ }
+
+ // Check that there is an APEX package with the same name only during install/first boot
+ // after OTA.
+ final boolean isUserInstall = (scanFlags & SCAN_BOOTING) == 0;
+ final boolean isFirstBootOrUpgrade = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;
+ if ((isUserInstall || isFirstBootOrUpgrade)
+ && mApexManager.isApexPackage(pkg.getPackageName())) {
+ throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
+ pkg.getPackageName()
+ + " is an APEX package and can't be installed as an APK.");
+ }
+
+ // Make sure we're not adding any bogus keyset info
+ final KeySetManagerService ksms = mPm.mSettings.getKeySetManagerService();
+ ksms.assertScannedPackageValid(pkg);
+
+ synchronized (mPm.mLock) {
+ // The special "android" package can only be defined once
+ if (pkg.getPackageName().equals("android")) {
+ if (mPm.getCoreAndroidApplication() != null) {
+ Slog.w(TAG, "*************************************************");
+ Slog.w(TAG, "Core android package being redefined. Skipping.");
+ Slog.w(TAG, " codePath=" + pkg.getPath());
+ Slog.w(TAG, "*************************************************");
+ throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
+ "Core android package being redefined. Skipping.");
+ }
+ }
+
+ // A package name must be unique; don't allow duplicates
+ if ((scanFlags & SCAN_NEW_INSTALL) == 0
+ && mPm.mPackages.containsKey(pkg.getPackageName())) {
+ throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
+ "Application package " + pkg.getPackageName()
+ + " already installed. Skipping duplicate.");
+ }
+
+ if (pkg.isStaticSharedLibrary()) {
+ // Static libs have a synthetic package name containing the version
+ // but we still want the base name to be unique.
+ if ((scanFlags & SCAN_NEW_INSTALL) == 0
+ && mPm.mPackages.containsKey(pkg.getManifestPackageName())) {
+ throw new PackageManagerException(
+ "Duplicate static shared lib provider package");
+ }
+ ScanPackageUtils.assertStaticSharedLibraryIsValid(pkg, scanFlags);
+ assertStaticSharedLibraryVersionCodeIsValid(pkg);
+ }
+
+ // If we're only installing presumed-existing packages, require that the
+ // scanned APK is both already known and at the path previously established
+ // for it. Previously unknown packages we pick up normally, but if we have an
+ // a priori expectation about this package's install presence, enforce it.
+ // With a singular exception for new system packages. When an OTA contains
+ // a new system package, we allow the codepath to change from a system location
+ // to the user-installed location. If we don't allow this change, any newer,
+ // user-installed version of the application will be ignored.
+ if ((scanFlags & SCAN_REQUIRE_KNOWN) != 0) {
+ if (mPm.isExpectingBetter(pkg.getPackageName())) {
+ Slog.w(TAG, "Relax SCAN_REQUIRE_KNOWN requirement for package "
+ + pkg.getPackageName());
+ } else {
+ PackageSetting known = mPm.mSettings.getPackageLPr(pkg.getPackageName());
+ if (known != null) {
+ if (DEBUG_PACKAGE_SCANNING) {
+ Log.d(TAG, "Examining " + pkg.getPath()
+ + " and requiring known path " + known.getPathString());
+ }
+ if (!pkg.getPath().equals(known.getPathString())) {
+ throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,
+ "Application package " + pkg.getPackageName()
+ + " found at " + pkg.getPath()
+ + " but expected at " + known.getPathString()
+ + "; ignoring.");
+ }
+ } else {
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
+ "Application package " + pkg.getPackageName()
+ + " not found; ignoring.");
+ }
+ }
+ }
+
+ // Verify that this new package doesn't have any content providers
+ // that conflict with existing packages. Only do this if the
+ // package isn't already installed, since we don't want to break
+ // things that are installed.
+ if ((scanFlags & SCAN_NEW_INSTALL) != 0) {
+ mPm.mComponentResolver.assertProvidersNotDefined(pkg);
+ }
+
+ // If this package has defined explicit processes, then ensure that these are
+ // the only processes used by its components.
+ ScanPackageUtils.assertProcessesAreValid(pkg);
+
+ // Verify that packages sharing a user with a privileged app are marked as privileged.
+ assertPackageWithSharedUserIdIsPrivileged(pkg);
+
+ // Apply policies specific for runtime resource overlays (RROs).
+ if (pkg.getOverlayTarget() != null) {
+ assertOverlayIsValid(pkg, parseFlags, scanFlags);
+ }
+
+ // If the package is not on a system partition ensure it is signed with at least the
+ // minimum signature scheme version required for its target SDK.
+ ScanPackageUtils.assertMinSignatureSchemeIsValid(pkg, parseFlags);
+ }
+ }
+
+ private void assertStaticSharedLibraryVersionCodeIsValid(AndroidPackage pkg)
+ throws PackageManagerException {
+ // The version codes must be ordered as lib versions
+ long minVersionCode = Long.MIN_VALUE;
+ long maxVersionCode = Long.MAX_VALUE;
+
+ WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mPm.mSharedLibraries.get(
+ pkg.getStaticSharedLibName());
+ if (versionedLib != null) {
+ final int versionCount = versionedLib.size();
+ for (int i = 0; i < versionCount; i++) {
+ SharedLibraryInfo libInfo = versionedLib.valueAt(i);
+ final long libVersionCode = libInfo.getDeclaringPackage()
+ .getLongVersionCode();
+ if (libInfo.getLongVersion() < pkg.getStaticSharedLibVersion()) {
+ minVersionCode = Math.max(minVersionCode, libVersionCode + 1);
+ } else if (libInfo.getLongVersion()
+ > pkg.getStaticSharedLibVersion()) {
+ maxVersionCode = Math.min(maxVersionCode, libVersionCode - 1);
+ } else {
+ minVersionCode = maxVersionCode = libVersionCode;
+ break;
+ }
+ }
+ }
+ if (pkg.getLongVersionCode() < minVersionCode
+ || pkg.getLongVersionCode() > maxVersionCode) {
+ throw new PackageManagerException("Static shared"
+ + " lib version codes must be ordered as lib versions");
+ }
+ }
+
+ private void assertOverlayIsValid(AndroidPackage pkg,
+ @ParsingPackageUtils.ParseFlags int parseFlags,
+ @PackageManagerService.ScanFlags int scanFlags) throws PackageManagerException {
+ // System overlays have some restrictions on their use of the 'static' state.
+ if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
+ // We are scanning a system overlay. This can be the first scan of the
+ // system/vendor/oem partition, or an update to the system overlay.
+ if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
+ // This must be an update to a system overlay. Immutable overlays cannot be
+ // upgraded.
+ if (!mPm.isOverlayMutable(pkg.getPackageName())) {
+ throw new PackageManagerException("Overlay "
+ + pkg.getPackageName()
+ + " is static and cannot be upgraded.");
+ }
+ } else {
+ if ((scanFlags & SCAN_AS_VENDOR) != 0) {
+ if (pkg.getTargetSdkVersion() < ScanPackageUtils.getVendorPartitionVersion()) {
+ Slog.w(TAG, "System overlay " + pkg.getPackageName()
+ + " targets an SDK below the required SDK level of vendor"
+ + " overlays ("
+ + ScanPackageUtils.getVendorPartitionVersion()
+ + ")."
+ + " This will become an install error in a future release");
+ }
+ } else if (pkg.getTargetSdkVersion() < Build.VERSION.SDK_INT) {
+ Slog.w(TAG, "System overlay " + pkg.getPackageName()
+ + " targets an SDK below the required SDK level of system"
+ + " overlays (" + Build.VERSION.SDK_INT + ")."
+ + " This will become an install error in a future release");
+ }
+ }
+ } else {
+ // A non-preloaded overlay packages must have targetSdkVersion >= Q, or be
+ // signed with the platform certificate. Check this in increasing order of
+ // computational cost.
+ if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.Q) {
+ final PackageSetting platformPkgSetting =
+ mPm.mSettings.getPackageLPr("android");
+ if (!comparePackageSignatures(platformPkgSetting,
+ pkg.getSigningDetails().getSignatures())) {
+ throw new PackageManagerException("Overlay "
+ + pkg.getPackageName()
+ + " must target Q or later, "
+ + "or be signed with the platform certificate");
+ }
+ }
+
+ // A non-preloaded overlay package, without <overlay android:targetName>, will
+ // only be used if it is signed with the same certificate as its target OR if
+ // it is signed with the same certificate as a reference package declared
+ // in 'overlay-config-signature' tag of SystemConfig.
+ // If the target is already installed or 'overlay-config-signature' tag in
+ // SystemConfig is set, check this here to augment the last line of defense
+ // which is OMS.
+ if (pkg.getOverlayTargetOverlayableName() == null) {
+ final PackageSetting targetPkgSetting =
+ mPm.mSettings.getPackageLPr(pkg.getOverlayTarget());
+ if (targetPkgSetting != null) {
+ if (!comparePackageSignatures(targetPkgSetting,
+ pkg.getSigningDetails().getSignatures())) {
+ // check reference signature
+ if (mPm.mOverlayConfigSignaturePackage == null) {
+ throw new PackageManagerException("Overlay "
+ + pkg.getPackageName() + " and target "
+ + pkg.getOverlayTarget() + " signed with"
+ + " different certificates, and the overlay lacks"
+ + " <overlay android:targetName>");
+ }
+ final PackageSetting refPkgSetting =
+ mPm.mSettings.getPackageLPr(
+ mPm.mOverlayConfigSignaturePackage);
+ if (!comparePackageSignatures(refPkgSetting,
+ pkg.getSigningDetails().getSignatures())) {
+ throw new PackageManagerException("Overlay "
+ + pkg.getPackageName() + " signed with a different "
+ + "certificate than both the reference package and "
+ + "target " + pkg.getOverlayTarget() + ", and the "
+ + "overlay lacks <overlay android:targetName>");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void assertPackageWithSharedUserIdIsPrivileged(AndroidPackage pkg)
+ throws PackageManagerException {
+ if (!pkg.isPrivileged() && (pkg.getSharedUserId() != null)) {
+ SharedUserSetting sharedUserSetting = null;
+ try {
+ sharedUserSetting = mPm.mSettings.getSharedUserLPw(pkg.getSharedUserId(),
+ 0, 0, false);
+ } catch (PackageManagerException ignore) {
+ }
+ if (sharedUserSetting != null && sharedUserSetting.isPrivileged()) {
+ // Exempt SharedUsers signed with the platform key.
+ PackageSetting platformPkgSetting = mPm.mSettings.getPackageLPr("android");
+ if (!comparePackageSignatures(platformPkgSetting,
+ pkg.getSigningDetails().getSignatures())) {
+ throw new PackageManagerException("Apps that share a user with a "
+ + "privileged app must themselves be marked as privileged. "
+ + pkg.getPackageName() + " shares privileged user "
+ + pkg.getSharedUserId() + ".");
+ }
+ }
+ }
+ }
+
+ private @PackageManagerService.ScanFlags int adjustScanFlags(
+ @PackageManagerService.ScanFlags int scanFlags,
+ PackageSetting pkgSetting, PackageSetting disabledPkgSetting, UserHandle user,
+ AndroidPackage pkg) {
+ scanFlags = ScanPackageUtils.adjustScanFlagsWithPackageSetting(scanFlags, pkgSetting,
+ disabledPkgSetting, user);
+
+ // Exception for privileged apps that share a user with a priv-app.
+ final boolean skipVendorPrivilegeScan = ((scanFlags & SCAN_AS_VENDOR) != 0)
+ && ScanPackageUtils.getVendorPartitionVersion() < 28;
+ if (((scanFlags & SCAN_AS_PRIVILEGED) == 0)
+ && !pkg.isPrivileged()
+ && (pkg.getSharedUserId() != null)
+ && !skipVendorPrivilegeScan) {
+ SharedUserSetting sharedUserSetting = null;
+ synchronized (mPm.mLock) {
+ try {
+ sharedUserSetting = mPm.mSettings.getSharedUserLPw(pkg.getSharedUserId(), 0,
+ 0, false);
+ } catch (PackageManagerException ignore) {
+ }
+ if (sharedUserSetting != null && sharedUserSetting.isPrivileged()) {
+ // Exempt SharedUsers signed with the platform key.
+ // TODO(b/72378145) Fix this exemption. Force signature apps
+ // to allowlist their privileged permissions just like other
+ // priv-apps.
+ PackageSetting platformPkgSetting = mPm.mSettings.getPackageLPr("android");
+ if ((compareSignatures(
+ platformPkgSetting.getSigningDetails().getSignatures(),
+ pkg.getSigningDetails().getSignatures())
+ != PackageManager.SIGNATURE_MATCH)) {
+ scanFlags |= SCAN_AS_PRIVILEGED;
+ }
+ }
+ }
+ }
+
+ return scanFlags;
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageHandler.java b/services/core/java/com/android/server/pm/PackageHandler.java
index 1a9c7a994fea..217bc2397f29 100644
--- a/services/core/java/com/android/server/pm/PackageHandler.java
+++ b/services/core/java/com/android/server/pm/PackageHandler.java
@@ -22,7 +22,6 @@ import static com.android.server.pm.PackageManagerService.CHECK_PENDING_INTEGRIT
import static com.android.server.pm.PackageManagerService.CHECK_PENDING_VERIFICATION;
import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
import static com.android.server.pm.PackageManagerService.DEFAULT_UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD;
-import static com.android.server.pm.PackageManagerService.DEFAULT_VERIFICATION_RESPONSE;
import static com.android.server.pm.PackageManagerService.DEFERRED_NO_KILL_INSTALL_OBSERVER;
import static com.android.server.pm.PackageManagerService.DEFERRED_NO_KILL_POST_DELETE;
import static com.android.server.pm.PackageManagerService.DOMAIN_VERIFICATION;
@@ -47,14 +46,12 @@ import android.content.pm.InstantAppRequest;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.net.Uri;
-import android.os.Binder;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.Trace;
import android.os.UserHandle;
-import android.os.UserManager;
import android.provider.Settings;
import android.util.Log;
import android.util.Slog;
@@ -154,45 +151,50 @@ final class PackageHandler extends Handler {
} break;
case CHECK_PENDING_VERIFICATION: {
final int verificationId = msg.arg1;
+ final boolean streaming = msg.arg2 != 0;
final PackageVerificationState state = mPm.mPendingVerification.get(verificationId);
- if ((state != null) && !state.isVerificationComplete()
- && !state.timeoutExtended()) {
- final VerificationParams params = state.getVerificationParams();
- final Uri originUri = Uri.fromFile(params.mOriginInfo.mResolvedFile);
+ if (state == null || state.isVerificationComplete()) {
+ // Not found or complete.
+ break;
+ }
+ if (!streaming && state.timeoutExtended()) {
+ // Timeout extended.
+ break;
+ }
- String errorMsg = "Verification timed out for " + originUri;
- Slog.i(TAG, errorMsg);
+ final PackageVerificationResponse response = (PackageVerificationResponse) msg.obj;
- final UserHandle user = params.getUser();
- if (getDefaultVerificationResponse(user)
- == PackageManager.VERIFICATION_ALLOW) {
- Slog.i(TAG, "Continuing with installation of " + originUri);
- state.setVerifierResponse(Binder.getCallingUid(),
- PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT);
- VerificationUtils.broadcastPackageVerified(verificationId, originUri,
- PackageManager.VERIFICATION_ALLOW, null, params.mDataLoaderType,
- user, mPm.mContext);
- } else {
- VerificationUtils.broadcastPackageVerified(verificationId, originUri,
- PackageManager.VERIFICATION_REJECT, null,
- params.mDataLoaderType, user, mPm.mContext);
- params.setReturnCode(
- PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE, errorMsg);
- state.setVerifierResponse(Binder.getCallingUid(),
- PackageManager.VERIFICATION_REJECT);
- }
+ final VerificationParams params = state.getVerificationParams();
+ final Uri originUri = Uri.fromFile(params.mOriginInfo.mResolvedFile);
- if (state.areAllVerificationsComplete()) {
- mPm.mPendingVerification.remove(verificationId);
- }
+ String errorMsg = "Verification timed out for " + originUri;
+ Slog.i(TAG, errorMsg);
- Trace.asyncTraceEnd(
- TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);
-
- params.handleVerificationFinished();
+ final UserHandle user = params.getUser();
+ if (response.code != PackageManager.VERIFICATION_REJECT) {
+ Slog.i(TAG, "Continuing with installation of " + originUri);
+ state.setVerifierResponse(response.callerUid, response.code);
+ VerificationUtils.broadcastPackageVerified(verificationId, originUri,
+ PackageManager.VERIFICATION_ALLOW, null, params.mDataLoaderType,
+ user, mPm.mContext);
+ } else {
+ VerificationUtils.broadcastPackageVerified(verificationId, originUri,
+ PackageManager.VERIFICATION_REJECT, null,
+ params.mDataLoaderType, user, mPm.mContext);
+ params.setReturnCode(
+ PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE, errorMsg);
+ state.setVerifierResponse(response.callerUid, response.code);
+ }
+ if (state.areAllVerificationsComplete()) {
+ mPm.mPendingVerification.remove(verificationId);
}
+
+ Trace.asyncTraceEnd(
+ TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);
+
+ params.handleVerificationFinished();
break;
}
case CHECK_PENDING_INTEGRITY_VERIFICATION: {
@@ -241,9 +243,12 @@ final class PackageHandler extends Handler {
+ " It may be invalid or overridden by integrity verification");
break;
}
+ if (state.isVerificationComplete()) {
+ Slog.w(TAG, "Verification with id " + verificationId + " already complete.");
+ break;
+ }
final PackageVerificationResponse response = (PackageVerificationResponse) msg.obj;
-
state.setVerifierResponse(response.callerUid, response.code);
if (state.isVerificationComplete()) {
@@ -397,21 +402,6 @@ final class PackageHandler extends Handler {
}
/**
- * Get the default verification agent response code.
- *
- * @return default verification response code
- */
- private int getDefaultVerificationResponse(UserHandle user) {
- if (mPm.mUserManager.hasUserRestriction(UserManager.ENSURE_VERIFY_APPS,
- user.getIdentifier())) {
- return PackageManager.VERIFICATION_REJECT;
- }
- return android.provider.Settings.Global.getInt(mPm.mContext.getContentResolver(),
- android.provider.Settings.Global.PACKAGE_VERIFIER_DEFAULT_RESPONSE,
- DEFAULT_VERIFICATION_RESPONSE);
- }
-
- /**
* Get the default integrity verification response code.
*/
private int getDefaultIntegrityVerificationResponse() {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 356d6c93ae84..f4740448b8a9 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -916,10 +916,26 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
}
}
+ private boolean checkOpenSessionAccess(final PackageInstallerSession session) {
+ if (session == null) {
+ return false;
+ }
+ if (isCallingUidOwner(session)) {
+ return true;
+ }
+ // Package verifiers have access to openSession for sealed sessions.
+ if (session.isSealed() && mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.PACKAGE_VERIFICATION_AGENT)
+ == PackageManager.PERMISSION_GRANTED) {
+ return true;
+ }
+ return false;
+ }
+
private IPackageInstallerSession openSessionInternal(int sessionId) throws IOException {
synchronized (mSessions) {
final PackageInstallerSession session = mSessions.get(sessionId);
- if (session == null || !isCallingUidOwner(session)) {
+ if (!checkOpenSessionAccess(session)) {
throw new SecurityException("Caller has no access to session " + sessionId);
}
session.open();
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 28204eadd394..a94985c226ea 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -1303,22 +1303,30 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@Override
public String[] getNames() {
- assertCallerIsOwnerOrRoot();
+ assertCallerIsOwnerRootOrVerifier();
synchronized (mLock) {
- assertPreparedAndNotCommittedOrDestroyedLocked("getNames");
+ assertPreparedAndNotDestroyedLocked("getNames");
+ if (!mCommitted.get()) {
+ return getNamesLocked();
+ } else {
+ return getStageDirContentsLocked();
+ }
+ }
+ }
- return getNamesLocked();
+ @GuardedBy("mLock")
+ private String[] getStageDirContentsLocked() {
+ String[] result = stageDir.list();
+ if (result == null) {
+ result = EmptyArray.STRING;
}
+ return result;
}
@GuardedBy("mLock")
private String[] getNamesLocked() {
if (!isDataLoaderInstallation()) {
- String[] result = stageDir.list();
- if (result == null) {
- result = EmptyArray.STRING;
- }
- return result;
+ return getStageDirContentsLocked();
}
InstallationFile[] files = getInstallationFilesLocked();
@@ -1403,6 +1411,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
public void requestChecksums(@NonNull String name, @Checksum.TypeMask int optional,
@Checksum.TypeMask int required, @Nullable List trustedInstallers,
@NonNull IOnChecksumsReadyListener onChecksumsReadyListener) {
+ assertCallerIsOwnerRootOrVerifier();
final File file = new File(stageDir, name);
final String installerPackageName = getInstallSource().initiatingPackageName;
try {
@@ -1670,6 +1679,23 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
/**
+ * Check if the caller is the owner of this session or a verifier.
+ * Otherwise throw a {@link SecurityException}.
+ */
+ private void assertCallerIsOwnerRootOrVerifier() {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid == Process.ROOT_UID || callingUid == mInstallerUid) {
+ return;
+ }
+ if (isSealed() && mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.PACKAGE_VERIFICATION_AGENT)
+ == PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ throw new SecurityException("Session does not belong to uid " + callingUid);
+ }
+
+ /**
* Check if the caller is the owner of this session. Otherwise throw a
* {@link SecurityException}.
*/
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 7ceae61f8726..6f8703bf5a5d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -57,6 +57,7 @@ import android.app.ApplicationPackageManager;
import android.app.IActivityManager;
import android.app.admin.IDevicePolicyManager;
import android.app.admin.SecurityLog;
+import android.app.backup.IBackupManager;
import android.app.role.RoleManager;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
@@ -582,13 +583,6 @@ public class PackageManagerService extends IPackageManager.Stub
/** Directory where installed applications are stored */
private final File mAppInstallDir;
- /** Directory where installed application's 32-bit native libraries are copied. */
- @VisibleForTesting
- final File mAppLib32InstallDir;
-
- static File getAppLib32InstallDir() {
- return new File(Environment.getDataDirectory(), "app-lib");
- }
// ----------------------------------------------------------------
@@ -975,6 +969,7 @@ public class PackageManagerService extends IPackageManager.Stub
private final DeletePackageHelper mDeletePackageHelper;
private final InitAndSystemPackageHelper mInitAndSystemPackageHelper;
private final AppDataHelper mAppDataHelper;
+ private final InstallPackageHelper mInstallPackageHelper;
private final PreferredActivityHelper mPreferredActivityHelper;
private final ResolveIntentHelper mResolveIntentHelper;
private final DexOptHelper mDexOptHelper;
@@ -1289,9 +1284,6 @@ public class PackageManagerService extends IPackageManager.Stub
if (!file.exists()) {
throw new FileNotFoundException(file.getAbsolutePath());
}
- if (TextUtils.isEmpty(installerPackageName)) {
- throw new FileNotFoundException(file.getAbsolutePath());
- }
final Executor executor = mInjector.getBackgroundExecutor();
final Handler handler = mInjector.getBackgroundHandler();
@@ -1528,7 +1520,9 @@ public class PackageManagerService extends IPackageManager.Stub
new DefaultSystemWrapper(),
LocalServices::getService,
context::getSystemService,
- (i, pm) -> new BackgroundDexOptService(i.getContext(), i.getDexManager(), pm));
+ (i, pm) -> new BackgroundDexOptService(i.getContext(), i.getDexManager(), pm),
+ (i, pm) -> IBackupManager.Stub.asInterface(ServiceManager.getService(
+ Context.BACKUP_SERVICE)));
if (Build.VERSION.SDK_INT <= 0) {
Slog.w(TAG, "**** ro.build.version.sdk not set!");
@@ -1700,7 +1694,6 @@ public class PackageManagerService extends IPackageManager.Stub
mEnableFreeCacheV2 = testParams.enableFreeCacheV2;
mSdkVersion = testParams.sdkVersion;
mAppInstallDir = testParams.appInstallDir;
- mAppLib32InstallDir = testParams.appLib32InstallDir;
mIsEngBuild = testParams.isEngBuild;
mIsUserDebugBuild = testParams.isUserDebugBuild;
mIncrementalVersion = testParams.incrementalVersion;
@@ -1708,6 +1701,7 @@ public class PackageManagerService extends IPackageManager.Stub
mBroadcastHelper = testParams.broadcastHelper;
mAppDataHelper = testParams.appDataHelper;
+ mInstallPackageHelper = testParams.installPackageHelper;
mRemovePackageHelper = testParams.removePackageHelper;
mInitAndSystemPackageHelper = testParams.initAndSystemPackageHelper;
mDeletePackageHelper = testParams.deletePackageHelper;
@@ -1840,7 +1834,6 @@ public class PackageManagerService extends IPackageManager.Stub
mInstantAppRegistry = new InstantAppRegistry(this, mPermissionManager, mPmInternal);
mAppInstallDir = new File(Environment.getDataDirectory(), "app");
- mAppLib32InstallDir = getAppLib32InstallDir();
mDomainVerificationConnection = new DomainVerificationConnection(this);
mDomainVerificationManager = injector.getDomainVerificationManagerInternal();
@@ -1848,6 +1841,7 @@ public class PackageManagerService extends IPackageManager.Stub
mBroadcastHelper = new BroadcastHelper(mInjector);
mAppDataHelper = new AppDataHelper(this);
+ mInstallPackageHelper = new InstallPackageHelper(this, mAppDataHelper);
mRemovePackageHelper = new RemovePackageHelper(this, mAppDataHelper);
mInitAndSystemPackageHelper = new InitAndSystemPackageHelper(this);
mDeletePackageHelper = new DeletePackageHelper(this, mRemovePackageHelper,
@@ -2009,7 +2003,7 @@ public class PackageManagerService extends IPackageManager.Stub
// the rest of the commands above) because there's precious little we
// can do about it. A settings error is reported, though.
final List<String> changedAbiCodePath =
- ScanPackageHelper.applyAdjustedAbiToSharedUser(
+ ScanPackageUtils.applyAdjustedAbiToSharedUser(
setting, null /*scannedPackage*/,
mInjector.getAbiHelper().getAdjustedAbiForSharedUser(
setting.packages, null /*scannedPackage*/));
@@ -4701,35 +4695,10 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public int installExistingPackageAsUser(String packageName, int userId, int installFlags,
int installReason, List<String> whiteListedPermissions) {
- final InstallPackageHelper installPackageHelper = new InstallPackageHelper(
- this, mAppDataHelper);
- return installPackageHelper.installExistingPackageAsUser(packageName, userId, installFlags,
+ return mInstallPackageHelper.installExistingPackageAsUser(packageName, userId, installFlags,
installReason, whiteListedPermissions, null);
}
- static void setInstantAppForUser(PackageManagerServiceInjector injector,
- PackageSetting pkgSetting, int userId, boolean instantApp, boolean fullApp) {
- // no state specified; do nothing
- if (!instantApp && !fullApp) {
- return;
- }
- if (userId != UserHandle.USER_ALL) {
- if (instantApp && !pkgSetting.getInstantApp(userId)) {
- pkgSetting.setInstantApp(true /*instantApp*/, userId);
- } else if (fullApp && pkgSetting.getInstantApp(userId)) {
- pkgSetting.setInstantApp(false /*instantApp*/, userId);
- }
- } else {
- for (int currentUserId : injector.getUserManagerInternal().getUserIds()) {
- if (instantApp && !pkgSetting.getInstantApp(currentUserId)) {
- pkgSetting.setInstantApp(true /*instantApp*/, currentUserId);
- } else if (fullApp && pkgSetting.getInstantApp(currentUserId)) {
- pkgSetting.setInstantApp(false /*instantApp*/, currentUserId);
- }
- }
- }
- }
-
boolean isUserRestricted(int userId, String restrictionKey) {
Bundle restrictions = mUserManager.getUserRestrictions(userId);
if (restrictions.getBoolean(restrictionKey, false)) {
@@ -5223,10 +5192,11 @@ public class PackageManagerService extends IPackageManager.Stub
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
"Only package verification agents can verify applications");
+ final int callingUid = Binder.getCallingUid();
final Message msg = mHandler.obtainMessage(PACKAGE_VERIFIED);
final PackageVerificationResponse response = new PackageVerificationResponse(
- verificationCode, Binder.getCallingUid());
+ verificationCode, callingUid);
msg.arg1 = id;
msg.obj = response;
mHandler.sendMessage(msg);
@@ -5238,11 +5208,12 @@ public class PackageManagerService extends IPackageManager.Stub
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
"Only package verification agents can extend verification timeouts");
+ final int callingUid = Binder.getCallingUid();
mHandler.post(() -> {
final PackageVerificationState state = mPendingVerification.get(id);
final PackageVerificationResponse response = new PackageVerificationResponse(
- verificationCodeAtTimeout, Binder.getCallingUid());
+ verificationCodeAtTimeout, callingUid);
long delay = millisecondsToDelay;
if (delay > PackageManager.MAXIMUM_VERIFICATION_TIMEOUT) {
@@ -6734,8 +6705,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (isSystemStub
&& (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
|| newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED)) {
- if (!new InstallPackageHelper(this).enableCompressedPackage(deletedPkg,
- pkgSetting)) {
+ if (!mInstallPackageHelper.enableCompressedPackage(deletedPkg, pkgSetting)) {
Slog.w(TAG, "Failed setApplicationEnabledSetting: failed to enable "
+ "commpressed package " + setting.getPackageName());
updateAllowed[i] = false;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
index 97a09ffc810f..d14cc1f3ebd9 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
@@ -17,6 +17,7 @@
package com.android.server.pm;
import android.app.ActivityManagerInternal;
+import android.app.backup.IBackupManager;
import android.content.ComponentName;
import android.content.Context;
import android.os.Handler;
@@ -135,6 +136,7 @@ public class PackageManagerServiceInjector {
mDomainVerificationManagerInternalProducer;
private final Singleton<Handler> mHandlerProducer;
private final Singleton<BackgroundDexOptService> mBackgroundDexOptService;
+ private final Singleton<IBackupManager> mIBackupManager;
PackageManagerServiceInjector(Context context, PackageManagerTracedLock lock,
Installer installer, Object installLock, PackageAbiHelper abiHelper,
@@ -170,7 +172,8 @@ public class PackageManagerServiceInjector {
SystemWrapper systemWrapper,
ServiceProducer getLocalServiceProducer,
ServiceProducer getSystemServiceProducer,
- Producer<BackgroundDexOptService> backgroundDexOptService) {
+ Producer<BackgroundDexOptService> backgroundDexOptService,
+ Producer<IBackupManager> iBackupManager) {
mContext = context;
mLock = lock;
mInstaller = installer;
@@ -220,6 +223,7 @@ public class PackageManagerServiceInjector {
domainVerificationManagerInternalProducer);
mHandlerProducer = new Singleton<>(handlerProducer);
mBackgroundDexOptService = new Singleton<>(backgroundDexOptService);
+ mIBackupManager = new Singleton<>(iBackupManager);
}
/**
@@ -384,6 +388,10 @@ public class PackageManagerServiceInjector {
return mBackgroundDexOptService.get(this, mPackageManager);
}
+ public IBackupManager getIBackupManager() {
+ return mIBackupManager.get(this, mPackageManager);
+ }
+
/** Provides an abstraction to static access to system state. */
public interface SystemWrapper {
void disablePackageCaches();
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
index 9327c5f10af7..a1acc388146e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
@@ -104,6 +104,7 @@ public final class PackageManagerServiceTestParams {
public final String incrementalVersion = Build.VERSION.INCREMENTAL;
public BroadcastHelper broadcastHelper;
public AppDataHelper appDataHelper;
+ public InstallPackageHelper installPackageHelper;
public RemovePackageHelper removePackageHelper;
public InitAndSystemPackageHelper initAndSystemPackageHelper;
public DeletePackageHelper deletePackageHelper;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 4082afd13089..898f67345031 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -18,9 +18,11 @@ package com.android.server.pm;
import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
+import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
import static android.system.OsConstants.O_CREAT;
import static android.system.OsConstants.O_RDWR;
+import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
import static com.android.server.pm.PackageManagerService.COMPRESSED_EXTENSION;
import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION;
import static com.android.server.pm.PackageManagerService.DEBUG_INTENT_MATCHING;
@@ -34,7 +36,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledAfter;
+import android.compat.annotation.EnabledSince;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -60,6 +62,7 @@ import android.os.FileUtils;
import android.os.Process;
import android.os.SystemProperties;
import android.os.incremental.IncrementalManager;
+import android.os.incremental.IncrementalStorage;
import android.os.incremental.V4Signature;
import android.os.incremental.V4Signature.HashingInfo;
import android.os.storage.DiskInfo;
@@ -84,6 +87,7 @@ import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.HexDump;
import com.android.server.EventLogTags;
import com.android.server.IntentResolver;
+import com.android.server.Watchdog;
import com.android.server.compat.PlatformCompat;
import com.android.server.pm.dex.PackageDexUsage;
import com.android.server.pm.parsing.pkg.AndroidPackage;
@@ -141,7 +145,7 @@ public class PackageManagerServiceUtils {
* allow 3P apps to trigger internal-only functionality.
*/
@ChangeId
- @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S)
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
private static final long ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS = 161252188;
/**
@@ -649,6 +653,58 @@ public class PackageManagerServiceUtils {
}
/**
+ * Extract native libraries to a target path
+ */
+ public static int extractNativeBinaries(File dstCodePath, String packageName) {
+ final File libraryRoot = new File(dstCodePath, LIB_DIR_NAME);
+ NativeLibraryHelper.Handle handle = null;
+ try {
+ handle = NativeLibraryHelper.Handle.create(dstCodePath);
+ return NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
+ null /*abiOverride*/, false /*isIncremental*/);
+ } catch (IOException e) {
+ logCriticalInfo(Log.ERROR, "Failed to extract native libraries"
+ + "; pkg: " + packageName);
+ return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+ } finally {
+ IoUtils.closeQuietly(handle);
+ }
+ }
+
+ /**
+ * Remove native libraries of a given package
+ */
+ public static void removeNativeBinariesLI(PackageSetting ps) {
+ if (ps != null) {
+ NativeLibraryHelper.removeNativeBinariesLI(ps.getLegacyNativeLibraryPath());
+ }
+ }
+
+ /**
+ * Wait for native library extraction to be done in IncrementalService
+ */
+ public static void waitForNativeBinariesExtractionForIncremental(
+ ArraySet<IncrementalStorage> incrementalStorages) {
+ if (incrementalStorages.isEmpty()) {
+ return;
+ }
+ try {
+ // Native library extraction may take very long time: each page could potentially
+ // wait for either 10s or 100ms (adb vs non-adb data loader), and that easily adds
+ // up to a full watchdog timeout of 1 min, killing the system after that. It doesn't
+ // make much sense as blocking here doesn't lock up the framework, but only blocks
+ // the installation session and the following ones.
+ Watchdog.getInstance().pauseWatchingCurrentThread("native_lib_extract");
+ for (int i = 0; i < incrementalStorages.size(); ++i) {
+ IncrementalStorage storage = incrementalStorages.valueAtUnchecked(i);
+ storage.waitForNativeBinariesExtraction();
+ }
+ } finally {
+ Watchdog.getInstance().resumeWatchingCurrentThread("native_lib_extract");
+ }
+ }
+
+ /**
* Decompress files stored in codePath to dstCodePath for a certain package.
*/
public static int decompressFiles(String codePath, File dstCodePath, String packageName) {
@@ -1280,4 +1336,39 @@ public class PackageManagerServiceUtils {
return cacheDir;
}
+
+ /**
+ * Check and throw if the given before/after packages would be considered a
+ * downgrade.
+ */
+ public static void checkDowngrade(AndroidPackage before, PackageInfoLite after)
+ throws PackageManagerException {
+ if (after.getLongVersionCode() < before.getLongVersionCode()) {
+ throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
+ "Update version code " + after.versionCode + " is older than current "
+ + before.getLongVersionCode());
+ } else if (after.getLongVersionCode() == before.getLongVersionCode()) {
+ if (after.baseRevisionCode < before.getBaseRevisionCode()) {
+ throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
+ "Update base revision code " + after.baseRevisionCode
+ + " is older than current " + before.getBaseRevisionCode());
+ }
+
+ if (!ArrayUtils.isEmpty(after.splitNames)) {
+ for (int i = 0; i < after.splitNames.length; i++) {
+ final String splitName = after.splitNames[i];
+ final int j = ArrayUtils.indexOf(before.getSplitNames(), splitName);
+ if (j != -1) {
+ if (after.splitRevisionCodes[i] < before.getSplitRevisionCodes()[j]) {
+ throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
+ "Update split " + splitName + " revision code "
+ + after.splitRevisionCodes[i]
+ + " is older than current "
+ + before.getSplitRevisionCodes()[j]);
+ }
+ }
+ }
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageRemovedInfo.java b/services/core/java/com/android/server/pm/PackageRemovedInfo.java
index a60d2c85f542..48dc3cbf8793 100644
--- a/services/core/java/com/android/server/pm/PackageRemovedInfo.java
+++ b/services/core/java/com/android/server/pm/PackageRemovedInfo.java
@@ -49,6 +49,7 @@ final class PackageRemovedInfo {
boolean mDataRemoved;
boolean mRemovedForAllUsers;
boolean mIsStaticSharedLib;
+ boolean mAppIdChanging = false;
// a two dimensional array mapping userId to the set of appIds that can receive notice
// of package changes
SparseArray<int[]> mBroadcastAllowList;
@@ -64,33 +65,43 @@ final class PackageRemovedInfo {
sendPackageRemovedBroadcastInternal(killApp, removedBySystem);
}
- void sendSystemPackageUpdatedBroadcasts() {
+ void sendSystemPackageUpdatedBroadcasts(int newAppId) {
if (mIsRemovedPackageSystemUpdate) {
- sendSystemPackageUpdatedBroadcastsInternal();
+ sendSystemPackageUpdatedBroadcastsInternal(newAppId);
}
}
- private void sendSystemPackageUpdatedBroadcastsInternal() {
+ private void sendSystemPackageUpdatedBroadcastsInternal(int newAppId) {
Bundle extras = new Bundle(2);
- extras.putInt(Intent.EXTRA_UID, mRemovedAppId >= 0 ? mRemovedAppId : mUid);
- extras.putBoolean(Intent.EXTRA_REPLACING, true);
+ extras.putInt(Intent.EXTRA_UID, newAppId);
+ // When appId changes, do not set the replacing extra
+ if (mAppIdChanging) {
+ extras.putBoolean(Intent.EXTRA_UID_CHANGING, true);
+ extras.putInt(Intent.EXTRA_PREVIOUS_UID, mRemovedAppId >= 0 ? mRemovedAppId : mUid);
+ } else {
+ extras.putBoolean(Intent.EXTRA_REPLACING, true);
+ }
mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, mRemovedPackage, extras,
0, null /*targetPackage*/, null, null, null, mBroadcastAllowList, null);
- mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, mRemovedPackage,
- extras, 0, null /*targetPackage*/, null, null, null, mBroadcastAllowList, null);
- mPackageSender.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null, null, 0,
- mRemovedPackage, null, null, null, null /* broadcastAllowList */,
- getTemporaryAppAllowlistBroadcastOptions(REASON_PACKAGE_REPLACED).toBundle());
if (mInstallerPackageName != null) {
mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
mRemovedPackage, extras, 0 /*flags*/,
mInstallerPackageName, null, null, null, null /* broadcastAllowList */,
null);
- mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
- mRemovedPackage, extras, 0 /*flags*/,
- mInstallerPackageName, null, null, null, null /* broadcastAllowList */,
- null);
}
+ if (!mAppIdChanging) {
+ mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, mRemovedPackage,
+ extras, 0, null /*targetPackage*/, null, null, null, mBroadcastAllowList, null);
+ if (mInstallerPackageName != null) {
+ mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
+ mRemovedPackage, extras, 0 /*flags*/,
+ mInstallerPackageName, null, null, null, null /* broadcastAllowList */,
+ null);
+ }
+ }
+ mPackageSender.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null, null, 0,
+ mRemovedPackage, null, null, null, null /* broadcastAllowList */,
+ getTemporaryAppAllowlistBroadcastOptions(REASON_PACKAGE_REPLACED).toBundle());
}
private static @NonNull BroadcastOptions getTemporaryAppAllowlistBroadcastOptions(
@@ -115,15 +126,20 @@ final class PackageRemovedInfo {
if (mIsStaticSharedLib) {
return;
}
- Bundle extras = new Bundle(2);
+ Bundle extras = new Bundle();
final int removedUid = mRemovedAppId >= 0 ? mRemovedAppId : mUid;
extras.putInt(Intent.EXTRA_UID, removedUid);
extras.putBoolean(Intent.EXTRA_DATA_REMOVED, mDataRemoved);
extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, !killApp);
extras.putBoolean(Intent.EXTRA_USER_INITIATED, !removedBySystem);
- if (mIsUpdate || mIsRemovedPackageSystemUpdate) {
+
+ // When appId changes, do not set the replacing extra
+ if (mAppIdChanging) {
+ extras.putBoolean(Intent.EXTRA_UID_CHANGING, true);
+ } else if (mIsUpdate || mIsRemovedPackageSystemUpdate) {
extras.putBoolean(Intent.EXTRA_REPLACING, true);
}
+
extras.putBoolean(Intent.EXTRA_REMOVED_FOR_ALL_USERS, mRemovedForAllUsers);
if (mRemovedPackage != null) {
mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED,
@@ -146,9 +162,9 @@ final class PackageRemovedInfo {
}
}
if (mRemovedAppId >= 0) {
- // If a system app's updates are uninstalled the UID is not actually removed. Some
- // services need to know the package name affected.
- if (extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
+ // If the package is not actually removed, some services need to know the
+ // package name affected.
+ if (mAppIdChanging || mIsUpdate || mIsRemovedPackageSystemUpdate) {
extras.putString(Intent.EXTRA_PACKAGE_NAME, mRemovedPackage);
}
diff --git a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
new file mode 100644
index 000000000000..5a250047da8f
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
+
+import static com.android.server.pm.PackageManagerService.SCAN_BOOTING;
+import static com.android.server.pm.PackageManagerService.SCAN_DONT_KILL_APP;
+import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
+import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures;
+
+import android.content.pm.PackageManager;
+import android.content.pm.SharedLibraryInfo;
+import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.os.SystemProperties;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.server.utils.WatchedLongSparseArray;
+
+import java.util.List;
+import java.util.Map;
+
+final class ReconcilePackageUtils {
+ public static Map<String, ReconciledPackage> reconcilePackages(
+ final ReconcileRequest request, KeySetManagerService ksms,
+ PackageManagerServiceInjector injector)
+ throws ReconcileFailure {
+ final Map<String, ScanResult> scannedPackages = request.mScannedPackages;
+
+ final Map<String, ReconciledPackage> result = new ArrayMap<>(scannedPackages.size());
+
+ // make a copy of the existing set of packages so we can combine them with incoming packages
+ final ArrayMap<String, AndroidPackage> combinedPackages =
+ new ArrayMap<>(request.mAllPackages.size() + scannedPackages.size());
+
+ combinedPackages.putAll(request.mAllPackages);
+
+ final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> incomingSharedLibraries =
+ new ArrayMap<>();
+
+ for (String installPackageName : scannedPackages.keySet()) {
+ final ScanResult scanResult = scannedPackages.get(installPackageName);
+
+ // add / replace existing with incoming packages
+ combinedPackages.put(scanResult.mPkgSetting.getPackageName(),
+ scanResult.mRequest.mParsedPackage);
+
+ // in the first pass, we'll build up the set of incoming shared libraries
+ final List<SharedLibraryInfo> allowedSharedLibInfos =
+ SharedLibraryHelper.getAllowedSharedLibInfos(scanResult,
+ request.mSharedLibrarySource);
+ if (allowedSharedLibInfos != null) {
+ for (SharedLibraryInfo info : allowedSharedLibInfos) {
+ if (!SharedLibraryHelper.addSharedLibraryToPackageVersionMap(
+ incomingSharedLibraries, info)) {
+ throw new ReconcileFailure("Shared Library " + info.getName()
+ + " is being installed twice in this set!");
+ }
+ }
+ }
+
+ // the following may be null if we're just reconciling on boot (and not during install)
+ final InstallArgs installArgs = request.mInstallArgs.get(installPackageName);
+ final PackageInstalledInfo res = request.mInstallResults.get(installPackageName);
+ final PrepareResult prepareResult = request.mPreparedPackages.get(installPackageName);
+ final boolean isInstall = installArgs != null;
+ if (isInstall && (res == null || prepareResult == null)) {
+ throw new ReconcileFailure("Reconcile arguments are not balanced for "
+ + installPackageName + "!");
+ }
+
+ final DeletePackageAction deletePackageAction;
+ // we only want to try to delete for non system apps
+ if (isInstall && prepareResult.mReplace && !prepareResult.mSystem) {
+ final boolean killApp = (scanResult.mRequest.mScanFlags & SCAN_DONT_KILL_APP) == 0;
+ final int deleteFlags = PackageManager.DELETE_KEEP_DATA
+ | (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP);
+ deletePackageAction = DeletePackageHelper.mayDeletePackageLocked(res.mRemovedInfo,
+ prepareResult.mOriginalPs, prepareResult.mDisabledPs,
+ deleteFlags, null /* all users */);
+ if (deletePackageAction == null) {
+ throw new ReconcileFailure(
+ PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE,
+ "May not delete " + installPackageName + " to replace");
+ }
+ } else {
+ deletePackageAction = null;
+ }
+
+ final int scanFlags = scanResult.mRequest.mScanFlags;
+ final int parseFlags = scanResult.mRequest.mParseFlags;
+ final ParsedPackage parsedPackage = scanResult.mRequest.mParsedPackage;
+
+ final PackageSetting disabledPkgSetting = scanResult.mRequest.mDisabledPkgSetting;
+ final PackageSetting lastStaticSharedLibSetting =
+ request.mLastStaticSharedLibSettings.get(installPackageName);
+ final PackageSetting signatureCheckPs =
+ (prepareResult != null && lastStaticSharedLibSetting != null)
+ ? lastStaticSharedLibSetting
+ : scanResult.mPkgSetting;
+ boolean removeAppKeySetData = false;
+ boolean sharedUserSignaturesChanged = false;
+ SigningDetails signingDetails = null;
+ if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
+ if (ksms.checkUpgradeKeySetLocked(signatureCheckPs, parsedPackage)) {
+ // We just determined the app is signed correctly, so bring
+ // over the latest parsed certs.
+ } else {
+ if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
+ throw new ReconcileFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
+ "Package " + parsedPackage.getPackageName()
+ + " upgrade keys do not match the previously installed"
+ + " version");
+ } else {
+ String msg = "System package " + parsedPackage.getPackageName()
+ + " signature changed; retaining data.";
+ PackageManagerService.reportSettingsProblem(Log.WARN, msg);
+ }
+ }
+ signingDetails = parsedPackage.getSigningDetails();
+ } else {
+ try {
+ final Settings.VersionInfo versionInfo =
+ request.mVersionInfos.get(installPackageName);
+ final boolean compareCompat = isCompatSignatureUpdateNeeded(versionInfo);
+ final boolean compareRecover = isRecoverSignatureUpdateNeeded(versionInfo);
+ final boolean isRollback = installArgs != null
+ && installArgs.mInstallReason == PackageManager.INSTALL_REASON_ROLLBACK;
+ final boolean compatMatch = verifySignatures(signatureCheckPs,
+ disabledPkgSetting, parsedPackage.getSigningDetails(), compareCompat,
+ compareRecover, isRollback);
+ // The new KeySets will be re-added later in the scanning process.
+ if (compatMatch) {
+ removeAppKeySetData = true;
+ }
+ // We just determined the app is signed correctly, so bring
+ // over the latest parsed certs.
+ signingDetails = parsedPackage.getSigningDetails();
+
+ // if this is is a sharedUser, check to see if the new package is signed by a
+ // newer
+ // signing certificate than the existing one, and if so, copy over the new
+ // details
+ if (signatureCheckPs.getSharedUser() != null) {
+ // Attempt to merge the existing lineage for the shared SigningDetails with
+ // the lineage of the new package; if the shared SigningDetails are not
+ // returned this indicates the new package added new signers to the lineage
+ // and/or changed the capabilities of existing signers in the lineage.
+ SigningDetails sharedSigningDetails =
+ signatureCheckPs.getSharedUser().signatures.mSigningDetails;
+ SigningDetails mergedDetails = sharedSigningDetails.mergeLineageWith(
+ signingDetails);
+ if (mergedDetails != sharedSigningDetails) {
+ signatureCheckPs.getSharedUser().signatures.mSigningDetails =
+ mergedDetails;
+ }
+ if (signatureCheckPs.getSharedUser().signaturesChanged == null) {
+ signatureCheckPs.getSharedUser().signaturesChanged = Boolean.FALSE;
+ }
+ }
+ } catch (PackageManagerException e) {
+ if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
+ throw new ReconcileFailure(e);
+ }
+ signingDetails = parsedPackage.getSigningDetails();
+
+ // If the system app is part of a shared user we allow that shared user to
+ // change
+ // signatures as well as part of an OTA. We still need to verify that the
+ // signatures
+ // are consistent within the shared user for a given boot, so only allow
+ // updating
+ // the signatures on the first package scanned for the shared user (i.e. if the
+ // signaturesChanged state hasn't been initialized yet in SharedUserSetting).
+ if (signatureCheckPs.getSharedUser() != null) {
+ final Signature[] sharedUserSignatures = signatureCheckPs.getSharedUser()
+ .signatures.mSigningDetails.getSignatures();
+ if (signatureCheckPs.getSharedUser().signaturesChanged != null
+ && compareSignatures(sharedUserSignatures,
+ parsedPackage.getSigningDetails().getSignatures())
+ != PackageManager.SIGNATURE_MATCH) {
+ if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 29) {
+ // Mismatched signatures is an error and silently skipping system
+ // packages will likely break the device in unforeseen ways.
+ // However, we allow the device to boot anyway because, prior to Q,
+ // vendors were not expecting the platform to crash in this
+ // situation.
+ // This WILL be a hard failure on any new API levels after Q.
+ throw new ReconcileFailure(
+ INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
+ "Signature mismatch for shared user: "
+ + scanResult.mPkgSetting.getSharedUser());
+ } else {
+ // Treat mismatched signatures on system packages using a shared
+ // UID as
+ // fatal for the system overall, rather than just failing to install
+ // whichever package happened to be scanned later.
+ throw new IllegalStateException(
+ "Signature mismatch on system package "
+ + parsedPackage.getPackageName()
+ + " for shared user "
+ + scanResult.mPkgSetting.getSharedUser());
+ }
+ }
+
+ sharedUserSignaturesChanged = true;
+ signatureCheckPs.getSharedUser().signatures.mSigningDetails =
+ parsedPackage.getSigningDetails();
+ signatureCheckPs.getSharedUser().signaturesChanged = Boolean.TRUE;
+ }
+ // File a report about this.
+ String msg = "System package " + parsedPackage.getPackageName()
+ + " signature changed; retaining data.";
+ PackageManagerService.reportSettingsProblem(Log.WARN, msg);
+ } catch (IllegalArgumentException e) {
+ // should never happen: certs matched when checking, but not when comparing
+ // old to new for sharedUser
+ throw new RuntimeException(
+ "Signing certificates comparison made on incomparable signing details"
+ + " but somehow passed verifySignatures!", e);
+ }
+ }
+
+ result.put(installPackageName,
+ new ReconciledPackage(request, installArgs, scanResult.mPkgSetting,
+ res, request.mPreparedPackages.get(installPackageName), scanResult,
+ deletePackageAction, allowedSharedLibInfos, signingDetails,
+ sharedUserSignaturesChanged, removeAppKeySetData));
+ }
+
+ for (String installPackageName : scannedPackages.keySet()) {
+ // Check all shared libraries and map to their actual file path.
+ // We only do this here for apps not on a system dir, because those
+ // are the only ones that can fail an install due to this. We
+ // will take care of the system apps by updating all of their
+ // library paths after the scan is done. Also during the initial
+ // scan don't update any libs as we do this wholesale after all
+ // apps are scanned to avoid dependency based scanning.
+ final ScanResult scanResult = scannedPackages.get(installPackageName);
+ if ((scanResult.mRequest.mScanFlags & SCAN_BOOTING) != 0
+ || (scanResult.mRequest.mParseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR)
+ != 0) {
+ continue;
+ }
+ try {
+ result.get(installPackageName).mCollectedSharedLibraryInfos =
+ SharedLibraryHelper.collectSharedLibraryInfos(
+ scanResult.mRequest.mParsedPackage,
+ combinedPackages, request.mSharedLibrarySource,
+ incomingSharedLibraries, injector.getCompatibility());
+
+ } catch (PackageManagerException e) {
+ throw new ReconcileFailure(e.error, e.getMessage());
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * If the database version for this type of package (internal storage or
+ * external storage) is less than the version where package signatures
+ * were updated, return true.
+ */
+ public static boolean isCompatSignatureUpdateNeeded(Settings.VersionInfo ver) {
+ return ver.databaseVersion < Settings.DatabaseVersion.SIGNATURE_END_ENTITY;
+ }
+
+ public static boolean isRecoverSignatureUpdateNeeded(Settings.VersionInfo ver) {
+ return ver.databaseVersion < Settings.DatabaseVersion.SIGNATURE_MALFORMED_RECOVER;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/ScanPackageHelper.java b/services/core/java/com/android/server/pm/ScanPackageHelper.java
deleted file mode 100644
index eafe0d98e505..000000000000
--- a/services/core/java/com/android/server/pm/ScanPackageHelper.java
+++ /dev/null
@@ -1,1716 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.pm;
-
-import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
-import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
-import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
-import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
-import static android.content.pm.PackageManager.INSTALL_FAILED_PROCESS_NOT_DEFINED;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
-import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
-
-import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
-import static com.android.server.pm.PackageManagerService.DEBUG_ABI_SELECTION;
-import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
-import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING;
-import static com.android.server.pm.PackageManagerService.DEBUG_UPGRADE;
-import static com.android.server.pm.PackageManagerService.DEBUG_VERIFY;
-import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_FULL_APP;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_INSTANT_APP;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_ODM;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_OEM;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_PRIVILEGED;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_PRODUCT;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM_EXT;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_VENDOR;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_VIRTUAL_PRELOAD;
-import static com.android.server.pm.PackageManagerService.SCAN_BOOTING;
-import static com.android.server.pm.PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE;
-import static com.android.server.pm.PackageManagerService.SCAN_MOVE;
-import static com.android.server.pm.PackageManagerService.SCAN_NEW_INSTALL;
-import static com.android.server.pm.PackageManagerService.SCAN_NO_DEX;
-import static com.android.server.pm.PackageManagerService.SCAN_REQUIRE_KNOWN;
-import static com.android.server.pm.PackageManagerService.SCAN_UPDATE_SIGNATURE;
-import static com.android.server.pm.PackageManagerService.SCAN_UPDATE_TIME;
-import static com.android.server.pm.PackageManagerService.TAG;
-import static com.android.server.pm.PackageManagerServiceUtils.comparePackageSignatures;
-import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
-import static com.android.server.pm.PackageManagerServiceUtils.compressedFileExists;
-import static com.android.server.pm.PackageManagerServiceUtils.deriveAbiOverride;
-import static com.android.server.pm.PackageManagerServiceUtils.getLastModifiedTime;
-import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.SharedLibraryInfo;
-import android.content.pm.SigningDetails;
-import android.content.pm.parsing.ParsingPackageUtils;
-import android.content.pm.parsing.component.ComponentMutateUtils;
-import android.content.pm.parsing.component.ParsedActivity;
-import android.content.pm.parsing.component.ParsedMainComponent;
-import android.content.pm.parsing.component.ParsedProcess;
-import android.content.pm.parsing.component.ParsedProvider;
-import android.content.pm.parsing.component.ParsedService;
-import android.content.pm.parsing.result.ParseResult;
-import android.content.pm.parsing.result.ParseTypeImpl;
-import android.os.Build;
-import android.os.Process;
-import android.os.SystemProperties;
-import android.os.Trace;
-import android.os.UserHandle;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.Log;
-import android.util.Pair;
-import android.util.Slog;
-import android.util.apk.ApkSignatureVerifier;
-import android.util.jar.StrictJarFile;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.security.VerityUtils;
-import com.android.internal.util.ArrayUtils;
-import com.android.server.SystemConfig;
-import com.android.server.pm.parsing.PackageInfoUtils;
-import com.android.server.pm.parsing.library.PackageBackwardCompatibility;
-import com.android.server.pm.parsing.pkg.AndroidPackage;
-import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
-import com.android.server.utils.WatchedLongSparseArray;
-
-import dalvik.system.VMRuntime;
-
-import java.io.File;
-import java.io.IOException;
-import java.security.DigestException;
-import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.UUID;
-
-/**
- * Helper class that handles package scanning logic
- */
-final class ScanPackageHelper {
- final PackageManagerService mPm;
- final PackageManagerServiceInjector mInjector;
-
- // TODO(b/198166813): remove PMS dependency
- public ScanPackageHelper(PackageManagerService pm) {
- mPm = pm;
- mInjector = pm.mInjector;
- }
-
- ScanPackageHelper(PackageManagerService pm, PackageManagerServiceInjector injector) {
- mPm = pm;
- mInjector = injector;
- }
-
- /**
- * Similar to the other scanPackageTracedLI but accepting a ParsedPackage instead of a File.
- */
- @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
- public ScanResult scanPackageTracedLI(ParsedPackage parsedPackage,
- final @ParsingPackageUtils.ParseFlags int parseFlags,
- @PackageManagerService.ScanFlags int scanFlags, long currentTime,
- @Nullable UserHandle user, String cpuAbiOverride) throws PackageManagerException {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage");
- try {
- return scanPackageNewLI(parsedPackage, parseFlags, scanFlags, currentTime, user,
- cpuAbiOverride);
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
- }
-
- // TODO(b/199937291): scanPackageNewLI() and scanPackageOnlyLI() should be merged.
- // But, first, committing the results / removing app data needs to be moved up a level to the
- // callers of this method. Also, we need to solve the problem of potentially creating a new
- // shared user setting. That can probably be done later and patch things up after the fact.
- @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
- private ScanResult scanPackageNewLI(@NonNull ParsedPackage parsedPackage,
- final @ParsingPackageUtils.ParseFlags int parseFlags,
- @PackageManagerService.ScanFlags int scanFlags, long currentTime,
- @Nullable UserHandle user, String cpuAbiOverride) throws PackageManagerException {
-
- final String renamedPkgName = mPm.mSettings.getRenamedPackageLPr(
- AndroidPackageUtils.getRealPackageOrNull(parsedPackage));
- final String realPkgName = getRealPackageName(parsedPackage, renamedPkgName);
- if (realPkgName != null) {
- ensurePackageRenamed(parsedPackage, renamedPkgName);
- }
- final PackageSetting originalPkgSetting = getOriginalPackageLocked(parsedPackage,
- renamedPkgName);
- final PackageSetting pkgSetting =
- mPm.mSettings.getPackageLPr(parsedPackage.getPackageName());
- final PackageSetting disabledPkgSetting =
- mPm.mSettings.getDisabledSystemPkgLPr(parsedPackage.getPackageName());
-
- if (mPm.mTransferredPackages.contains(parsedPackage.getPackageName())) {
- Slog.w(TAG, "Package " + parsedPackage.getPackageName()
- + " was transferred to another, but its .apk remains");
- }
-
- scanFlags = adjustScanFlags(scanFlags, pkgSetting, disabledPkgSetting, user, parsedPackage);
- synchronized (mPm.mLock) {
- boolean isUpdatedSystemApp;
- if (pkgSetting != null) {
- isUpdatedSystemApp = pkgSetting.getPkgState().isUpdatedSystemApp();
- } else {
- isUpdatedSystemApp = disabledPkgSetting != null;
- }
- applyPolicy(parsedPackage, scanFlags, mPm.getPlatformPackage(), isUpdatedSystemApp);
- assertPackageIsValid(parsedPackage, parseFlags, scanFlags);
-
- SharedUserSetting sharedUserSetting = null;
- if (parsedPackage.getSharedUserId() != null) {
- // SIDE EFFECTS; may potentially allocate a new shared user
- sharedUserSetting = mPm.mSettings.getSharedUserLPw(parsedPackage.getSharedUserId(),
- 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true /*create*/);
- if (DEBUG_PACKAGE_SCANNING) {
- if ((parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0) {
- Log.d(TAG, "Shared UserID " + parsedPackage.getSharedUserId()
- + " (uid=" + sharedUserSetting.userId + "):"
- + " packages=" + sharedUserSetting.packages);
- }
- }
- }
- String platformPackageName = mPm.getPlatformPackage() == null
- ? null : mPm.getPlatformPackage().getPackageName();
- final ScanRequest request = new ScanRequest(parsedPackage, sharedUserSetting,
- pkgSetting == null ? null : pkgSetting.getPkg(), pkgSetting, disabledPkgSetting,
- originalPkgSetting, realPkgName, parseFlags, scanFlags,
- Objects.equals(parsedPackage.getPackageName(), platformPackageName), user,
- cpuAbiOverride);
- return scanPackageOnlyLI(request, mInjector, mPm.mFactoryTest, currentTime);
- }
- }
-
- /**
- * Just scans the package without any side effects.
- * <p>Not entirely true at the moment. There is still one side effect -- this
- * method potentially modifies a live {@link PackageSetting} object representing
- * the package being scanned. This will be resolved in the future.
- *
- * @param injector injector for acquiring dependencies
- * @param request Information about the package to be scanned
- * @param isUnderFactoryTest Whether or not the device is under factory test
- * @param currentTime The current time, in millis
- * @return The results of the scan
- */
- @GuardedBy("mPm.mInstallLock")
- @VisibleForTesting
- @NonNull
- public ScanResult scanPackageOnlyLI(@NonNull ScanRequest request,
- PackageManagerServiceInjector injector,
- boolean isUnderFactoryTest, long currentTime)
- throws PackageManagerException {
- final PackageAbiHelper packageAbiHelper = injector.getAbiHelper();
- ParsedPackage parsedPackage = request.mParsedPackage;
- PackageSetting pkgSetting = request.mPkgSetting;
- final PackageSetting disabledPkgSetting = request.mDisabledPkgSetting;
- final PackageSetting originalPkgSetting = request.mOriginalPkgSetting;
- final @ParsingPackageUtils.ParseFlags int parseFlags = request.mParseFlags;
- final @PackageManagerService.ScanFlags int scanFlags = request.mScanFlags;
- final String realPkgName = request.mRealPkgName;
- final SharedUserSetting sharedUserSetting = request.mSharedUserSetting;
- final UserHandle user = request.mUser;
- final boolean isPlatformPackage = request.mIsPlatformPackage;
-
- List<String> changedAbiCodePath = null;
-
- if (DEBUG_PACKAGE_SCANNING) {
- if ((parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0) {
- Log.d(TAG, "Scanning package " + parsedPackage.getPackageName());
- }
- }
-
- // Initialize package source and resource directories
- final File destCodeFile = new File(parsedPackage.getPath());
-
- // We keep references to the derived CPU Abis from settings in oder to reuse
- // them in the case where we're not upgrading or booting for the first time.
- String primaryCpuAbiFromSettings = null;
- String secondaryCpuAbiFromSettings = null;
- boolean needToDeriveAbi = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;
- if (!needToDeriveAbi) {
- if (pkgSetting != null) {
- // TODO(b/154610922): if it is not first boot or upgrade, we should directly use
- // API info from existing package setting. However, stub packages currently do not
- // preserve ABI info, thus the special condition check here. Remove the special
- // check after we fix the stub generation.
- if (pkgSetting.getPkg() != null && pkgSetting.getPkg().isStub()) {
- needToDeriveAbi = true;
- } else {
- primaryCpuAbiFromSettings = pkgSetting.getPrimaryCpuAbi();
- secondaryCpuAbiFromSettings = pkgSetting.getSecondaryCpuAbi();
- }
- } else {
- // Re-scanning a system package after uninstalling updates; need to derive ABI
- needToDeriveAbi = true;
- }
- }
-
- int previousAppId = Process.INVALID_UID;
-
- if (pkgSetting != null && pkgSetting.getSharedUser() != sharedUserSetting) {
- if (pkgSetting.getSharedUser() != null && sharedUserSetting == null) {
- previousAppId = pkgSetting.getAppId();
- // Log that something is leaving shareduid and keep going
- Slog.i(TAG,
- "Package " + parsedPackage.getPackageName() + " shared user changed from "
- + pkgSetting.getSharedUser().name + " to " + "<nothing>.");
- } else {
- PackageManagerService.reportSettingsProblem(Log.WARN,
- "Package " + parsedPackage.getPackageName() + " shared user changed from "
- + (pkgSetting.getSharedUser() != null
- ? pkgSetting.getSharedUser().name : "<nothing>")
- + " to "
- + (sharedUserSetting != null ? sharedUserSetting.name : "<nothing>")
- + "; replacing with new");
- pkgSetting = null;
- }
- }
-
- String[] usesSdkLibraries = null;
- if (!parsedPackage.getUsesSdkLibraries().isEmpty()) {
- usesSdkLibraries = new String[parsedPackage.getUsesSdkLibraries().size()];
- parsedPackage.getUsesSdkLibraries().toArray(usesSdkLibraries);
- }
-
- String[] usesStaticLibraries = null;
- if (!parsedPackage.getUsesStaticLibraries().isEmpty()) {
- usesStaticLibraries = new String[parsedPackage.getUsesStaticLibraries().size()];
- parsedPackage.getUsesStaticLibraries().toArray(usesStaticLibraries);
- }
-
- final UUID newDomainSetId = injector.getDomainVerificationManagerInternal().generateNewId();
-
- // TODO(b/135203078): Remove appInfoFlag usage in favor of individually assigned booleans
- // to avoid adding something that's unsupported due to lack of state, since it's called
- // with null.
- final boolean createNewPackage = (pkgSetting == null);
- if (createNewPackage) {
- final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
- final boolean virtualPreload = (scanFlags & SCAN_AS_VIRTUAL_PRELOAD) != 0;
-
- // Flags contain system values stored in the server variant of AndroidPackage,
- // and so the server-side PackageInfoUtils is still called, even without a
- // PackageSetting to pass in.
- int pkgFlags = PackageInfoUtils.appInfoFlags(parsedPackage, null);
- int pkgPrivateFlags = PackageInfoUtils.appInfoPrivateFlags(parsedPackage, null);
-
- // REMOVE SharedUserSetting from method; update in a separate call
- pkgSetting = Settings.createNewSetting(parsedPackage.getPackageName(),
- originalPkgSetting, disabledPkgSetting, realPkgName, sharedUserSetting,
- destCodeFile, parsedPackage.getNativeLibraryRootDir(),
- AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage),
- AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage),
- parsedPackage.getLongVersionCode(), pkgFlags, pkgPrivateFlags, user,
- true /*allowInstall*/, instantApp, virtualPreload,
- UserManagerService.getInstance(), usesSdkLibraries,
- parsedPackage.getUsesSdkLibrariesVersionsMajor(), usesStaticLibraries,
- parsedPackage.getUsesStaticLibrariesVersions(), parsedPackage.getMimeGroups(),
- newDomainSetId);
- } else {
- // make a deep copy to avoid modifying any existing system state.
- pkgSetting = new PackageSetting(pkgSetting);
- pkgSetting.setPkg(parsedPackage);
-
- // REMOVE SharedUserSetting from method; update in a separate call.
- //
- // TODO(narayan): This update is bogus. nativeLibraryDir & primaryCpuAbi,
- // secondaryCpuAbi are not known at this point so we always update them
- // to null here, only to reset them at a later point.
- Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, sharedUserSetting,
- destCodeFile, parsedPackage.getNativeLibraryDir(),
- AndroidPackageUtils.getPrimaryCpuAbi(parsedPackage, pkgSetting),
- AndroidPackageUtils.getSecondaryCpuAbi(parsedPackage, pkgSetting),
- PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting),
- PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting),
- UserManagerService.getInstance(),
- usesSdkLibraries, parsedPackage.getUsesSdkLibrariesVersionsMajor(),
- usesStaticLibraries, parsedPackage.getUsesStaticLibrariesVersions(),
- parsedPackage.getMimeGroups(), newDomainSetId);
- }
- if (createNewPackage && originalPkgSetting != null) {
- // This is the initial transition from the original package, so,
- // fix up the new package's name now. We must do this after looking
- // up the package under its new name, so getPackageLP takes care of
- // fiddling things correctly.
- parsedPackage.setPackageName(originalPkgSetting.getPackageName());
-
- // File a report about this.
- String msg = "New package " + pkgSetting.getRealName()
- + " renamed to replace old package " + pkgSetting.getPackageName();
- PackageManagerService.reportSettingsProblem(Log.WARN, msg);
- }
-
- final int userId = (user == null ? UserHandle.USER_SYSTEM : user.getIdentifier());
- // for existing packages, change the install state; but, only if it's explicitly specified
- if (!createNewPackage) {
- final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
- final boolean fullApp = (scanFlags & SCAN_AS_FULL_APP) != 0;
- PackageManagerService.setInstantAppForUser(
- injector, pkgSetting, userId, instantApp, fullApp);
- }
- // TODO(patb): see if we can do away with disabled check here.
- if (disabledPkgSetting != null
- || (0 != (scanFlags & SCAN_NEW_INSTALL)
- && pkgSetting != null && pkgSetting.isSystem())) {
- pkgSetting.getPkgState().setUpdatedSystemApp(true);
- }
-
- parsedPackage.setSeInfo(SELinuxMMAC.getSeInfo(parsedPackage, sharedUserSetting,
- injector.getCompatibility()));
-
- if (parsedPackage.isSystem()) {
- configurePackageComponents(parsedPackage);
- }
-
- final String cpuAbiOverride = deriveAbiOverride(request.mCpuAbiOverride);
- final boolean isUpdatedSystemApp = pkgSetting.getPkgState().isUpdatedSystemApp();
-
- final File appLib32InstallDir = PackageManagerService.getAppLib32InstallDir();
- if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
- if (needToDeriveAbi) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi");
- final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths> derivedAbi =
- packageAbiHelper.derivePackageAbi(parsedPackage, isUpdatedSystemApp,
- cpuAbiOverride, appLib32InstallDir);
- derivedAbi.first.applyTo(parsedPackage);
- derivedAbi.second.applyTo(parsedPackage);
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-
- // Some system apps still use directory structure for native libraries
- // in which case we might end up not detecting abi solely based on apk
- // structure. Try to detect abi based on directory structure.
-
- String pkgRawPrimaryCpuAbi = AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage);
- if (parsedPackage.isSystem() && !isUpdatedSystemApp
- && pkgRawPrimaryCpuAbi == null) {
- final PackageAbiHelper.Abis abis = packageAbiHelper.getBundledAppAbis(
- parsedPackage);
- abis.applyTo(parsedPackage);
- abis.applyTo(pkgSetting);
- final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
- packageAbiHelper.deriveNativeLibraryPaths(parsedPackage,
- isUpdatedSystemApp, appLib32InstallDir);
- nativeLibraryPaths.applyTo(parsedPackage);
- }
- } else {
- // This is not a first boot or an upgrade, don't bother deriving the
- // ABI during the scan. Instead, trust the value that was stored in the
- // package setting.
- parsedPackage.setPrimaryCpuAbi(primaryCpuAbiFromSettings)
- .setSecondaryCpuAbi(secondaryCpuAbiFromSettings);
-
- final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
- packageAbiHelper.deriveNativeLibraryPaths(parsedPackage,
- isUpdatedSystemApp, appLib32InstallDir);
- nativeLibraryPaths.applyTo(parsedPackage);
-
- if (DEBUG_ABI_SELECTION) {
- Slog.i(TAG, "Using ABIS and native lib paths from settings : "
- + parsedPackage.getPackageName() + " "
- + AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage)
- + ", "
- + AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage));
- }
- }
- } else {
- if ((scanFlags & SCAN_MOVE) != 0) {
- // We haven't run dex-opt for this move (since we've moved the compiled output too)
- // but we already have this packages package info in the PackageSetting. We just
- // use that and derive the native library path based on the new code path.
- parsedPackage.setPrimaryCpuAbi(pkgSetting.getPrimaryCpuAbi())
- .setSecondaryCpuAbi(pkgSetting.getSecondaryCpuAbi());
- }
-
- // Set native library paths again. For moves, the path will be updated based on the
- // ABIs we've determined above. For non-moves, the path will be updated based on the
- // ABIs we determined during compilation, but the path will depend on the final
- // package path (after the rename away from the stage path).
- final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
- packageAbiHelper.deriveNativeLibraryPaths(parsedPackage, isUpdatedSystemApp,
- appLib32InstallDir);
- nativeLibraryPaths.applyTo(parsedPackage);
- }
-
- // This is a special case for the "system" package, where the ABI is
- // dictated by the zygote configuration (and init.rc). We should keep track
- // of this ABI so that we can deal with "normal" applications that run under
- // the same UID correctly.
- if (isPlatformPackage) {
- parsedPackage.setPrimaryCpuAbi(VMRuntime.getRuntime().is64Bit()
- ? Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0]);
- }
-
- // If there's a mismatch between the abi-override in the package setting
- // and the abiOverride specified for the install. Warn about this because we
- // would've already compiled the app without taking the package setting into
- // account.
- if ((scanFlags & SCAN_NO_DEX) == 0 && (scanFlags & SCAN_NEW_INSTALL) != 0) {
- if (cpuAbiOverride == null) {
- Slog.w(TAG, "Ignoring persisted ABI override for package "
- + parsedPackage.getPackageName());
- }
- }
-
- pkgSetting.setPrimaryCpuAbi(AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage))
- .setSecondaryCpuAbi(AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage))
- .setCpuAbiOverride(cpuAbiOverride);
-
- if (DEBUG_ABI_SELECTION) {
- Slog.d(TAG, "Resolved nativeLibraryRoot for " + parsedPackage.getPackageName()
- + " to root=" + parsedPackage.getNativeLibraryRootDir()
- + ", to dir=" + parsedPackage.getNativeLibraryDir()
- + ", isa=" + parsedPackage.isNativeLibraryRootRequiresIsa());
- }
-
- // Push the derived path down into PackageSettings so we know what to
- // clean up at uninstall time.
- pkgSetting.setLegacyNativeLibraryPath(parsedPackage.getNativeLibraryRootDir());
-
- if (DEBUG_ABI_SELECTION) {
- Log.d(TAG, "Abis for package[" + parsedPackage.getPackageName() + "] are"
- + " primary=" + pkgSetting.getPrimaryCpuAbi()
- + " secondary=" + pkgSetting.getSecondaryCpuAbi()
- + " abiOverride=" + pkgSetting.getCpuAbiOverride());
- }
-
- if ((scanFlags & SCAN_BOOTING) == 0 && pkgSetting.getSharedUser() != null) {
- // We don't do this here during boot because we can do it all
- // at once after scanning all existing packages.
- //
- // We also do this *before* we perform dexopt on this package, so that
- // we can avoid redundant dexopts, and also to make sure we've got the
- // code and package path correct.
- changedAbiCodePath = applyAdjustedAbiToSharedUser(pkgSetting.getSharedUser(),
- parsedPackage, packageAbiHelper.getAdjustedAbiForSharedUser(
- pkgSetting.getSharedUser().packages, parsedPackage));
- }
-
- parsedPackage.setFactoryTest(isUnderFactoryTest && parsedPackage.getRequestedPermissions()
- .contains(android.Manifest.permission.FACTORY_TEST));
-
- if (parsedPackage.isSystem()) {
- pkgSetting.setIsOrphaned(true);
- }
-
- // Take care of first install / last update times.
- final long scanFileTime = getLastModifiedTime(parsedPackage);
- if (currentTime != 0) {
- if (pkgSetting.getFirstInstallTime() == 0) {
- pkgSetting.setFirstInstallTime(currentTime)
- .setLastUpdateTime(currentTime);
- } else if ((scanFlags & SCAN_UPDATE_TIME) != 0) {
- pkgSetting.setLastUpdateTime(currentTime);
- }
- } else if (pkgSetting.getFirstInstallTime() == 0) {
- // We need *something*. Take time time stamp of the file.
- pkgSetting.setFirstInstallTime(scanFileTime)
- .setLastUpdateTime(scanFileTime);
- } else if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0) {
- if (scanFileTime != pkgSetting.getLastModifiedTime()) {
- // A package on the system image has changed; consider this
- // to be an update.
- pkgSetting.setLastUpdateTime(scanFileTime);
- }
- }
- pkgSetting.setLastModifiedTime(scanFileTime);
- // TODO(b/135203078): Remove, move to constructor
- pkgSetting.setPkg(parsedPackage)
- .setFlags(PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting))
- .setPrivateFlags(
- PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting));
- if (parsedPackage.getLongVersionCode() != pkgSetting.getVersionCode()) {
- pkgSetting.setLongVersionCode(parsedPackage.getLongVersionCode());
- }
- // Update volume if needed
- final String volumeUuid = parsedPackage.getVolumeUuid();
- if (!Objects.equals(volumeUuid, pkgSetting.getVolumeUuid())) {
- Slog.i(PackageManagerService.TAG,
- "Update" + (pkgSetting.isSystem() ? " system" : "")
- + " package " + parsedPackage.getPackageName()
- + " volume from " + pkgSetting.getVolumeUuid()
- + " to " + volumeUuid);
- pkgSetting.setVolumeUuid(volumeUuid);
- }
-
- SharedLibraryInfo sdkLibraryInfo = null;
- if (!TextUtils.isEmpty(parsedPackage.getSdkLibName())) {
- sdkLibraryInfo = AndroidPackageUtils.createSharedLibraryForSdk(parsedPackage);
- }
- SharedLibraryInfo staticSharedLibraryInfo = null;
- if (!TextUtils.isEmpty(parsedPackage.getStaticSharedLibName())) {
- staticSharedLibraryInfo =
- AndroidPackageUtils.createSharedLibraryForStatic(parsedPackage);
- }
- List<SharedLibraryInfo> dynamicSharedLibraryInfos = null;
- if (!ArrayUtils.isEmpty(parsedPackage.getLibraryNames())) {
- dynamicSharedLibraryInfos = new ArrayList<>(parsedPackage.getLibraryNames().size());
- for (String name : parsedPackage.getLibraryNames()) {
- dynamicSharedLibraryInfos.add(
- AndroidPackageUtils.createSharedLibraryForDynamic(parsedPackage, name));
- }
- }
-
- return new ScanResult(request, true, pkgSetting, changedAbiCodePath,
- !createNewPackage /* existingSettingCopied */,
- previousAppId, sdkLibraryInfo, staticSharedLibraryInfo,
- dynamicSharedLibraryInfos);
- }
-
- /**
- * Returns the actual scan flags depending upon the state of the other settings.
- * <p>Updated system applications will not have the following flags set
- * by default and need to be adjusted after the fact:
- * <ul>
- * <li>{@link PackageManagerService.SCAN_AS_SYSTEM}</li>
- * <li>{@link PackageManagerService.SCAN_AS_PRIVILEGED}</li>
- * <li>{@link PackageManagerService.SCAN_AS_OEM}</li>
- * <li>{@link PackageManagerService.SCAN_AS_VENDOR}</li>
- * <li>{@link PackageManagerService.SCAN_AS_PRODUCT}</li>
- * <li>{@link PackageManagerService.SCAN_AS_SYSTEM_EXT}</li>
- * <li>{@link PackageManagerService.SCAN_AS_INSTANT_APP}</li>
- * <li>{@link PackageManagerService.SCAN_AS_VIRTUAL_PRELOAD}</li>
- * <li>{@link PackageManagerService.SCAN_AS_ODM}</li>
- * </ul>
- */
- private @PackageManagerService.ScanFlags int adjustScanFlags(
- @PackageManagerService.ScanFlags int scanFlags,
- PackageSetting pkgSetting, PackageSetting disabledPkgSetting, UserHandle user,
- AndroidPackage pkg) {
-
- // TODO(patb): Do away entirely with disabledPkgSetting here. PkgSetting will always contain
- // the correct isSystem value now that we don't disable system packages before scan.
- final PackageSetting systemPkgSetting =
- (scanFlags & SCAN_NEW_INSTALL) != 0 && disabledPkgSetting == null
- && pkgSetting != null && pkgSetting.isSystem()
- ? pkgSetting
- : disabledPkgSetting;
- if (systemPkgSetting != null) {
- // updated system application, must at least have SCAN_AS_SYSTEM
- scanFlags |= SCAN_AS_SYSTEM;
- if ((systemPkgSetting.getPrivateFlags()
- & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
- scanFlags |= SCAN_AS_PRIVILEGED;
- }
- if ((systemPkgSetting.getPrivateFlags()
- & ApplicationInfo.PRIVATE_FLAG_OEM) != 0) {
- scanFlags |= SCAN_AS_OEM;
- }
- if ((systemPkgSetting.getPrivateFlags()
- & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0) {
- scanFlags |= SCAN_AS_VENDOR;
- }
- if ((systemPkgSetting.getPrivateFlags()
- & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0) {
- scanFlags |= SCAN_AS_PRODUCT;
- }
- if ((systemPkgSetting.getPrivateFlags()
- & ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT) != 0) {
- scanFlags |= SCAN_AS_SYSTEM_EXT;
- }
- if ((systemPkgSetting.getPrivateFlags()
- & ApplicationInfo.PRIVATE_FLAG_ODM) != 0) {
- scanFlags |= SCAN_AS_ODM;
- }
- }
- if (pkgSetting != null) {
- final int userId = ((user == null) ? 0 : user.getIdentifier());
- if (pkgSetting.getInstantApp(userId)) {
- scanFlags |= SCAN_AS_INSTANT_APP;
- }
- if (pkgSetting.getVirtualPreload(userId)) {
- scanFlags |= SCAN_AS_VIRTUAL_PRELOAD;
- }
- }
-
- // Scan as privileged apps that share a user with a priv-app.
- final boolean skipVendorPrivilegeScan = ((scanFlags & SCAN_AS_VENDOR) != 0)
- && getVendorPartitionVersion() < 28;
- if (((scanFlags & SCAN_AS_PRIVILEGED) == 0)
- && !pkg.isPrivileged()
- && (pkg.getSharedUserId() != null)
- && !skipVendorPrivilegeScan) {
- SharedUserSetting sharedUserSetting = null;
- try {
- sharedUserSetting = mPm.mSettings.getSharedUserLPw(pkg.getSharedUserId(), 0,
- 0, false);
- } catch (PackageManagerException ignore) {
- }
- if (sharedUserSetting != null && sharedUserSetting.isPrivileged()) {
- // Exempt SharedUsers signed with the platform key.
- // TODO(b/72378145) Fix this exemption. Force signature apps
- // to allowlist their privileged permissions just like other
- // priv-apps.
- synchronized (mPm.mLock) {
- PackageSetting platformPkgSetting = mPm.mSettings.getPackageLPr("android");
- if ((compareSignatures(
- platformPkgSetting.getSigningDetails().getSignatures(),
- pkg.getSigningDetails().getSignatures())
- != PackageManager.SIGNATURE_MATCH)) {
- scanFlags |= SCAN_AS_PRIVILEGED;
- }
- }
- }
- }
-
- return scanFlags;
- }
-
- public Pair<ScanResult, Boolean> scanSystemPackageLI(ParsedPackage parsedPackage,
- @ParsingPackageUtils.ParseFlags int parseFlags,
- @PackageManagerService.ScanFlags int scanFlags, long currentTime,
- @Nullable UserHandle user) throws PackageManagerException {
- final boolean scanSystemPartition =
- (parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0;
- final String renamedPkgName;
- final PackageSetting disabledPkgSetting;
- final boolean isSystemPkgUpdated;
- final boolean pkgAlreadyExists;
- PackageSetting pkgSetting;
- AndroidPackage platformPackage;
- final boolean isUpgrade = mPm.isDeviceUpgrading();
-
- synchronized (mPm.mLock) {
- platformPackage = mPm.getPlatformPackage();
- renamedPkgName = mPm.mSettings.getRenamedPackageLPr(
- AndroidPackageUtils.getRealPackageOrNull(parsedPackage));
- final String realPkgName = getRealPackageName(parsedPackage,
- renamedPkgName);
- if (realPkgName != null) {
- ensurePackageRenamed(parsedPackage, renamedPkgName);
- }
- final PackageSetting originalPkgSetting = getOriginalPackageLocked(parsedPackage,
- renamedPkgName);
- final PackageSetting installedPkgSetting = mPm.mSettings.getPackageLPr(
- parsedPackage.getPackageName());
- pkgSetting = originalPkgSetting == null ? installedPkgSetting : originalPkgSetting;
- pkgAlreadyExists = pkgSetting != null;
- final String disabledPkgName = pkgAlreadyExists
- ? pkgSetting.getPackageName() : parsedPackage.getPackageName();
- if (scanSystemPartition && !pkgAlreadyExists
- && mPm.mSettings.getDisabledSystemPkgLPr(disabledPkgName) != null) {
- // The updated-package data for /system apk remains inconsistently
- // after the package data for /data apk is lost accidentally.
- // To recover it, enable /system apk and install it as non-updated system app.
- Slog.w(TAG, "Inconsistent package setting of updated system app for "
- + disabledPkgName + ". To recover it, enable the system app"
- + "and install it as non-updated system app.");
- mPm.mSettings.removeDisabledSystemPackageLPw(disabledPkgName);
- }
- disabledPkgSetting = mPm.mSettings.getDisabledSystemPkgLPr(disabledPkgName);
- isSystemPkgUpdated = disabledPkgSetting != null;
-
- if (DEBUG_INSTALL && isSystemPkgUpdated) {
- Slog.d(TAG, "updatedPkg = " + disabledPkgSetting);
- }
-
- final SharedUserSetting sharedUserSetting = (parsedPackage.getSharedUserId() != null)
- ? mPm.mSettings.getSharedUserLPw(parsedPackage.getSharedUserId(),
- 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true)
- : null;
- if (DEBUG_PACKAGE_SCANNING
- && (parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0
- && sharedUserSetting != null) {
- Log.d(TAG, "Shared UserID " + parsedPackage.getSharedUserId()
- + " (uid=" + sharedUserSetting.userId + "):"
- + " packages=" + sharedUserSetting.packages);
- }
-
- if (scanSystemPartition) {
- if (isSystemPkgUpdated) {
- // we're updating the disabled package, so, scan it as the package setting
- boolean isPlatformPackage = platformPackage != null
- && platformPackage.getPackageName().equals(
- parsedPackage.getPackageName());
- final ScanRequest request = new ScanRequest(parsedPackage, sharedUserSetting,
- null, disabledPkgSetting /* pkgSetting */,
- null /* disabledPkgSetting */, null /* originalPkgSetting */,
- null, parseFlags, scanFlags, isPlatformPackage, user, null);
- applyPolicy(parsedPackage, scanFlags,
- platformPackage, true);
- final ScanResult scanResult =
- scanPackageOnlyLI(request, mInjector,
- mPm.mFactoryTest, -1L);
- if (scanResult.mExistingSettingCopied
- && scanResult.mRequest.mPkgSetting != null) {
- scanResult.mRequest.mPkgSetting.updateFrom(scanResult.mPkgSetting);
- }
- }
- }
- }
-
- final boolean newPkgChangedPaths = pkgAlreadyExists
- && !pkgSetting.getPathString().equals(parsedPackage.getPath());
- final boolean newPkgVersionGreater = pkgAlreadyExists
- && parsedPackage.getLongVersionCode() > pkgSetting.getVersionCode();
- final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated
- && newPkgChangedPaths && newPkgVersionGreater;
- if (isSystemPkgBetter) {
- // The version of the application on /system is greater than the version on
- // /data. Switch back to the application on /system.
- // It's safe to assume the application on /system will correctly scan. If not,
- // there won't be a working copy of the application.
- synchronized (mPm.mLock) {
- // just remove the loaded entries from package lists
- mPm.mPackages.remove(pkgSetting.getPackageName());
- }
-
- logCriticalInfo(Log.WARN,
- "System package updated;"
- + " name: " + pkgSetting.getPackageName()
- + "; " + pkgSetting.getVersionCode() + " --> "
- + parsedPackage.getLongVersionCode()
- + "; " + pkgSetting.getPathString()
- + " --> " + parsedPackage.getPath());
-
- final InstallArgs args = new FileInstallArgs(
- pkgSetting.getPathString(), getAppDexInstructionSets(
- pkgSetting.getPrimaryCpuAbi(), pkgSetting.getSecondaryCpuAbi()), mPm);
- args.cleanUpResourcesLI();
- synchronized (mPm.mLock) {
- mPm.mSettings.enableSystemPackageLPw(pkgSetting.getPackageName());
- }
- }
-
- // The version of the application on the /system partition is less than or
- // equal to the version on the /data partition. Throw an exception and use
- // the application already installed on the /data partition.
- if (scanSystemPartition && isSystemPkgUpdated && !isSystemPkgBetter) {
- // In the case of a skipped package, commitReconciledScanResultLocked is not called to
- // add the object to the "live" data structures, so this is the final mutation step
- // for the package. Which means it needs to be finalized here to cache derived fields.
- // This is relevant for cases where the disabled system package is used for flags or
- // other metadata.
- parsedPackage.hideAsFinal();
- throw new PackageManagerException(Log.WARN, "Package " + parsedPackage.getPackageName()
- + " at " + parsedPackage.getPath() + " ignored: updated version "
- + (pkgAlreadyExists ? String.valueOf(pkgSetting.getVersionCode()) : "unknown")
- + " better than this " + parsedPackage.getLongVersionCode());
- }
-
- // Verify certificates against what was last scanned. Force re-collecting certificate in two
- // special cases:
- // 1) when scanning system, force re-collect only if system is upgrading.
- // 2) when scannning /data, force re-collect only if the app is privileged (updated from
- // preinstall, or treated as privileged, e.g. due to shared user ID).
- final boolean forceCollect = scanSystemPartition ? isUpgrade
- : PackageManagerServiceUtils.isApkVerificationForced(pkgSetting);
- if (DEBUG_VERIFY && forceCollect) {
- Slog.d(TAG, "Force collect certificate of " + parsedPackage.getPackageName());
- }
-
- // Full APK verification can be skipped during certificate collection, only if the file is
- // in verified partition, or can be verified on access (when apk verity is enabled). In both
- // cases, only data in Signing Block is verified instead of the whole file.
- // TODO(b/136132412): skip for Incremental installation
- final boolean skipVerify = scanSystemPartition
- || (forceCollect && canSkipForcedPackageVerification(parsedPackage));
- collectCertificatesLI(pkgSetting, parsedPackage, forceCollect, skipVerify);
-
- // Reset profile if the application version is changed
- maybeClearProfilesForUpgradesLI(pkgSetting, parsedPackage);
-
- /*
- * A new system app appeared, but we already had a non-system one of the
- * same name installed earlier.
- */
- boolean shouldHideSystemApp = false;
- // A new application appeared on /system, but, we already have a copy of
- // the application installed on /data.
- if (scanSystemPartition && !isSystemPkgUpdated && pkgAlreadyExists
- && !pkgSetting.isSystem()) {
-
- if (!parsedPackage.getSigningDetails()
- .checkCapability(pkgSetting.getSigningDetails(),
- SigningDetails.CertCapabilities.INSTALLED_DATA)
- && !pkgSetting.getSigningDetails().checkCapability(
- parsedPackage.getSigningDetails(),
- SigningDetails.CertCapabilities.ROLLBACK)) {
- logCriticalInfo(Log.WARN,
- "System package signature mismatch;"
- + " name: " + pkgSetting.getPackageName());
- try (@SuppressWarnings("unused") PackageFreezer freezer = mPm.freezePackage(
- parsedPackage.getPackageName(),
- "scanPackageInternalLI")) {
- DeletePackageHelper deletePackageHelper = new DeletePackageHelper(mPm);
- deletePackageHelper.deletePackageLIF(parsedPackage.getPackageName(), null, true,
- mPm.mUserManager.getUserIds(), 0, null, false);
- }
- pkgSetting = null;
- } else if (newPkgVersionGreater) {
- // The application on /system is newer than the application on /data.
- // Simply remove the application on /data [keeping application data]
- // and replace it with the version on /system.
- logCriticalInfo(Log.WARN,
- "System package enabled;"
- + " name: " + pkgSetting.getPackageName()
- + "; " + pkgSetting.getVersionCode() + " --> "
- + parsedPackage.getLongVersionCode()
- + "; " + pkgSetting.getPathString() + " --> "
- + parsedPackage.getPath());
- InstallArgs args = new FileInstallArgs(
- pkgSetting.getPathString(), getAppDexInstructionSets(
- pkgSetting.getPrimaryCpuAbi(), pkgSetting.getSecondaryCpuAbi()),
- mPm);
- synchronized (mPm.mInstallLock) {
- args.cleanUpResourcesLI();
- }
- } else {
- // The application on /system is older than the application on /data. Hide
- // the application on /system and the version on /data will be scanned later
- // and re-added like an update.
- shouldHideSystemApp = true;
- logCriticalInfo(Log.INFO,
- "System package disabled;"
- + " name: " + pkgSetting.getPackageName()
- + "; old: " + pkgSetting.getPathString() + " @ "
- + pkgSetting.getVersionCode()
- + "; new: " + parsedPackage.getPath() + " @ "
- + parsedPackage.getPath());
- }
- }
-
- final ScanResult scanResult = scanPackageNewLI(parsedPackage, parseFlags,
- scanFlags | SCAN_UPDATE_SIGNATURE, currentTime, user, null);
- return new Pair<>(scanResult, shouldHideSystemApp);
- }
-
- /**
- * Returns if forced apk verification can be skipped for the whole package, including splits.
- */
- private boolean canSkipForcedPackageVerification(AndroidPackage pkg) {
- final String packageName = pkg.getPackageName();
- if (!canSkipForcedApkVerification(packageName, pkg.getBaseApkPath())) {
- return false;
- }
- // TODO: Allow base and splits to be verified individually.
- String[] splitCodePaths = pkg.getSplitCodePaths();
- if (!ArrayUtils.isEmpty(splitCodePaths)) {
- for (int i = 0; i < splitCodePaths.length; i++) {
- if (!canSkipForcedApkVerification(packageName, splitCodePaths[i])) {
- return false;
- }
- }
- }
- return true;
- }
-
- /**
- * Returns if forced apk verification can be skipped, depending on current FSVerity setup and
- * whether the apk contains signed root hash. Note that the signer's certificate still needs to
- * match one in a trusted source, and should be done separately.
- */
- private boolean canSkipForcedApkVerification(String packageName, String apkPath) {
- if (!PackageManagerServiceUtils.isLegacyApkVerityEnabled()) {
- return VerityUtils.hasFsverity(apkPath);
- }
-
- try {
- final byte[] rootHashObserved = VerityUtils.generateApkVerityRootHash(apkPath);
- if (rootHashObserved == null) {
- return false; // APK does not contain Merkle tree root hash.
- }
- synchronized (mPm.mInstallLock) {
- // Returns whether the observed root hash matches what kernel has.
- mPm.mInstaller.assertFsverityRootHashMatches(packageName, apkPath,
- rootHashObserved);
- return true;
- }
- } catch (Installer.InstallerException | IOException | DigestException
- | NoSuchAlgorithmException e) {
- Slog.w(TAG, "Error in fsverity check. Fallback to full apk verification.", e);
- }
- return false;
- }
-
- private void collectCertificatesLI(PackageSetting ps, ParsedPackage parsedPackage,
- boolean forceCollect, boolean skipVerify) throws PackageManagerException {
- // When upgrading from pre-N MR1, verify the package time stamp using the package
- // directory and not the APK file.
- final long lastModifiedTime = mPm.isPreNMR1Upgrade()
- ? new File(parsedPackage.getPath()).lastModified()
- : getLastModifiedTime(parsedPackage);
- final Settings.VersionInfo settingsVersionForPackage =
- mPm.getSettingsVersionForPackage(parsedPackage);
- if (ps != null && !forceCollect
- && ps.getPathString().equals(parsedPackage.getPath())
- && ps.getLastModifiedTime() == lastModifiedTime
- && !InstallPackageHelper.isCompatSignatureUpdateNeeded(settingsVersionForPackage)
- && !InstallPackageHelper.isRecoverSignatureUpdateNeeded(
- settingsVersionForPackage)) {
- if (ps.getSigningDetails().getSignatures() != null
- && ps.getSigningDetails().getSignatures().length != 0
- && ps.getSigningDetails().getSignatureSchemeVersion()
- != SigningDetails.SignatureSchemeVersion.UNKNOWN) {
- // Optimization: reuse the existing cached signing data
- // if the package appears to be unchanged.
- parsedPackage.setSigningDetails(
- new SigningDetails(ps.getSigningDetails()));
- return;
- }
-
- Slog.w(TAG, "PackageSetting for " + ps.getPackageName()
- + " is missing signatures. Collecting certs again to recover them.");
- } else {
- Slog.i(TAG, parsedPackage.getPath() + " changed; collecting certs"
- + (forceCollect ? " (forced)" : ""));
- }
-
- try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
- final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
- final ParseResult<SigningDetails> result = ParsingPackageUtils.getSigningDetails(
- input, parsedPackage, skipVerify);
- if (result.isError()) {
- throw new PackageManagerException(
- result.getErrorCode(), result.getErrorMessage(), result.getException());
- }
- parsedPackage.setSigningDetails(result.getResult());
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
- }
-
- /**
- * Clear the package profile if this was an upgrade and the package
- * version was updated.
- */
- private void maybeClearProfilesForUpgradesLI(
- @Nullable PackageSetting originalPkgSetting,
- @NonNull AndroidPackage pkg) {
- if (originalPkgSetting == null || !mPm.isDeviceUpgrading()) {
- return;
- }
- if (originalPkgSetting.getVersionCode() == pkg.getLongVersionCode()) {
- return;
- }
-
- final AppDataHelper appDataHelper = new AppDataHelper(mPm);
- appDataHelper.clearAppProfilesLIF(pkg);
- if (DEBUG_INSTALL) {
- Slog.d(TAG, originalPkgSetting.getPackageName()
- + " clear profile due to version change "
- + originalPkgSetting.getVersionCode() + " != "
- + pkg.getLongVersionCode());
- }
- }
-
- /**
- * Returns the original package setting.
- * <p>A package can migrate its name during an update. In this scenario, a package
- * designates a set of names that it considers as one of its original names.
- * <p>An original package must be signed identically and it must have the same
- * shared user [if any].
- */
- @GuardedBy("mPm.mLock")
- @Nullable
- private PackageSetting getOriginalPackageLocked(@NonNull AndroidPackage pkg,
- @Nullable String renamedPkgName) {
- if (isPackageRenamed(pkg, renamedPkgName)) {
- return null;
- }
- for (int i = ArrayUtils.size(pkg.getOriginalPackages()) - 1; i >= 0; --i) {
- final PackageSetting originalPs =
- mPm.mSettings.getPackageLPr(pkg.getOriginalPackages().get(i));
- if (originalPs != null) {
- // the package is already installed under its original name...
- // but, should we use it?
- if (!verifyPackageUpdateLPr(originalPs, pkg)) {
- // the new package is incompatible with the original
- continue;
- } else if (originalPs.getSharedUser() != null) {
- if (!originalPs.getSharedUser().name.equals(pkg.getSharedUserId())) {
- // the shared user id is incompatible with the original
- Slog.w(TAG, "Unable to migrate data from " + originalPs.getPackageName()
- + " to " + pkg.getPackageName() + ": old uid "
- + originalPs.getSharedUser().name
- + " differs from " + pkg.getSharedUserId());
- continue;
- }
- // TODO: Add case when shared user id is added [b/28144775]
- } else {
- if (DEBUG_UPGRADE) {
- Log.v(TAG, "Renaming new package "
- + pkg.getPackageName() + " to old name "
- + originalPs.getPackageName());
- }
- }
- return originalPs;
- }
- }
- return null;
- }
-
- @GuardedBy("mPm.mLock")
- private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, AndroidPackage newPkg) {
- if ((oldPkg.getFlags() & ApplicationInfo.FLAG_SYSTEM) == 0) {
- Slog.w(TAG, "Unable to update from " + oldPkg.getPackageName()
- + " to " + newPkg.getPackageName()
- + ": old package not in system partition");
- return false;
- } else if (mPm.mPackages.get(oldPkg.getPackageName()) != null) {
- Slog.w(TAG, "Unable to update from " + oldPkg.getPackageName()
- + " to " + newPkg.getPackageName()
- + ": old package still exists");
- return false;
- }
- return true;
- }
-
- /**
- * Asserts the parsed package is valid according to the given policy. If the
- * package is invalid, for whatever reason, throws {@link PackageManagerException}.
- * <p>
- * Implementation detail: This method must NOT have any side effects. It would
- * ideally be static, but, it requires locks to read system state.
- *
- * @throws PackageManagerException If the package fails any of the validation checks
- */
- private void assertPackageIsValid(AndroidPackage pkg,
- final @ParsingPackageUtils.ParseFlags int parseFlags,
- final @PackageManagerService.ScanFlags int scanFlags)
- throws PackageManagerException {
- if ((parseFlags & ParsingPackageUtils.PARSE_ENFORCE_CODE) != 0) {
- assertCodePolicy(pkg);
- }
-
- if (pkg.getPath() == null) {
- // Bail out. The resource and code paths haven't been set.
- throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
- "Code and resource paths haven't been set correctly");
- }
-
- // Check that there is an APEX package with the same name only during install/first boot
- // after OTA.
- final boolean isUserInstall = (scanFlags & SCAN_BOOTING) == 0;
- final boolean isFirstBootOrUpgrade = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;
- if ((isUserInstall || isFirstBootOrUpgrade)
- && mPm.mApexManager.isApexPackage(pkg.getPackageName())) {
- throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
- pkg.getPackageName()
- + " is an APEX package and can't be installed as an APK.");
- }
-
- // Make sure we're not adding any bogus keyset info
- final KeySetManagerService ksms = mPm.mSettings.getKeySetManagerService();
- ksms.assertScannedPackageValid(pkg);
-
- synchronized (mPm.mLock) {
- // The special "android" package can only be defined once
- if (pkg.getPackageName().equals("android")) {
- if (mPm.getCoreAndroidApplication() != null) {
- Slog.w(TAG, "*************************************************");
- Slog.w(TAG, "Core android package being redefined. Skipping.");
- Slog.w(TAG, " codePath=" + pkg.getPath());
- Slog.w(TAG, "*************************************************");
- throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
- "Core android package being redefined. Skipping.");
- }
- }
-
- // A package name must be unique; don't allow duplicates
- if ((scanFlags & SCAN_NEW_INSTALL) == 0
- && mPm.mPackages.containsKey(pkg.getPackageName())) {
- throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
- "Application package " + pkg.getPackageName()
- + " already installed. Skipping duplicate.");
- }
-
- if (pkg.isStaticSharedLibrary()) {
- // Static libs have a synthetic package name containing the version
- // but we still want the base name to be unique.
- if ((scanFlags & SCAN_NEW_INSTALL) == 0
- && mPm.mPackages.containsKey(pkg.getManifestPackageName())) {
- throw new PackageManagerException(
- "Duplicate static shared lib provider package");
- }
-
- // Static shared libraries should have at least O target SDK
- if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.O) {
- throw new PackageManagerException(
- "Packages declaring static-shared libs must target O SDK or higher");
- }
-
- // Package declaring static a shared lib cannot be instant apps
- if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
- throw new PackageManagerException(
- "Packages declaring static-shared libs cannot be instant apps");
- }
-
- // Package declaring static a shared lib cannot be renamed since the package
- // name is synthetic and apps can't code around package manager internals.
- if (!ArrayUtils.isEmpty(pkg.getOriginalPackages())) {
- throw new PackageManagerException(
- "Packages declaring static-shared libs cannot be renamed");
- }
-
- // Package declaring static a shared lib cannot declare dynamic libs
- if (!ArrayUtils.isEmpty(pkg.getLibraryNames())) {
- throw new PackageManagerException(
- "Packages declaring static-shared libs cannot declare dynamic libs");
- }
-
- // Package declaring static a shared lib cannot declare shared users
- if (pkg.getSharedUserId() != null) {
- throw new PackageManagerException(
- "Packages declaring static-shared libs cannot declare shared users");
- }
-
- // Static shared libs cannot declare activities
- if (!pkg.getActivities().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare activities");
- }
-
- // Static shared libs cannot declare services
- if (!pkg.getServices().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare services");
- }
-
- // Static shared libs cannot declare providers
- if (!pkg.getProviders().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare content providers");
- }
-
- // Static shared libs cannot declare receivers
- if (!pkg.getReceivers().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare broadcast receivers");
- }
-
- // Static shared libs cannot declare permission groups
- if (!pkg.getPermissionGroups().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare permission groups");
- }
-
- // Static shared libs cannot declare attributions
- if (!pkg.getAttributions().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare features");
- }
-
- // Static shared libs cannot declare permissions
- if (!pkg.getPermissions().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare permissions");
- }
-
- // Static shared libs cannot declare protected broadcasts
- if (!pkg.getProtectedBroadcasts().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare protected broadcasts");
- }
-
- // Static shared libs cannot be overlay targets
- if (pkg.getOverlayTarget() != null) {
- throw new PackageManagerException(
- "Static shared libs cannot be overlay targets");
- }
-
- // The version codes must be ordered as lib versions
- long minVersionCode = Long.MIN_VALUE;
- long maxVersionCode = Long.MAX_VALUE;
-
- WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mPm.mSharedLibraries.get(
- pkg.getStaticSharedLibName());
- if (versionedLib != null) {
- final int versionCount = versionedLib.size();
- for (int i = 0; i < versionCount; i++) {
- SharedLibraryInfo libInfo = versionedLib.valueAt(i);
- final long libVersionCode = libInfo.getDeclaringPackage()
- .getLongVersionCode();
- if (libInfo.getLongVersion() < pkg.getStaticSharedLibVersion()) {
- minVersionCode = Math.max(minVersionCode, libVersionCode + 1);
- } else if (libInfo.getLongVersion()
- > pkg.getStaticSharedLibVersion()) {
- maxVersionCode = Math.min(maxVersionCode, libVersionCode - 1);
- } else {
- minVersionCode = maxVersionCode = libVersionCode;
- break;
- }
- }
- }
- if (pkg.getLongVersionCode() < minVersionCode
- || pkg.getLongVersionCode() > maxVersionCode) {
- throw new PackageManagerException("Static shared"
- + " lib version codes must be ordered as lib versions");
- }
- }
-
- // If we're only installing presumed-existing packages, require that the
- // scanned APK is both already known and at the path previously established
- // for it. Previously unknown packages we pick up normally, but if we have an
- // a priori expectation about this package's install presence, enforce it.
- // With a singular exception for new system packages. When an OTA contains
- // a new system package, we allow the codepath to change from a system location
- // to the user-installed location. If we don't allow this change, any newer,
- // user-installed version of the application will be ignored.
- if ((scanFlags & SCAN_REQUIRE_KNOWN) != 0) {
- if (mPm.isExpectingBetter(pkg.getPackageName())) {
- Slog.w(TAG, "Relax SCAN_REQUIRE_KNOWN requirement for package "
- + pkg.getPackageName());
- } else {
- PackageSetting known = mPm.mSettings.getPackageLPr(pkg.getPackageName());
- if (known != null) {
- if (DEBUG_PACKAGE_SCANNING) {
- Log.d(TAG, "Examining " + pkg.getPath()
- + " and requiring known path " + known.getPathString());
- }
- if (!pkg.getPath().equals(known.getPathString())) {
- throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,
- "Application package " + pkg.getPackageName()
- + " found at " + pkg.getPath()
- + " but expected at " + known.getPathString()
- + "; ignoring.");
- }
- } else {
- throw new PackageManagerException(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
- "Application package " + pkg.getPackageName()
- + " not found; ignoring.");
- }
- }
- }
-
- // Verify that this new package doesn't have any content providers
- // that conflict with existing packages. Only do this if the
- // package isn't already installed, since we don't want to break
- // things that are installed.
- if ((scanFlags & SCAN_NEW_INSTALL) != 0) {
- mPm.mComponentResolver.assertProvidersNotDefined(pkg);
- }
-
- // If this package has defined explicit processes, then ensure that these are
- // the only processes used by its components.
- final Map<String, ParsedProcess> procs = pkg.getProcesses();
- if (!procs.isEmpty()) {
- if (!procs.containsKey(pkg.getProcessName())) {
- throw new PackageManagerException(
- INSTALL_FAILED_PROCESS_NOT_DEFINED,
- "Can't install because application tag's process attribute "
- + pkg.getProcessName()
- + " (in package " + pkg.getPackageName()
- + ") is not included in the <processes> list");
- }
- assertPackageProcesses(pkg, pkg.getActivities(), procs, "activity");
- assertPackageProcesses(pkg, pkg.getServices(), procs, "service");
- assertPackageProcesses(pkg, pkg.getReceivers(), procs, "receiver");
- assertPackageProcesses(pkg, pkg.getProviders(), procs, "provider");
- }
-
- // Verify that packages sharing a user with a privileged app are marked as privileged.
- if (!pkg.isPrivileged() && (pkg.getSharedUserId() != null)) {
- SharedUserSetting sharedUserSetting = null;
- try {
- sharedUserSetting = mPm.mSettings.getSharedUserLPw(pkg.getSharedUserId(),
- 0, 0, false);
- } catch (PackageManagerException ignore) {
- }
- if (sharedUserSetting != null && sharedUserSetting.isPrivileged()) {
- // Exempt SharedUsers signed with the platform key.
- PackageSetting platformPkgSetting = mPm.mSettings.getPackageLPr("android");
- if (!comparePackageSignatures(platformPkgSetting,
- pkg.getSigningDetails().getSignatures())) {
- throw new PackageManagerException("Apps that share a user with a "
- + "privileged app must themselves be marked as privileged. "
- + pkg.getPackageName() + " shares privileged user "
- + pkg.getSharedUserId() + ".");
- }
- }
- }
-
- // Apply policies specific for runtime resource overlays (RROs).
- if (pkg.getOverlayTarget() != null) {
- // System overlays have some restrictions on their use of the 'static' state.
- if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
- // We are scanning a system overlay. This can be the first scan of the
- // system/vendor/oem partition, or an update to the system overlay.
- if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
- // This must be an update to a system overlay. Immutable overlays cannot be
- // upgraded.
- if (!mPm.isOverlayMutable(pkg.getPackageName())) {
- throw new PackageManagerException("Overlay "
- + pkg.getPackageName()
- + " is static and cannot be upgraded.");
- }
- } else {
- if ((scanFlags & SCAN_AS_VENDOR) != 0) {
- if (pkg.getTargetSdkVersion() < getVendorPartitionVersion()) {
- Slog.w(TAG, "System overlay " + pkg.getPackageName()
- + " targets an SDK below the required SDK level of vendor"
- + " overlays (" + getVendorPartitionVersion() + ")."
- + " This will become an install error in a future release");
- }
- } else if (pkg.getTargetSdkVersion() < Build.VERSION.SDK_INT) {
- Slog.w(TAG, "System overlay " + pkg.getPackageName()
- + " targets an SDK below the required SDK level of system"
- + " overlays (" + Build.VERSION.SDK_INT + ")."
- + " This will become an install error in a future release");
- }
- }
- } else {
- // A non-preloaded overlay packages must have targetSdkVersion >= Q, or be
- // signed with the platform certificate. Check this in increasing order of
- // computational cost.
- if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.Q) {
- final PackageSetting platformPkgSetting =
- mPm.mSettings.getPackageLPr("android");
- if (!comparePackageSignatures(platformPkgSetting,
- pkg.getSigningDetails().getSignatures())) {
- throw new PackageManagerException("Overlay "
- + pkg.getPackageName()
- + " must target Q or later, "
- + "or be signed with the platform certificate");
- }
- }
-
- // A non-preloaded overlay package, without <overlay android:targetName>, will
- // only be used if it is signed with the same certificate as its target OR if
- // it is signed with the same certificate as a reference package declared
- // in 'overlay-config-signature' tag of SystemConfig.
- // If the target is already installed or 'overlay-config-signature' tag in
- // SystemConfig is set, check this here to augment the last line of defense
- // which is OMS.
- if (pkg.getOverlayTargetOverlayableName() == null) {
- final PackageSetting targetPkgSetting =
- mPm.mSettings.getPackageLPr(pkg.getOverlayTarget());
- if (targetPkgSetting != null) {
- if (!comparePackageSignatures(targetPkgSetting,
- pkg.getSigningDetails().getSignatures())) {
- // check reference signature
- if (mPm.mOverlayConfigSignaturePackage == null) {
- throw new PackageManagerException("Overlay "
- + pkg.getPackageName() + " and target "
- + pkg.getOverlayTarget() + " signed with"
- + " different certificates, and the overlay lacks"
- + " <overlay android:targetName>");
- }
- final PackageSetting refPkgSetting =
- mPm.mSettings.getPackageLPr(
- mPm.mOverlayConfigSignaturePackage);
- if (!comparePackageSignatures(refPkgSetting,
- pkg.getSigningDetails().getSignatures())) {
- throw new PackageManagerException("Overlay "
- + pkg.getPackageName() + " signed with a different "
- + "certificate than both the reference package and "
- + "target " + pkg.getOverlayTarget() + ", and the "
- + "overlay lacks <overlay android:targetName>");
- }
- }
- }
- }
- }
- }
-
- // If the package is not on a system partition ensure it is signed with at least the
- // minimum signature scheme version required for its target SDK.
- if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
- int minSignatureSchemeVersion =
- ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
- pkg.getTargetSdkVersion());
- if (pkg.getSigningDetails().getSignatureSchemeVersion()
- < minSignatureSchemeVersion) {
- throw new PackageManagerException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
- "No signature found in package of version " + minSignatureSchemeVersion
- + " or newer for package " + pkg.getPackageName());
- }
- }
- }
- }
-
- private static <T extends ParsedMainComponent> void assertPackageProcesses(AndroidPackage pkg,
- List<T> components, Map<String, ParsedProcess> procs, String compName)
- throws PackageManagerException {
- if (components == null) {
- return;
- }
- for (int i = components.size() - 1; i >= 0; i--) {
- final ParsedMainComponent component = components.get(i);
- if (!procs.containsKey(component.getProcessName())) {
- throw new PackageManagerException(
- INSTALL_FAILED_PROCESS_NOT_DEFINED,
- "Can't install because " + compName + " " + component.getClassName()
- + "'s process attribute " + component.getProcessName()
- + " (in package " + pkg.getPackageName()
- + ") is not included in the <processes> list");
- }
- }
- }
-
- /**
- * Applies the adjusted ABI calculated by
- * {@link PackageAbiHelper#getAdjustedAbiForSharedUser(Set, AndroidPackage)} to all
- * relevant packages and settings.
- * @param sharedUserSetting The {@code SharedUserSetting} to adjust
- * @param scannedPackage the package being scanned or null
- * @param adjustedAbi the adjusted ABI calculated by {@link PackageAbiHelper}
- * @return the list of code paths that belong to packages that had their ABIs adjusted.
- */
- public static List<String> applyAdjustedAbiToSharedUser(SharedUserSetting sharedUserSetting,
- ParsedPackage scannedPackage, String adjustedAbi) {
- if (scannedPackage != null) {
- scannedPackage.setPrimaryCpuAbi(adjustedAbi);
- }
- List<String> changedAbiCodePath = null;
- for (PackageSetting ps : sharedUserSetting.packages) {
- if (scannedPackage == null
- || !scannedPackage.getPackageName().equals(ps.getPackageName())) {
- if (ps.getPrimaryCpuAbi() != null) {
- continue;
- }
-
- ps.setPrimaryCpuAbi(adjustedAbi);
- if (ps.getPkg() != null) {
- if (!TextUtils.equals(adjustedAbi,
- AndroidPackageUtils.getRawPrimaryCpuAbi(ps.getPkg()))) {
- if (DEBUG_ABI_SELECTION) {
- Slog.i(TAG,
- "Adjusting ABI for " + ps.getPackageName() + " to "
- + adjustedAbi + " (scannedPackage="
- + (scannedPackage != null ? scannedPackage : "null")
- + ")");
- }
- if (changedAbiCodePath == null) {
- changedAbiCodePath = new ArrayList<>();
- }
- changedAbiCodePath.add(ps.getPathString());
- }
- }
- }
- }
- return changedAbiCodePath;
- }
-
- /**
- * Applies policy to the parsed package based upon the given policy flags.
- * Ensures the package is in a good state.
- * <p>
- * Implementation detail: This method must NOT have any side effect. It would
- * ideally be static, but, it requires locks to read system state.
- */
- private static void applyPolicy(ParsedPackage parsedPackage,
- final @PackageManagerService.ScanFlags int scanFlags, AndroidPackage platformPkg,
- boolean isUpdatedSystemApp) {
- if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
- parsedPackage.setSystem(true);
- // TODO(b/135203078): Can this be done in PackageParser? Or just inferred when the flag
- // is set during parse.
- if (parsedPackage.isDirectBootAware()) {
- parsedPackage.setAllComponentsDirectBootAware(true);
- }
- if (compressedFileExists(parsedPackage.getPath())) {
- parsedPackage.setStub(true);
- }
- } else {
- parsedPackage
- // Non system apps cannot mark any broadcast as protected
- .clearProtectedBroadcasts()
- // non system apps can't be flagged as core
- .setCoreApp(false)
- // clear flags not applicable to regular apps
- .setPersistent(false)
- .setDefaultToDeviceProtectedStorage(false)
- .setDirectBootAware(false)
- // non system apps can't have permission priority
- .capPermissionPriorities();
- }
- if ((scanFlags & SCAN_AS_PRIVILEGED) == 0) {
- parsedPackage
- .markNotActivitiesAsNotExportedIfSingleUser();
- }
-
- parsedPackage.setPrivileged((scanFlags & SCAN_AS_PRIVILEGED) != 0)
- .setOem((scanFlags & SCAN_AS_OEM) != 0)
- .setVendor((scanFlags & SCAN_AS_VENDOR) != 0)
- .setProduct((scanFlags & SCAN_AS_PRODUCT) != 0)
- .setSystemExt((scanFlags & SCAN_AS_SYSTEM_EXT) != 0)
- .setOdm((scanFlags & SCAN_AS_ODM) != 0);
-
- // Check if the package is signed with the same key as the platform package.
- parsedPackage.setSignedWithPlatformKey(
- (PLATFORM_PACKAGE_NAME.equals(parsedPackage.getPackageName())
- || (platformPkg != null && compareSignatures(
- platformPkg.getSigningDetails().getSignatures(),
- parsedPackage.getSigningDetails().getSignatures()
- ) == PackageManager.SIGNATURE_MATCH))
- );
-
- if (!parsedPackage.isSystem()) {
- // Only system apps can use these features.
- parsedPackage.clearOriginalPackages()
- .clearAdoptPermissions();
- }
-
- PackageBackwardCompatibility.modifySharedLibraries(parsedPackage, isUpdatedSystemApp);
- }
-
- /**
- * Enforces code policy for the package. This ensures that if an APK has
- * declared hasCode="true" in its manifest that the APK actually contains
- * code.
- *
- * @throws PackageManagerException If bytecode could not be found when it should exist
- */
- private static void assertCodePolicy(AndroidPackage pkg)
- throws PackageManagerException {
- final boolean shouldHaveCode = pkg.isHasCode();
- if (shouldHaveCode && !apkHasCode(pkg.getBaseApkPath())) {
- throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
- "Package " + pkg.getBaseApkPath() + " code is missing");
- }
-
- if (!ArrayUtils.isEmpty(pkg.getSplitCodePaths())) {
- for (int i = 0; i < pkg.getSplitCodePaths().length; i++) {
- final boolean splitShouldHaveCode =
- (pkg.getSplitFlags()[i] & ApplicationInfo.FLAG_HAS_CODE) != 0;
- if (splitShouldHaveCode && !apkHasCode(pkg.getSplitCodePaths()[i])) {
- throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
- "Package " + pkg.getSplitCodePaths()[i] + " code is missing");
- }
- }
- }
- }
-
- /**
- * Returns the "real" name of the package.
- * <p>This may differ from the package's actual name if the application has already
- * been installed under one of this package's original names.
- */
- private static @Nullable String getRealPackageName(@NonNull AndroidPackage pkg,
- @Nullable String renamedPkgName) {
- if (isPackageRenamed(pkg, renamedPkgName)) {
- return AndroidPackageUtils.getRealPackageOrNull(pkg);
- }
- return null;
- }
-
- /** Returns {@code true} if the package has been renamed. Otherwise, {@code false}. */
- private static boolean isPackageRenamed(@NonNull AndroidPackage pkg,
- @Nullable String renamedPkgName) {
- return pkg.getOriginalPackages().contains(renamedPkgName);
- }
-
- /**
- * Renames the package if it was installed under a different name.
- * <p>When we've already installed the package under an original name, update
- * the new package so we can continue to have the old name.
- */
- private static void ensurePackageRenamed(@NonNull ParsedPackage parsedPackage,
- @NonNull String renamedPackageName) {
- if (!parsedPackage.getOriginalPackages().contains(renamedPackageName)
- || parsedPackage.getPackageName().equals(renamedPackageName)) {
- return;
- }
- parsedPackage.setPackageName(renamedPackageName);
- }
-
- /**
- * Returns {@code true} if the given file contains code. Otherwise {@code false}.
- */
- private static boolean apkHasCode(String fileName) {
- StrictJarFile jarFile = null;
- try {
- jarFile = new StrictJarFile(fileName,
- false /*verify*/, false /*signatureSchemeRollbackProtectionsEnforced*/);
- return jarFile.findEntry("classes.dex") != null;
- } catch (IOException ignore) {
- } finally {
- try {
- if (jarFile != null) {
- jarFile.close();
- }
- } catch (IOException ignore) {
- }
- }
- return false;
- }
-
- /**
- * Sets the enabled state of components configured through {@link SystemConfig}.
- * This modifies the {@link PackageSetting} object.
- *
- * TODO(b/135203078): Move this to package parsing
- **/
- private static void configurePackageComponents(AndroidPackage pkg) {
- final ArrayMap<String, Boolean> componentsEnabledStates = SystemConfig.getInstance()
- .getComponentsEnabledStates(pkg.getPackageName());
- if (componentsEnabledStates == null) {
- return;
- }
-
- for (int i = ArrayUtils.size(pkg.getActivities()) - 1; i >= 0; i--) {
- final ParsedActivity component = pkg.getActivities().get(i);
- final Boolean enabled = componentsEnabledStates.get(component.getName());
- if (enabled != null) {
- ComponentMutateUtils.setEnabled(component, enabled);
- }
- }
-
- for (int i = ArrayUtils.size(pkg.getReceivers()) - 1; i >= 0; i--) {
- final ParsedActivity component = pkg.getReceivers().get(i);
- final Boolean enabled = componentsEnabledStates.get(component.getName());
- if (enabled != null) {
- ComponentMutateUtils.setEnabled(component, enabled);
- }
- }
-
- for (int i = ArrayUtils.size(pkg.getProviders()) - 1; i >= 0; i--) {
- final ParsedProvider component = pkg.getProviders().get(i);
- final Boolean enabled = componentsEnabledStates.get(component.getName());
- if (enabled != null) {
- ComponentMutateUtils.setEnabled(component, enabled);
- }
- }
-
- for (int i = ArrayUtils.size(pkg.getServices()) - 1; i >= 0; i--) {
- final ParsedService component = pkg.getServices().get(i);
- final Boolean enabled = componentsEnabledStates.get(component.getName());
- if (enabled != null) {
- ComponentMutateUtils.setEnabled(component, enabled);
- }
- }
- }
-
- private static int getVendorPartitionVersion() {
- final String version = SystemProperties.get("ro.vndk.version");
- if (!version.isEmpty()) {
- try {
- return Integer.parseInt(version);
- } catch (NumberFormatException ignore) {
- if (ArrayUtils.contains(Build.VERSION.ACTIVE_CODENAMES, version)) {
- return Build.VERSION_CODES.CUR_DEVELOPMENT;
- }
- }
- }
- return Build.VERSION_CODES.P;
- }
-}
diff --git a/services/core/java/com/android/server/pm/ScanPackageUtils.java b/services/core/java/com/android/server/pm/ScanPackageUtils.java
new file mode 100644
index 000000000000..378c9e07566e
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ScanPackageUtils.java
@@ -0,0 +1,1006 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
+import static android.content.pm.PackageManager.INSTALL_FAILED_PROCESS_NOT_DEFINED;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
+import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+
+import static com.android.server.pm.PackageManagerService.DEBUG_ABI_SELECTION;
+import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING;
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_FULL_APP;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_INSTANT_APP;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_ODM;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_OEM;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_PRIVILEGED;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_PRODUCT;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM_EXT;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_VENDOR;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_VIRTUAL_PRELOAD;
+import static com.android.server.pm.PackageManagerService.SCAN_BOOTING;
+import static com.android.server.pm.PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE;
+import static com.android.server.pm.PackageManagerService.SCAN_MOVE;
+import static com.android.server.pm.PackageManagerService.SCAN_NEW_INSTALL;
+import static com.android.server.pm.PackageManagerService.SCAN_NO_DEX;
+import static com.android.server.pm.PackageManagerService.SCAN_UPDATE_TIME;
+import static com.android.server.pm.PackageManagerService.TAG;
+import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
+import static com.android.server.pm.PackageManagerServiceUtils.compressedFileExists;
+import static com.android.server.pm.PackageManagerServiceUtils.deriveAbiOverride;
+import static com.android.server.pm.PackageManagerServiceUtils.getLastModifiedTime;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.SharedLibraryInfo;
+import android.content.pm.SigningDetails;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.component.ComponentMutateUtils;
+import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedMainComponent;
+import android.content.pm.parsing.component.ParsedProcess;
+import android.content.pm.parsing.component.ParsedProvider;
+import android.content.pm.parsing.component.ParsedService;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
+import android.os.Build;
+import android.os.Environment;
+import android.os.Process;
+import android.os.SystemProperties;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.apk.ApkSignatureVerifier;
+import android.util.jar.StrictJarFile;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+import com.android.server.SystemConfig;
+import com.android.server.pm.parsing.PackageInfoUtils;
+import com.android.server.pm.parsing.library.PackageBackwardCompatibility;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+
+import dalvik.system.VMRuntime;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Helper class that handles package scanning logic
+ */
+final class ScanPackageUtils {
+ /**
+ * Just scans the package without any side effects.
+ *
+ * @param injector injector for acquiring dependencies
+ * @param request Information about the package to be scanned
+ * @param isUnderFactoryTest Whether or not the device is under factory test
+ * @param currentTime The current time, in millis
+ * @return The results of the scan
+ */
+ @GuardedBy("mPm.mInstallLock")
+ @VisibleForTesting
+ @NonNull
+ public static ScanResult scanPackageOnlyLI(@NonNull ScanRequest request,
+ PackageManagerServiceInjector injector,
+ boolean isUnderFactoryTest, long currentTime)
+ throws PackageManagerException {
+ final PackageAbiHelper packageAbiHelper = injector.getAbiHelper();
+ ParsedPackage parsedPackage = request.mParsedPackage;
+ PackageSetting pkgSetting = request.mPkgSetting;
+ final PackageSetting disabledPkgSetting = request.mDisabledPkgSetting;
+ final PackageSetting originalPkgSetting = request.mOriginalPkgSetting;
+ final @ParsingPackageUtils.ParseFlags int parseFlags = request.mParseFlags;
+ final @PackageManagerService.ScanFlags int scanFlags = request.mScanFlags;
+ final String realPkgName = request.mRealPkgName;
+ final SharedUserSetting sharedUserSetting = request.mSharedUserSetting;
+ final UserHandle user = request.mUser;
+ final boolean isPlatformPackage = request.mIsPlatformPackage;
+
+ List<String> changedAbiCodePath = null;
+
+ if (DEBUG_PACKAGE_SCANNING) {
+ if ((parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0) {
+ Log.d(TAG, "Scanning package " + parsedPackage.getPackageName());
+ }
+ }
+
+ // Initialize package source and resource directories
+ final File destCodeFile = new File(parsedPackage.getPath());
+
+ // We keep references to the derived CPU Abis from settings in oder to reuse
+ // them in the case where we're not upgrading or booting for the first time.
+ String primaryCpuAbiFromSettings = null;
+ String secondaryCpuAbiFromSettings = null;
+ boolean needToDeriveAbi = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;
+ if (!needToDeriveAbi) {
+ if (pkgSetting != null) {
+ // TODO(b/154610922): if it is not first boot or upgrade, we should directly use
+ // API info from existing package setting. However, stub packages currently do not
+ // preserve ABI info, thus the special condition check here. Remove the special
+ // check after we fix the stub generation.
+ if (pkgSetting.getPkg() != null && pkgSetting.getPkg().isStub()) {
+ needToDeriveAbi = true;
+ } else {
+ primaryCpuAbiFromSettings = pkgSetting.getPrimaryCpuAbi();
+ secondaryCpuAbiFromSettings = pkgSetting.getSecondaryCpuAbi();
+ }
+ } else {
+ // Re-scanning a system package after uninstalling updates; need to derive ABI
+ needToDeriveAbi = true;
+ }
+ }
+
+ int previousAppId = Process.INVALID_UID;
+
+ if (pkgSetting != null && pkgSetting.getSharedUser() != sharedUserSetting) {
+ if (pkgSetting.getSharedUser() != null && sharedUserSetting == null) {
+ previousAppId = pkgSetting.getAppId();
+ // Log that something is leaving shareduid and keep going
+ Slog.i(TAG,
+ "Package " + parsedPackage.getPackageName() + " shared user changed from "
+ + pkgSetting.getSharedUser().name + " to " + "<nothing>.");
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Package " + parsedPackage.getPackageName() + " shared user changed from "
+ + (pkgSetting.getSharedUser() != null
+ ? pkgSetting.getSharedUser().name : "<nothing>")
+ + " to "
+ + (sharedUserSetting != null ? sharedUserSetting.name : "<nothing>")
+ + "; replacing with new");
+ pkgSetting = null;
+ }
+ }
+
+ String[] usesSdkLibraries = null;
+ if (!parsedPackage.getUsesSdkLibraries().isEmpty()) {
+ usesSdkLibraries = new String[parsedPackage.getUsesSdkLibraries().size()];
+ parsedPackage.getUsesSdkLibraries().toArray(usesSdkLibraries);
+ }
+
+ String[] usesStaticLibraries = null;
+ if (!parsedPackage.getUsesStaticLibraries().isEmpty()) {
+ usesStaticLibraries = new String[parsedPackage.getUsesStaticLibraries().size()];
+ parsedPackage.getUsesStaticLibraries().toArray(usesStaticLibraries);
+ }
+
+ final UUID newDomainSetId = injector.getDomainVerificationManagerInternal().generateNewId();
+
+ // TODO(b/135203078): Remove appInfoFlag usage in favor of individually assigned booleans
+ // to avoid adding something that's unsupported due to lack of state, since it's called
+ // with null.
+ final boolean createNewPackage = (pkgSetting == null);
+ if (createNewPackage) {
+ final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
+ final boolean virtualPreload = (scanFlags & SCAN_AS_VIRTUAL_PRELOAD) != 0;
+
+ // Flags contain system values stored in the server variant of AndroidPackage,
+ // and so the server-side PackageInfoUtils is still called, even without a
+ // PackageSetting to pass in.
+ int pkgFlags = PackageInfoUtils.appInfoFlags(parsedPackage, null);
+ int pkgPrivateFlags = PackageInfoUtils.appInfoPrivateFlags(parsedPackage, null);
+
+ // REMOVE SharedUserSetting from method; update in a separate call
+ pkgSetting = Settings.createNewSetting(parsedPackage.getPackageName(),
+ originalPkgSetting, disabledPkgSetting, realPkgName, sharedUserSetting,
+ destCodeFile, parsedPackage.getNativeLibraryRootDir(),
+ AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage),
+ AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage),
+ parsedPackage.getLongVersionCode(), pkgFlags, pkgPrivateFlags, user,
+ true /*allowInstall*/, instantApp, virtualPreload,
+ UserManagerService.getInstance(), usesSdkLibraries,
+ parsedPackage.getUsesSdkLibrariesVersionsMajor(), usesStaticLibraries,
+ parsedPackage.getUsesStaticLibrariesVersions(), parsedPackage.getMimeGroups(),
+ newDomainSetId);
+ } else {
+ // make a deep copy to avoid modifying any existing system state.
+ pkgSetting = new PackageSetting(pkgSetting);
+ pkgSetting.setPkg(parsedPackage);
+
+ // REMOVE SharedUserSetting from method; update in a separate call.
+ //
+ // TODO(narayan): This update is bogus. nativeLibraryDir & primaryCpuAbi,
+ // secondaryCpuAbi are not known at this point so we always update them
+ // to null here, only to reset them at a later point.
+ Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, sharedUserSetting,
+ destCodeFile, parsedPackage.getNativeLibraryDir(),
+ AndroidPackageUtils.getPrimaryCpuAbi(parsedPackage, pkgSetting),
+ AndroidPackageUtils.getSecondaryCpuAbi(parsedPackage, pkgSetting),
+ PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting),
+ PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting),
+ UserManagerService.getInstance(),
+ usesSdkLibraries, parsedPackage.getUsesSdkLibrariesVersionsMajor(),
+ usesStaticLibraries, parsedPackage.getUsesStaticLibrariesVersions(),
+ parsedPackage.getMimeGroups(), newDomainSetId);
+ }
+ if (createNewPackage && originalPkgSetting != null) {
+ // This is the initial transition from the original package, so,
+ // fix up the new package's name now. We must do this after looking
+ // up the package under its new name, so getPackageLP takes care of
+ // fiddling things correctly.
+ parsedPackage.setPackageName(originalPkgSetting.getPackageName());
+
+ // File a report about this.
+ String msg = "New package " + pkgSetting.getRealName()
+ + " renamed to replace old package " + pkgSetting.getPackageName();
+ PackageManagerService.reportSettingsProblem(Log.WARN, msg);
+ }
+
+ final int userId = (user == null ? UserHandle.USER_SYSTEM : user.getIdentifier());
+ // for existing packages, change the install state; but, only if it's explicitly specified
+ if (!createNewPackage) {
+ final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
+ final boolean fullApp = (scanFlags & SCAN_AS_FULL_APP) != 0;
+ setInstantAppForUser(injector, pkgSetting, userId, instantApp, fullApp);
+ }
+ // TODO(patb): see if we can do away with disabled check here.
+ if (disabledPkgSetting != null
+ || (0 != (scanFlags & SCAN_NEW_INSTALL)
+ && pkgSetting != null && pkgSetting.isSystem())) {
+ pkgSetting.getPkgState().setUpdatedSystemApp(true);
+ }
+
+ parsedPackage.setSeInfo(SELinuxMMAC.getSeInfo(parsedPackage, sharedUserSetting,
+ injector.getCompatibility()));
+
+ if (parsedPackage.isSystem()) {
+ configurePackageComponents(parsedPackage);
+ }
+
+ final String cpuAbiOverride = deriveAbiOverride(request.mCpuAbiOverride);
+ final boolean isUpdatedSystemApp = pkgSetting.getPkgState().isUpdatedSystemApp();
+
+ final File appLib32InstallDir = getAppLib32InstallDir();
+ if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
+ if (needToDeriveAbi) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi");
+ final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths> derivedAbi =
+ packageAbiHelper.derivePackageAbi(parsedPackage, isUpdatedSystemApp,
+ cpuAbiOverride, appLib32InstallDir);
+ derivedAbi.first.applyTo(parsedPackage);
+ derivedAbi.second.applyTo(parsedPackage);
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+
+ // Some system apps still use directory structure for native libraries
+ // in which case we might end up not detecting abi solely based on apk
+ // structure. Try to detect abi based on directory structure.
+
+ String pkgRawPrimaryCpuAbi = AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage);
+ if (parsedPackage.isSystem() && !isUpdatedSystemApp
+ && pkgRawPrimaryCpuAbi == null) {
+ final PackageAbiHelper.Abis abis = packageAbiHelper.getBundledAppAbis(
+ parsedPackage);
+ abis.applyTo(parsedPackage);
+ abis.applyTo(pkgSetting);
+ final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
+ packageAbiHelper.deriveNativeLibraryPaths(parsedPackage,
+ isUpdatedSystemApp, appLib32InstallDir);
+ nativeLibraryPaths.applyTo(parsedPackage);
+ }
+ } else {
+ // This is not a first boot or an upgrade, don't bother deriving the
+ // ABI during the scan. Instead, trust the value that was stored in the
+ // package setting.
+ parsedPackage.setPrimaryCpuAbi(primaryCpuAbiFromSettings)
+ .setSecondaryCpuAbi(secondaryCpuAbiFromSettings);
+
+ final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
+ packageAbiHelper.deriveNativeLibraryPaths(parsedPackage,
+ isUpdatedSystemApp, appLib32InstallDir);
+ nativeLibraryPaths.applyTo(parsedPackage);
+
+ if (DEBUG_ABI_SELECTION) {
+ Slog.i(TAG, "Using ABIS and native lib paths from settings : "
+ + parsedPackage.getPackageName() + " "
+ + AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage)
+ + ", "
+ + AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage));
+ }
+ }
+ } else {
+ if ((scanFlags & SCAN_MOVE) != 0) {
+ // We haven't run dex-opt for this move (since we've moved the compiled output too)
+ // but we already have this packages package info in the PackageSetting. We just
+ // use that and derive the native library path based on the new code path.
+ parsedPackage.setPrimaryCpuAbi(pkgSetting.getPrimaryCpuAbi())
+ .setSecondaryCpuAbi(pkgSetting.getSecondaryCpuAbi());
+ }
+
+ // Set native library paths again. For moves, the path will be updated based on the
+ // ABIs we've determined above. For non-moves, the path will be updated based on the
+ // ABIs we determined during compilation, but the path will depend on the final
+ // package path (after the rename away from the stage path).
+ final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
+ packageAbiHelper.deriveNativeLibraryPaths(parsedPackage, isUpdatedSystemApp,
+ appLib32InstallDir);
+ nativeLibraryPaths.applyTo(parsedPackage);
+ }
+
+ // This is a special case for the "system" package, where the ABI is
+ // dictated by the zygote configuration (and init.rc). We should keep track
+ // of this ABI so that we can deal with "normal" applications that run under
+ // the same UID correctly.
+ if (isPlatformPackage) {
+ parsedPackage.setPrimaryCpuAbi(VMRuntime.getRuntime().is64Bit()
+ ? Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0]);
+ }
+
+ // If there's a mismatch between the abi-override in the package setting
+ // and the abiOverride specified for the install. Warn about this because we
+ // would've already compiled the app without taking the package setting into
+ // account.
+ if ((scanFlags & SCAN_NO_DEX) == 0 && (scanFlags & SCAN_NEW_INSTALL) != 0) {
+ if (cpuAbiOverride == null) {
+ Slog.w(TAG, "Ignoring persisted ABI override for package "
+ + parsedPackage.getPackageName());
+ }
+ }
+
+ pkgSetting.setPrimaryCpuAbi(AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage))
+ .setSecondaryCpuAbi(AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage))
+ .setCpuAbiOverride(cpuAbiOverride);
+
+ if (DEBUG_ABI_SELECTION) {
+ Slog.d(TAG, "Resolved nativeLibraryRoot for " + parsedPackage.getPackageName()
+ + " to root=" + parsedPackage.getNativeLibraryRootDir()
+ + ", to dir=" + parsedPackage.getNativeLibraryDir()
+ + ", isa=" + parsedPackage.isNativeLibraryRootRequiresIsa());
+ }
+
+ // Push the derived path down into PackageSettings so we know what to
+ // clean up at uninstall time.
+ pkgSetting.setLegacyNativeLibraryPath(parsedPackage.getNativeLibraryRootDir());
+
+ if (DEBUG_ABI_SELECTION) {
+ Log.d(TAG, "Abis for package[" + parsedPackage.getPackageName() + "] are"
+ + " primary=" + pkgSetting.getPrimaryCpuAbi()
+ + " secondary=" + pkgSetting.getSecondaryCpuAbi()
+ + " abiOverride=" + pkgSetting.getCpuAbiOverride());
+ }
+
+ if ((scanFlags & SCAN_BOOTING) == 0 && pkgSetting.getSharedUser() != null) {
+ // We don't do this here during boot because we can do it all
+ // at once after scanning all existing packages.
+ //
+ // We also do this *before* we perform dexopt on this package, so that
+ // we can avoid redundant dexopts, and also to make sure we've got the
+ // code and package path correct.
+ changedAbiCodePath = applyAdjustedAbiToSharedUser(pkgSetting.getSharedUser(),
+ parsedPackage, packageAbiHelper.getAdjustedAbiForSharedUser(
+ pkgSetting.getSharedUser().packages, parsedPackage));
+ }
+
+ parsedPackage.setFactoryTest(isUnderFactoryTest && parsedPackage.getRequestedPermissions()
+ .contains(android.Manifest.permission.FACTORY_TEST));
+
+ if (parsedPackage.isSystem()) {
+ pkgSetting.setIsOrphaned(true);
+ }
+
+ // Take care of first install / last update times.
+ final long scanFileTime = getLastModifiedTime(parsedPackage);
+ if (currentTime != 0) {
+ if (pkgSetting.getFirstInstallTime() == 0) {
+ pkgSetting.setFirstInstallTime(currentTime)
+ .setLastUpdateTime(currentTime);
+ } else if ((scanFlags & SCAN_UPDATE_TIME) != 0) {
+ pkgSetting.setLastUpdateTime(currentTime);
+ }
+ } else if (pkgSetting.getFirstInstallTime() == 0) {
+ // We need *something*. Take time time stamp of the file.
+ pkgSetting.setFirstInstallTime(scanFileTime)
+ .setLastUpdateTime(scanFileTime);
+ } else if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0) {
+ if (scanFileTime != pkgSetting.getLastModifiedTime()) {
+ // A package on the system image has changed; consider this
+ // to be an update.
+ pkgSetting.setLastUpdateTime(scanFileTime);
+ }
+ }
+ pkgSetting.setLastModifiedTime(scanFileTime);
+ // TODO(b/135203078): Remove, move to constructor
+ pkgSetting.setPkg(parsedPackage)
+ .setFlags(PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting))
+ .setPrivateFlags(
+ PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting));
+ if (parsedPackage.getLongVersionCode() != pkgSetting.getVersionCode()) {
+ pkgSetting.setLongVersionCode(parsedPackage.getLongVersionCode());
+ }
+ // Update volume if needed
+ final String volumeUuid = parsedPackage.getVolumeUuid();
+ if (!Objects.equals(volumeUuid, pkgSetting.getVolumeUuid())) {
+ Slog.i(PackageManagerService.TAG,
+ "Update" + (pkgSetting.isSystem() ? " system" : "")
+ + " package " + parsedPackage.getPackageName()
+ + " volume from " + pkgSetting.getVolumeUuid()
+ + " to " + volumeUuid);
+ pkgSetting.setVolumeUuid(volumeUuid);
+ }
+
+ SharedLibraryInfo sdkLibraryInfo = null;
+ if (!TextUtils.isEmpty(parsedPackage.getSdkLibName())) {
+ sdkLibraryInfo = AndroidPackageUtils.createSharedLibraryForSdk(parsedPackage);
+ }
+ SharedLibraryInfo staticSharedLibraryInfo = null;
+ if (!TextUtils.isEmpty(parsedPackage.getStaticSharedLibName())) {
+ staticSharedLibraryInfo =
+ AndroidPackageUtils.createSharedLibraryForStatic(parsedPackage);
+ }
+ List<SharedLibraryInfo> dynamicSharedLibraryInfos = null;
+ if (!ArrayUtils.isEmpty(parsedPackage.getLibraryNames())) {
+ dynamicSharedLibraryInfos = new ArrayList<>(parsedPackage.getLibraryNames().size());
+ for (String name : parsedPackage.getLibraryNames()) {
+ dynamicSharedLibraryInfos.add(
+ AndroidPackageUtils.createSharedLibraryForDynamic(parsedPackage, name));
+ }
+ }
+
+ return new ScanResult(request, true, pkgSetting, changedAbiCodePath,
+ !createNewPackage /* existingSettingCopied */,
+ previousAppId, sdkLibraryInfo, staticSharedLibraryInfo,
+ dynamicSharedLibraryInfos);
+ }
+
+ /**
+ * Returns the actual scan flags depending upon the state of the other settings.
+ * <p>Updated system applications will not have the following flags set
+ * by default and need to be adjusted after the fact:
+ * <ul>
+ * <li>{@link PackageManagerService.SCAN_AS_SYSTEM}</li>
+ * <li>{@link PackageManagerService.SCAN_AS_PRIVILEGED}</li>
+ * <li>{@link PackageManagerService.SCAN_AS_OEM}</li>
+ * <li>{@link PackageManagerService.SCAN_AS_VENDOR}</li>
+ * <li>{@link PackageManagerService.SCAN_AS_PRODUCT}</li>
+ * <li>{@link PackageManagerService.SCAN_AS_SYSTEM_EXT}</li>
+ * <li>{@link PackageManagerService.SCAN_AS_INSTANT_APP}</li>
+ * <li>{@link PackageManagerService.SCAN_AS_VIRTUAL_PRELOAD}</li>
+ * <li>{@link PackageManagerService.SCAN_AS_ODM}</li>
+ * </ul>
+ */
+ public static @PackageManagerService.ScanFlags int adjustScanFlagsWithPackageSetting(
+ @PackageManagerService.ScanFlags int scanFlags,
+ PackageSetting pkgSetting, PackageSetting disabledPkgSetting, UserHandle user) {
+
+ // TODO(patb): Do away entirely with disabledPkgSetting here. PkgSetting will always contain
+ // the correct isSystem value now that we don't disable system packages before scan.
+ final PackageSetting systemPkgSetting =
+ (scanFlags & SCAN_NEW_INSTALL) != 0 && disabledPkgSetting == null
+ && pkgSetting != null && pkgSetting.isSystem()
+ ? pkgSetting
+ : disabledPkgSetting;
+ if (systemPkgSetting != null) {
+ // updated system application, must at least have SCAN_AS_SYSTEM
+ scanFlags |= SCAN_AS_SYSTEM;
+ if ((systemPkgSetting.getPrivateFlags()
+ & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
+ scanFlags |= SCAN_AS_PRIVILEGED;
+ }
+ if ((systemPkgSetting.getPrivateFlags()
+ & ApplicationInfo.PRIVATE_FLAG_OEM) != 0) {
+ scanFlags |= SCAN_AS_OEM;
+ }
+ if ((systemPkgSetting.getPrivateFlags()
+ & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0) {
+ scanFlags |= SCAN_AS_VENDOR;
+ }
+ if ((systemPkgSetting.getPrivateFlags()
+ & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0) {
+ scanFlags |= SCAN_AS_PRODUCT;
+ }
+ if ((systemPkgSetting.getPrivateFlags()
+ & ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT) != 0) {
+ scanFlags |= SCAN_AS_SYSTEM_EXT;
+ }
+ if ((systemPkgSetting.getPrivateFlags()
+ & ApplicationInfo.PRIVATE_FLAG_ODM) != 0) {
+ scanFlags |= SCAN_AS_ODM;
+ }
+ }
+ if (pkgSetting != null) {
+ final int userId = ((user == null) ? 0 : user.getIdentifier());
+ if (pkgSetting.getInstantApp(userId)) {
+ scanFlags |= SCAN_AS_INSTANT_APP;
+ }
+ if (pkgSetting.getVirtualPreload(userId)) {
+ scanFlags |= SCAN_AS_VIRTUAL_PRELOAD;
+ }
+ }
+
+ return scanFlags;
+ }
+
+ /**
+ * Enforces code policy for the package. This ensures that if an APK has
+ * declared hasCode="true" in its manifest that the APK actually contains
+ * code.
+ *
+ * @throws PackageManagerException If bytecode could not be found when it should exist
+ */
+ public static void assertCodePolicy(AndroidPackage pkg)
+ throws PackageManagerException {
+ final boolean shouldHaveCode = pkg.isHasCode();
+ if (shouldHaveCode && !apkHasCode(pkg.getBaseApkPath())) {
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+ "Package " + pkg.getBaseApkPath() + " code is missing");
+ }
+
+ if (!ArrayUtils.isEmpty(pkg.getSplitCodePaths())) {
+ for (int i = 0; i < pkg.getSplitCodePaths().length; i++) {
+ final boolean splitShouldHaveCode =
+ (pkg.getSplitFlags()[i] & ApplicationInfo.FLAG_HAS_CODE) != 0;
+ if (splitShouldHaveCode && !apkHasCode(pkg.getSplitCodePaths()[i])) {
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+ "Package " + pkg.getSplitCodePaths()[i] + " code is missing");
+ }
+ }
+ }
+ }
+
+ public static void assertStaticSharedLibraryIsValid(AndroidPackage pkg,
+ @PackageManagerService.ScanFlags int scanFlags) throws PackageManagerException {
+ // Static shared libraries should have at least O target SDK
+ if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.O) {
+ throw new PackageManagerException(
+ "Packages declaring static-shared libs must target O SDK or higher");
+ }
+
+ // Package declaring static a shared lib cannot be instant apps
+ if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
+ throw new PackageManagerException(
+ "Packages declaring static-shared libs cannot be instant apps");
+ }
+
+ // Package declaring static a shared lib cannot be renamed since the package
+ // name is synthetic and apps can't code around package manager internals.
+ if (!ArrayUtils.isEmpty(pkg.getOriginalPackages())) {
+ throw new PackageManagerException(
+ "Packages declaring static-shared libs cannot be renamed");
+ }
+
+ // Package declaring static a shared lib cannot declare dynamic libs
+ if (!ArrayUtils.isEmpty(pkg.getLibraryNames())) {
+ throw new PackageManagerException(
+ "Packages declaring static-shared libs cannot declare dynamic libs");
+ }
+
+ // Package declaring static a shared lib cannot declare shared users
+ if (pkg.getSharedUserId() != null) {
+ throw new PackageManagerException(
+ "Packages declaring static-shared libs cannot declare shared users");
+ }
+
+ // Static shared libs cannot declare activities
+ if (!pkg.getActivities().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare activities");
+ }
+
+ // Static shared libs cannot declare services
+ if (!pkg.getServices().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare services");
+ }
+
+ // Static shared libs cannot declare providers
+ if (!pkg.getProviders().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare content providers");
+ }
+
+ // Static shared libs cannot declare receivers
+ if (!pkg.getReceivers().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare broadcast receivers");
+ }
+
+ // Static shared libs cannot declare permission groups
+ if (!pkg.getPermissionGroups().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare permission groups");
+ }
+
+ // Static shared libs cannot declare attributions
+ if (!pkg.getAttributions().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare features");
+ }
+
+ // Static shared libs cannot declare permissions
+ if (!pkg.getPermissions().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare permissions");
+ }
+
+ // Static shared libs cannot declare protected broadcasts
+ if (!pkg.getProtectedBroadcasts().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare protected broadcasts");
+ }
+
+ // Static shared libs cannot be overlay targets
+ if (pkg.getOverlayTarget() != null) {
+ throw new PackageManagerException(
+ "Static shared libs cannot be overlay targets");
+ }
+ }
+
+ public static void assertProcessesAreValid(AndroidPackage pkg) throws PackageManagerException {
+ final Map<String, ParsedProcess> procs = pkg.getProcesses();
+ if (!procs.isEmpty()) {
+ if (!procs.containsKey(pkg.getProcessName())) {
+ throw new PackageManagerException(
+ INSTALL_FAILED_PROCESS_NOT_DEFINED,
+ "Can't install because application tag's process attribute "
+ + pkg.getProcessName()
+ + " (in package " + pkg.getPackageName()
+ + ") is not included in the <processes> list");
+ }
+ assertPackageProcesses(pkg, pkg.getActivities(), procs, "activity");
+ assertPackageProcesses(pkg, pkg.getServices(), procs, "service");
+ assertPackageProcesses(pkg, pkg.getReceivers(), procs, "receiver");
+ assertPackageProcesses(pkg, pkg.getProviders(), procs, "provider");
+ }
+ }
+
+ private static <T extends ParsedMainComponent> void assertPackageProcesses(AndroidPackage pkg,
+ List<T> components, Map<String, ParsedProcess> procs, String compName)
+ throws PackageManagerException {
+ if (components == null) {
+ return;
+ }
+ for (int i = components.size() - 1; i >= 0; i--) {
+ final ParsedMainComponent component = components.get(i);
+ if (!procs.containsKey(component.getProcessName())) {
+ throw new PackageManagerException(
+ INSTALL_FAILED_PROCESS_NOT_DEFINED,
+ "Can't install because " + compName + " " + component.getClassName()
+ + "'s process attribute " + component.getProcessName()
+ + " (in package " + pkg.getPackageName()
+ + ") is not included in the <processes> list");
+ }
+ }
+ }
+
+ public static void assertMinSignatureSchemeIsValid(AndroidPackage pkg,
+ @ParsingPackageUtils.ParseFlags int parseFlags) throws PackageManagerException {
+ if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
+ int minSignatureSchemeVersion =
+ ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
+ pkg.getTargetSdkVersion());
+ if (pkg.getSigningDetails().getSignatureSchemeVersion()
+ < minSignatureSchemeVersion) {
+ throw new PackageManagerException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+ "No signature found in package of version " + minSignatureSchemeVersion
+ + " or newer for package " + pkg.getPackageName());
+ }
+ }
+ }
+
+ /**
+ * Returns the "real" name of the package.
+ * <p>This may differ from the package's actual name if the application has already
+ * been installed under one of this package's original names.
+ */
+ public static @Nullable String getRealPackageName(@NonNull AndroidPackage pkg,
+ @Nullable String renamedPkgName) {
+ if (isPackageRenamed(pkg, renamedPkgName)) {
+ return AndroidPackageUtils.getRealPackageOrNull(pkg);
+ }
+ return null;
+ }
+
+ /** Returns {@code true} if the package has been renamed. Otherwise, {@code false}. */
+ public static boolean isPackageRenamed(@NonNull AndroidPackage pkg,
+ @Nullable String renamedPkgName) {
+ return pkg.getOriginalPackages().contains(renamedPkgName);
+ }
+
+ /**
+ * Renames the package if it was installed under a different name.
+ * <p>When we've already installed the package under an original name, update
+ * the new package so we can continue to have the old name.
+ */
+ public static void ensurePackageRenamed(@NonNull ParsedPackage parsedPackage,
+ @NonNull String renamedPackageName) {
+ if (!parsedPackage.getOriginalPackages().contains(renamedPackageName)
+ || parsedPackage.getPackageName().equals(renamedPackageName)) {
+ return;
+ }
+ parsedPackage.setPackageName(renamedPackageName);
+ }
+
+ /**
+ * Returns {@code true} if the given file contains code. Otherwise {@code false}.
+ */
+ public static boolean apkHasCode(String fileName) {
+ StrictJarFile jarFile = null;
+ try {
+ jarFile = new StrictJarFile(fileName,
+ false /*verify*/, false /*signatureSchemeRollbackProtectionsEnforced*/);
+ return jarFile.findEntry("classes.dex") != null;
+ } catch (IOException ignore) {
+ } finally {
+ try {
+ if (jarFile != null) {
+ jarFile.close();
+ }
+ } catch (IOException ignore) {
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Sets the enabled state of components configured through {@link SystemConfig}.
+ * This modifies the {@link PackageSetting} object.
+ *
+ * TODO(b/135203078): Move this to package parsing
+ **/
+ public static void configurePackageComponents(AndroidPackage pkg) {
+ final ArrayMap<String, Boolean> componentsEnabledStates = SystemConfig.getInstance()
+ .getComponentsEnabledStates(pkg.getPackageName());
+ if (componentsEnabledStates == null) {
+ return;
+ }
+
+ for (int i = ArrayUtils.size(pkg.getActivities()) - 1; i >= 0; i--) {
+ final ParsedActivity component = pkg.getActivities().get(i);
+ final Boolean enabled = componentsEnabledStates.get(component.getName());
+ if (enabled != null) {
+ ComponentMutateUtils.setEnabled(component, enabled);
+ }
+ }
+
+ for (int i = ArrayUtils.size(pkg.getReceivers()) - 1; i >= 0; i--) {
+ final ParsedActivity component = pkg.getReceivers().get(i);
+ final Boolean enabled = componentsEnabledStates.get(component.getName());
+ if (enabled != null) {
+ ComponentMutateUtils.setEnabled(component, enabled);
+ }
+ }
+
+ for (int i = ArrayUtils.size(pkg.getProviders()) - 1; i >= 0; i--) {
+ final ParsedProvider component = pkg.getProviders().get(i);
+ final Boolean enabled = componentsEnabledStates.get(component.getName());
+ if (enabled != null) {
+ ComponentMutateUtils.setEnabled(component, enabled);
+ }
+ }
+
+ for (int i = ArrayUtils.size(pkg.getServices()) - 1; i >= 0; i--) {
+ final ParsedService component = pkg.getServices().get(i);
+ final Boolean enabled = componentsEnabledStates.get(component.getName());
+ if (enabled != null) {
+ ComponentMutateUtils.setEnabled(component, enabled);
+ }
+ }
+ }
+
+ public static int getVendorPartitionVersion() {
+ final String version = SystemProperties.get("ro.vndk.version");
+ if (!version.isEmpty()) {
+ try {
+ return Integer.parseInt(version);
+ } catch (NumberFormatException ignore) {
+ if (ArrayUtils.contains(Build.VERSION.ACTIVE_CODENAMES, version)) {
+ return Build.VERSION_CODES.CUR_DEVELOPMENT;
+ }
+ }
+ }
+ return Build.VERSION_CODES.P;
+ }
+
+ /**
+ * Applies policy to the parsed package based upon the given policy flags.
+ * Ensures the package is in a good state.
+ * <p>
+ * Implementation detail: This method must NOT have any side effect. It would
+ * ideally be static, but, it requires locks to read system state.
+ */
+ public static void applyPolicy(ParsedPackage parsedPackage,
+ final @PackageManagerService.ScanFlags int scanFlags, AndroidPackage platformPkg,
+ boolean isUpdatedSystemApp) {
+ if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
+ parsedPackage.setSystem(true);
+ // TODO(b/135203078): Can this be done in PackageParser? Or just inferred when the flag
+ // is set during parse.
+ if (parsedPackage.isDirectBootAware()) {
+ parsedPackage.setAllComponentsDirectBootAware(true);
+ }
+ if (compressedFileExists(parsedPackage.getPath())) {
+ parsedPackage.setStub(true);
+ }
+ } else {
+ parsedPackage
+ // Non system apps cannot mark any broadcast as protected
+ .clearProtectedBroadcasts()
+ // non system apps can't be flagged as core
+ .setCoreApp(false)
+ // clear flags not applicable to regular apps
+ .setPersistent(false)
+ .setDefaultToDeviceProtectedStorage(false)
+ .setDirectBootAware(false)
+ // non system apps can't have permission priority
+ .capPermissionPriorities();
+ }
+ if ((scanFlags & SCAN_AS_PRIVILEGED) == 0) {
+ parsedPackage
+ .markNotActivitiesAsNotExportedIfSingleUser();
+ }
+
+ parsedPackage.setPrivileged((scanFlags & SCAN_AS_PRIVILEGED) != 0)
+ .setOem((scanFlags & SCAN_AS_OEM) != 0)
+ .setVendor((scanFlags & SCAN_AS_VENDOR) != 0)
+ .setProduct((scanFlags & SCAN_AS_PRODUCT) != 0)
+ .setSystemExt((scanFlags & SCAN_AS_SYSTEM_EXT) != 0)
+ .setOdm((scanFlags & SCAN_AS_ODM) != 0);
+
+ // Check if the package is signed with the same key as the platform package.
+ parsedPackage.setSignedWithPlatformKey(
+ (PLATFORM_PACKAGE_NAME.equals(parsedPackage.getPackageName())
+ || (platformPkg != null && compareSignatures(
+ platformPkg.getSigningDetails().getSignatures(),
+ parsedPackage.getSigningDetails().getSignatures()
+ ) == PackageManager.SIGNATURE_MATCH))
+ );
+
+ if (!parsedPackage.isSystem()) {
+ // Only system apps can use these features.
+ parsedPackage.clearOriginalPackages()
+ .clearAdoptPermissions();
+ }
+
+ PackageBackwardCompatibility.modifySharedLibraries(parsedPackage, isUpdatedSystemApp);
+ }
+
+ /**
+ * Applies the adjusted ABI calculated by
+ * {@link PackageAbiHelper#getAdjustedAbiForSharedUser(Set, AndroidPackage)} to all
+ * relevant packages and settings.
+ * @param sharedUserSetting The {@code SharedUserSetting} to adjust
+ * @param scannedPackage the package being scanned or null
+ * @param adjustedAbi the adjusted ABI calculated by {@link PackageAbiHelper}
+ * @return the list of code paths that belong to packages that had their ABIs adjusted.
+ */
+ public static List<String> applyAdjustedAbiToSharedUser(SharedUserSetting sharedUserSetting,
+ ParsedPackage scannedPackage, String adjustedAbi) {
+ if (scannedPackage != null) {
+ scannedPackage.setPrimaryCpuAbi(adjustedAbi);
+ }
+ List<String> changedAbiCodePath = null;
+ for (PackageSetting ps : sharedUserSetting.packages) {
+ if (scannedPackage == null
+ || !scannedPackage.getPackageName().equals(ps.getPackageName())) {
+ if (ps.getPrimaryCpuAbi() != null) {
+ continue;
+ }
+
+ ps.setPrimaryCpuAbi(adjustedAbi);
+ if (ps.getPkg() != null) {
+ if (!TextUtils.equals(adjustedAbi,
+ AndroidPackageUtils.getRawPrimaryCpuAbi(ps.getPkg()))) {
+ if (DEBUG_ABI_SELECTION) {
+ Slog.i(TAG,
+ "Adjusting ABI for " + ps.getPackageName() + " to "
+ + adjustedAbi + " (scannedPackage="
+ + (scannedPackage != null ? scannedPackage : "null")
+ + ")");
+ }
+ if (changedAbiCodePath == null) {
+ changedAbiCodePath = new ArrayList<>();
+ }
+ changedAbiCodePath.add(ps.getPathString());
+ }
+ }
+ }
+ }
+ return changedAbiCodePath;
+ }
+
+ public static void collectCertificatesLI(PackageSetting ps, ParsedPackage parsedPackage,
+ Settings.VersionInfo settingsVersionForPackage, boolean forceCollect,
+ boolean skipVerify, boolean isPreNMR1Upgrade)
+ throws PackageManagerException {
+ // When upgrading from pre-N MR1, verify the package time stamp using the package
+ // directory and not the APK file.
+ final long lastModifiedTime = isPreNMR1Upgrade
+ ? new File(parsedPackage.getPath()).lastModified()
+ : getLastModifiedTime(parsedPackage);
+ if (ps != null && !forceCollect
+ && ps.getPathString().equals(parsedPackage.getPath())
+ && ps.getLastModifiedTime() == lastModifiedTime
+ && !ReconcilePackageUtils.isCompatSignatureUpdateNeeded(settingsVersionForPackage)
+ && !ReconcilePackageUtils.isRecoverSignatureUpdateNeeded(
+ settingsVersionForPackage)) {
+ if (ps.getSigningDetails().getSignatures() != null
+ && ps.getSigningDetails().getSignatures().length != 0
+ && ps.getSigningDetails().getSignatureSchemeVersion()
+ != SigningDetails.SignatureSchemeVersion.UNKNOWN) {
+ // Optimization: reuse the existing cached signing data
+ // if the package appears to be unchanged.
+ parsedPackage.setSigningDetails(
+ new SigningDetails(ps.getSigningDetails()));
+ return;
+ }
+
+ Slog.w(TAG, "PackageSetting for " + ps.getPackageName()
+ + " is missing signatures. Collecting certs again to recover them.");
+ } else {
+ Slog.i(TAG, parsedPackage.getPath() + " changed; collecting certs"
+ + (forceCollect ? " (forced)" : ""));
+ }
+
+ try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
+ final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+ final ParseResult<SigningDetails> result = ParsingPackageUtils.getSigningDetails(
+ input, parsedPackage, skipVerify);
+ if (result.isError()) {
+ throw new PackageManagerException(
+ result.getErrorCode(), result.getErrorMessage(), result.getException());
+ }
+ parsedPackage.setSigningDetails(result.getResult());
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+
+ public static void setInstantAppForUser(PackageManagerServiceInjector injector,
+ PackageSetting pkgSetting, int userId, boolean instantApp, boolean fullApp) {
+ // no state specified; do nothing
+ if (!instantApp && !fullApp) {
+ return;
+ }
+ if (userId != UserHandle.USER_ALL) {
+ if (instantApp && !pkgSetting.getInstantApp(userId)) {
+ pkgSetting.setInstantApp(true /*instantApp*/, userId);
+ } else if (fullApp && pkgSetting.getInstantApp(userId)) {
+ pkgSetting.setInstantApp(false /*instantApp*/, userId);
+ }
+ } else {
+ for (int currentUserId : injector.getUserManagerInternal().getUserIds()) {
+ if (instantApp && !pkgSetting.getInstantApp(currentUserId)) {
+ pkgSetting.setInstantApp(true /*instantApp*/, currentUserId);
+ } else if (fullApp && pkgSetting.getInstantApp(currentUserId)) {
+ pkgSetting.setInstantApp(false /*instantApp*/, currentUserId);
+ }
+ }
+ }
+ }
+
+ /** Directory where installed application's 32-bit native libraries are copied. */
+ public static File getAppLib32InstallDir() {
+ return new File(Environment.getDataDirectory(), "app-lib");
+ }
+}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 45994f65d697..b1ce6a2a5366 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -1254,11 +1254,14 @@ public final class Settings implements Watchable, Snappable {
}
}
- private void checkAndPruneSharedUserLPw(SharedUserSetting s, boolean skipCheck) {
+ boolean checkAndPruneSharedUserLPw(SharedUserSetting s, boolean skipCheck) {
if (skipCheck || (s.packages.isEmpty() && s.mDisabledPackages.isEmpty())) {
- mSharedUsers.remove(s.name);
- removeAppIdLPw(s.userId);
+ if (mSharedUsers.remove(s.name) != null) {
+ removeAppIdLPw(s.userId);
+ return true;
+ }
}
+ return false;
}
int removePackageLPw(String name) {
@@ -1267,7 +1270,9 @@ public final class Settings implements Watchable, Snappable {
removeInstallerPackageStatus(name);
if (p.getSharedUser() != null) {
p.getSharedUser().removePackage(p);
- checkAndPruneSharedUserLPw(p.getSharedUser(), false);
+ if (checkAndPruneSharedUserLPw(p.getSharedUser(), false)) {
+ return p.getSharedUser().userId;
+ }
} else {
removeAppIdLPw(p.getAppId());
return p.getAppId();
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 42c88b3eee8a..3d10b6f06488 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -96,8 +96,6 @@ import java.util.stream.Collectors;
/**
* Package information used by {@link ShortcutService}.
* User information used by {@link ShortcutService}.
- *
- * All methods should be guarded by {@code #mShortcutUser.mService.mLock}.
*/
class ShortcutPackage extends ShortcutPackageItem {
private static final String TAG = ShortcutService.TAG;
@@ -162,11 +160,19 @@ class ShortcutPackage extends ShortcutPackageItem {
private final Executor mExecutor;
/**
- * An temp in-memory copy of shortcuts for this package that was loaded from xml, keyed on IDs.
+ * An in-memory copy of shortcuts for this package that was loaded from xml, keyed on IDs.
*/
+ @GuardedBy("mLock")
final ArrayMap<String, ShortcutInfo> mShortcuts = new ArrayMap<>();
/**
+ * A temporary copy of shortcuts that are to be cleared once persisted into AppSearch, keyed on
+ * IDs.
+ */
+ @GuardedBy("mLock")
+ private ArrayMap<String, ShortcutInfo> mTransientShortcuts = new ArrayMap<>(0);
+
+ /**
* All the share targets from the package
*/
private final ArrayList<ShareTargetInfo> mShareTargets = new ArrayList<>(0);
@@ -330,6 +336,15 @@ class ShortcutPackage extends ShortcutPackageItem {
}
}
+ public void ensureAllShortcutsVisibleToLauncher(@NonNull List<ShortcutInfo> shortcuts) {
+ for (ShortcutInfo shortcut : shortcuts) {
+ if (!shortcut.isIncludedIn(ShortcutInfo.SURFACE_LAUNCHER)) {
+ throw new IllegalArgumentException("Shortcut ID=" + shortcut.getId()
+ + " is hidden from launcher and may not be manipulated via APIs");
+ }
+ }
+ }
+
/**
* Delete a shortcut by ID. This will *always* remove it even if it's immutable or invisible.
*/
@@ -384,7 +399,15 @@ class ShortcutPackage extends ShortcutPackageItem {
& (ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_CACHED_ALL));
}
- forceReplaceShortcutInner(newShortcut);
+ if (!newShortcut.isIncludedIn(ShortcutInfo.SURFACE_LAUNCHER)) {
+ if (isAppSearchEnabled()) {
+ synchronized (mLock) {
+ mTransientShortcuts.put(newShortcut.getId(), newShortcut);
+ }
+ }
+ } else {
+ forceReplaceShortcutInner(newShortcut);
+ }
return oldShortcut != null;
}
@@ -444,7 +467,15 @@ class ShortcutPackage extends ShortcutPackageItem {
& (ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_CACHED_ALL));
}
- forceReplaceShortcutInner(newShortcut);
+ if (!newShortcut.isIncludedIn(ShortcutInfo.SURFACE_LAUNCHER)) {
+ if (isAppSearchEnabled()) {
+ synchronized (mLock) {
+ mTransientShortcuts.put(newShortcut.getId(), newShortcut);
+ }
+ }
+ } else {
+ forceReplaceShortcutInner(newShortcut);
+ }
if (isAppSearchEnabled()) {
runAsSystem(() -> fromAppSearch().thenAccept(session ->
session.reportUsage(new ReportUsageRequest.Builder(
@@ -669,7 +700,6 @@ class ShortcutPackage extends ShortcutPackageItem {
forEachShortcutMutate(si -> {
if (!pinnedShortcuts.contains(si.getId()) && si.isPinned()) {
si.clearFlags(ShortcutInfo.FLAG_PINNED);
- return;
}
});
@@ -1704,8 +1734,15 @@ class ShortcutPackage extends ShortcutPackageItem {
for (int j = 0; j < shareTargetSize; j++) {
mShareTargets.get(j).saveToXml(out);
}
- saveShortcutsAsync(mShortcuts.values().stream().filter(ShortcutInfo::usesQuota)
- .collect(Collectors.toList()));
+ synchronized (mLock) {
+ final Map<String, ShortcutInfo> copy = mShortcuts;
+ if (!mTransientShortcuts.isEmpty()) {
+ copy.putAll(mTransientShortcuts);
+ mTransientShortcuts.clear();
+ }
+ saveShortcutsAsync(copy.values().stream().filter(ShortcutInfo::usesQuota).collect(
+ Collectors.toList()));
+ }
}
out.endTag(null, TAG_ROOT);
@@ -2233,26 +2270,6 @@ class ShortcutPackage extends ShortcutPackageItem {
}
}
- void updateVisibility(String packageName, byte[] certificate, boolean visible) {
- if (!isAppSearchEnabled()) {
- return;
- }
- if (visible) {
- mPackageIdentifiers.put(packageName, new PackageIdentifier(packageName, certificate));
- } else {
- mPackageIdentifiers.remove(packageName);
- }
- synchronized (mLock) {
- mIsAppSearchSchemaUpToDate = false;
- }
- final long callingIdentity = Binder.clearCallingIdentity();
- try {
- fromAppSearch();
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
- }
- }
-
void mutateShortcut(@NonNull final String id, @Nullable final ShortcutInfo shortcut,
@NonNull final Consumer<ShortcutInfo> transform) {
Objects.requireNonNull(id);
@@ -2358,6 +2375,7 @@ class ShortcutPackage extends ShortcutPackageItem {
.addFilterSchemas(AppSearchShortcutInfo.SCHEMA_TYPE)
.addFilterNamespaces(getPackageName())
.setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .setResultCountPerPage(mShortcutUser.mService.getMaxActivityShortcuts())
.build();
}
@@ -2451,6 +2469,9 @@ class ShortcutPackage extends ShortcutPackageItem {
@VisibleForTesting
void getTopShortcutsFromPersistence(AndroidFuture<List<ShortcutInfo>> cb) {
+ if (!isAppSearchEnabled()) {
+ cb.complete(null);
+ }
runAsSystem(() -> fromAppSearch().thenAccept(session -> {
SearchResults res = session.search("", getSearchSpec());
res.getNextPage(mShortcutUser.mExecutor, results -> {
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 85b743594b75..a482f9a619ba 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -2014,6 +2014,7 @@ public class ShortcutService extends IShortcutService.Stub {
ps.ensureImmutableShortcutsNotIncluded(newShortcuts, /*ignoreInvisible=*/ true);
ps.ensureNoBitmapIconIfShortcutIsLongLived(newShortcuts);
+ ps.ensureAllShortcutsVisibleToLauncher(newShortcuts);
// For update, don't fill in the default activity. Having null activity means
// "don't update the activity" here.
@@ -2214,6 +2215,9 @@ public class ShortcutService extends IShortcutService.Stub {
IntentSender resultIntent, int userId, AndroidFuture<String> ret) {
Objects.requireNonNull(shortcut);
Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled");
+ Preconditions.checkArgument(
+ shortcut.isIncludedIn(ShortcutInfo.SURFACE_LAUNCHER),
+ "Shortcut excluded from launcher cannot be pinned");
ret.complete(String.valueOf(requestPinItem(
packageName, userId, shortcut, null, null, resultIntent)));
}
diff --git a/services/core/java/com/android/server/pm/VerificationParams.java b/services/core/java/com/android/server/pm/VerificationParams.java
index 6d681399460e..4334cbdce1f2 100644
--- a/services/core/java/com/android/server/pm/VerificationParams.java
+++ b/services/core/java/com/android/server/pm/VerificationParams.java
@@ -30,6 +30,7 @@ import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static com.android.server.pm.PackageManagerService.CHECK_PENDING_INTEGRITY_VERIFICATION;
import static com.android.server.pm.PackageManagerService.CHECK_PENDING_VERIFICATION;
import static com.android.server.pm.PackageManagerService.DEBUG_VERIFY;
+import static com.android.server.pm.PackageManagerService.DEFAULT_VERIFICATION_RESPONSE;
import static com.android.server.pm.PackageManagerService.ENABLE_ROLLBACK_TIMEOUT;
import static com.android.server.pm.PackageManagerService.PACKAGE_MIME_TYPE;
import static com.android.server.pm.PackageManagerService.TAG;
@@ -59,10 +60,13 @@ import android.os.Bundle;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.DeviceConfig;
import android.provider.Settings;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Pair;
import android.util.Slog;
@@ -76,6 +80,11 @@ import java.util.Set;
final class VerificationParams extends HandlerParams {
/**
+ * Whether verification is enabled by default.
+ */
+ private static final boolean DEFAULT_VERIFY_ENABLE = true;
+
+ /**
* Whether integrity verification is enabled by default.
*/
private static final boolean DEFAULT_INTEGRITY_VERIFY_ENABLE = true;
@@ -333,157 +342,289 @@ final class VerificationParams extends HandlerParams {
if (verifierUser == UserHandle.ALL) {
verifierUser = UserHandle.SYSTEM;
}
+ final int verifierUserId = verifierUser.getIdentifier();
+
+ String requiredVerifierPackage = mPm.mRequiredVerifierPackage;
+ boolean requiredVerifierPackageOverridden = false;
+
+ // Allow verifier override for ADB installations which could already be unverified using
+ // PackageManager.INSTALL_DISABLE_VERIFICATION flag.
+ if ((mInstallFlags & PackageManager.INSTALL_FROM_ADB) != 0
+ && (mInstallFlags & PackageManager.INSTALL_DISABLE_VERIFICATION) == 0) {
+ final String adbVerifierOverridePackage = SystemProperties.get(
+ "debug.pm.adb_verifier_override_package", "");
+ // Check if the package installed.
+ if (!TextUtils.isEmpty(adbVerifierOverridePackage)
+ && packageExists(adbVerifierOverridePackage)) {
+ // Pretend we requested to disable verification from command line.
+ boolean requestedDisableVerification = true;
+ // If this returns false then the caller can already skip verification, so we are
+ // not adding a new way to disable verifications.
+ if (!isAdbVerificationEnabled(pkgLite, verifierUserId,
+ requestedDisableVerification)) {
+ requiredVerifierPackage = adbVerifierOverridePackage;
+ requiredVerifierPackageOverridden = true;
+ }
+ }
+ }
/*
* Determine if we have any installed package verifiers. If we
* do, then we'll defer to them to verify the packages.
*/
- final int requiredUid = mPm.mRequiredVerifierPackage == null ? -1
- : mPm.getPackageUid(mPm.mRequiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING,
- verifierUser.getIdentifier());
+ final int requiredUid = requiredVerifierPackage == null ? -1
+ : mPm.getPackageUid(requiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING,
+ verifierUserId);
verificationState.setRequiredVerifierUid(requiredUid);
- final int installerUid =
- mVerificationInfo == null ? -1 : mVerificationInfo.mInstallerUid;
- final boolean isVerificationEnabled = mInstallPackageHelper.isVerificationEnabled(
- pkgLite, verifierUser.getIdentifier(), mInstallFlags, installerUid);
- final boolean isV4Signed =
- (mSigningDetails.getSignatureSchemeVersion() == SIGNING_BLOCK_V4);
- final boolean isIncrementalInstall =
- (mDataLoaderType == DataLoaderType.INCREMENTAL);
- // NOTE: We purposefully skip verification for only incremental installs when there's
- // a v4 signature block. Otherwise, proceed with verification as usual.
- if (!mOriginInfo.mExisting
- && isVerificationEnabled
- && (!isIncrementalInstall || !isV4Signed)) {
- final Intent verification = new Intent(
- Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
- verification.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- verification.setDataAndType(Uri.fromFile(new File(mOriginInfo.mResolvedPath)),
- PACKAGE_MIME_TYPE);
- verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-
- // Query all live verifiers based on current user state
- final ParceledListSlice<ResolveInfo> receivers = mPm.queryIntentReceivers(verification,
- PACKAGE_MIME_TYPE, 0, verifierUser.getIdentifier());
+ final boolean isVerificationEnabled = isVerificationEnabled(pkgLite,
+ verifierUserId);
- if (DEBUG_VERIFY) {
- Slog.d(TAG, "Found " + receivers.getList().size() + " verifiers for intent "
- + verification.toString() + " with " + pkgLite.verifiers.length
- + " optional verifiers");
- }
+ if (mOriginInfo.mExisting || !isVerificationEnabled) {
+ verificationState.setVerifierResponse(requiredUid, PackageManager.VERIFICATION_ALLOW);
+ return;
+ }
- verification.putExtra(PackageManager.EXTRA_VERIFICATION_ID, verificationId);
+ final Intent verification = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
+ verification.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ verification.setDataAndType(Uri.fromFile(new File(mOriginInfo.mResolvedPath)),
+ PACKAGE_MIME_TYPE);
+ verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- verification.putExtra(
- PackageManager.EXTRA_VERIFICATION_INSTALL_FLAGS, mInstallFlags);
+ // Query all live verifiers based on current user state
+ final ParceledListSlice<ResolveInfo> receivers = mPm.queryIntentReceivers(verification,
+ PACKAGE_MIME_TYPE, 0, verifierUserId);
- verification.putExtra(
- PackageManager.EXTRA_VERIFICATION_PACKAGE_NAME, pkgLite.packageName);
+ if (DEBUG_VERIFY) {
+ Slog.d(TAG, "Found " + receivers.getList().size() + " verifiers for intent "
+ + verification.toString() + " with " + pkgLite.verifiers.length
+ + " optional verifiers");
+ }
- verification.putExtra(
- PackageManager.EXTRA_VERIFICATION_VERSION_CODE, pkgLite.versionCode);
+ verification.putExtra(PackageManager.EXTRA_VERIFICATION_ID, verificationId);
- verification.putExtra(
- PackageManager.EXTRA_VERIFICATION_LONG_VERSION_CODE,
- pkgLite.getLongVersionCode());
+ verification.putExtra(
+ PackageManager.EXTRA_VERIFICATION_INSTALL_FLAGS, mInstallFlags);
- final String baseCodePath = mPackageLite.getBaseApkPath();
- final String[] splitCodePaths = mPackageLite.getSplitApkPaths();
- final String rootHashString =
- PackageManagerServiceUtils.buildVerificationRootHashString(baseCodePath,
- splitCodePaths);
+ verification.putExtra(
+ PackageManager.EXTRA_VERIFICATION_PACKAGE_NAME, pkgLite.packageName);
- if (rootHashString != null) {
- verification.putExtra(PackageManager.EXTRA_VERIFICATION_ROOT_HASH, rootHashString);
- }
+ verification.putExtra(
+ PackageManager.EXTRA_VERIFICATION_VERSION_CODE, pkgLite.versionCode);
- verification.putExtra(PackageInstaller.EXTRA_DATA_LOADER_TYPE, mDataLoaderType);
-
- verification.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId);
-
- populateInstallerExtras(verification);
-
- final List<ComponentName> sufficientVerifiers = matchVerifiers(pkgLite,
- receivers.getList(), verificationState);
-
- DeviceIdleInternal idleController =
- mPm.mInjector.getLocalService(DeviceIdleInternal.class);
- final long idleDuration = VerificationUtils.getVerificationTimeout(mPm.mContext);
- final BroadcastOptions options = BroadcastOptions.makeBasic();
- options.setTemporaryAppAllowlist(idleDuration,
- TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
- REASON_PACKAGE_VERIFIER, "");
-
- /*
- * If any sufficient verifiers were listed in the package
- * manifest, attempt to ask them.
- */
- if (sufficientVerifiers != null) {
- final int n = sufficientVerifiers.size();
- if (n == 0) {
- String errorMsg = "Additional verifiers required, but none installed.";
- Slog.i(TAG, errorMsg);
- setReturnCode(PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE, errorMsg);
- } else {
- for (int i = 0; i < n; i++) {
- final ComponentName verifierComponent = sufficientVerifiers.get(i);
- idleController.addPowerSaveTempWhitelistApp(Process.myUid(),
- verifierComponent.getPackageName(), idleDuration,
- verifierUser.getIdentifier(), false,
- REASON_PACKAGE_VERIFIER, "package verifier");
-
- final Intent sufficientIntent = new Intent(verification);
- sufficientIntent.setComponent(verifierComponent);
- mPm.mContext.sendBroadcastAsUser(sufficientIntent, verifierUser,
- /* receiverPermission= */ null,
- options.toBundle());
- }
+ verification.putExtra(
+ PackageManager.EXTRA_VERIFICATION_LONG_VERSION_CODE,
+ pkgLite.getLongVersionCode());
+
+ final String baseCodePath = mPackageLite.getBaseApkPath();
+ final String[] splitCodePaths = mPackageLite.getSplitApkPaths();
+ final String rootHashString = PackageManagerServiceUtils.buildVerificationRootHashString(
+ baseCodePath, splitCodePaths);
+
+ if (rootHashString != null) {
+ verification.putExtra(PackageManager.EXTRA_VERIFICATION_ROOT_HASH, rootHashString);
+ }
+
+ verification.putExtra(PackageInstaller.EXTRA_DATA_LOADER_TYPE, mDataLoaderType);
+
+ verification.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId);
+
+ populateInstallerExtras(verification);
+
+ // Streaming installation timeout schema is enabled only for:
+ // 1. Incremental installs with v4,
+ // 2. If device/policy allow unverified app installs by default.
+ final boolean streaming = (mDataLoaderType == DataLoaderType.INCREMENTAL)
+ && (mSigningDetails.getSignatureSchemeVersion() == SIGNING_BLOCK_V4)
+ && (getDefaultVerificationResponse() == PackageManager.VERIFICATION_ALLOW);
+
+ final long verificationTimeout = VerificationUtils.getVerificationTimeout(mPm.mContext,
+ streaming);
+
+ final List<ComponentName> sufficientVerifiers = matchVerifiers(pkgLite,
+ receivers.getList(), verificationState);
+
+ DeviceIdleInternal idleController =
+ mPm.mInjector.getLocalService(DeviceIdleInternal.class);
+ final BroadcastOptions options = BroadcastOptions.makeBasic();
+ options.setTemporaryAppAllowlist(verificationTimeout,
+ TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+ REASON_PACKAGE_VERIFIER, "");
+
+ /*
+ * If any sufficient verifiers were listed in the package
+ * manifest, attempt to ask them.
+ */
+ if (sufficientVerifiers != null) {
+ final int n = sufficientVerifiers.size();
+ if (n == 0) {
+ String errorMsg = "Additional verifiers required, but none installed.";
+ Slog.i(TAG, errorMsg);
+ setReturnCode(PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE, errorMsg);
+ } else {
+ for (int i = 0; i < n; i++) {
+ final ComponentName verifierComponent = sufficientVerifiers.get(i);
+ idleController.addPowerSaveTempWhitelistApp(Process.myUid(),
+ verifierComponent.getPackageName(), verificationTimeout,
+ verifierUserId, false,
+ REASON_PACKAGE_VERIFIER, "package verifier");
+
+ final Intent sufficientIntent = new Intent(verification);
+ sufficientIntent.setComponent(verifierComponent);
+ mPm.mContext.sendBroadcastAsUser(sufficientIntent, verifierUser,
+ /* receiverPermission= */ null,
+ options.toBundle());
}
}
+ }
- if (mPm.mRequiredVerifierPackage != null) {
- final ComponentName requiredVerifierComponent = matchComponentForVerifier(
- mPm.mRequiredVerifierPackage, receivers.getList());
- /*
- * Send the intent to the required verification agent,
- * but only start the verification timeout after the
- * target BroadcastReceivers have run.
- */
- verification.setComponent(requiredVerifierComponent);
- idleController.addPowerSaveTempWhitelistApp(Process.myUid(),
- mPm.mRequiredVerifierPackage, idleDuration,
- verifierUser.getIdentifier(), false,
- REASON_PACKAGE_VERIFIER, "package verifier");
- mPm.mContext.sendOrderedBroadcastAsUser(verification, verifierUser,
- android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
- /* appOp= */ AppOpsManager.OP_NONE,
- /* options= */ options.toBundle(),
- new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- final Message msg = mPm.mHandler
- .obtainMessage(CHECK_PENDING_VERIFICATION);
- msg.arg1 = verificationId;
- mPm.mHandler.sendMessageDelayed(msg,
- VerificationUtils.getVerificationTimeout(mPm.mContext));
- }
- }, null, 0, null, null);
-
- Trace.asyncTraceBegin(
- TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);
-
- /*
- * We don't want the copy to proceed until verification
- * succeeds.
- */
- mWaitForVerificationToComplete = true;
- }
+ if (requiredVerifierPackage == null) {
+ Slog.e(TAG, "Required verifier is null");
+ return;
+ }
+
+ final int verificationCodeAtTimeout;
+ if (getDefaultVerificationResponse() == PackageManager.VERIFICATION_ALLOW) {
+ verificationCodeAtTimeout = PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT;
} else {
- verificationState.setVerifierResponse(
- requiredUid, PackageManager.VERIFICATION_ALLOW);
+ verificationCodeAtTimeout = PackageManager.VERIFICATION_REJECT;
+ }
+ final PackageVerificationResponse response = new PackageVerificationResponse(
+ verificationCodeAtTimeout, requiredUid);
+
+ /*
+ * Send the intent to the required verification agent,
+ * but only start the verification timeout after the
+ * target BroadcastReceivers have run.
+ */
+ if (!requiredVerifierPackageOverridden) {
+ final ComponentName requiredVerifierComponent = matchComponentForVerifier(
+ requiredVerifierPackage, receivers.getList());
+ verification.setComponent(requiredVerifierComponent);
+ } else {
+ verification.setPackage(requiredVerifierPackage);
+ }
+ idleController.addPowerSaveTempWhitelistApp(Process.myUid(),
+ requiredVerifierPackage, verificationTimeout,
+ verifierUserId, false,
+ REASON_PACKAGE_VERIFIER, "package verifier");
+
+ if (streaming) {
+ // For streaming installations, count verification timeout from the broadcast.
+ startVerificationTimeoutCountdown(verificationId, streaming, response,
+ verificationTimeout);
}
+
+ mPm.mContext.sendOrderedBroadcastAsUser(verification, verifierUser,
+ android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
+ /* appOp= */ AppOpsManager.OP_NONE,
+ /* options= */ options.toBundle(),
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!streaming) {
+ // For NON-streaming installations, count verification timeout from
+ // the broadcast was processed by all receivers.
+ startVerificationTimeoutCountdown(verificationId, streaming, response,
+ verificationTimeout);
+ }
+ }
+ }, null, 0, null, null);
+
+ Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);
+
+ /*
+ * We don't want the copy to proceed until verification
+ * succeeds.
+ */
+ mWaitForVerificationToComplete = true;
+ }
+
+ private void startVerificationTimeoutCountdown(int verificationId, boolean streaming,
+ PackageVerificationResponse response, long verificationTimeout) {
+ final Message msg = mPm.mHandler.obtainMessage(CHECK_PENDING_VERIFICATION);
+ msg.arg1 = verificationId;
+ msg.arg2 = streaming ? 1 : 0;
+ msg.obj = response;
+ mPm.mHandler.sendMessageDelayed(msg, verificationTimeout);
+ }
+
+ /**
+ * Get the default verification agent response code.
+ *
+ * @return default verification response code
+ */
+ int getDefaultVerificationResponse() {
+ if (mPm.mUserManager.hasUserRestriction(UserManager.ENSURE_VERIFY_APPS,
+ getUser().getIdentifier())) {
+ return PackageManager.VERIFICATION_REJECT;
+ }
+ return android.provider.Settings.Global.getInt(mPm.mContext.getContentResolver(),
+ android.provider.Settings.Global.PACKAGE_VERIFIER_DEFAULT_RESPONSE,
+ DEFAULT_VERIFICATION_RESPONSE);
}
+ private boolean packageExists(String packageName) {
+ synchronized (mPm.mLock) {
+ return mPm.mSettings.getPackageLPr(packageName) != null;
+ }
+ }
+
+ private boolean isAdbVerificationEnabled(PackageInfoLite pkgInfoLite, int userId,
+ boolean requestedDisableVerification) {
+ if (mPm.isUserRestricted(userId, UserManager.ENSURE_VERIFY_APPS)) {
+ return true;
+ }
+ // Check if the developer wants to skip verification for ADB installs
+ if (requestedDisableVerification) {
+ if (!packageExists(pkgInfoLite.packageName)) {
+ // Always verify fresh install
+ return true;
+ }
+ // Only skip when apk is debuggable
+ return !pkgInfoLite.debuggable;
+ }
+ return android.provider.Settings.Global.getInt(mPm.mContext.getContentResolver(),
+ android.provider.Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1) != 0;
+ }
+
+ /**
+ * Check whether package verification has been enabled.
+ *
+ * @return true if verification should be performed
+ */
+ private boolean isVerificationEnabled(PackageInfoLite pkgInfoLite, int userId) {
+ if (!DEFAULT_VERIFY_ENABLE) {
+ return false;
+ }
+
+ final int installerUid = mVerificationInfo == null ? -1 : mVerificationInfo.mInstallerUid;
+ final int installFlags = mInstallFlags;
+
+ // Check if installing from ADB
+ if ((installFlags & PackageManager.INSTALL_FROM_ADB) != 0) {
+ boolean requestedDisableVerification =
+ (mInstallFlags & PackageManager.INSTALL_DISABLE_VERIFICATION) != 0;
+ return isAdbVerificationEnabled(pkgInfoLite, userId, requestedDisableVerification);
+ }
+
+ // only when not installed from ADB, skip verification for instant apps when
+ // the installer and verifier are the same.
+ if ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
+ if (mPm.mInstantAppInstallerActivity != null
+ && mPm.mInstantAppInstallerActivity.packageName.equals(
+ mPm.mRequiredVerifierPackage)) {
+ try {
+ mPm.mInjector.getSystemService(AppOpsManager.class)
+ .checkPackage(installerUid, mPm.mRequiredVerifierPackage);
+ if (DEBUG_VERIFY) {
+ Slog.i(TAG, "disable verification for instant app");
+ }
+ return false;
+ } catch (SecurityException ignore) { }
+ }
+ }
+ return true;
+ }
private List<ComponentName> matchVerifiers(PackageInfoLite pkgInfo,
List<ResolveInfo> receivers, final PackageVerificationState verificationState) {
diff --git a/services/core/java/com/android/server/pm/VerificationUtils.java b/services/core/java/com/android/server/pm/VerificationUtils.java
index 4392b4720fcb..c132028a4dae 100644
--- a/services/core/java/com/android/server/pm/VerificationUtils.java
+++ b/services/core/java/com/android/server/pm/VerificationUtils.java
@@ -35,12 +35,25 @@ final class VerificationUtils {
private static final long DEFAULT_VERIFICATION_TIMEOUT = 10 * 1000;
/**
- * Get the verification agent timeout. Used for both the APK verifier and the
+ * The default maximum time to wait for the verification agent to return in
+ * milliseconds.
+ */
+ private static final long DEFAULT_STREAMING_VERIFICATION_TIMEOUT = 3 * 1000;
+
+ public static long getVerificationTimeout(Context context, boolean streaming) {
+ if (streaming) {
+ return getDefaultStreamingVerificationTimeout(context);
+ }
+ return getDefaultVerificationTimeout(context);
+ }
+
+ /**
+ * Get the default verification agent timeout. Used for both the APK verifier and the
* intent filter verifier.
*
* @return verification timeout in milliseconds
*/
- public static long getVerificationTimeout(Context context) {
+ public static long getDefaultVerificationTimeout(Context context) {
long timeout = Settings.Global.getLong(context.getContentResolver(),
Settings.Global.PACKAGE_VERIFIER_TIMEOUT, DEFAULT_VERIFICATION_TIMEOUT);
// The setting can be used to increase the timeout but not decrease it, since that is
@@ -48,6 +61,20 @@ final class VerificationUtils {
return Math.max(timeout, DEFAULT_VERIFICATION_TIMEOUT);
}
+ /**
+ * Get the default verification agent timeout for streaming installations.
+ *
+ * @return verification timeout in milliseconds
+ */
+ public static long getDefaultStreamingVerificationTimeout(Context context) {
+ long timeout = Settings.Global.getLong(context.getContentResolver(),
+ Settings.Global.PACKAGE_STREAMING_VERIFIER_TIMEOUT,
+ DEFAULT_STREAMING_VERIFICATION_TIMEOUT);
+ // The setting can be used to increase the timeout but not decrease it, since that is
+ // equivalent to disabling the verifier.
+ return Math.max(timeout, DEFAULT_STREAMING_VERIFICATION_TIMEOUT);
+ }
+
public static void broadcastPackageVerified(int verificationId, Uri packageUri,
int verificationCode, @Nullable String rootHashString, int dataLoaderType,
UserHandle user, Context context) {
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 8643b5f60563..6c1ef2eda41d 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -435,7 +435,8 @@ final class DefaultPermissionGrantPolicy {
|| !pm.isGranted(Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
pkg, UserHandle.of(userId))
|| !pm.isGranted(Manifest.permission.READ_PHONE_STATE, pkg,
- UserHandle.of(userId))) {
+ UserHandle.of(userId))
+ || pm.isSysComponentOrPersistentPlatformSignedPrivApp(pkg)) {
continue;
}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
index 0b48b5c6dd70..2208e3c02c4d 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
@@ -70,8 +70,11 @@ public class DomainVerificationEnforcer {
break;
default:
if (!proxy.isCallerVerifier(callingUid)) {
- throw new SecurityException(
- "Caller is not allowed to query domain verification state");
+ mContext.enforcePermission(android.Manifest.permission.DUMP,
+ Binder.getCallingPid(), callingUid,
+ "Caller " + callingUid
+ + " is not allowed to query domain verification state");
+ break;
}
mContext.enforcePermission(android.Manifest.permission.QUERY_ALL_PACKAGES,
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
index 111087f0baf2..a3b0e3e7d02d 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
@@ -1197,6 +1197,7 @@ public class DomainVerificationService extends SystemService
@Nullable @UserIdInt Integer userId,
@NonNull Function<String, PackageStateInternal> pkgSettingFunction)
throws NameNotFoundException {
+ mEnforcer.assertApprovedQuerent(mConnection.getCallingUid(), mProxy);
synchronized (mLock) {
mDebug.printState(writer, packageName, userId, pkgSettingFunction, mAttachedPkgStates);
}
diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
index 48d8fed32aca..045b06c077ba 100644
--- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
+++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
@@ -42,6 +42,8 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.LatencyTracker;
import com.android.server.infra.AbstractPerUserSystemService;
+import java.io.PrintWriter;
+
/**
* Manages the Rotation Resolver Service on a per-user basis.
*/
@@ -216,6 +218,13 @@ final class RotationResolverManagerPerUserService extends
mCurrentRequest = null;
}
+ @Override
+ @GuardedBy("mLock")
+ protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
+ super.dumpLocked(prefix, pw);
+ dumpInternal(new IndentingPrintWriter(pw, " "));
+ }
+
void dumpInternal(IndentingPrintWriter ipw) {
synchronized (mLock) {
if (mRemoteService != null) {
diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
index 19a246ef0ee5..8f603fc34b32 100644
--- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
+++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
@@ -30,6 +30,7 @@ import static com.android.internal.util.FrameworkStatsLog.AUTO_ROTATE_REPORTED__
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
+import android.content.ComponentName;
import android.content.Context;
import android.hardware.SensorPrivacyManager;
import android.os.Binder;
@@ -42,7 +43,6 @@ import android.rotationresolver.RotationResolverInternal;
import android.service.rotationresolver.RotationResolutionRequest;
import android.service.rotationresolver.RotationResolverService;
import android.text.TextUtils;
-import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.view.Surface;
@@ -143,6 +143,30 @@ public class RotationResolverManagerService extends
return !TextUtils.isEmpty(getServiceConfigPackage(context));
}
+ ComponentName getComponentNameShellCommand(@UserIdInt int userId) {
+ synchronized (mLock) {
+ final RotationResolverManagerPerUserService service = getServiceForUserLocked(userId);
+ if (service != null) {
+ return service.getComponentName();
+ }
+ }
+ return null;
+ }
+
+ void resolveRotationShellCommand(@UserIdInt int userId,
+ RotationResolverInternal.RotationResolverCallbackInternal callbackInternal,
+ RotationResolutionRequest request) {
+ synchronized (mLock) {
+ final RotationResolverManagerPerUserService service = getServiceForUserLocked(userId);
+ if (service != null) {
+ service.resolveRotationLocked(callbackInternal, request, new CancellationSignal());
+ } else {
+ Slog.i(TAG, "service not available for user_id: " + userId);
+ }
+ }
+ }
+
+
static String getServiceConfigPackage(Context context) {
return context.getPackageManager().getRotationResolverPackageName();
}
@@ -201,9 +225,7 @@ public class RotationResolverManagerService extends
return;
}
synchronized (mLock) {
- final RotationResolverManagerPerUserService service = getServiceForUserLocked(
- UserHandle.getCallingUserId());
- service.dumpInternal(new IndentingPrintWriter(pw, " "));
+ dumpLocked("", pw);
}
}
@@ -214,9 +236,8 @@ public class RotationResolverManagerService extends
ResultReceiver resultReceiver) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROTATION_RESOLVER,
TAG);
- final RotationResolverManagerPerUserService service = getServiceForUserLocked(
- UserHandle.getCallingUserId());
- new RotationResolverShellCommand(service).exec(this, in, out, err, args, callback,
+ new RotationResolverShellCommand(RotationResolverManagerService.this).exec(this, in,
+ out, err, args, callback,
resultReceiver);
}
}
diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java b/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java
index 9f3f0968028f..e450dd34a512 100644
--- a/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java
+++ b/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java
@@ -19,7 +19,6 @@ package com.android.server.rotationresolver;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
-import android.os.CancellationSignal;
import android.os.ShellCommand;
import android.rotationresolver.RotationResolverInternal.RotationResolverCallbackInternal;
import android.service.rotationresolver.RotationResolutionRequest;
@@ -31,9 +30,9 @@ final class RotationResolverShellCommand extends ShellCommand {
private static final int INITIAL_RESULT_CODE = -1;
@NonNull
- private final RotationResolverManagerPerUserService mService;
+ private final RotationResolverManagerService mService;
- RotationResolverShellCommand(@NonNull RotationResolverManagerPerUserService service) {
+ RotationResolverShellCommand(@NonNull RotationResolverManagerService service) {
mService = service;
}
@@ -77,7 +76,7 @@ final class RotationResolverShellCommand extends ShellCommand {
case "get-bound-package":
return getBoundPackageName();
case "set-temporary-service":
- return setTemporaryService(getNextArgRequired());
+ return setTemporaryService();
default:
return handleDefaultCommands(cmd);
}
@@ -85,31 +84,33 @@ final class RotationResolverShellCommand extends ShellCommand {
private int getBoundPackageName() {
final PrintWriter out = getOutPrintWriter();
- final ComponentName componentName = mService.getComponentName();
+ final int userId = Integer.parseInt(getNextArgRequired());
+ final ComponentName componentName = mService.getComponentNameShellCommand(userId);
out.println(componentName == null ? "" : componentName.getPackageName());
return 0;
}
- private int setTemporaryService(String serviceName) {
+ private int setTemporaryService() {
final PrintWriter out = getOutPrintWriter();
+ final int userId = Integer.parseInt(getNextArgRequired());
+ final String serviceName = getNextArg();
if (serviceName == null) {
- mService.getMaster().resetTemporaryService(mService.getUserId());
+ mService.resetTemporaryService(userId);
out.println("RotationResolverService temporary reset. ");
return 0;
}
-
final int duration = Integer.parseInt(getNextArgRequired());
- mService.getMaster().setTemporaryService(mService.getUserId(), serviceName, duration);
+ mService.setTemporaryService(userId, serviceName, duration);
out.println("RotationResolverService temporarily set to " + serviceName
+ " for " + duration + "ms");
return 0;
}
private int runResolveRotation() {
+ final int userId = Integer.parseInt(getNextArgRequired());
final RotationResolutionRequest request = new RotationResolutionRequest("",
- Surface.ROTATION_0, Surface.ROTATION_0, true, 2000L);
- mService.resolveRotationLocked(sTestableRotationCallbackInternal, request,
- new CancellationSignal());
+ Surface.ROTATION_0, Surface.ROTATION_0, true, 2000L);
+ mService.resolveRotationShellCommand(userId, sTestableRotationCallbackInternal, request);
return 0;
}
@@ -126,11 +127,12 @@ final class RotationResolverShellCommand extends ShellCommand {
pw.println(" help");
pw.println(" Print this help text.");
pw.println();
- pw.println(" resolve-rotation: request a rotation resolution.");
+ pw.println(" resolve-rotation USER_ID: request a rotation resolution.");
pw.println(" get-last-resolution: show the last rotation resolution result.");
- pw.println(" get-bound-package: print the bound package that implements the service.");
- pw.println(" set-temporary-service [COMPONENT_NAME DURATION]");
+ pw.println(" get-bound-package USER_ID:");
+ pw.println(" 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 no argument.");
+ pw.println(" To reset, call with just the USER_ID argument.");
}
}
diff --git a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
index 122b3f35d8d1..d70f97095816 100644
--- a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
@@ -42,6 +42,7 @@ import android.media.tv.interactive.TvIAppInfo;
import android.media.tv.interactive.TvIAppService;
import android.net.Uri;
import android.os.Binder;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteCallbackList;
@@ -633,6 +634,7 @@ public class TvIAppManagerService extends SystemService {
@Override
public void prepare(String tiasId, int type, int userId) {
+ // TODO: bind service
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
Binder.getCallingUid(), userId, "prepare");
final long identity = Binder.clearCallingIdentity();
@@ -652,6 +654,9 @@ public class TvIAppManagerService extends SystemService {
userState.mServiceStateMap.put(componentName, serviceState);
} else if (serviceState.mService != null) {
serviceState.mService.prepare(type);
+ } else {
+ serviceState.mPendingPrepare = true;
+ serviceState.mPendingPrepareType = type;
}
}
} catch (RemoteException e) {
@@ -662,6 +667,74 @@ public class TvIAppManagerService extends SystemService {
}
@Override
+ public void notifyAppLinkInfo(String tiasId, Bundle appLinkInfo, int userId) {
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, "notifyAppLinkInfo");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ UserState userState = getOrCreateUserStateLocked(resolvedUserId);
+ TvIAppState iAppState = userState.mIAppMap.get(tiasId);
+ if (iAppState == null) {
+ Slogf.e(TAG, "failed to notifyAppLinkInfo - unknown TIAS id "
+ + tiasId);
+ return;
+ }
+ ComponentName componentName = iAppState.mInfo.getComponent();
+ ServiceState serviceState = userState.mServiceStateMap.get(componentName);
+ if (serviceState == null) {
+ serviceState = new ServiceState(
+ componentName, tiasId, resolvedUserId);
+ serviceState.addPendingAppLink(appLinkInfo);
+ userState.mServiceStateMap.put(componentName, serviceState);
+ } else if (serviceState.mService != null) {
+ serviceState.mService.notifyAppLinkInfo(appLinkInfo);
+ } else {
+ serviceState.addPendingAppLink(appLinkInfo);
+ }
+ }
+ } catch (RemoteException e) {
+ Slogf.e(TAG, "error in notifyAppLinkInfo", e);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void sendAppLinkCommand(String tiasId, Bundle command, int userId) {
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, "sendAppLinkCommand");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ UserState userState = getOrCreateUserStateLocked(resolvedUserId);
+ TvIAppState iAppState = userState.mIAppMap.get(tiasId);
+ if (iAppState == null) {
+ Slogf.e(TAG, "failed to sendAppLinkCommand - unknown TIAS id "
+ + tiasId);
+ return;
+ }
+ ComponentName componentName = iAppState.mInfo.getComponent();
+ ServiceState serviceState = userState.mServiceStateMap.get(componentName);
+ if (serviceState == null) {
+ serviceState = new ServiceState(
+ componentName, tiasId, resolvedUserId);
+ serviceState.addPendingAppLinkCommand(command);
+ userState.mServiceStateMap.put(componentName, serviceState);
+ } else if (serviceState.mService != null) {
+ serviceState.mService.sendAppLinkCommand(command);
+ } else {
+ serviceState.addPendingAppLinkCommand(command);
+ }
+ }
+ } catch (RemoteException e) {
+ Slogf.e(TAG, "error in sendAppLinkCommand", e);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void createSession(final ITvIAppClient client, final String iAppServiceId, int type,
int seq, int userId) {
final int callingUid = Binder.getCallingUid();
@@ -1236,7 +1309,9 @@ public class TvIAppManagerService extends SystemService {
private final List<IBinder> mSessionTokens = new ArrayList<>();
private final ServiceConnection mConnection;
private final ComponentName mComponent;
- private final String mIAppSeriviceId;
+ private final String mIAppServiceId;
+ private final List<Bundle> mPendingAppLinkInfo = new ArrayList<>();
+ private final List<Bundle> mPendingAppLinkCommand = new ArrayList<>();
private boolean mPendingPrepare = false;
private Integer mPendingPrepareType = null;
@@ -1255,7 +1330,15 @@ public class TvIAppManagerService extends SystemService {
mPendingPrepare = pendingPrepare;
mPendingPrepareType = prepareType;
mConnection = new IAppServiceConnection(component, userId);
- mIAppSeriviceId = tias;
+ mIAppServiceId = tias;
+ }
+
+ private void addPendingAppLink(Bundle info) {
+ mPendingAppLinkInfo.add(info);
+ }
+
+ private void addPendingAppLinkCommand(Bundle command) {
+ mPendingAppLinkCommand.add(command);
}
}
@@ -1296,6 +1379,40 @@ public class TvIAppManagerService extends SystemService {
}
}
+ if (!serviceState.mPendingAppLinkInfo.isEmpty()) {
+ for (Iterator<Bundle> it = serviceState.mPendingAppLinkInfo.iterator();
+ it.hasNext(); ) {
+ Bundle appLinkInfo = it.next();
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ serviceState.mService.notifyAppLinkInfo(appLinkInfo);
+ it.remove();
+ } catch (RemoteException e) {
+ Slogf.e(TAG, "error in notifyAppLinkInfo(" + appLinkInfo
+ + ") when onServiceConnected", e);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
+ if (!serviceState.mPendingAppLinkCommand.isEmpty()) {
+ for (Iterator<Bundle> it = serviceState.mPendingAppLinkCommand.iterator();
+ it.hasNext(); ) {
+ Bundle command = it.next();
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ serviceState.mService.sendAppLinkCommand(command);
+ it.remove();
+ } catch (RemoteException e) {
+ Slogf.e(TAG, "error in sendAppLinkCommand(" + command
+ + ") when onServiceConnected", e);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
List<IBinder> tokensToBeRemoved = new ArrayList<>();
// And create sessions, if any.
@@ -1351,7 +1468,7 @@ public class TvIAppManagerService extends SystemService {
try {
synchronized (mLock) {
ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
- String iAppServiceId = serviceState.mIAppSeriviceId;
+ String iAppServiceId = serviceState.mIAppServiceId;
UserState userState = getUserStateLocked(mUserId);
notifyStateChangedLocked(userState, iAppServiceId, type, state);
}
@@ -1432,6 +1549,25 @@ public class TvIAppManagerService extends SystemService {
}
@Override
+ public void onCommandRequest(@TvIAppService.IAppServiceCommandType String cmdType,
+ Bundle parameters) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slogf.d(TAG, "onCommandRequest (cmdType=" + cmdType + ", parameters="
+ + parameters.toString() + ")");
+ }
+ if (mSessionState.mSession == null || mSessionState.mClient == null) {
+ return;
+ }
+ try {
+ mSessionState.mClient.onCommandRequest(cmdType, parameters, mSessionState.mSeq);
+ } catch (RemoteException e) {
+ Slogf.e(TAG, "error in onCommandRequest", e);
+ }
+ }
+ }
+
+ @Override
public void onSessionStateChanged(int state) {
synchronized (mLock) {
if (DEBUG) {
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index b17257a9db80..344270427569 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -117,7 +117,6 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
PackageManagerInternal mPmInternal;
/** File storing persisted {@link #mGrantedUriPermissions}. */
- @GuardedBy("mLock")
private final AtomicFile mGrantFile;
/** XML constants used in {@link #mGrantFile} */
@@ -1299,15 +1298,14 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
return false;
}
- @GuardedBy("mLock")
- private void writeGrantedUriPermissionsLocked() {
+ private void writeGrantedUriPermissions() {
if (DEBUG) Slog.v(TAG, "writeGrantedUriPermissions()");
final long startTime = SystemClock.uptimeMillis();
// Snapshot permissions so we can persist without lock
ArrayList<UriPermission.Snapshot> persist = Lists.newArrayList();
- synchronized (this) {
+ synchronized (mLock) {
final int size = mGrantedUriPermissions.size();
for (int i = 0; i < size; i++) {
final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.valueAt(i);
@@ -1330,8 +1328,8 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
out.startTag(null, TAG_URI_GRANT);
out.attributeInt(null, ATTR_SOURCE_USER_ID, perm.uri.sourceUserId);
out.attributeInt(null, ATTR_TARGET_USER_ID, perm.targetUserId);
- out.attribute(null, ATTR_SOURCE_PKG, perm.sourcePkg);
- out.attribute(null, ATTR_TARGET_PKG, perm.targetPkg);
+ out.attributeInterned(null, ATTR_SOURCE_PKG, perm.sourcePkg);
+ out.attributeInterned(null, ATTR_TARGET_PKG, perm.targetPkg);
out.attribute(null, ATTR_URI, String.valueOf(perm.uri.uri));
writeBooleanAttribute(out, ATTR_PREFIX, perm.uri.prefix);
out.attributeInt(null, ATTR_MODE_FLAGS, perm.persistedModeFlags);
@@ -1360,9 +1358,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
public void handleMessage(Message msg) {
switch (msg.what) {
case PERSIST_URI_GRANTS_MSG: {
- synchronized (mLock) {
- writeGrantedUriPermissionsLocked();
- }
+ writeGrantedUriPermissions();
break;
}
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index ec0b5f083a38..0396a1101c64 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -98,6 +98,7 @@ import android.view.animation.Interpolator;
import com.android.internal.R;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.TraceBuffer;
+import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.wm.WindowManagerInternal.AccessibilityControllerInternal;
@@ -277,20 +278,6 @@ final class AccessibilityController {
}
}
- void onRectangleOnScreenRequested(int displayId, Rect rectangle) {
- if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
- mAccessibilityTracing.logTrace(
- TAG + ".onRectangleOnScreenRequested",
- FLAGS_MAGNIFICATION_CALLBACK,
- "displayId=" + displayId + "; rectangle={" + rectangle + "}");
- }
- final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
- if (displayMagnifier != null) {
- displayMagnifier.onRectangleOnScreenRequested(rectangle);
- }
- // Not relevant for the window observer.
- }
-
void onWindowLayersChanged(int displayId) {
if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
| FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
@@ -634,31 +621,6 @@ final class AccessibilityController {
return mForceShowMagnifiableBounds;
}
- void onRectangleOnScreenRequested(Rect rectangle) {
- if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
- mAccessibilityTracing.logTrace(LOG_TAG + ".onRectangleOnScreenRequested",
- FLAGS_MAGNIFICATION_CALLBACK, "rectangle={" + rectangle + "}");
- }
- if (DEBUG_RECTANGLE_REQUESTED) {
- Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle);
- }
- if (!mMagnifedViewport.isMagnifying()) {
- return;
- }
- Rect magnifiedRegionBounds = mTempRect2;
- mMagnifedViewport.getMagnifiedFrameInContentCoords(magnifiedRegionBounds);
- if (magnifiedRegionBounds.contains(rectangle)) {
- return;
- }
- SomeArgs args = SomeArgs.obtain();
- args.argi1 = rectangle.left;
- args.argi2 = rectangle.top;
- args.argi3 = rectangle.right;
- args.argi4 = rectangle.bottom;
- mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED,
- args).sendToTarget();
- }
-
void onWindowLayersChanged() {
if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
mAccessibilityTracing.logTrace(
@@ -1352,7 +1314,6 @@ final class AccessibilityController {
private class MyHandler extends Handler {
public static final int MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED = 1;
- public static final int MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 2;
public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3;
public static final int MESSAGE_NOTIFY_DISPLAY_SIZE_CHANGED = 4;
public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5;
@@ -1372,16 +1333,6 @@ final class AccessibilityController {
magnifiedBounds.recycle();
} break;
- case MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED: {
- SomeArgs args = (SomeArgs) message.obj;
- final int left = args.argi1;
- final int top = args.argi2;
- final int right = args.argi3;
- final int bottom = args.argi4;
- mCallbacks.onRectangleOnScreenRequested(left, top, right, bottom);
- args.recycle();
- } break;
-
case MESSAGE_NOTIFY_USER_CONTEXT_CHANGED: {
mCallbacks.onUserContextChanged();
} break;
@@ -1902,7 +1853,7 @@ final class AccessibilityController {
}
}
- private static final class AccessibilityControllerInternalImpl
+ static final class AccessibilityControllerInternalImpl
implements AccessibilityControllerInternal {
private static AccessibilityControllerInternalImpl sInstance;
@@ -1917,8 +1868,11 @@ final class AccessibilityController {
private final AccessibilityTracing mTracing;
private volatile long mEnabledTracingFlags;
+ private UiChangesForAccessibilityCallbacksDispatcher mCallbacksDispatcher;
+ private final Looper mLooper;
private AccessibilityControllerInternalImpl(WindowManagerService service) {
+ mLooper = service.mH.getLooper();
mTracing = AccessibilityTracing.getInstance(service);
mEnabledTracingFlags = 0L;
}
@@ -1972,6 +1926,90 @@ final class AccessibilityController {
mTracing.logState(where, loggingTypes, callingParams, a11yDump, callingUid, callStack,
timeStamp, processId, threadId, ignoreStackEntries);
}
+
+ @Override
+ public void setUiChangesForAccessibilityCallbacks(
+ UiChangesForAccessibilityCallbacks callbacks) {
+ if (isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ logTrace(
+ TAG + ".setAccessibilityWindowManagerCallbacks",
+ FLAGS_MAGNIFICATION_CALLBACK,
+ "callbacks={" + callbacks + "}");
+ }
+ if (callbacks != null) {
+ if (mCallbacksDispatcher != null) {
+ throw new IllegalStateException("Accessibility window manager callback already "
+ + "set!");
+ }
+ mCallbacksDispatcher =
+ new UiChangesForAccessibilityCallbacksDispatcher(this, mLooper,
+ callbacks);
+ } else {
+ if (mCallbacksDispatcher == null) {
+ throw new IllegalStateException("Accessibility window manager callback already "
+ + "cleared!");
+ }
+ mCallbacksDispatcher = null;
+ }
+ }
+
+ public boolean hasWindowManagerEventDispatcher() {
+ if (isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
+ | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ logTrace(TAG + ".hasCallbacks",
+ FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK);
+ }
+ return mCallbacksDispatcher != null;
+ }
+
+ public void onRectangleOnScreenRequested(int displayId, Rect rectangle) {
+ if (isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ logTrace(
+ TAG + ".onRectangleOnScreenRequested",
+ FLAGS_MAGNIFICATION_CALLBACK,
+ "rectangle={" + rectangle + "}");
+ }
+ if (mCallbacksDispatcher != null) {
+ mCallbacksDispatcher.onRectangleOnScreenRequested(displayId, rectangle);
+ }
+ }
+
+ private static final class UiChangesForAccessibilityCallbacksDispatcher {
+
+ private static final String LOG_TAG = TAG_WITH_CLASS_NAME
+ ? "WindowManagerEventDispatcher" : TAG_WM;
+
+ private static final boolean DEBUG_RECTANGLE_REQUESTED = false;
+
+ private final AccessibilityControllerInternalImpl mAccessibilityTracing;
+
+ @NonNull
+ private final UiChangesForAccessibilityCallbacks mCallbacks;
+
+ private final Handler mHandler;
+
+ UiChangesForAccessibilityCallbacksDispatcher(
+ AccessibilityControllerInternalImpl accessibilityControllerInternal,
+ Looper looper, @NonNull UiChangesForAccessibilityCallbacks callbacks) {
+ mAccessibilityTracing = accessibilityControllerInternal;
+ mCallbacks = callbacks;
+ mHandler = new Handler(looper);
+ }
+
+ void onRectangleOnScreenRequested(int displayId, Rect rectangle) {
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".onRectangleOnScreenRequested",
+ FLAGS_MAGNIFICATION_CALLBACK, "rectangle={" + rectangle + "}");
+ }
+ if (DEBUG_RECTANGLE_REQUESTED) {
+ Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle);
+ }
+ final Message m = PooledLambda.obtainMessage(
+ mCallbacks::onRectangleOnScreenRequested, displayId, rectangle.left,
+ rectangle.top, rectangle.right, rectangle.bottom);
+ mHandler.sendMessage(m);
+ }
+ }
}
private static final class AccessibilityTracing {
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 6c9f1e5bee61..7164c6c601ef 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -49,6 +49,7 @@ import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.IActivityClientController;
+import android.app.ICompatCameraControlCallback;
import android.app.IRequestFinishCallback;
import android.app.PictureInPictureParams;
import android.app.PictureInPictureUiState;
@@ -766,6 +767,22 @@ class ActivityClientController extends IActivityClientController.Stub {
Binder.restoreCallingIdentity(origId);
}
+ @Override
+ public void requestCompatCameraControl(IBinder token, boolean showControl,
+ boolean transformationApplied, ICompatCameraControlCallback callback) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
+ if (r != null) {
+ r.updateCameraCompatState(showControl, transformationApplied, callback);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
/**
* Checks the state of the system and the activity associated with the given {@param token} to
* verify that picture-in-picture is supported for that activity.
diff --git a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
index d736ede7e97e..30cd3c4ab8bf 100644
--- a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
+++ b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityOptions;
import android.app.TaskInfo;
@@ -33,12 +34,13 @@ import java.lang.annotation.RetentionPolicy;
public abstract class ActivityInterceptorCallback {
/**
* Intercept the launch intent based on various signals. If an interception happened, returns
- * a new/existing non-null {@link Intent} which may redirect to another activity.
+ * a new/existing non-null {@link ActivityInterceptResult} which may redirect to another
+ * activity or with new {@link ActivityOptions}.
*
- * @return null if no interception occurred, or a non-null intent which replaces the
- * existing intent.
+ * @return null if no interception occurred, or a non-null result which replaces the existing
+ * intent and activity options.
*/
- public abstract @Nullable Intent intercept(ActivityInterceptorInfo info);
+ public abstract @Nullable ActivityInterceptResult intercept(ActivityInterceptorInfo info);
/**
* Called when an activity is successfully launched.
@@ -108,4 +110,19 @@ public abstract class ActivityInterceptorCallback {
this.checkedOptions = checkedOptions;
}
}
+
+ /**
+ * Data class for storing the intercept result.
+ */
+ public static final class ActivityInterceptResult {
+ @NonNull public final Intent intent;
+ @NonNull public final ActivityOptions activityOptions;
+
+ public ActivityInterceptResult(
+ @NonNull Intent intent,
+ @NonNull ActivityOptions activityOptions) {
+ this.intent = intent;
+ this.activityOptions = activityOptions;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 447f4bed3300..cde5273b38dd 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -232,9 +232,12 @@ import android.annotation.Size;
import android.app.Activity;
import android.app.ActivityManager.TaskDescription;
import android.app.ActivityOptions;
+import android.app.ICompatCameraControlCallback;
import android.app.PendingIntent;
import android.app.PictureInPictureParams;
import android.app.ResultInfo;
+import android.app.TaskInfo;
+import android.app.TaskInfo.CameraCompatControlState;
import android.app.WaitResult;
import android.app.WindowConfiguration;
import android.app.servertransaction.ActivityConfigurationChangeItem;
@@ -712,6 +715,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
@Nullable
private Rect mLetterboxBoundsForFixedOrientationAndAspectRatio;
+ // State of the Camera app compat control which is used to correct stretched viewfinder
+ // in apps that don't handle all possible configurations and changes between them correctly.
+ @CameraCompatControlState
+ private int mCameraCompatControlState = TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
+
+
+ // The callback that allows to ask the calling View to apply the treatment for stretched
+ // issues affecting camera viewfinders when the user clicks on the camera compat control.
+ @Nullable
+ private ICompatCameraControlCallback mCompatCameraControlCallback;
+
+ private final boolean mCameraCompatControlEnabled;
+ private boolean mCameraCompatControlClickedByUser;
+
// activity is not displayed?
// TODO: rename to mNoDisplay
@VisibleForTesting
@@ -1167,6 +1184,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
mLetterboxUiController.dump(pw, prefix);
+
+ pw.println(prefix + "mCameraCompatControlState="
+ + TaskInfo.cameraCompatControlStateToString(mCameraCompatControlState));
+ pw.println(prefix + "mCameraCompatControlEnabled=" + mCameraCompatControlEnabled);
}
static boolean dumpActivity(FileDescriptor fd, PrintWriter pw, int index, ActivityRecord r,
@@ -1572,6 +1593,91 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mLetterboxUiController.getLetterboxInnerBounds(outBounds);
}
+ void updateCameraCompatState(boolean showControl, boolean transformationApplied,
+ ICompatCameraControlCallback callback) {
+ if (!isCameraCompatControlEnabled()) {
+ // Feature is disabled by config_isCameraCompatControlForStretchedIssuesEnabled.
+ return;
+ }
+ if (mCameraCompatControlClickedByUser && (showControl
+ || mCameraCompatControlState == TaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED)) {
+ // The user already applied treatment on this activity or dismissed control.
+ // Respecting their choice.
+ return;
+ }
+ mCompatCameraControlCallback = callback;
+ int newCameraCompatControlState = !showControl
+ ? TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN
+ : transformationApplied
+ ? TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED
+ : TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
+ boolean changed = setCameraCompatControlState(newCameraCompatControlState);
+ if (!changed) {
+ return;
+ }
+ if (newCameraCompatControlState == TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN) {
+ mCameraCompatControlClickedByUser = false;
+ mCompatCameraControlCallback = null;
+ }
+ // Trigger TaskInfoChanged to update the camera compat UI.
+ getTask().dispatchTaskInfoChangedIfNeeded(true /* force */);
+ }
+
+ void updateCameraCompatStateFromUser(@CameraCompatControlState int state) {
+ if (!isCameraCompatControlEnabled()) {
+ // Feature is disabled by config_isCameraCompatControlForStretchedIssuesEnabled.
+ return;
+ }
+ if (state == TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN) {
+ Slog.w(TAG, "Unexpected hidden state in updateCameraCompatState");
+ return;
+ }
+ boolean changed = setCameraCompatControlState(state);
+ mCameraCompatControlClickedByUser = true;
+ if (!changed) {
+ return;
+ }
+ if (state == TaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED) {
+ mCompatCameraControlCallback = null;
+ return;
+ }
+ if (mCompatCameraControlCallback == null) {
+ Slog.w(TAG, "Callback for a camera compat control is null");
+ return;
+ }
+ try {
+ if (state == TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED) {
+ mCompatCameraControlCallback.applyCameraCompatTreatment();
+ } else {
+ mCompatCameraControlCallback.revertCameraCompatTreatment();
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to apply or revert camera compat treatment", e);
+ }
+ }
+
+ private boolean setCameraCompatControlState(@CameraCompatControlState int state) {
+ if (!isCameraCompatControlEnabled()) {
+ // Feature is disabled by config_isCameraCompatControlForStretchedIssuesEnabled.
+ return false;
+ }
+ if (mCameraCompatControlState != state) {
+ mCameraCompatControlState = state;
+ return true;
+ }
+ return false;
+ }
+
+ @CameraCompatControlState
+ int getCameraCompatControlState() {
+ return mCameraCompatControlState;
+ }
+
+ @VisibleForTesting
+ boolean isCameraCompatControlEnabled() {
+ return mCameraCompatControlEnabled;
+ }
+
/**
* @return {@code true} if bar shown within a given rectangle is allowed to be fully transparent
* when the current activity is displayed.
@@ -1794,6 +1900,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
taskDescription = _taskDescription;
mLetterboxUiController = new LetterboxUiController(mWmService, this);
+ mCameraCompatControlEnabled = mWmService.mContext.getResources()
+ .getBoolean(R.bool.config_isCameraCompatControlForStretchedIssuesEnabled);
if (_createTime > 0) {
createTime = _createTime;
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index 352a0709d0d2..658a17bd9ec7 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -61,6 +61,7 @@ import com.android.internal.app.SuspendedAppActivity;
import com.android.internal.app.UnlaunchableAppActivity;
import com.android.server.LocalServices;
import com.android.server.am.ActivityManagerService;
+import com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptResult;
/**
* A class that contains activity intercepting logic for {@link ActivityStarter#startActivityLocked}
@@ -194,11 +195,12 @@ class ActivityStartInterceptor {
for (int i = 0; i < callbacks.size(); i++) {
final ActivityInterceptorCallback callback = callbacks.valueAt(i);
- final Intent newIntent = callback.intercept(interceptorInfo);
- if (newIntent == null) {
+ final ActivityInterceptResult interceptResult = callback.intercept(interceptorInfo);
+ if (interceptResult == null) {
continue;
}
- mIntent = newIntent;
+ mIntent = interceptResult.intent;
+ mActivityOptions = interceptResult.activityOptions;
mCallingPid = mRealCallingPid;
mCallingUid = mRealCallingUid;
mRInfo = mSupervisor.resolveIntent(mIntent, null, mUserId, 0, mRealCallingUid);
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 023db13e18d5..65f9e83dc5df 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1672,7 +1672,8 @@ class ActivityStarter {
}
if (newTransition != null) {
transitionController.requestStartTransition(newTransition,
- mTargetTask, remoteTransition);
+ mTargetTask == null ? r.getTask() : mTargetTask,
+ remoteTransition, null /* displayChange */);
} else if (started) {
// Make the collecting transition wait until this request is ready.
transitionController.setReady(r, false);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index be01646bc25a..173545c30291 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -4441,6 +4441,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
private void updateFontScaleIfNeeded(@UserIdInt int userId) {
+ if (userId != getCurrentUserId()) {
+ return;
+ }
+
final float scaleFactor = Settings.System.getFloatForUser(mContext.getContentResolver(),
FONT_SCALE, 1.0f, userId);
@@ -4457,6 +4461,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
private void updateFontWeightAdjustmentIfNeeded(@UserIdInt int userId) {
+ if (userId != getCurrentUserId()) {
+ return;
+ }
+
final int fontWeightAdjustment =
Settings.Secure.getIntForUser(
mContext.getContentResolver(),
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 05b74dd67fa6..51c8dafa7c85 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -1387,7 +1387,8 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
task.mTransitionController.requestTransitionIfNeeded(TRANSIT_TO_FRONT,
0 /* flags */, task, task /* readyGroupRef */,
- options != null ? options.getRemoteTransition() : null);
+ options != null ? options.getRemoteTransition() : null,
+ null /* displayChange */);
reason = reason + " findTaskToMoveToFront";
boolean reparented = false;
if (task.isResizeable() && canUseActivityOptionsLaunchBounds(options)) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index eb6895264c60..e80a9b9f2a8e 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -167,7 +167,6 @@ import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.ColorSpace;
-import android.graphics.GraphicBuffer;
import android.graphics.Insets;
import android.graphics.Point;
import android.graphics.Rect;
@@ -225,6 +224,7 @@ import android.view.WindowManager.DisplayImePolicy;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
import android.window.DisplayWindowPolicyController;
import android.window.IDisplayAreaOrganizer;
+import android.window.TransitionRequestInfo;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
@@ -1423,7 +1423,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
if (configChanged) {
mWaitingForConfig = true;
if (mTransitionController.isShellTransitionsEnabled()) {
- requestChangeTransitionIfNeeded(changes);
+ requestChangeTransitionIfNeeded(changes, null /* displayChange */);
} else {
mWmService.startFreezingDisplay(0 /* exitAnim */, 0 /* enterAnim */, this);
}
@@ -1843,6 +1843,17 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
}
+ /** Returns {@code true} if the decided new rotation has not applied to configuration yet. */
+ private boolean isRotationChanging() {
+ return mDisplayRotation.getRotation() != getWindowConfiguration().getRotation();
+ }
+
+ private void startFadeRotationAnimationIfNeeded() {
+ if (isRotationChanging()) {
+ startFadeRotationAnimation(false /* shouldDebounce */);
+ }
+ }
+
/**
* Starts the hide animation for the windows which will be rotated seamlessly.
*
@@ -1996,23 +2007,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
void configureDisplayPolicy() {
- final int width = mBaseDisplayWidth;
- final int height = mBaseDisplayHeight;
- final int shortSize;
- final int longSize;
- if (width > height) {
- shortSize = height;
- longSize = width;
- } else {
- shortSize = width;
- longSize = height;
- }
-
- final int shortSizeDp = shortSize * DENSITY_DEFAULT / mBaseDisplayDensity;
- final int longSizeDp = longSize * DENSITY_DEFAULT / mBaseDisplayDensity;
-
mDisplayPolicy.updateConfigurationAndScreenSizeDependentBehaviors();
- mDisplayRotation.configure(width, height, shortSizeDp, longSizeDp);
+ mDisplayRotation.configure(mBaseDisplayWidth, mBaseDisplayHeight);
}
/**
@@ -2795,6 +2791,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mIsSizeForced ? mBaseDisplayHeight : newHeight,
mIsDensityForced ? mBaseDisplayDensity : newDensity);
+ configureDisplayPolicy();
+
+ if (physicalDisplayChanged) {
+ // Reapply the rotation window settings, we are doing this after updating
+ // the screen size and configuring display policy as the rotation depends
+ // on the display size
+ mWmService.mDisplayWindowSettings.applyRotationSettingsToDisplayLocked(this);
+ }
+
// Real display metrics changed, so we should also update initial values.
mInitialDisplayWidth = newWidth;
mInitialDisplayHeight = newHeight;
@@ -3202,11 +3207,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// Hide the windows which are not significant in rotation animation. So that the windows
// don't need to block the unfreeze time.
- if (screenRotationAnimation != null && screenRotationAnimation.hasScreenshot()
- // Do not fade for freezing without rotation change.
- && mDisplayRotation.getRotation() != getWindowConfiguration().getRotation()
- && mFadeRotationAnimationController == null) {
- startFadeRotationAnimation(false /* shouldDebounce */);
+ if (screenRotationAnimation != null && screenRotationAnimation.hasScreenshot()) {
+ startFadeRotationAnimationIfNeeded();
}
}
@@ -3218,18 +3220,24 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
* Requests to start a transition for the display configuration change. The given changes must
* be non-zero. This method is no-op if the display has been collected.
*/
- void requestChangeTransitionIfNeeded(@ActivityInfo.Config int changes) {
+ void requestChangeTransitionIfNeeded(@ActivityInfo.Config int changes,
+ @Nullable TransitionRequestInfo.DisplayChange displayChange) {
final TransitionController controller = mTransitionController;
if (controller.isCollecting()) {
+ if (displayChange != null) {
+ throw new IllegalArgumentException("Provided displayChange for non-new transition");
+ }
if (!controller.isCollecting(this)) {
controller.collect(this);
+ startFadeRotationAnimationIfNeeded();
}
return;
}
- final Transition t = controller.requestTransitionIfNeeded(TRANSIT_CHANGE, this);
+ final Transition t = controller.requestTransitionIfNeeded(TRANSIT_CHANGE, 0 /* flags */,
+ this, this, null /* remoteTransition */, displayChange);
if (t != null) {
mAtmService.startLaunchPowerMode(POWER_MODE_REASON_CHANGE_DISPLAY);
- if (getRotation() != getWindowConfiguration().getRotation()) {
+ if (isRotationChanging()) {
mWmService.mLatencyTracker.onActionStart(ACTION_ROTATE_SCREEN);
controller.mTransitionMetricsReporter.associate(t,
startTime -> mWmService.mLatencyTracker.onActionEnd(ACTION_ROTATE_SCREEN));
@@ -4092,8 +4100,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// Make IME snapshot as trusted overlay
InputMonitor.setTrustedOverlayInputInfo(imeSurface, t, getDisplayId(),
"IME-snapshot-surface");
- GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(buffer);
- t.setBuffer(imeSurface, graphicBuffer);
+ t.setBuffer(imeSurface, buffer);
t.setColorSpace(mSurfaceControl, ColorSpace.get(ColorSpace.Named.SRGB));
t.setRelativeLayer(imeSurface, activity.getSurfaceControl(), 1);
t.setPosition(imeSurface, mInputMethodWindow.getDisplayFrame().left,
@@ -5694,7 +5701,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mWmService.mDisplayNotificationController.dispatchDisplayChanged(
this, getConfiguration());
if (isReady() && mTransitionController.isShellTransitionsEnabled()) {
- requestChangeTransitionIfNeeded(changes);
+ requestChangeTransitionIfNeeded(changes, null /* displayChange */);
}
}
return changes;
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 4684cb18ebfb..8c8b33f344fd 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -62,6 +62,7 @@ import android.util.proto.ProtoOutputStream;
import android.view.IDisplayWindowRotationCallback;
import android.view.IWindowManager;
import android.view.Surface;
+import android.window.TransitionRequestInfo;
import android.window.WindowContainerTransaction;
import com.android.internal.R;
@@ -293,7 +294,7 @@ public class DisplayRotation {
currentUserRes.getBoolean(R.bool.config_allowSeamlessRotationDespiteNavBarMoving);
}
- void configure(int width, int height, int shortSizeDp, int longSizeDp) {
+ void configure(int width, int height) {
final Resources res = mContext.getResources();
if (width > height) {
mLandscapeRotation = Surface.ROTATION_0;
@@ -509,8 +510,11 @@ public class DisplayRotation {
if (useShellTransitions) {
final boolean wasCollecting = mDisplayContent.mTransitionController.isCollecting();
+ final TransitionRequestInfo.DisplayChange change = wasCollecting ? null
+ : new TransitionRequestInfo.DisplayChange(mDisplayContent.getDisplayId(),
+ oldRotation, mRotation);
mDisplayContent.requestChangeTransitionIfNeeded(
- ActivityInfo.CONFIG_WINDOW_CONFIGURATION);
+ ActivityInfo.CONFIG_WINDOW_CONFIGURATION, change);
if (wasCollecting) {
// Use remote-rotation infra since the transition has already been requested
// TODO(shell-transitions): Remove this once lifecycle management can cover all
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index 6d5abe1e2f31..8260fd6c09f4 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -265,10 +265,6 @@ class DisplayWindowSettings {
dc.mIsDensityForced = hasDensityOverride;
dc.mIsSizeForced = hasSizeOverride;
- final boolean ignoreOrientationRequest = settings.mIgnoreOrientationRequest != null
- ? settings.mIgnoreOrientationRequest : false;
- dc.setIgnoreOrientationRequest(ignoreOrientationRequest);
-
final boolean ignoreDisplayCutout = settings.mIgnoreDisplayCutout != null
? settings.mIgnoreDisplayCutout : false;
dc.mIgnoreDisplayCutout = ignoreDisplayCutout;
@@ -288,6 +284,15 @@ class DisplayWindowSettings {
dc.mDontMoveToTop = dontMoveToTop;
}
+ void applyRotationSettingsToDisplayLocked(DisplayContent dc) {
+ final DisplayInfo displayInfo = dc.getDisplayInfo();
+ final SettingsProvider.SettingsEntry settings = mSettingsProvider.getSettings(displayInfo);
+
+ final boolean ignoreOrientationRequest = settings.mIgnoreOrientationRequest != null
+ ? settings.mIgnoreOrientationRequest : false;
+ dc.setIgnoreOrientationRequest(ignoreOrientationRequest);
+ }
+
/**
* Updates settings for the given display after system features are loaded into window manager
* service, e.g. if this device is PC and if this device supports freeform.
diff --git a/services/core/java/com/android/server/wm/FadeAnimationController.java b/services/core/java/com/android/server/wm/FadeAnimationController.java
index 817b27a55c1b..561a07061bb4 100644
--- a/services/core/java/com/android/server/wm/FadeAnimationController.java
+++ b/services/core/java/com/android/server/wm/FadeAnimationController.java
@@ -21,7 +21,6 @@ import static com.android.server.wm.WindowAnimationSpecProto.ANIMATION;
import android.annotation.NonNull;
import android.content.Context;
-import android.util.ArrayMap;
import android.util.proto.ProtoOutputStream;
import android.view.SurfaceControl;
import android.view.animation.Animation;
@@ -38,7 +37,6 @@ import java.io.PrintWriter;
public class FadeAnimationController {
protected final DisplayContent mDisplayContent;
protected final Context mContext;
- protected final ArrayMap<WindowToken, Runnable> mDeferredFinishCallbacks = new ArrayMap<>();
public FadeAnimationController(DisplayContent displayContent) {
mDisplayContent = displayContent;
@@ -78,16 +76,8 @@ public class FadeAnimationController {
return;
}
- // We deferred the end of the animation when hiding the token, so we need to end it now that
- // it's shown again.
- final SurfaceAnimator.OnAnimationFinishedCallback finishedCallback = show ? (t, r) -> {
- final Runnable runnable = mDeferredFinishCallbacks.remove(windowToken);
- if (runnable != null) {
- runnable.run();
- }
- } : null;
windowToken.startAnimation(windowToken.getPendingTransaction(), animationAdapter,
- show /* hidden */, animationType, finishedCallback);
+ show /* hidden */, animationType, null /* finishedCallback */);
}
protected FadeAnimationAdapter createAdapter(LocalAnimationAdapter.AnimationSpec animationSpec,
@@ -135,7 +125,7 @@ public class FadeAnimationController {
};
}
- protected class FadeAnimationAdapter extends LocalAnimationAdapter {
+ protected static class FadeAnimationAdapter extends LocalAnimationAdapter {
protected final boolean mShow;
protected final WindowToken mToken;
@@ -149,13 +139,10 @@ public class FadeAnimationController {
@Override
public boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) {
- // We defer the end of the hide animation to ensure the tokens stay hidden until
- // we show them again.
- if (!mShow) {
- mDeferredFinishCallbacks.put(mToken, endDeferFinishCallback);
- return true;
- }
- return false;
+ // Defer the finish callback (restore leash) of the hide animation to ensure the token
+ // stay hidden until it needs to show again. Besides, when starting the show animation,
+ // the previous hide animation will be cancelled, so the callback can be ignored.
+ return !mShow;
}
}
}
diff --git a/services/core/java/com/android/server/wm/FadeRotationAnimationController.java b/services/core/java/com/android/server/wm/FadeRotationAnimationController.java
index cf36c85ebabf..bbda577e9c57 100644
--- a/services/core/java/com/android/server/wm/FadeRotationAnimationController.java
+++ b/services/core/java/com/android/server/wm/FadeRotationAnimationController.java
@@ -50,7 +50,7 @@ public class FadeRotationAnimationController extends FadeAnimationController {
/** Whether to use constant zero alpha animation. */
private boolean mHideImmediately;
- /** Whether this controller is triggered from shell transition. */
+ /** Whether this controller is triggered from shell transition with type CHANGE. */
private final boolean mIsChangeTransition;
/** Whether the start transaction of the transition is committed (by shell). */
@@ -59,21 +59,30 @@ public class FadeRotationAnimationController extends FadeAnimationController {
/** The list to store the drawn tokens before the rotation animation starts. */
private ArrayList<WindowToken> mPendingShowTokens;
+ /** It is used when the display has rotated, but some windows fade out in old rotation. */
+ private SeamlessRotator mRotator;
+
+ private final int mOriginalRotation;
+ private final boolean mHasScreenRotationAnimation;
+
public FadeRotationAnimationController(DisplayContent displayContent) {
super(displayContent);
mService = displayContent.mWmService;
- mIsChangeTransition = displayContent.inTransition()
- && displayContent.mTransitionController.getCollectingTransitionType()
- == WindowManager.TRANSIT_CHANGE;
+ mOriginalRotation = displayContent.getWindowConfiguration().getRotation();
+ final int transitionType =
+ displayContent.mTransitionController.getCollectingTransitionType();
+ mIsChangeTransition = transitionType == WindowManager.TRANSIT_CHANGE;
+ // Only CHANGE type (rotation animation) needs to wait for the start transaction.
mIsStartTransactionCommitted = !mIsChangeTransition;
- mTimeoutRunnable = displayContent.getRotationAnimation() != null
- || mIsChangeTransition ? () -> {
+ mTimeoutRunnable = displayContent.inTransition() ? () -> {
synchronized (mService.mGlobalLock) {
displayContent.finishFadeRotationAnimationIfPossible();
mService.mWindowPlacerLocked.performSurfacePlacement();
}
} : null;
- if (mTimeoutRunnable != null) {
+ mHasScreenRotationAnimation =
+ displayContent.getRotationAnimation() != null || mIsChangeTransition;
+ if (mHasScreenRotationAnimation) {
// Hide the windows immediately because screen should have been covered by screenshot.
mHideImmediately = true;
}
@@ -103,6 +112,19 @@ public class FadeRotationAnimationController extends FadeAnimationController {
}, true /* traverseTopToBottom */);
}
+ @Override
+ public void fadeWindowToken(boolean show, WindowToken windowToken, int animationType) {
+ if (show) {
+ final SurfaceControl leash = mTargetWindowTokens.remove(windowToken);
+ if (leash != null && mRotator != null) {
+ // The leash was unrotated by start transaction of transition. Clear the transform
+ // to reshow the window in current rotation.
+ mRotator.setIdentityMatrix(mDisplayContent.getPendingTransaction(), leash);
+ }
+ }
+ super.fadeWindowToken(show, windowToken, animationType);
+ }
+
/** Applies show animation on the previously hidden window tokens. */
void show() {
for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
@@ -125,19 +147,23 @@ public class FadeRotationAnimationController extends FadeAnimationController {
* controller is created for normal rotation.
*/
boolean show(WindowToken token) {
+ if (!isTargetToken(token)) return false;
if (!mIsStartTransactionCommitted) {
// The fade-in animation should only start after the screenshot layer is shown by shell.
// Otherwise the window will be blinking before the rotation animation starts. So store
// to a pending list and animate them until the transaction is committed.
- if (mTargetWindowTokens.containsKey(token)) {
- if (mPendingShowTokens == null) {
- mPendingShowTokens = new ArrayList<>();
- }
- mPendingShowTokens.add(token);
+ if (mPendingShowTokens == null) {
+ mPendingShowTokens = new ArrayList<>();
}
+ mPendingShowTokens.add(token);
+ return false;
+ }
+ if (!mHasScreenRotationAnimation && token.mTransitionController.inTransition()) {
+ // Defer showing to onTransitionFinished().
return false;
}
- if (mTimeoutRunnable != null && mTargetWindowTokens.remove(token) != null) {
+ // If the timeout runnable is null (fixed rotation), the case will be handled by show().
+ if (mTimeoutRunnable != null) {
fadeWindowToken(true /* show */, token, ANIMATION_TYPE_FIXED_TRANSFORM);
if (mTargetWindowTokens.isEmpty()) {
mService.mH.removeCallbacks(mTimeoutRunnable);
@@ -177,6 +203,15 @@ public class FadeRotationAnimationController extends FadeAnimationController {
return mTargetWindowTokens.containsKey(token);
}
+ /**
+ * Whether the insets animation leash should use previous position when running fade out
+ * animation in rotated display.
+ */
+ boolean shouldFreezeInsetsPosition(WindowState w) {
+ return !mHasScreenRotationAnimation && w.mTransitionController.inTransition()
+ && isTargetToken(w.mToken);
+ }
+
void setOnShowRunnable(Runnable onShowRunnable) {
mOnShowRunnable = onShowRunnable;
}
@@ -186,6 +221,22 @@ public class FadeRotationAnimationController extends FadeAnimationController {
* transition starts. And associate transaction callback to consume pending animations.
*/
void setupStartTransaction(SurfaceControl.Transaction t) {
+ if (!mIsChangeTransition) {
+ // Take OPEN/CLOSE transition type as the example, the non-activity windows need to
+ // fade out in previous rotation while display has rotated to the new rotation, so
+ // their leashes are unrotated with the start transaction.
+ mRotator = new SeamlessRotator(mOriginalRotation,
+ mDisplayContent.getWindowConfiguration().getRotation(),
+ mDisplayContent.getDisplayInfo(),
+ false /* applyFixedTransformationHint */);
+ for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
+ final SurfaceControl leash = mTargetWindowTokens.valueAt(i);
+ if (leash != null) {
+ mRotator.applyTransform(t, leash);
+ }
+ }
+ return;
+ }
// Hide the windows immediately because a screenshot layer should cover the screen.
for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
final SurfaceControl leash = mTargetWindowTokens.valueAt(i);
@@ -208,9 +259,30 @@ public class FadeRotationAnimationController extends FadeAnimationController {
});
}
+ void onTransitionFinished() {
+ if (mIsChangeTransition) {
+ // With screen rotation animation, the windows are always faded in when they are drawn.
+ // Because if they are drawn fast enough, the fade animation should not be observable.
+ return;
+ }
+ // For other transition types, the fade-in animation runs after the transition to make the
+ // transition animation (e.g. launch activity) look cleaner.
+ for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
+ final WindowToken token = mTargetWindowTokens.keyAt(i);
+ for (int j = token.getChildCount() - 1; j >= 0; j--) {
+ // Only fade in the drawn windows. If the remaining windows are drawn later,
+ // show(WindowToken) will be called to fade in them.
+ if (token.getChildAt(j).isDrawFinishedLw()) {
+ mDisplayContent.finishFadeRotationAnimation(token);
+ break;
+ }
+ }
+ }
+ }
+
@Override
public Animation getFadeInAnimation() {
- if (mTimeoutRunnable != null) {
+ if (mHasScreenRotationAnimation) {
// Use a shorter animation so it is easier to align with screen rotation animation.
return AnimationUtils.loadAnimation(mContext, R.anim.screen_rotate_0_enter);
}
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index af917264698e..a8a923140a41 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -297,6 +297,14 @@ class InsetsSourceProvider {
}
private Point getWindowFrameSurfacePosition() {
+ if (mControl != null) {
+ final FadeRotationAnimationController fadeController =
+ mWin.mDisplayContent.getFadeRotationAnimationController();
+ if (fadeController != null && fadeController.shouldFreezeInsetsPosition(mWin)) {
+ // Use previous position because the fade-out animation runs in old rotation.
+ return mControl.getSurfacePosition();
+ }
+ }
final Rect frame = mWin.getFrame();
final Point position = new Point();
mWin.transformFrameToSurfacePosition(frame.left, frame.top, position);
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index 94a175caba22..8a2d11636fe3 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -251,15 +251,47 @@ public class LockTaskController {
*/
boolean activityBlockedFromFinish(ActivityRecord activity) {
final Task task = activity.getTask();
- if (activity == task.getRootActivity()
- && activity == task.getTopNonFinishingActivity()
- && task.mLockTaskAuth != LOCK_TASK_AUTH_LAUNCHABLE_PRIV
- && isRootTask(task)) {
- Slog.i(TAG, "Not finishing task in lock task mode");
- showLockTaskToast();
- return true;
+ if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE_PRIV || !isRootTask(task)) {
+ return false;
}
- return false;
+
+ final ActivityRecord taskTop = task.getTopNonFinishingActivity();
+ final ActivityRecord taskRoot = task.getRootActivity();
+ // If task has more than one Activity, verify if there's only adjacent TaskFragments that
+ // should be finish together in the Task.
+ if (activity != taskRoot || activity != taskTop) {
+ final TaskFragment taskFragment = activity.getTaskFragment();
+ final TaskFragment adjacentTaskFragment = taskFragment.getAdjacentTaskFragment();
+ if (taskFragment.asTask() != null
+ || !taskFragment.isDelayLastActivityRemoval()
+ || adjacentTaskFragment == null) {
+ // Don't block activity from finishing if the TaskFragment don't have any adjacent
+ // TaskFragment, or it won't finish together with its adjacent TaskFragment.
+ return false;
+ }
+
+ final boolean hasOtherActivityInTaskFragment =
+ taskFragment.getActivity(a -> !a.finishing && a != activity) != null;
+ if (hasOtherActivityInTaskFragment) {
+ // Don't block activity from finishing if there's other Activity in the same
+ // TaskFragment.
+ return false;
+ }
+
+ final boolean hasOtherActivityInTask = task.getActivity(a -> !a.finishing
+ && a != activity && a.getTaskFragment() != adjacentTaskFragment) != null;
+ if (hasOtherActivityInTask) {
+ // Do not block activity from finishing if there are another running activities
+ // after the current and adjacent TaskFragments are removed. Note that we don't
+ // check activities in adjacent TaskFragment because it will be finished together
+ // with TaskFragment regardless of numbers of activities.
+ return false;
+ }
+ }
+
+ Slog.i(TAG, "Not finishing task in lock task mode");
+ showLockTaskToast();
+ return true;
}
/**
diff --git a/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java b/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java
index af8293aab977..80f2ab62f120 100644
--- a/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java
+++ b/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java
@@ -95,14 +95,6 @@ public class NavBarFadeAnimationController extends FadeAnimationController{
} else {
fadeAnim.run();
}
- } else {
- // If fade rotation animation is running and controlling the nav bar, make sure we empty
- // the mDeferredFinishCallbacks and defer the runnable until fade rotation animation
- // finishes.
- final Runnable runnable = mDeferredFinishCallbacks.remove(mNavigationBar.mToken);
- if (runnable != null) {
- controller.setOnShowRunnable(runnable);
- }
}
}
diff --git a/services/core/java/com/android/server/wm/PinnedTaskController.java b/services/core/java/com/android/server/wm/PinnedTaskController.java
index 9ad30da1cb54..1da0fe731709 100644
--- a/services/core/java/com/android/server/wm/PinnedTaskController.java
+++ b/services/core/java/com/android/server/wm/PinnedTaskController.java
@@ -243,7 +243,8 @@ class PinnedTaskController {
int oldRotation, int newRotation) {
final Rect bounds = mDestRotatedBounds;
final PictureInPictureSurfaceTransaction pipTx = mPipTransaction;
- if (bounds == null && pipTx == null) {
+ final boolean emptyPipPositionTx = pipTx == null || pipTx.mPosition == null;
+ if (bounds == null && emptyPipPositionTx) {
return;
}
final TaskDisplayArea taskArea = mDisplayContent.getDefaultTaskDisplayArea();
@@ -255,7 +256,7 @@ class PinnedTaskController {
mDestRotatedBounds = null;
mPipTransaction = null;
final Rect areaBounds = taskArea.getBounds();
- if (pipTx != null && pipTx.mPosition != null) {
+ if (!emptyPipPositionTx) {
// The transaction from recents animation is in old rotation. So the position needs to
// be rotated.
float dx = pipTx.mPosition.x;
diff --git a/services/core/java/com/android/server/wm/RefreshRatePolicy.java b/services/core/java/com/android/server/wm/RefreshRatePolicy.java
index b63843dd20d4..7bddb620c94d 100644
--- a/services/core/java/com/android/server/wm/RefreshRatePolicy.java
+++ b/services/core/java/com/android/server/wm/RefreshRatePolicy.java
@@ -164,7 +164,19 @@ class RefreshRatePolicy {
return 0;
}
- return w.mAttrs.preferredMinDisplayRefreshRate;
+ if (w.mAttrs.preferredMinDisplayRefreshRate > 0) {
+ return w.mAttrs.preferredMinDisplayRefreshRate;
+ }
+
+ String packageName = w.getOwningPackage();
+ // If app is using Camera, we set both the min and max refresh rate to the camera's
+ // preferred refresh rate to make sure we don't end up with a refresh rate lower
+ // than the camera capture rate, which will lead to dropping camera frames.
+ if (mNonHighRefreshRatePackages.contains(packageName)) {
+ return mLowRefreshRateMode.getRefreshRate();
+ }
+
+ return 0;
}
float getPreferredMaxRefreshRate(WindowState w) {
diff --git a/services/core/java/com/android/server/wm/SeamlessRotator.java b/services/core/java/com/android/server/wm/SeamlessRotator.java
index 4cc369f0a187..c20b85858c44 100644
--- a/services/core/java/com/android/server/wm/SeamlessRotator.java
+++ b/services/core/java/com/android/server/wm/SeamlessRotator.java
@@ -20,7 +20,6 @@ import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import android.graphics.Matrix;
-import android.os.IBinder;
import android.view.DisplayInfo;
import android.view.Surface.Rotation;
import android.view.SurfaceControl;
@@ -73,7 +72,7 @@ public class SeamlessRotator {
* global display rotation.
*/
public void unrotate(Transaction transaction, WindowContainer win) {
- transaction.setMatrix(win.getSurfaceControl(), mTransform, mFloat9);
+ applyTransform(transaction, win.getSurfaceControl());
// WindowState sets the position of the window so transform the position and update it.
final float[] winSurfacePos = {win.mLastSurfacePosition.x, win.mLastSurfacePosition.y};
mTransform.mapPoints(winSurfacePos);
@@ -83,6 +82,10 @@ public class SeamlessRotator {
}
}
+ void applyTransform(Transaction t, SurfaceControl sc) {
+ t.setMatrix(sc, mTransform, mFloat9);
+ }
+
/**
* Returns the rotation of the display before it started rotating.
*
@@ -106,14 +109,17 @@ public class SeamlessRotator {
return;
}
- mTransform.reset();
- t.setMatrix(win.mSurfaceControl, mTransform, mFloat9);
+ setIdentityMatrix(t, win.mSurfaceControl);
t.setPosition(win.mSurfaceControl, win.mLastSurfacePosition.x, win.mLastSurfacePosition.y);
if (mApplyFixedTransformHint) {
t.unsetFixedTransformHint(win.mSurfaceControl);
}
}
+ void setIdentityMatrix(Transaction t, SurfaceControl sc) {
+ t.setMatrix(sc, Matrix.IDENTITY_MATRIX, mFloat9);
+ }
+
public void dump(PrintWriter pw) {
pw.print("{old="); pw.print(mOldRotation); pw.print(", new="); pw.print(mNewRotation);
pw.print("}");
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 3e558114abdc..7c3432eca70b 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3395,11 +3395,18 @@ class Task extends TaskFragment {
info.topActivityInfo = mReuseActivitiesReport.top != null
? mReuseActivitiesReport.top.info
: null;
+
+ boolean isTopActivityResumed = mReuseActivitiesReport.top != null
+ && mReuseActivitiesReport.top.getOrganizedTask() == this
+ && mReuseActivitiesReport.top.isState(RESUMED);
// Whether the direct top activity is in size compat mode on foreground.
- info.topActivityInSizeCompat = mReuseActivitiesReport.top != null
- && mReuseActivitiesReport.top.getOrganizedTask() == this
- && mReuseActivitiesReport.top.inSizeCompatMode()
- && mReuseActivitiesReport.top.isState(RESUMED);
+ info.topActivityInSizeCompat = isTopActivityResumed
+ && mReuseActivitiesReport.top.inSizeCompatMode();
+ // Whether the direct top activity requested showing camera compat control.
+ info.cameraCompatControlState = isTopActivityResumed
+ ? mReuseActivitiesReport.top.getCameraCompatControlState()
+ : TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
+
info.launchCookies.clear();
info.addLaunchCookie(mLaunchCookie);
forAllActivities(r -> {
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index c845dca57c18..97cb512455f6 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -240,7 +240,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
/**
* Whether to delay the last activity of TaskFragment being immediately removed while finishing.
* This should only be set on a embedded TaskFragment, where the organizer can have the
- * opportunity to perform other actions or animations.
+ * opportunity to perform animations and finishing the adjacent TaskFragment.
*/
private boolean mDelayLastActivityRemoval;
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 3d5f9881e044..037d582edc30 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import static android.app.TaskInfo.cameraCompatControlStateToString;
+
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
import static com.android.server.wm.ActivityTaskManagerService.enforceTaskPermission;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
@@ -931,6 +933,35 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
}
}
+ @Override
+ public void updateCameraCompatControlState(WindowContainerToken token, int state) {
+ enforceTaskPermission("updateCameraCompatControlState()");
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final WindowContainer wc = WindowContainer.fromBinder(token.asBinder());
+ if (wc == null) {
+ Slog.w(TAG, "Could not resolve window from token");
+ return;
+ }
+ final Task task = wc.asTask();
+ if (task == null) {
+ Slog.w(TAG, "Could not resolve task from token");
+ return;
+ }
+ ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
+ "Update camera compat control state to %s for taskId=%d",
+ cameraCompatControlStateToString(state), task.mTaskId);
+ final ActivityRecord activity = task.getTopNonFinishingActivity();
+ if (activity != null) {
+ activity.updateCameraCompatStateFromUser(state);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
public boolean handleInterceptBackPressedOnTaskRoot(Task task) {
if (task == null || !task.isOrganized()
|| !mInterceptBackPressedOnRootTasks.contains(task.mTaskId)) {
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index dae004dd5f6f..c7c3bb6b31e2 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -143,6 +143,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
/** The final animation targets derived from participants after promotion. */
private ArraySet<WindowContainer> mTargets = null;
+ /** The main display running this transition. */
+ private DisplayContent mTargetDisplay;
+
/**
* Set of participating windowtokens (activity/wallpaper) which are visible at the end of
* the transition animation.
@@ -473,6 +476,12 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
mController.mAtm.mRootWindowContainer.getDisplayContent(mRecentsDisplayId);
dc.getInputMonitor().setActiveRecents(null /* activity */, null /* layer */);
}
+
+ final FadeRotationAnimationController fadeRotationController =
+ mTargetDisplay.getFadeRotationAnimationController();
+ if (fadeRotationController != null) {
+ fadeRotationController.onTransitionFinished();
+ }
}
void abort() {
@@ -514,6 +523,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
}
if (dc == null) dc = mController.mAtm.mRootWindowContainer.getDefaultDisplay();
+ mTargetDisplay = dc;
if (mState == STATE_ABORT) {
mController.abort(this);
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 06030dd3cca1..ffe146219c6c 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -247,7 +247,7 @@ class TransitionController {
@WindowManager.TransitionFlags int flags, @Nullable WindowContainer trigger,
@NonNull WindowContainer readyGroupRef) {
return requestTransitionIfNeeded(type, flags, trigger, readyGroupRef,
- null /* remoteTransition */);
+ null /* remoteTransition */, null /* displayChange */);
}
private static boolean isExistenceType(@WindowManager.TransitionType int type) {
@@ -264,12 +264,16 @@ class TransitionController {
@Nullable
Transition requestTransitionIfNeeded(@WindowManager.TransitionType int type,
@WindowManager.TransitionFlags int flags, @Nullable WindowContainer trigger,
- @NonNull WindowContainer readyGroupRef, @Nullable RemoteTransition remoteTransition) {
+ @NonNull WindowContainer readyGroupRef, @Nullable RemoteTransition remoteTransition,
+ @Nullable TransitionRequestInfo.DisplayChange displayChange) {
if (mTransitionPlayer == null) {
return null;
}
Transition newTransition = null;
if (isCollecting()) {
+ if (displayChange != null) {
+ throw new IllegalArgumentException("Provided displayChange for a non-new request");
+ }
// Make the collecting transition wait until this request is ready.
mCollectingTransition.setReady(readyGroupRef, false);
if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0) {
@@ -278,7 +282,7 @@ class TransitionController {
}
} else {
newTransition = requestStartTransition(createTransition(type, flags),
- trigger != null ? trigger.asTask() : null, remoteTransition);
+ trigger != null ? trigger.asTask() : null, remoteTransition, displayChange);
}
if (trigger != null) {
if (isExistenceType(type)) {
@@ -293,7 +297,8 @@ class TransitionController {
/** Asks the transition player (shell) to start a created but not yet started transition. */
@NonNull
Transition requestStartTransition(@NonNull Transition transition, @Nullable Task startTask,
- @Nullable RemoteTransition remoteTransition) {
+ @Nullable RemoteTransition remoteTransition,
+ @Nullable TransitionRequestInfo.DisplayChange displayChange) {
try {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
"Requesting StartTransition: %s", transition);
@@ -303,7 +308,7 @@ class TransitionController {
startTask.fillTaskInfo(info);
}
mTransitionPlayer.requestStartTransition(transition, new TransitionRequestInfo(
- transition.mType, info, remoteTransition));
+ transition.mType, info, remoteTransition, displayChange));
} catch (RemoteException e) {
Slog.e(TAG, "Error requesting transition", e);
transition.start();
@@ -317,7 +322,7 @@ class TransitionController {
if (wc.isVisibleRequested()) {
if (!isCollecting()) {
requestStartTransition(createTransition(TRANSIT_CLOSE, 0 /* flags */),
- wc.asTask(), null /* remoteTransition */);
+ wc.asTask(), null /* remoteTransition */, null /* displayChange */);
}
collectExistenceChange(wc);
} else {
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 34cb5ae032ba..62c674b15f89 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -104,6 +104,32 @@ public abstract class WindowManagerInternal {
void logTrace(String where, long loggingTypeFlags, String callingParams,
byte[] a11yDump, int callingUid, StackTraceElement[] callStack, long timeStamp,
int processId, long threadId, Set<String> ignoreStackEntries);
+
+ /**
+ * Set by the accessibility related modules which want to listen the event dispatched from
+ * window manager. Accessibility modules can use these callbacks to handle some display
+ * manipulations.
+ * @param callbacks The callbacks to invoke.
+ */
+ void setUiChangesForAccessibilityCallbacks(UiChangesForAccessibilityCallbacks callbacks);
+
+ /**
+ * This interface is used by window manager to dispatch some ui change events which may
+ * affect the screen accessibility features.
+ */
+ interface UiChangesForAccessibilityCallbacks {
+ /**
+ * Called when an application requests a rectangle focus on the screen.
+ *
+ * @param displayId The logical display id
+ * @param left The rectangle left.
+ * @param top The rectangle top.
+ * @param right The rectangle right.
+ * @param bottom The rectangle bottom.
+ */
+ void onRectangleOnScreenRequested(int displayId, int left, int top, int right,
+ int bottom);
+ }
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 58860de3dcdd..86775f629df0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2144,11 +2144,13 @@ public class WindowManagerService extends IWindowManager.Stub
}
public void onRectangleOnScreenRequested(IBinder token, Rect rectangle) {
+ final AccessibilityController.AccessibilityControllerInternalImpl a11yControllerInternal =
+ AccessibilityController.getAccessibilityControllerInternal(this);
synchronized (mGlobalLock) {
- if (mAccessibilityController.hasCallbacks()) {
+ if (a11yControllerInternal.hasWindowManagerEventDispatcher()) {
WindowState window = mWindowMap.get(token);
if (window != null) {
- mAccessibilityController.onRectangleOnScreenRequested(
+ a11yControllerInternal.onRectangleOnScreenRequested(
window.getDisplayId(), rectangle);
}
}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index f5eb0a7a34d3..7b4fd365905c 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -163,7 +163,7 @@ cc_defaults {
"android.hardware.memtrack-V1-ndk",
"android.hardware.power@1.0",
"android.hardware.power@1.1",
- "android.hardware.power-V2-cpp",
+ "android.hardware.power-V3-cpp",
"android.hardware.power.stats@1.0",
"android.hardware.power.stats-V1-ndk",
"android.hardware.thermal@1.0",
diff --git a/services/core/jni/com_android_server_UsbAlsaJackDetector.cpp b/services/core/jni/com_android_server_UsbAlsaJackDetector.cpp
index ccb4f5995330..1c574fbb8df7 100644
--- a/services/core/jni/com_android_server_UsbAlsaJackDetector.cpp
+++ b/services/core/jni/com_android_server_UsbAlsaJackDetector.cpp
@@ -151,13 +151,8 @@ int register_android_server_UsbAlsaJackDetector(JNIEnv *env)
return -1;
}
- if (!jniRegisterNativeMethods(env, "com/android/server/usb/UsbAlsaJackDetector",
- method_table, NELEM(method_table))) {
- ALOGE("Can't register UsbAlsaJackDetector native methods");
- return -1;
- }
-
- return 0;
+ return jniRegisterNativeMethods(env, "com/android/server/usb/UsbAlsaJackDetector",
+ method_table, NELEM(method_table));
}
}
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 06f5aed026ce..f0f779dc9ac9 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -997,7 +997,7 @@ static void android_location_gnss_hal_GnssNative_init_once(JNIEnv* env, jobject
}
}
- if (gnssHalAidl != nullptr) {
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
sp<IAGnssAidl> agnssAidl;
auto status = gnssHalAidl->getExtensionAGnss(&agnssAidl);
if (checkAidlStatus(status, "Unable to get a handle to AGnss interface.")) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index bfceeda08e3d..23b685f5936c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -91,18 +91,18 @@ import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_UNKNOWN;
import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_SET_ERROR_FAILURE_SETTING;
import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_SET_NO_ERROR;
import static android.app.admin.DevicePolicyManager.PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
-import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_ADMIN_PACKAGE_INSTALLATION_FAILED;
-import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_PRE_CONDITION_FAILED;
-import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_PROFILE_CREATION_FAILED;
-import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_REMOVE_NON_REQUIRED_APPS_FAILED;
-import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_SETTING_PROFILE_OWNER_FAILED;
-import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED;
-import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_STARTING_PROFILE_FAILED;
import static android.app.admin.DevicePolicyManager.STATE_USER_UNMANAGED;
import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
import static android.app.admin.DevicePolicyManager.WIPE_SILENTLY;
+import static android.app.admin.ProvisioningException.ERROR_ADMIN_PACKAGE_INSTALLATION_FAILED;
+import static android.app.admin.ProvisioningException.ERROR_PRE_CONDITION_FAILED;
+import static android.app.admin.ProvisioningException.ERROR_PROFILE_CREATION_FAILED;
+import static android.app.admin.ProvisioningException.ERROR_REMOVE_NON_REQUIRED_APPS_FAILED;
+import static android.app.admin.ProvisioningException.ERROR_SETTING_PROFILE_OWNER_FAILED;
+import static android.app.admin.ProvisioningException.ERROR_SET_DEVICE_OWNER_FAILED;
+import static android.app.admin.ProvisioningException.ERROR_STARTING_PROFILE_FAILED;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
@@ -10653,15 +10653,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
final int userHandle = user.getIdentifier();
- final Intent intent = new Intent(DevicePolicyManager.ACTION_MANAGED_USER_CREATED)
- .putExtra(Intent.EXTRA_USER_HANDLE, userHandle)
- .putExtra(
- DevicePolicyManager.EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED,
- leaveAllSystemAppsEnabled)
- .setPackage(getManagedProvisioningPackage(mContext))
- .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
-
final long id = mInjector.binderClearCallingIdentity();
try {
manageUserUnchecked(admin, profileOwner, userHandle, adminExtras,
@@ -10672,6 +10663,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Settings.Secure.USER_SETUP_COMPLETE, 1, userHandle);
}
+ sendProvisioningCompletedBroadcast(
+ userHandle, ACTION_PROVISION_MANAGED_USER, leaveAllSystemAppsEnabled);
+
return user;
} catch (Throwable re) {
mUserManager.removeUser(userHandle);
@@ -10686,6 +10680,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
+ private void sendProvisioningCompletedBroadcast(
+ int user, String action, boolean leaveAllSystemAppsEnabled) {
+ final Intent intent = new Intent(DevicePolicyManager.ACTION_PROVISIONING_COMPLETED)
+ .putExtra(Intent.EXTRA_USER_HANDLE, user)
+ .putExtra(
+ DevicePolicyManager.EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED,
+ leaveAllSystemAppsEnabled)
+ .putExtra(DevicePolicyManager.EXTRA_PROVISIONING_ACTION,
+ action)
+ .setPackage(getManagedProvisioningPackage(mContext))
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
+ }
+
private void manageUserUnchecked(ComponentName admin, ComponentName profileOwner,
@UserIdInt int userId, @Nullable PersistableBundle adminExtras,
boolean showDisclaimer) {
@@ -17230,7 +17238,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
ACTION_PROVISION_MANAGED_PROFILE, admin.getPackageName());
if (result != CODE_OK) {
throw new ServiceSpecificException(
- PROVISIONING_RESULT_PRE_CONDITION_FAILED,
+ ERROR_PRE_CONDITION_FAILED,
"Provisioning preconditions failed with result: " + result);
}
@@ -17247,7 +17255,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
nonRequiredApps.toArray(new String[nonRequiredApps.size()]));
if (userInfo == null) {
throw new ServiceSpecificException(
- PROVISIONING_RESULT_PROFILE_CREATION_FAILED,
+ ERROR_PROFILE_CREATION_FAILED,
"Error creating profile, createProfileForUserEvenWhenDisallowed "
+ "returned null.");
}
@@ -17261,7 +17269,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!enableAdminAndSetProfileOwner(
userInfo.id, caller.getUserId(), admin, provisioningParams.getOwnerName())) {
throw new ServiceSpecificException(
- PROVISIONING_RESULT_SETTING_PROFILE_OWNER_FAILED,
+ ERROR_SETTING_PROFILE_OWNER_FAILED,
"Error setting profile owner.");
}
setUserSetupComplete(userInfo.id);
@@ -17269,7 +17277,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
startUser(userInfo.id, callerPackage);
maybeMigrateAccount(
userInfo.id, caller.getUserId(), provisioningParams.getAccountToMigrate(),
- provisioningParams.isKeepAccountMigrated(), callerPackage);
+ provisioningParams.isKeepingAccountOnMigration(), callerPackage);
if (provisioningParams.isOrganizationOwnedProvisioning()) {
synchronized (getLockObject()) {
@@ -17277,6 +17285,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
+ sendProvisioningCompletedBroadcast(
+ userInfo.id,
+ ACTION_PROVISION_MANAGED_PROFILE,
+ provisioningParams.isLeaveAllSystemAppsEnabled());
+
return userInfo.getUserHandle();
} catch (Exception e) {
DevicePolicyEventLogger
@@ -17336,14 +17349,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
userId);
if (status != PackageManager.INSTALL_SUCCEEDED) {
throw new ServiceSpecificException(
- PROVISIONING_RESULT_ADMIN_PACKAGE_INSTALLATION_FAILED,
+ ERROR_ADMIN_PACKAGE_INSTALLATION_FAILED,
String.format("Failed to install existing package %s for user %d with "
+ "result code %d",
packageName, userId, status));
}
} catch (NameNotFoundException e) {
throw new ServiceSpecificException(
- PROVISIONING_RESULT_ADMIN_PACKAGE_INSTALLATION_FAILED,
+ ERROR_ADMIN_PACKAGE_INSTALLATION_FAILED,
String.format("Failed to install existing package %s for user %d: %s",
packageName, userId, e.getMessage()));
}
@@ -17401,12 +17414,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
/* scheduler= */ null);
try {
if (!mInjector.getIActivityManager().startUserInBackground(userId)) {
- throw new ServiceSpecificException(PROVISIONING_RESULT_STARTING_PROFILE_FAILED,
+ throw new ServiceSpecificException(ERROR_STARTING_PROFILE_FAILED,
String.format("Unable to start user %d in background", userId));
}
if (!unlockedReceiver.waitForUserUnlocked()) {
- throw new ServiceSpecificException(PROVISIONING_RESULT_STARTING_PROFILE_FAILED,
+ throw new ServiceSpecificException(ERROR_STARTING_PROFILE_FAILED,
String.format("Timeout whilst waiting for unlock of user %d.", userId));
}
logEventDuration(
@@ -17529,7 +17542,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
ACTION_PROVISION_MANAGED_DEVICE, deviceAdmin.getPackageName());
if (result != CODE_OK) {
throw new ServiceSpecificException(
- PROVISIONING_RESULT_PRE_CONDITION_FAILED,
+ ERROR_PRE_CONDITION_FAILED,
"Provisioning preconditions failed with result: " + result);
}
setTimeAndTimezone(provisioningParams.getTimeZone(), provisioningParams.getLocalTime());
@@ -17542,7 +17555,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
provisioningParams.isLeaveAllSystemAppsEnabled(),
deviceAdmin)) {
throw new ServiceSpecificException(
- PROVISIONING_RESULT_REMOVE_NON_REQUIRED_APPS_FAILED,
+ ERROR_REMOVE_NON_REQUIRED_APPS_FAILED,
"PackageManager failed to remove non required apps.");
}
@@ -17550,13 +17563,17 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!setActiveAdminAndDeviceOwner(
deviceOwnerUserId, deviceAdmin, provisioningParams.getOwnerName())) {
throw new ServiceSpecificException(
- PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED,
+ ERROR_SET_DEVICE_OWNER_FAILED,
"Failed to set device owner.");
}
disallowAddUser();
setAdminCanGrantSensorsPermissionForUserUnchecked(deviceOwnerUserId,
provisioningParams.canDeviceOwnerGrantSensorsPermissions());
+ sendProvisioningCompletedBroadcast(
+ deviceOwnerUserId,
+ ACTION_PROVISION_MANAGED_DEVICE,
+ provisioningParams.isLeaveAllSystemAppsEnabled());
} catch (Exception e) {
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.PLATFORM_PROVISIONING_ERROR)
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index a72cf3a79d37..663e17bc9784 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -151,6 +151,7 @@ import com.android.server.os.DeviceIdentifiersPolicyService;
import com.android.server.os.NativeTombstoneManagerService;
import com.android.server.os.SchedulingPolicyService;
import com.android.server.people.PeopleService;
+import com.android.server.pm.ApexManager;
import com.android.server.pm.CrossProfileAppsService;
import com.android.server.pm.DataLoaderManagerService;
import com.android.server.pm.DynamicCodeLoggingService;
@@ -220,6 +221,7 @@ import java.util.Arrays;
import java.util.Date;
import java.util.LinkedList;
import java.util.Locale;
+import java.util.Map;
import java.util.Timer;
import java.util.TreeSet;
import java.util.concurrent.CountDownLatch;
@@ -332,6 +334,8 @@ public final class SystemServer implements Dumpable {
"com.android.server.contentcapture.ContentCaptureManagerService";
private static final String TRANSLATION_MANAGER_SERVICE_CLASS =
"com.android.server.translation.TranslationManagerService";
+ private static final String SELECTION_TOOLBAR_MANAGER_SERVICE_CLASS =
+ "com.android.server.selectiontoolbar.SelectionToolbarManagerService";
private static final String MUSIC_RECOGNITION_MANAGER_SERVICE_CLASS =
"com.android.server.musicrecognition.MusicRecognitionManagerService";
private static final String SYSTEM_CAPTIONS_MANAGER_SERVICE_CLASS =
@@ -916,6 +920,13 @@ public final class SystemServer implements Dumpable {
startBootstrapServices(t);
startCoreServices(t);
startOtherServices(t);
+ // Apex services must be the last category of services to start. No other service must
+ // be starting after this point. This is to prevent unnessary stability issues when
+ // these apexes are updated outside of OTA; and to avoid breaking dependencies from
+ // system into apexes.
+ // TODO(satayev): lock mSystemServiceManager.startService to stop accepting new services
+ // after this step
+ startApexServices(t);
} catch (Throwable ex) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting system services", ex);
@@ -2592,6 +2603,11 @@ public final class SystemServer implements Dumpable {
Slog.d(TAG, "TranslationService not defined by OEM");
}
+ // Selection toolbar service
+ t.traceBegin("StartSelectionToolbarManagerService");
+ mSystemServiceManager.startService(SELECTION_TOOLBAR_MANAGER_SERVICE_CLASS);
+ t.traceEnd();
+
// NOTE: ClipboardService depends on ContentCapture and Autofill
t.traceBegin("StartClipboardService");
mSystemServiceManager.startService(ClipboardService.class);
@@ -3044,6 +3060,27 @@ public final class SystemServer implements Dumpable {
t.traceEnd(); // startOtherServices
}
+ /**
+ * Starts system services defined in apexes.
+ */
+ private void startApexServices(@NonNull TimingsTraceAndSlog t) {
+ t.traceBegin("startApexServices");
+ Map<String, String> services = ApexManager.getInstance().getApexSystemServices();
+ // TODO(satayev): filter out already started services
+ // TODO(satayev): introduce android:order for services coming the same apexes
+ for (String name : new TreeSet<>(services.keySet())) {
+ String jarPath = services.get(name);
+ t.traceBegin("starting " + name);
+ if (TextUtils.isEmpty(jarPath)) {
+ mSystemServiceManager.startService(name);
+ } else {
+ mSystemServiceManager.startServiceFromJar(name, jarPath);
+ }
+ t.traceEnd();
+ }
+ t.traceEnd(); // startApexServices
+ }
+
private boolean deviceHasConfigString(@NonNull Context context, @StringRes int resId) {
String serviceName = context.getString(resId);
return !TextUtils.isEmpty(serviceName);
diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index fd295c0739cf..9e83f8e7bda8 100644
--- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -278,7 +278,7 @@ public class KeyValueBackupTaskTest {
assertThat(mBackupManagerService.getPendingInits()).isEmpty();
assertThat(mBackupManagerService.isBackupRunning()).isFalse();
- assertThat(mBackupManagerService.getCurrentOperations().size()).isEqualTo(0);
+ assertThat(mBackupManagerService.getOperationStorage().numOperations()).isEqualTo(0);
verify(mOldJournal).delete();
}
@@ -449,7 +449,7 @@ public class KeyValueBackupTaskTest {
assertThat(mBackupManagerService.getPendingInits()).isEmpty();
assertThat(mBackupManagerService.isBackupRunning()).isFalse();
- assertThat(mBackupManagerService.getCurrentOperations().size()).isEqualTo(0);
+ assertThat(mBackupManagerService.getOperationStorage().numOperations()).isEqualTo(0);
assertThat(mBackupManagerService.getCurrentToken()).isEqualTo(1234L);
verify(mBackupManagerService).writeRestoreTokens();
verify(mOldJournal).delete();
@@ -2665,6 +2665,7 @@ public class KeyValueBackupTaskTest {
KeyValueBackupTask task =
new KeyValueBackupTask(
mBackupManagerService,
+ mBackupManagerService.getOperationStorage(),
transportMock.mTransportConnection,
transportMock.transportData.transportDirName,
queue,
diff --git a/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java b/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
index 9eb99aed2ba8..e0812d6a77ea 100644
--- a/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
@@ -51,6 +51,7 @@ import android.platform.test.annotations.Presubmit;
import com.android.server.EventLogTags;
import com.android.server.backup.BackupAgentTimeoutParameters;
+import com.android.server.backup.OperationStorage;
import com.android.server.backup.TransportManager;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.internal.BackupHandler;
@@ -98,6 +99,7 @@ public class ActiveRestoreSessionTest {
@Mock private IRestoreObserver mObserver;
@Mock private IBackupManagerMonitor mMonitor;
@Mock private BackupEligibilityRules mBackupEligibilityRules;
+ @Mock private OperationStorage mOperationStorage;
private ShadowLooper mShadowBackupLooper;
private ShadowApplication mShadowApplication;
private UserBackupManagerService.BackupWakeLock mWakeLock;
@@ -132,7 +134,9 @@ public class ActiveRestoreSessionTest {
// We need to mock BMS timeout parameters before initializing the BackupHandler since
// the constructor of BackupHandler relies on it.
when(mBackupManagerService.getAgentTimeoutParameters()).thenReturn(agentTimeoutParameters);
- BackupHandler backupHandler = new BackupHandler(mBackupManagerService, handlerThread);
+
+ BackupHandler backupHandler =
+ new BackupHandler(mBackupManagerService, mOperationStorage, handlerThread);
mWakeLock = createBackupWakeLock(application);
diff --git a/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java b/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
index 77b5b61b8f01..fc3ec7b44f4f 100644
--- a/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
+++ b/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
@@ -120,7 +120,6 @@ public class BackupManagerServiceTestUtils {
when(backupManagerService.getTransportManager()).thenReturn(transportManager);
when(backupManagerService.getPackageManager()).thenReturn(packageManager);
when(backupManagerService.getBackupHandler()).thenReturn(backupHandler);
- when(backupManagerService.getCurrentOpLock()).thenReturn(new Object());
when(backupManagerService.getQueueLock()).thenReturn(new Object());
when(backupManagerService.getActivityManager()).thenReturn(mock(IActivityManager.class));
when(backupManagerService.getWakelock()).thenReturn(wakeLock);
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupTask.java b/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupTask.java
index 06b7fb7e6ae3..6a7d03176b1c 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupTask.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupTask.java
@@ -19,6 +19,7 @@ package com.android.server.testing.shadows;
import android.annotation.Nullable;
import com.android.server.backup.DataChangedJournal;
+import com.android.server.backup.OperationStorage;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.keyvalue.KeyValueBackupReporter;
@@ -56,6 +57,7 @@ public class ShadowKeyValueBackupTask {
@Implementation
protected void __constructor__(
UserBackupManagerService backupManagerService,
+ OperationStorage operationStorage,
TransportConnection transportConnection,
String transportDirName,
List<String> queue,
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java b/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java
index 71010a9fe935..d985e1b0f341 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java
@@ -21,6 +21,7 @@ import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IRestoreObserver;
import android.content.pm.PackageInfo;
+import com.android.server.backup.OperationStorage;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.restore.PerformUnifiedRestoreTask;
@@ -57,6 +58,7 @@ public class ShadowPerformUnifiedRestoreTask {
@Implementation
protected void __constructor__(
UserBackupManagerService backupManagerService,
+ OperationStorage operationStorage,
TransportConnection transportConnection,
IRestoreObserver observer,
IBackupManagerMonitor monitor,
diff --git a/services/selectiontoolbar/Android.bp b/services/selectiontoolbar/Android.bp
new file mode 100644
index 000000000000..cc6405f97bc3
--- /dev/null
+++ b/services/selectiontoolbar/Android.bp
@@ -0,0 +1,22 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+ name: "services.selectiontoolbar-sources",
+ srcs: ["java/**/*.java"],
+ path: "java",
+ visibility: ["//frameworks/base/services"],
+}
+
+java_library_static {
+ name: "services.selectiontoolbar",
+ defaults: ["platform_service_defaults"],
+ srcs: [":services.selectiontoolbar-sources"],
+ libs: ["services.core"],
+}
diff --git a/services/selectiontoolbar/OWNERS b/services/selectiontoolbar/OWNERS
new file mode 100644
index 000000000000..ed9425cc26c9
--- /dev/null
+++ b/services/selectiontoolbar/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/view/selectiontoolbar/OWNERS
diff --git a/services/selectiontoolbar/java/com/android/server/selectiontoolbar/DefaultSelectionToolbarRenderService.java b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/DefaultSelectionToolbarRenderService.java
new file mode 100644
index 000000000000..c26965dadff1
--- /dev/null
+++ b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/DefaultSelectionToolbarRenderService.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.selectiontoolbar;
+
+import android.service.selectiontoolbar.SelectionToolbarRenderService;
+import android.util.Log;
+import android.view.selectiontoolbar.ShowInfo;
+
+/**
+ * The default implementation of {@link SelectionToolbarRenderService}.
+ */
+public final class DefaultSelectionToolbarRenderService extends SelectionToolbarRenderService {
+
+ private static final String TAG = "DefaultSelectionToolbarRenderService";
+
+ @Override
+ public void onShow(ShowInfo showInfo,
+ SelectionToolbarRenderService.RemoteCallbackWrapper callbackWrapper) {
+ // TODO: Add implementation
+ Log.w(TAG, "onShow()");
+ }
+
+ @Override
+ public void onHide(long widgetToken) {
+ // TODO: Add implementation
+ Log.w(TAG, "onHide()");
+ }
+
+ @Override
+ public void onDismiss(long widgetToken) {
+ // TODO: Add implementation
+ Log.w(TAG, "onDismiss()");
+ }
+}
diff --git a/services/selectiontoolbar/java/com/android/server/selectiontoolbar/RemoteSelectionToolbarRenderService.java b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/RemoteSelectionToolbarRenderService.java
new file mode 100644
index 000000000000..ced24e00da63
--- /dev/null
+++ b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/RemoteSelectionToolbarRenderService.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.selectiontoolbar;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.service.selectiontoolbar.ISelectionToolbarRenderService;
+import android.service.selectiontoolbar.SelectionToolbarRenderService;
+import android.view.selectiontoolbar.ISelectionToolbarCallback;
+import android.view.selectiontoolbar.ShowInfo;
+
+import com.android.internal.infra.AbstractRemoteService;
+import com.android.internal.infra.ServiceConnector;
+
+final class RemoteSelectionToolbarRenderService extends
+ ServiceConnector.Impl<ISelectionToolbarRenderService> {
+ private static final String TAG = "RemoteSelectionToolbarRenderService";
+
+ private static final long TIMEOUT_IDLE_UNBIND_MS =
+ AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS;
+
+ private final ComponentName mComponentName;
+
+
+ RemoteSelectionToolbarRenderService(Context context, ComponentName serviceName, int userId) {
+ super(context, new Intent(SelectionToolbarRenderService.SERVICE_INTERFACE).setComponent(
+ serviceName), 0, userId, ISelectionToolbarRenderService.Stub::asInterface);
+ mComponentName = serviceName;
+ // Bind right away.
+ connect();
+ }
+
+ @Override // from AbstractRemoteService
+ protected long getAutoDisconnectTimeoutMs() {
+ return TIMEOUT_IDLE_UNBIND_MS;
+ }
+
+ public ComponentName getComponentName() {
+ return mComponentName;
+ }
+
+ public void onShow(ShowInfo showInfo, ISelectionToolbarCallback callback) {
+ run((s) -> s.onShow(showInfo, callback));
+ }
+
+ public void onHide(long widgetToken) {
+ run((s) -> s.onHide(widgetToken));
+ }
+
+ public void onDismiss(long widgetToken) {
+ run((s) -> s.onDismiss(widgetToken));
+ }
+}
diff --git a/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerService.java b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerService.java
new file mode 100644
index 000000000000..3bdf55ccb5c8
--- /dev/null
+++ b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerService.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.selectiontoolbar;
+
+import android.content.Context;
+import android.util.Slog;
+import android.view.selectiontoolbar.ISelectionToolbarCallback;
+import android.view.selectiontoolbar.ISelectionToolbarManager;
+import android.view.selectiontoolbar.ShowInfo;
+
+import com.android.internal.util.DumpUtils;
+import com.android.server.infra.AbstractMasterSystemService;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Entry point service for selection toolbar management.
+ */
+public final class SelectionToolbarManagerService extends
+ AbstractMasterSystemService<SelectionToolbarManagerService,
+ SelectionToolbarManagerServiceImpl> {
+
+ private static final String TAG = "SelectionToolbarManagerService";
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.SELECTION_TOOLBAR_SERVICE,
+ new SelectionToolbarManagerService.SelectionToolbarManagerServiceStub());
+ }
+
+ public SelectionToolbarManagerService(Context context) {
+ super(context, new SelectionToolbarServiceNameResolver(), /* disallowProperty= */
+ null, PACKAGE_UPDATE_POLICY_REFRESH_EAGER);
+ }
+
+ @Override
+ protected SelectionToolbarManagerServiceImpl newServiceLocked(int resolvedUserId,
+ boolean disabled) {
+ return new SelectionToolbarManagerServiceImpl(this, mLock, resolvedUserId);
+ }
+
+ final class SelectionToolbarManagerServiceStub extends ISelectionToolbarManager.Stub {
+
+ @Override
+ public void showToolbar(ShowInfo showInfo, ISelectionToolbarCallback callback, int userId) {
+ synchronized (mLock) {
+ SelectionToolbarManagerServiceImpl service = getServiceForUserLocked(userId);
+ if (service != null) {
+ service.showToolbar(showInfo, callback);
+ } else {
+ Slog.v(TAG, "showToolbar(): no service for " + userId);
+ }
+ }
+ }
+
+ @Override
+ public void hideToolbar(long widgetToken, int userId) {
+ synchronized (mLock) {
+ SelectionToolbarManagerServiceImpl service = getServiceForUserLocked(userId);
+ if (service != null) {
+ service.hideToolbar(widgetToken);
+ } else {
+ Slog.v(TAG, "hideToolbar(): no service for " + userId);
+ }
+ }
+ }
+
+ @Override
+ public void dismissToolbar(long widgetToken, int userId) {
+ synchronized (mLock) {
+ SelectionToolbarManagerServiceImpl service = getServiceForUserLocked(userId);
+ if (service != null) {
+ service.dismissToolbar(widgetToken);
+ } else {
+ Slog.v(TAG, "dismissToolbar(): no service for " + userId);
+ }
+ }
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
+
+ synchronized (mLock) {
+ dumpLocked("", pw);
+ }
+ }
+ }
+}
diff --git a/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerServiceImpl.java b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerServiceImpl.java
new file mode 100644
index 000000000000..235f547feed0
--- /dev/null
+++ b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerServiceImpl.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.selectiontoolbar;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.AppGlobals;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.os.RemoteException;
+import android.util.Slog;
+import android.view.selectiontoolbar.ISelectionToolbarCallback;
+import android.view.selectiontoolbar.ShowInfo;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.infra.AbstractPerUserSystemService;
+
+final class SelectionToolbarManagerServiceImpl extends
+ AbstractPerUserSystemService<SelectionToolbarManagerServiceImpl,
+ SelectionToolbarManagerService> {
+
+ private static final String TAG = "SelectionToolbarManagerServiceImpl";
+
+ @GuardedBy("mLock")
+ @Nullable
+ private RemoteSelectionToolbarRenderService mRemoteService;
+
+ protected SelectionToolbarManagerServiceImpl(@NonNull SelectionToolbarManagerService master,
+ @NonNull Object lock, int userId) {
+ super(master, lock, userId);
+ updateRemoteServiceLocked();
+ }
+
+ @GuardedBy("mLock")
+ @Override // from PerUserSystemService
+ protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
+ throws PackageManager.NameNotFoundException {
+ return getServiceInfoOrThrow(serviceComponent, mUserId);
+ }
+
+ @GuardedBy("mLock")
+ @Override // from PerUserSystemService
+ protected boolean updateLocked(boolean disabled) {
+ final boolean enabledChanged = super.updateLocked(disabled);
+ updateRemoteServiceLocked();
+ return enabledChanged;
+ }
+
+ /**
+ * Updates the reference to the remote service.
+ */
+ @GuardedBy("mLock")
+ private void updateRemoteServiceLocked() {
+ if (mRemoteService != null) {
+ Slog.d(TAG, "updateRemoteService(): destroying old remote service");
+ mRemoteService.unbind();
+ mRemoteService = null;
+ }
+ }
+
+ @GuardedBy("mLock")
+ void showToolbar(ShowInfo showInfo, ISelectionToolbarCallback callback) {
+ final RemoteSelectionToolbarRenderService remoteService = ensureRemoteServiceLocked();
+ if (remoteService != null) {
+ remoteService.onShow(showInfo, callback);
+ }
+ }
+
+ @GuardedBy("mLock")
+ void hideToolbar(long widgetToken) {
+ final RemoteSelectionToolbarRenderService remoteService = ensureRemoteServiceLocked();
+ if (remoteService != null) {
+ remoteService.onHide(widgetToken);
+ }
+ }
+
+ @GuardedBy("mLock")
+ void dismissToolbar(long widgetToken) {
+ final RemoteSelectionToolbarRenderService remoteService = ensureRemoteServiceLocked();
+ if (remoteService != null) {
+ remoteService.onDismiss(widgetToken);
+ }
+ }
+
+ @GuardedBy("mLock")
+ @Nullable
+ private RemoteSelectionToolbarRenderService ensureRemoteServiceLocked() {
+ if (mRemoteService == null) {
+ final String serviceName = getComponentNameLocked();
+ final ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
+ mRemoteService = new RemoteSelectionToolbarRenderService(getContext(), serviceComponent,
+ mUserId);
+ }
+ return mRemoteService;
+ }
+
+ private static ServiceInfo getServiceInfoOrThrow(ComponentName comp, @UserIdInt int userId)
+ throws PackageManager.NameNotFoundException {
+ int flags = PackageManager.GET_META_DATA;
+
+ ServiceInfo si = null;
+ try {
+ si = AppGlobals.getPackageManager().getServiceInfo(comp, flags, userId);
+ } catch (RemoteException e) {
+ }
+ if (si == null) {
+ throw new PackageManager.NameNotFoundException("Could not get serviceInfo for "
+ + comp.flattenToShortString());
+ }
+ return si;
+ }
+}
diff --git a/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarServiceNameResolver.java b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarServiceNameResolver.java
new file mode 100644
index 000000000000..1d4c94dadb75
--- /dev/null
+++ b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarServiceNameResolver.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.selectiontoolbar;
+
+import com.android.server.infra.ServiceNameResolver;
+
+import java.io.PrintWriter;
+
+final class SelectionToolbarServiceNameResolver implements ServiceNameResolver {
+
+ // TODO: move to SysUi or ExtServices
+ private static final String SELECTION_TOOLBAR_SERVICE_NAME =
+ "android/com.android.server.selectiontoolbar.DefaultSelectionToolbarRenderService";
+
+ @Override
+ public String getDefaultServiceName(int userId) {
+ return SELECTION_TOOLBAR_SERVICE_NAME;
+ }
+
+ @Override
+ public void dumpShort(PrintWriter pw) {
+ pw.print("service="); pw.print(SELECTION_TOOLBAR_SERVICE_NAME);
+ }
+
+ @Override
+ public void dumpShort(PrintWriter pw, int userId) {
+ pw.print("defaultService="); pw.print(getDefaultServiceName(userId));
+ }
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt
index e6338507e970..005d3e81d3a3 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt
@@ -18,6 +18,7 @@ package com.android.server.pm.test.parsing.parcelling
import android.content.pm.parsing.component.ParsedProcess
import android.content.pm.parsing.component.ParsedProcessImpl
+import android.util.ArrayMap
import kotlin.contracts.ExperimentalContracts
@ExperimentalContracts
@@ -29,6 +30,8 @@ class ParsedProcessTest : ParcelableComponentTest(ParsedProcess::class, ParsedPr
override val excludedMethods = listOf(
// Copying method
"addStateFrom",
+ // Utility method
+ "putAppClassNameForPackage",
)
override val baseParams = listOf(
@@ -39,6 +42,9 @@ class ParsedProcessTest : ParcelableComponentTest(ParsedProcess::class, ParsedPr
)
override fun extraParams() = listOf(
- getter(ParsedProcess::getDeniedPermissions, setOf("testDeniedPermission"))
+ getter(ParsedProcess::getDeniedPermissions, setOf("testDeniedPermission")),
+ getter(ParsedProcess::getAppClassNamesByPackage, ArrayMap<String, String>().apply {
+ put("package1", "classname1");
+ }),
)
}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
index d1d7cc6422c3..089e9db9a755 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
@@ -19,6 +19,7 @@ package com.android.server.pm.test.verify.domain
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
+import android.content.pm.SigningDetails
import android.content.pm.parsing.component.ParsedActivityImpl
import android.content.pm.parsing.component.ParsedIntentInfoImpl
import android.content.pm.verify.domain.DomainVerificationManager
@@ -26,6 +27,7 @@ import android.content.pm.verify.domain.DomainVerificationState
import android.os.Build
import android.os.Process
import android.util.ArraySet
+import android.util.IndentingPrintWriter
import android.util.SparseArray
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.pm.parsing.pkg.AndroidPackage
@@ -46,6 +48,7 @@ import org.mockito.Mockito.anyInt
import org.mockito.Mockito.anyLong
import org.mockito.Mockito.anyString
import org.mockito.Mockito.eq
+import org.mockito.Mockito.mock
import org.mockito.Mockito.verifyNoMoreInteractions
import java.util.UUID
import java.util.concurrent.atomic.AtomicBoolean
@@ -204,6 +207,14 @@ class DomainVerificationEnforcerTest {
service(Type.QUERENT, "getInfo") {
getDomainVerificationInfo(it.targetPackageName)
},
+ service(Type.QUERENT, "printState") {
+ printState(mock(IndentingPrintWriter::class.java), null, null)
+ },
+ service(Type.QUERENT, "printStateInternal") {
+ printState(mock(IndentingPrintWriter::class.java), null, null) {
+ mockPkgState(it, UUID.randomUUID())
+ }
+ },
service(Type.VERIFIER, "setStatus") {
setDomainVerificationStatus(
it.targetDomainSetId,
@@ -311,6 +322,7 @@ class DomainVerificationEnforcerTest {
}
)
}
+ whenever(signingDetails) { SigningDetails.UNKNOWN }
}
fun mockPkgState(packageName: String, domainSetId: UUID) =
@@ -327,6 +339,7 @@ class DomainVerificationEnforcerTest {
}
}
whenever(isSystem) { false }
+ whenever(signingDetails) { SigningDetails.UNKNOWN }
}
}
@@ -373,6 +386,7 @@ class DomainVerificationEnforcerTest {
val allowUserState = AtomicBoolean(false)
val allowPreferredApps = AtomicBoolean(false)
val allowQueryAll = AtomicBoolean(false)
+ val allowDump = AtomicBoolean(false)
val context: Context = mockThrowOnUnmocked {
initPermission(
allowUserState,
@@ -383,6 +397,7 @@ class DomainVerificationEnforcerTest {
android.Manifest.permission.SET_PREFERRED_APPLICATIONS
)
initPermission(allowQueryAll, android.Manifest.permission.QUERY_ALL_PACKAGES)
+ initPermission(allowDump, android.Manifest.permission.DUMP)
}
val target = params.construct(context)
@@ -409,6 +424,10 @@ class DomainVerificationEnforcerTest {
allowQueryAll.set(true)
assertFails { runMethod(target, NON_VERIFIER_UID) }
+
+ allowDump.set(true)
+
+ runMethod(target, NON_VERIFIER_UID)
}
private fun approvedVerifier() {
@@ -794,8 +813,12 @@ class DomainVerificationEnforcerTest {
}
val valueAsInt = value as? Int
- if (valueAsInt != null && valueAsInt == DomainVerificationManager.STATUS_OK) {
- throw AssertionError("Expected call to return false, was $value")
+ if (valueAsInt != null) {
+ if (valueAsInt == DomainVerificationManager.STATUS_OK) {
+ throw AssertionError("Expected call to return false, was $value")
+ }
+ } else {
+ throw AssertionError("Expected call to fail")
}
} catch (e: SecurityException) {
} catch (e: PackageManager.NameNotFoundException) {
@@ -807,7 +830,7 @@ class DomainVerificationEnforcerTest {
// System/shell only
INTERNAL,
- // INTERNAL || non-legacy domain verification agent
+ // INTERNAL || non-legacy domain verification agent || DUMP permission
QUERENT,
// INTERNAL || domain verification agent
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeAppOpsHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeAppOpsHelper.java
index d728451d92b9..189396f5c727 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeAppOpsHelper.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeAppOpsHelper.java
@@ -31,7 +31,7 @@ public class FakeAppOpsHelper extends AppOpsHelper {
private static class AppOp {
AppOp() {}
boolean mAllowed = true;
- boolean mStarted = false;
+ int mStarted = 0;
int mNoteCount = 0;
}
@@ -49,7 +49,7 @@ public class FakeAppOpsHelper extends AppOpsHelper {
public boolean isAppOpStarted(int appOp, String packageName) {
AppOp myAppOp = getOp(packageName, appOp);
- return myAppOp.mStarted;
+ return myAppOp.mStarted > 0;
}
public int getAppOpNoteCount(int appOp, String packageName) {
@@ -63,16 +63,15 @@ public class FakeAppOpsHelper extends AppOpsHelper {
if (!myAppOp.mAllowed) {
return false;
}
- Preconditions.checkState(!myAppOp.mStarted);
- myAppOp.mStarted = true;
+ myAppOp.mStarted++;
return true;
}
@Override
public void finishOp(int appOp, CallerIdentity callerIdentity) {
AppOp myAppOp = getOp(callerIdentity.getPackageName(), appOp);
- Preconditions.checkState(myAppOp.mStarted);
- myAppOp.mStarted = false;
+ Preconditions.checkState(myAppOp.mStarted > 0);
+ myAppOp.mStarted--;
}
@Override
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/LocationAttributionHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/LocationAttributionHelperTest.java
deleted file mode 100644
index 94dcdf92d9d4..000000000000
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/LocationAttributionHelperTest.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.location.injector;
-
-import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
-import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import android.location.util.identity.CallerIdentity;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-
-@Presubmit
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class LocationAttributionHelperTest {
-
- @Mock private AppOpsHelper mAppOpsHelper;
-
- private LocationAttributionHelper mHelper;
-
- @Before
- public void setUp() {
- initMocks(this);
-
- when(mAppOpsHelper.startOpNoThrow(anyInt(), any(CallerIdentity.class))).thenReturn(true);
-
- mHelper = new LocationAttributionHelper(mAppOpsHelper);
- }
-
- @Test
- public void testLocationMonitoring() {
- CallerIdentity caller1 = CallerIdentity.forTest(1, 1, "test1", null);
- CallerIdentity caller2 = CallerIdentity.forTest(2, 2, "test2", null);
-
- mHelper.reportLocationStart(caller1);
- verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION,
- CallerIdentity.forAggregation(caller1));
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION,
- CallerIdentity.forAggregation(caller1));
-
- mHelper.reportLocationStart(caller1);
- verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION,
- CallerIdentity.forAggregation(caller1));
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION,
- CallerIdentity.forAggregation(caller1));
-
- mHelper.reportLocationStart(caller2);
- verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION,
- CallerIdentity.forAggregation(caller2));
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION,
- CallerIdentity.forAggregation(caller2));
-
- mHelper.reportLocationStart(caller2);
- verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION,
- CallerIdentity.forAggregation(caller2));
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION,
- CallerIdentity.forAggregation(caller2));
-
- mHelper.reportLocationStop(caller1);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION,
- CallerIdentity.forAggregation(caller1));
- mHelper.reportLocationStop(caller1);
- verify(mAppOpsHelper).finishOp(OP_MONITOR_LOCATION, CallerIdentity.forAggregation(caller1));
-
- mHelper.reportLocationStop(caller2);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION,
- CallerIdentity.forAggregation(caller2));
- mHelper.reportLocationStop(caller2);
- verify(mAppOpsHelper).finishOp(OP_MONITOR_LOCATION, CallerIdentity.forAggregation(caller2));
- }
-
- @Test
- public void testHighPowerLocationMonitoring() {
- CallerIdentity caller1 = CallerIdentity.forTest(1, 1, "test1", null);
- CallerIdentity caller2 = CallerIdentity.forTest(2, 2, "test2", null);
-
- mHelper.reportHighPowerLocationStart(caller1);
- verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION,
- CallerIdentity.forAggregation(caller1));
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
- CallerIdentity.forAggregation(caller1));
-
- mHelper.reportHighPowerLocationStart(caller1);
- verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION,
- CallerIdentity.forAggregation(caller1));
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
- CallerIdentity.forAggregation(caller1));
-
- mHelper.reportHighPowerLocationStart(caller2);
- verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION,
- CallerIdentity.forAggregation(caller2));
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
- CallerIdentity.forAggregation(caller2));
-
- mHelper.reportHighPowerLocationStart(caller2);
- verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION,
- CallerIdentity.forAggregation(caller2));
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
- CallerIdentity.forAggregation(caller2));
-
- mHelper.reportHighPowerLocationStop(caller1);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
- CallerIdentity.forAggregation(caller1));
- mHelper.reportHighPowerLocationStop(caller1);
- verify(mAppOpsHelper).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
- CallerIdentity.forAggregation(caller1));
-
- mHelper.reportHighPowerLocationStop(caller2);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
- CallerIdentity.forAggregation(caller2));
- mHelper.reportHighPowerLocationStop(caller2);
- verify(mAppOpsHelper).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
- CallerIdentity.forAggregation(caller2));
- }
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
index bd24cfd78a2c..02cacb7bc57c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
@@ -33,7 +33,6 @@ public class TestInjector implements Injector {
private final FakeScreenInteractiveHelper mScreenInteractiveHelper;
private final FakeDeviceStationaryHelper mDeviceStationaryHelper;
private final FakeDeviceIdleHelper mDeviceIdleHelper;
- private final LocationAttributionHelper mLocationAttributionHelper;
private final FakeEmergencyHelper mEmergencyHelper;
private final LocationUsageLogger mLocationUsageLogger;
@@ -49,7 +48,6 @@ public class TestInjector implements Injector {
mScreenInteractiveHelper = new FakeScreenInteractiveHelper();
mDeviceStationaryHelper = new FakeDeviceStationaryHelper();
mDeviceIdleHelper = new FakeDeviceIdleHelper();
- mLocationAttributionHelper = new LocationAttributionHelper(mAppOpsHelper);
mEmergencyHelper = new FakeEmergencyHelper();
mLocationUsageLogger = new LocationUsageLogger();
}
@@ -110,11 +108,6 @@ public class TestInjector implements Injector {
}
@Override
- public LocationAttributionHelper getLocationAttributionHelper() {
- return mLocationAttributionHelper;
- }
-
- @Override
public EmergencyHelper getEmergencyHelper() {
return mEmergencyHelper;
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index f92b872e1d26..d18030f10603 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -66,6 +66,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
+import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.IAccessibilityServiceClient;
@@ -585,8 +586,8 @@ public class AbstractAccessibilityServiceConnectionTest {
doAnswer((invocation) -> {
((Region) invocation.getArguments()[1]).set(region);
return null;
- }).when(mMockMagnificationProcessor).getMagnificationRegion(eq(displayId),
- any(), anyBoolean());
+ }).when(mMockMagnificationProcessor).getFullscreenMagnificationRegion(eq(displayId), any(),
+ anyBoolean());
when(mMockSystemSupport.getCurrentUserIdLocked()).thenReturn(USER_ID2);
final Region result = mServiceConnection.getMagnificationRegion(displayId);
@@ -620,7 +621,8 @@ public class AbstractAccessibilityServiceConnectionTest {
@Test
public void resetMagnification() {
final int displayId = 1;
- when(mMockMagnificationProcessor.reset(displayId, true)).thenReturn(true);
+ when(mMockMagnificationProcessor.resetFullscreenMagnification(displayId, true)).thenReturn(
+ true);
final boolean result = mServiceConnection.resetMagnification(displayId, true);
assertThat(result, is(true));
@@ -629,7 +631,8 @@ public class AbstractAccessibilityServiceConnectionTest {
@Test
public void resetMagnification_cantControlMagnification_returnFalse() {
final int displayId = 1;
- when(mMockMagnificationProcessor.reset(displayId, true)).thenReturn(true);
+ when(mMockMagnificationProcessor.resetFullscreenMagnification(displayId, true)).thenReturn(
+ true);
when(mMockSecurityPolicy.canControlMagnification(mServiceConnection)).thenReturn(false);
final boolean result = mServiceConnection.resetMagnification(displayId, true);
@@ -639,7 +642,8 @@ public class AbstractAccessibilityServiceConnectionTest {
@Test
public void resetMagnification_serviceNotBelongCurrentUser_returnFalse() {
final int displayId = 1;
- when(mMockMagnificationProcessor.reset(displayId, true)).thenReturn(true);
+ when(mMockMagnificationProcessor.resetFullscreenMagnification(displayId, true)).thenReturn(
+ true);
when(mMockSystemSupport.getCurrentUserIdLocked()).thenReturn(USER_ID2);
final boolean result = mServiceConnection.resetMagnification(displayId, true);
@@ -839,6 +843,11 @@ public class AbstractAccessibilityServiceConnectionTest {
}
@Override
+ public int setInputMethodEnabled(String imeId, boolean enabled) throws RemoteException {
+ return AccessibilityService.SoftKeyboardController.ENABLE_IME_FAIL_UNKNOWN;
+ }
+
+ @Override
public boolean isAccessibilityButtonAvailable() throws RemoteException {
return false;
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
index 9ebec981fa21..621507e2bfc8 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
@@ -98,7 +98,7 @@ public class MagnificationProcessorTest {
.setScale(TEST_SCALE).build();
setMagnificationActivated(TEST_DISPLAY, config);
- float scale = mMagnificationProcessor.getScale(TEST_DISPLAY);
+ float scale = mMagnificationProcessor.getMagnificationConfig(TEST_DISPLAY).getScale();
assertEquals(scale, TEST_SCALE, 0);
}
@@ -111,20 +111,19 @@ public class MagnificationProcessorTest {
setMagnificationActivated(TEST_DISPLAY, config);
float centerX = mMagnificationProcessor.getCenterX(
- TEST_DISPLAY, /* canControlMagnification= */true);
+ TEST_DISPLAY, /* canControlMagnification= */true);
assertEquals(centerX, TEST_CENTER_X, 0);
}
@Test
- public void getCenterX_canControlWindowMagnification_returnCenterX() {
+ public void getCenterX_controlWindowMagnification_returnCenterX() {
final MagnificationConfig config = new MagnificationConfig.Builder()
.setMode(MAGNIFICATION_MODE_WINDOW)
.setCenterX(TEST_CENTER_X).build();
setMagnificationActivated(TEST_DISPLAY, config);
- float centerX = mMagnificationProcessor.getCenterX(
- TEST_DISPLAY, /* canControlMagnification= */true);
+ float centerX = mMagnificationProcessor.getMagnificationConfig(TEST_DISPLAY).getCenterX();
assertEquals(centerX, TEST_CENTER_X, 0);
}
@@ -143,14 +142,13 @@ public class MagnificationProcessorTest {
}
@Test
- public void getCenterY_canControlWindowMagnification_returnCenterY() {
+ public void getCenterY_controlWindowMagnification_returnCenterY() {
final MagnificationConfig config = new MagnificationConfig.Builder()
.setMode(MAGNIFICATION_MODE_WINDOW)
.setCenterY(TEST_CENTER_Y).build();
setMagnificationActivated(TEST_DISPLAY, config);
- float centerY = mMagnificationProcessor.getCenterY(
- TEST_DISPLAY, /* canControlMagnification= */false);
+ float centerY = mMagnificationProcessor.getMagnificationConfig(TEST_DISPLAY).getCenterY();
assertEquals(centerY, TEST_CENTER_Y, 0);
}
@@ -159,7 +157,7 @@ public class MagnificationProcessorTest {
public void getMagnificationRegion_canControlFullscreenMagnification_returnRegion() {
final Region region = new Region(10, 20, 100, 200);
setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_FULLSCREEN);
- mMagnificationProcessor.getMagnificationRegion(TEST_DISPLAY,
+ mMagnificationProcessor.getFullscreenMagnificationRegion(TEST_DISPLAY,
region, /* canControlMagnification= */true);
verify(mMockFullScreenMagnificationController).getMagnificationRegion(eq(TEST_DISPLAY),
@@ -167,17 +165,6 @@ public class MagnificationProcessorTest {
}
@Test
- public void getMagnificationRegion_canControlWindowMagnification_returnRegion() {
- final Region region = new Region(10, 20, 100, 200);
- setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_WINDOW);
- mMagnificationProcessor.getMagnificationRegion(TEST_DISPLAY,
- region, /* canControlMagnification= */true);
-
- verify(mMockWindowMagnificationManager).getMagnificationSourceBounds(eq(TEST_DISPLAY),
- eq(region));
- }
-
- @Test
public void getMagnificationRegion_fullscreenModeNotRegistered_shouldRegisterThenUnregister() {
final Region region = new Region(10, 20, 100, 200);
setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_FULLSCREEN);
@@ -188,7 +175,7 @@ public class MagnificationProcessorTest {
any());
final Region result = new Region();
- mMagnificationProcessor.getMagnificationRegion(TEST_DISPLAY,
+ mMagnificationProcessor.getFullscreenMagnificationRegion(TEST_DISPLAY,
result, /* canControlMagnification= */true);
assertEquals(region, result);
verify(mMockFullScreenMagnificationController).register(TEST_DISPLAY);
@@ -237,7 +224,7 @@ public class MagnificationProcessorTest {
public void reset_fullscreenMagnificationActivated() {
setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_FULLSCREEN);
- mMagnificationProcessor.reset(TEST_DISPLAY, /* animate= */false);
+ mMagnificationProcessor.resetFullscreenMagnification(TEST_DISPLAY, /* animate= */false);
verify(mMockFullScreenMagnificationController).reset(TEST_DISPLAY, false);
}
@@ -246,7 +233,7 @@ public class MagnificationProcessorTest {
public void reset_windowMagnificationActivated() {
setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_WINDOW);
- mMagnificationProcessor.reset(TEST_DISPLAY, /* animate= */false);
+ mMagnificationProcessor.resetCurrentMagnification(TEST_DISPLAY, /* animate= */false);
verify(mMockWindowMagnificationManager).reset(TEST_DISPLAY);
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index ec1a0c2f1d0d..08421de3183a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -55,8 +55,10 @@ import android.view.accessibility.MagnificationAnimationCallback;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.LocalServices;
import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.accessibility.AccessibilityTraceManager;
+import com.android.server.wm.WindowManagerInternal;
import org.junit.After;
import org.junit.Before;
@@ -79,6 +81,7 @@ public class MagnificationControllerTest {
private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY;
private static final int TEST_SERVICE_ID = 1;
private static final Region MAGNIFICATION_REGION = new Region(0, 0, 500, 600);
+ private static final Rect TEST_RECT = new Rect(0, 50, 100, 51);
private static final float MAGNIFIED_CENTER_X = 100;
private static final float MAGNIFIED_CENTER_Y = 200;
private static final float DEFAULT_SCALE = 3f;
@@ -108,6 +111,11 @@ public class MagnificationControllerTest {
private MagnificationController mMagnificationController;
private FullScreenMagnificationControllerStubber mScreenMagnificationControllerStubber;
+ @Mock
+ private WindowManagerInternal mMockWindowManagerInternal;
+ @Mock
+ private WindowManagerInternal.AccessibilityControllerInternal mMockA11yController;
+
// To mock package-private class
@Rule
public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
@@ -117,6 +125,12 @@ public class MagnificationControllerTest {
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
FakeSettingsProvider.clearSettingsProvider();
+
+ LocalServices.removeServiceForTest(WindowManagerInternal.class);
+ LocalServices.addService(WindowManagerInternal.class, mMockWindowManagerInternal);
+ when(mMockWindowManagerInternal.getAccessibilityController()).thenReturn(
+ mMockA11yController);
+
mMockResolver = new MockContentResolver();
mMockResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
when(mContext.getContentResolver()).thenReturn(mMockResolver);
@@ -132,8 +146,8 @@ public class MagnificationControllerTest {
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
mScreenMagnificationControllerStubber = new FullScreenMagnificationControllerStubber(
mScreenMagnificationController);
- mMagnificationController = spy(new MagnificationController(mService, new Object(), mContext,
- mScreenMagnificationController, mWindowMagnificationManager, mScaleProvider));
+ mMagnificationController = new MagnificationController(mService, new Object(), mContext,
+ mScreenMagnificationController, mWindowMagnificationManager, mScaleProvider);
mMagnificationController.setMagnificationCapabilities(
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
@@ -439,16 +453,65 @@ public class MagnificationControllerTest {
@Test
public void onWindowMagnificationActivationState_windowActivated_logWindowDuration() {
- mMagnificationController.onWindowMagnificationActivationState(TEST_DISPLAY, true);
+ MagnificationController spyController = spy(mMagnificationController);
+ spyController.onWindowMagnificationActivationState(TEST_DISPLAY, true);
- mMagnificationController.onWindowMagnificationActivationState(TEST_DISPLAY, false);
+ spyController.onWindowMagnificationActivationState(TEST_DISPLAY, false);
- verify(mMagnificationController).logMagnificationUsageState(
+ verify(spyController).logMagnificationUsageState(
eq(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW), anyLong());
}
@Test
- public void onWinodwModeActivated_fullScreenIsActivatedByExternal_fullScreenIsDisabled() {
+ public void onRectangleOnScreenRequested_fullScreenIsActivated_fullScreenDispatchEvent() {
+ mMagnificationController.onFullScreenMagnificationActivationState(TEST_DISPLAY,
+ true);
+ WindowManagerInternal.AccessibilityControllerInternal.UiChangesForAccessibilityCallbacks
+ callbacks = getUiChangesForAccessibilityCallbacks();
+
+ callbacks.onRectangleOnScreenRequested(TEST_DISPLAY,
+ TEST_RECT.left, TEST_RECT.top, TEST_RECT.right, TEST_RECT.bottom);
+
+ verify(mScreenMagnificationController).onRectangleOnScreenRequested(eq(TEST_DISPLAY),
+ eq(TEST_RECT.left), eq(TEST_RECT.top), eq(TEST_RECT.right), eq(TEST_RECT.bottom));
+ verify(mWindowMagnificationManager, never()).onRectangleOnScreenRequested(anyInt(),
+ anyInt(), anyInt(), anyInt(), anyInt());
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_fullScreenIsInactivated_noneDispatchEvent() {
+ mMagnificationController.onFullScreenMagnificationActivationState(TEST_DISPLAY,
+ true);
+ mMagnificationController.onFullScreenMagnificationActivationState(TEST_DISPLAY,
+ false);
+ WindowManagerInternal.AccessibilityControllerInternal.UiChangesForAccessibilityCallbacks
+ callbacks = getUiChangesForAccessibilityCallbacks();
+
+ callbacks.onRectangleOnScreenRequested(TEST_DISPLAY,
+ TEST_RECT.left, TEST_RECT.top, TEST_RECT.right, TEST_RECT.bottom);
+
+ verify(mScreenMagnificationController, never()).onRectangleOnScreenRequested(anyInt(),
+ anyInt(), anyInt(), anyInt(), anyInt());
+ verify(mWindowMagnificationManager, never()).onRectangleOnScreenRequested(anyInt(),
+ anyInt(), anyInt(), anyInt(), anyInt());
+ }
+
+ @Test
+ public void onRectangleOnScreenRequested_NoneIsActivated_noneDispatchEvent() {
+ WindowManagerInternal.AccessibilityControllerInternal.UiChangesForAccessibilityCallbacks
+ callbacks = getUiChangesForAccessibilityCallbacks();
+
+ callbacks.onRectangleOnScreenRequested(TEST_DISPLAY,
+ TEST_RECT.left, TEST_RECT.top, TEST_RECT.right, TEST_RECT.bottom);
+
+ verify(mScreenMagnificationController, never()).onRectangleOnScreenRequested(
+ eq(TEST_DISPLAY), anyInt(), anyInt(), anyInt(), anyInt());
+ verify(mWindowMagnificationManager, never()).onRectangleOnScreenRequested(anyInt(),
+ anyInt(), anyInt(), anyInt(), anyInt());
+ }
+
+ @Test
+ public void onWindowModeActivated_fullScreenIsActivatedByExternal_fullScreenIsDisabled() {
mScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY,
DEFAULT_SCALE, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y,
true, TEST_SERVICE_ID);
@@ -461,11 +524,12 @@ public class MagnificationControllerTest {
@Test
public void
onFullScreenMagnificationActivationState_fullScreenActivated_logFullScreenDuration() {
- mMagnificationController.onFullScreenMagnificationActivationState(TEST_DISPLAY, true);
+ MagnificationController spyController = spy(mMagnificationController);
+ spyController.onFullScreenMagnificationActivationState(TEST_DISPLAY, true);
- mMagnificationController.onFullScreenMagnificationActivationState(TEST_DISPLAY, false);
+ spyController.onFullScreenMagnificationActivationState(TEST_DISPLAY, false);
- verify(mMagnificationController).logMagnificationUsageState(
+ verify(spyController).logMagnificationUsageState(
eq(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN), anyLong());
}
@@ -627,43 +691,48 @@ public class MagnificationControllerTest {
@Test
public void imeWindowStateShown_windowMagnifying_logWindowMode() {
- mMagnificationController.onWindowMagnificationActivationState(TEST_DISPLAY, true);
+ MagnificationController spyController = spy(mMagnificationController);
+ spyController.onWindowMagnificationActivationState(TEST_DISPLAY, true);
- mMagnificationController.onImeWindowVisibilityChanged(true);
+ spyController.onImeWindowVisibilityChanged(true);
- verify(mMagnificationController).logMagnificationModeWithIme(
+ verify(spyController).logMagnificationModeWithIme(
eq(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW));
}
@Test
public void imeWindowStateShown_fullScreenMagnifying_logFullScreenMode() {
- mMagnificationController.onFullScreenMagnificationActivationState(TEST_DISPLAY, true);
+ MagnificationController spyController = spy(mMagnificationController);
+ spyController.onFullScreenMagnificationActivationState(TEST_DISPLAY, true);
- mMagnificationController.onImeWindowVisibilityChanged(true);
+ spyController.onImeWindowVisibilityChanged(true);
- verify(mMagnificationController).logMagnificationModeWithIme(
+ verify(spyController).logMagnificationModeWithIme(
eq(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN));
}
@Test
public void imeWindowStateShown_noMagnifying_noLogAnyMode() {
- mMagnificationController.onImeWindowVisibilityChanged(true);
+ MagnificationController spyController = spy(mMagnificationController);
+ spyController.onImeWindowVisibilityChanged(true);
- verify(mMagnificationController, never()).logMagnificationModeWithIme(anyInt());
+ verify(spyController, never()).logMagnificationModeWithIme(anyInt());
}
@Test
public void imeWindowStateHidden_windowMagnifying_noLogAnyMode() {
- mMagnificationController.onFullScreenMagnificationActivationState(TEST_DISPLAY, true);
+ MagnificationController spyController = spy(mMagnificationController);
+ spyController.onFullScreenMagnificationActivationState(TEST_DISPLAY, true);
- verify(mMagnificationController, never()).logMagnificationModeWithIme(anyInt());
+ verify(spyController, never()).logMagnificationModeWithIme(anyInt());
}
@Test
public void imeWindowStateHidden_fullScreenMagnifying_noLogAnyMode() {
- mMagnificationController.onWindowMagnificationActivationState(TEST_DISPLAY, true);
+ MagnificationController spyController = spy(mMagnificationController);
+ spyController.onWindowMagnificationActivationState(TEST_DISPLAY, true);
- verify(mMagnificationController, never()).logMagnificationModeWithIme(anyInt());
+ verify(spyController, never()).logMagnificationModeWithIme(anyInt());
}
@Test
@@ -718,6 +787,17 @@ public class MagnificationControllerTest {
MAGNIFIED_CENTER_X).setCenterY(MAGNIFIED_CENTER_Y).build();
}
+ private WindowManagerInternal.AccessibilityControllerInternal.UiChangesForAccessibilityCallbacks
+ getUiChangesForAccessibilityCallbacks() {
+ ArgumentCaptor<WindowManagerInternal.AccessibilityControllerInternal
+ .UiChangesForAccessibilityCallbacks> captor = ArgumentCaptor.forClass(
+ WindowManagerInternal.AccessibilityControllerInternal
+ .UiChangesForAccessibilityCallbacks.class);
+ verify(mMockWindowManagerInternal.getAccessibilityController())
+ .setUiChangesForAccessibilityCallbacks(captor.capture());
+ return captor.getValue();
+ }
+
/**
* Stubs public methods to simulate the real beahviours.
*/
diff --git a/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
index c36e1a841a7d..bccd8a0b14b4 100644
--- a/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -19,8 +19,12 @@ package com.android.server.backup;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.backup.BackupAgent;
@@ -35,18 +39,23 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.backup.internal.LifecycleOperationStorage;
import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.params.BackupParams;
import com.android.server.backup.transport.BackupTransportClient;
import com.android.server.backup.transport.TransportConnection;
import com.android.server.backup.utils.BackupEligibilityRules;
+import com.google.common.collect.ImmutableSet;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.function.IntConsumer;
+
@Presubmit
@RunWith(AndroidJUnit4.class)
public class UserBackupManagerServiceTest {
@@ -60,7 +69,7 @@ public class UserBackupManagerServiceTest {
@Mock TransportConnection mTransportConnection;
@Mock BackupTransportClient mBackupTransport;
@Mock BackupEligibilityRules mBackupEligibilityRules;
-
+ @Mock LifecycleOperationStorage mOperationStorage;
private TestBackupService mService;
@@ -68,7 +77,7 @@ public class UserBackupManagerServiceTest {
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mService = new TestBackupService(mContext, mPackageManager);
+ mService = new TestBackupService(mContext, mPackageManager, mOperationStorage);
mService.setEnabled(true);
mService.setSetupComplete(true);
}
@@ -162,6 +171,31 @@ public class UserBackupManagerServiceTest {
assertThat(operationType).isEqualTo(OperationType.MIGRATION);
}
+ @Test
+ public void testAgentDisconnected_cancelsCurrentOperations() throws Exception {
+ when(mOperationStorage.operationTokensForPackage(eq("com.android.foo"))).thenReturn(
+ ImmutableSet.of(123, 456, 789)
+ );
+
+ mService.agentDisconnected("com.android.foo");
+
+ verify(mOperationStorage).cancelOperation(eq(123), eq(true), any(IntConsumer.class));
+ verify(mOperationStorage).cancelOperation(eq(456), eq(true), any());
+ verify(mOperationStorage).cancelOperation(eq(789), eq(true), any());
+ }
+
+ @Test
+ public void testAgentDisconnected_unknownPackageName_cancelsNothing() throws Exception {
+ when(mOperationStorage.operationTokensForPackage(eq("com.android.foo"))).thenReturn(
+ ImmutableSet.of()
+ );
+
+ mService.agentDisconnected("com.android.foo");
+
+ verify(mOperationStorage, never())
+ .cancelOperation(anyInt(), anyBoolean(), any(IntConsumer.class));
+ }
+
private static PackageInfo getPackageInfo(String packageName) {
PackageInfo packageInfo = new PackageInfo();
packageInfo.applicationInfo = new ApplicationInfo();
@@ -173,8 +207,9 @@ public class UserBackupManagerServiceTest {
boolean isEnabledStatePersisted = false;
boolean shouldUseNewBackupEligibilityRules = false;
- TestBackupService(Context context, PackageManager packageManager) {
- super(context, packageManager);
+ TestBackupService(Context context, PackageManager packageManager,
+ LifecycleOperationStorage operationStorage) {
+ super(context, packageManager, operationStorage);
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/backup/internal/BackupHandlerTest.java b/services/tests/servicestests/src/com/android/server/backup/internal/BackupHandlerTest.java
index fa35e3f8646e..3c79d8bde37b 100644
--- a/services/tests/servicestests/src/com/android/server/backup/internal/BackupHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/internal/BackupHandlerTest.java
@@ -18,7 +18,6 @@ package com.android.server.backup.internal;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertThrows;
import static org.testng.Assert.assertTrue;
import android.os.HandlerThread;
@@ -28,6 +27,7 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.backup.BackupAgentTimeoutParameters;
+import com.android.server.backup.OperationStorage;
import com.android.server.backup.UserBackupManagerService;
import org.junit.After;
@@ -46,6 +46,7 @@ public class BackupHandlerTest {
private static final int MESSAGE_TIMEOUT_MINUTES = 1;
@Mock private UserBackupManagerService mUserBackupManagerService;
+ @Mock private OperationStorage mOperationStorage;
@Mock private BackupAgentTimeoutParameters mTimeoutParameters;
private HandlerThread mHandlerThread;
@@ -114,7 +115,7 @@ public class BackupHandlerTest {
private final boolean mShouldStop;
TestBackupHandler(boolean shouldStop) {
- super(mUserBackupManagerService, mHandlerThread);
+ super(mUserBackupManagerService, mOperationStorage, mHandlerThread);
mShouldStop = shouldStop;
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
index 847fe2ecf52d..7f7c716bc1f0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
@@ -61,6 +61,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.Map;
@SmallTest
@Presubmit
@@ -130,6 +131,17 @@ public class ApexManagerTest {
}
@Test
+ public void testGetApexSystemServices() throws RemoteException {
+ when(mApexService.getAllPackages()).thenReturn(createApexInfoForTestPkg(true, false));
+ mApexManager.scanApexPackagesTraced(mPackageParser2,
+ ParallelPackageParser.makeExecutorService());
+
+ Map<String, String> services = mApexManager.getApexSystemServices();
+ assertThat(services).hasSize(1);
+ assertThat(services).containsKey("com.android.apex.test.ApexSystemService");
+ }
+
+ @Test
public void testGetActivePackages() throws RemoteException {
when(mApexService.getAllPackages()).thenReturn(createApexInfoForTestPkg(true, true));
mApexManager.scanApexPackagesTraced(mPackageParser2,
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 58c9db76c282..ea7804d632a5 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -1500,6 +1500,20 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0);
}
+ /**
+ * Make a hidden shortcut with an ID.
+ */
+ protected ShortcutInfo makeShortcutExcludedFromLauncher(String id) {
+ final ShortcutInfo.Builder b = new ShortcutInfo.Builder(mClientContext, id)
+ .setActivity(new ComponentName(mClientContext.getPackageName(), "main"))
+ .setShortLabel("Title-" + id)
+ .setIntent(makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class))
+ .setExcludedFromSurfaces(ShortcutInfo.SURFACE_LAUNCHER);
+ final ShortcutInfo s = b.build();
+ s.setTimestamp(mInjectedCurrentTimeMillis);
+ return s;
+ }
+
@Deprecated // Title was renamed to short label.
protected ShortcutInfo makeShortcutWithTitle(String id, String title) {
return makeShortcut(
@@ -1889,6 +1903,18 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
assertEquals("Exception type different", expectedException, thrown.getClass());
}
+ protected void assertThrown(@NonNull final Class<?> expectedException,
+ @NonNull final Runnable fn) {
+ Exception thrown = null;
+ try {
+ fn.run();
+ } catch (Exception e) {
+ thrown = e;
+ }
+ assertNotNull("Exception was not thrown", thrown);
+ assertEquals("Exception type different", expectedException, thrown.getClass());
+ }
+
protected void assertBitmapDirectories(int userId, String... expectedDirectories) {
final Set<String> expected = hashSet(set(expectedDirectories));
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
index 71d5b77306a4..28f24f2b55ee 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -470,9 +470,7 @@ public class ScanTests {
.addUsesPermission(
new ParsedUsesPermissionImpl(Manifest.permission.FACTORY_TEST, 0));
- final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(
- mMockPackageManager, mMockInjector);
- final ScanResult scanResult = scanPackageHelper.scanPackageOnlyLI(
+ final ScanResult scanResult = ScanPackageUtils.scanPackageOnlyLI(
createBasicScanRequestBuilder(basicPackage).build(),
mMockInjector,
true /*isUnderFactoryTest*/,
@@ -520,9 +518,7 @@ public class ScanTests {
private ScanResult executeScan(
ScanRequest scanRequest) throws PackageManagerException {
- final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(
- mMockPackageManager, mMockInjector);
- ScanResult result = scanPackageHelper.scanPackageOnlyLI(
+ ScanResult result = ScanPackageUtils.scanPackageOnlyLI(
scanRequest,
mMockInjector,
false /*isUnderFactoryTest*/,
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index 32a88bd22986..a350dfbad662 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -8900,6 +8900,48 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
filter_any));
}
+ public void testAddingShortcuts_ExcludesHiddenFromLauncherShortcuts() {
+ final ShortcutInfo s1 = makeShortcutExcludedFromLauncher("s1");
+ final ShortcutInfo s2 = makeShortcutExcludedFromLauncher("s2");
+ final ShortcutInfo s3 = makeShortcutExcludedFromLauncher("s3");
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(s1, s2, s3)));
+ assertEmpty(mManager.getDynamicShortcuts());
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.addDynamicShortcuts(list(s1, s2, s3)));
+ assertEmpty(mManager.getDynamicShortcuts());
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.pushDynamicShortcut(s1);
+ assertEmpty(mManager.getDynamicShortcuts());
+ });
+ }
+
+ public void testUpdateShortcuts_ExcludesHiddenFromLauncherShortcuts() {
+ final ShortcutInfo s1 = makeShortcut("s1");
+ final ShortcutInfo s2 = makeShortcut("s2");
+ final ShortcutInfo s3 = makeShortcut("s3");
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(s1, s2, s3)));
+ assertThrown(IllegalArgumentException.class, () -> {
+ mManager.updateShortcuts(list(makeShortcutExcludedFromLauncher("s1")));
+ });
+ });
+ }
+
+ public void testPinHiddenShortcuts_ThrowsException() {
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertThrown(IllegalArgumentException.class, () -> {
+ mManager.requestPinShortcut(makeShortcutExcludedFromLauncher("s1"), null);
+ });
+ });
+ }
+
private Uri getFileUriFromResource(String fileName, int resId) throws IOException {
File file = new File(getTestContext().getFilesDir(), fileName);
// Make sure we are not leaving phantom files behind.
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
index bcd216dd35d4..0708be2fb0c3 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
@@ -257,6 +257,28 @@ public class ShortcutManagerTest12 extends BaseShortcutManagerTest {
assertEquals("custom", map.get("s3").getShortLabel());
}
+ public void testShortcutsExcludedFromLauncher_PersistedToDisk() {
+ if (!mService.isAppSearchEnabled()) {
+ return;
+ }
+ setCaller(CALLING_PACKAGE_1, USER_0);
+ mManager.setDynamicShortcuts(list(
+ makeShortcutExcludedFromLauncher("s1"),
+ makeShortcutExcludedFromLauncher("s2"),
+ makeShortcutExcludedFromLauncher("s3"),
+ makeShortcutExcludedFromLauncher("s4"),
+ makeShortcutExcludedFromLauncher("s5")
+ ));
+ final List<ShortcutInfo> shortcuts = getAllPersistedShortcuts();
+ assertNotNull(shortcuts);
+ assertEquals(5, shortcuts.size());
+ final Map<String, ShortcutInfo> map = shortcuts.stream()
+ .collect(Collectors.toMap(ShortcutInfo::getId, Function.identity()));
+ assertTrue(map.containsKey("s3"));
+ assertEquals("Title-s3", map.get("s3").getShortLabel());
+ }
+
+
private List<ShortcutInfo> getAllPersistedShortcuts() {
try {
SystemClock.sleep(500);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index e8a279907c5e..d49cf670f471 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -5112,6 +5112,11 @@ public class PreferencesHelperTest extends UiServiceTestCase {
assertTrue(expected.containsKey(uid));
assertThat(expected.get(uid)).isEqualTo(
builder.getInt(PackageNotificationPreferences.IMPORTANCE_FIELD_NUMBER));
+
+ // pre-migration, the userSet field will always default to false
+ boolean userSet = builder.getBoolean(
+ PackageNotificationPreferences.USER_SET_IMPORTANCE_FIELD_NUMBER);
+ assertFalse(userSet);
}
}
}
@@ -5123,8 +5128,8 @@ public class PreferencesHelperTest extends UiServiceTestCase {
// build a collection of app permissions that should be passed in but ignored
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
appPermissions.put(new Pair(1, "first"), new Pair(true, false)); // not in local prefs
- appPermissions.put(new Pair(3, "third"), new Pair(false, false)); // not in local prefs
- appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false)); // in local prefs
+ appPermissions.put(new Pair(3, "third"), new Pair(false, true)); // not in local prefs
+ appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, true)); // in local prefs
// package preferences: PKG_O not banned based on local importance, and PKG_P is
mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
@@ -5132,11 +5137,11 @@ public class PreferencesHelperTest extends UiServiceTestCase {
// expected output. format: uid -> importance, as only uid (and not package name)
// is in PackageNotificationPreferences
- ArrayMap<Integer, Integer> expected = new ArrayMap<>();
- expected.put(1, IMPORTANCE_DEFAULT);
- expected.put(3, IMPORTANCE_NONE);
- expected.put(UID_O, IMPORTANCE_NONE); // banned by permissions
- expected.put(UID_P, IMPORTANCE_NONE); // defaults to none
+ ArrayMap<Integer, Pair<Integer, Boolean>> expected = new ArrayMap<>();
+ expected.put(1, new Pair(IMPORTANCE_DEFAULT, false));
+ expected.put(3, new Pair(IMPORTANCE_NONE, true));
+ expected.put(UID_O, new Pair(IMPORTANCE_NONE, true)); // banned by permissions
+ expected.put(UID_P, new Pair(IMPORTANCE_NONE, false)); // defaults to none, false
ArrayList<StatsEvent> events = new ArrayList<>();
mHelper.pullPackagePreferencesStats(events, appPermissions);
@@ -5144,11 +5149,14 @@ public class PreferencesHelperTest extends UiServiceTestCase {
for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
if (builder.getAtomId() == PACKAGE_NOTIFICATION_PREFERENCES) {
int uid = builder.getInt(PackageNotificationPreferences.UID_FIELD_NUMBER);
+ boolean userSet = builder.getBoolean(
+ PackageNotificationPreferences.USER_SET_IMPORTANCE_FIELD_NUMBER);
// if it's one of the expected ids, then make sure the importance matches
assertTrue(expected.containsKey(uid));
- assertThat(expected.get(uid)).isEqualTo(
+ assertThat(expected.get(uid).first).isEqualTo(
builder.getInt(PackageNotificationPreferences.IMPORTANCE_FIELD_NUMBER));
+ assertThat(expected.get(uid).second).isEqualTo(userSet);
}
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 4a8e121dc3d1..2e62286f2969 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -16,6 +16,10 @@
package com.android.server.wm;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
+import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -103,6 +107,7 @@ import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.never;
import android.app.ActivityOptions;
+import android.app.ICompatCameraControlCallback;
import android.app.servertransaction.ActivityConfigurationChangeItem;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.DestroyActivityItem;
@@ -3084,6 +3089,188 @@ public class ActivityRecordTests extends WindowTestsBase {
eq(null));
}
+ @Test
+ public void testUpdateCameraCompatState_flagIsEnabled_controlStateIsUpdated() {
+ final ActivityRecord activity = createActivityWithTask();
+ // Mock a flag being enabled.
+ doReturn(true).when(activity).isCameraCompatControlEnabled();
+
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ false, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(),
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ true, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(),
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+
+ activity.updateCameraCompatState(/* showControl */ false,
+ /* transformationApplied */ false, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_HIDDEN);
+
+ activity.updateCameraCompatState(/* showControl */ false,
+ /* transformationApplied */ true, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_HIDDEN);
+ }
+
+ @Test
+ public void testUpdateCameraCompatState_flagIsDisabled_controlStateIsHidden() {
+ final ActivityRecord activity = createActivityWithTask();
+ // Mock a flag being disabled.
+ doReturn(false).when(activity).isCameraCompatControlEnabled();
+
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ false, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_HIDDEN);
+
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ true, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_HIDDEN);
+ }
+
+ @Test
+ public void testUpdateCameraCompatStateFromUser_clickedOnDismiss() throws RemoteException {
+ final ActivityRecord activity = createActivityWithTask();
+ // Mock a flag being enabled.
+ doReturn(true).when(activity).isCameraCompatControlEnabled();
+
+ ICompatCameraControlCallback callback = getCompatCameraControlCallback();
+ spyOn(callback);
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ false, callback);
+
+ assertEquals(activity.getCameraCompatControlState(),
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ // Clicking on the button.
+ activity.updateCameraCompatStateFromUser(CAMERA_COMPAT_CONTROL_DISMISSED);
+
+ verify(callback, never()).revertCameraCompatTreatment();
+ verify(callback, never()).applyCameraCompatTreatment();
+ assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_DISMISSED);
+
+ // All following updates are ignored.
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ false, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_DISMISSED);
+
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ true, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_DISMISSED);
+
+ activity.updateCameraCompatState(/* showControl */ false,
+ /* transformationApplied */ true, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_DISMISSED);
+ }
+
+ @Test
+ public void testUpdateCameraCompatStateFromUser_clickedOnApplyTreatment()
+ throws RemoteException {
+ final ActivityRecord activity = createActivityWithTask();
+ // Mock a flag being enabled.
+ doReturn(true).when(activity).isCameraCompatControlEnabled();
+
+ ICompatCameraControlCallback callback = getCompatCameraControlCallback();
+ spyOn(callback);
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ false, callback);
+
+ assertEquals(activity.getCameraCompatControlState(),
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ // Clicking on the button.
+ activity.updateCameraCompatStateFromUser(CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+
+ verify(callback, never()).revertCameraCompatTreatment();
+ verify(callback).applyCameraCompatTreatment();
+ assertEquals(activity.getCameraCompatControlState(),
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+
+ // Request from the client to show the control are ignored respecting the user choice.
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ false, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(),
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+
+ // Request from the client to hide the control is respected.
+ activity.updateCameraCompatState(/* showControl */ false,
+ /* transformationApplied */ true, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_HIDDEN);
+
+ // Request from the client to show the control again is respected.
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ false, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(),
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ }
+
+ @Test
+ public void testUpdateCameraCompatStateFromUser_clickedOnRevertTreatment()
+ throws RemoteException {
+ final ActivityRecord activity = createActivityWithTask();
+ // Mock a flag being enabled.
+ doReturn(true).when(activity).isCameraCompatControlEnabled();
+
+ ICompatCameraControlCallback callback = getCompatCameraControlCallback();
+ spyOn(callback);
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ true, callback);
+
+ assertEquals(activity.getCameraCompatControlState(),
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+
+ // Clicking on the button.
+ activity.updateCameraCompatStateFromUser(CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ verify(callback).revertCameraCompatTreatment();
+ verify(callback, never()).applyCameraCompatTreatment();
+ assertEquals(activity.getCameraCompatControlState(),
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ // Request from the client to show the control are ignored respecting the user choice.
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ true, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(),
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+
+ // Request from the client to hide the control is respected.
+ activity.updateCameraCompatState(/* showControl */ false,
+ /* transformationApplied */ true, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(), CAMERA_COMPAT_CONTROL_HIDDEN);
+
+ // Request from the client to show the control again is respected.
+ activity.updateCameraCompatState(/* showControl */ true,
+ /* transformationApplied */ true, /* callback */ null);
+
+ assertEquals(activity.getCameraCompatControlState(),
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+ }
+
+ private ICompatCameraControlCallback getCompatCameraControlCallback() {
+ return new ICompatCameraControlCallback.Stub() {
+ @Override
+ public void applyCameraCompatTreatment() {}
+
+ @Override
+ public void revertCameraCompatTreatment() {}
+ };
+ }
+
private void assertHasStartingWindow(ActivityRecord atoken) {
assertNotNull(atoken.mStartingSurface);
assertNotNull(atoken.mStartingData);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index 3e617d58caa0..55d6df95e66f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -34,6 +34,7 @@ import static org.mockito.ArgumentMatchers.nullable;
import android.annotation.Nullable;
import android.app.ActivityManagerInternal;
+import android.app.ActivityOptions;
import android.app.KeyguardManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.content.Context;
@@ -58,6 +59,7 @@ import com.android.internal.app.UnlaunchableAppActivity;
import com.android.server.LocalServices;
import com.android.server.am.ActivityManagerService;
import com.android.server.pm.PackageManagerService;
+import com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptResult;
import org.junit.After;
import org.junit.Before;
@@ -298,35 +300,44 @@ public class ActivityStartInterceptorTest {
assertFalse(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
}
- public void addMockInterceptorCallback(@Nullable Intent intent) {
+ public void addMockInterceptorCallback(
+ @Nullable Intent intent, @Nullable ActivityOptions activityOptions) {
int size = mActivityInterceptorCallbacks.size();
mActivityInterceptorCallbacks.put(size, new ActivityInterceptorCallback() {
@Override
- public Intent intercept(ActivityInterceptorInfo info) {
- return intent;
+ public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
+ if (intent == null && activityOptions == null) {
+ return null;
+ }
+ return new ActivityInterceptResult(
+ intent != null ? intent : info.intent,
+ activityOptions != null ? activityOptions : info.checkedOptions);
}
});
}
@Test
public void testInterceptionCallback_singleCallback() {
- addMockInterceptorCallback(new Intent("android.test.foo"));
+ addMockInterceptorCallback(
+ new Intent("android.test.foo"),
+ ActivityOptions.makeBasic().setLaunchDisplayId(3));
assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
assertEquals("android.test.foo", mInterceptor.mIntent.getAction());
+ assertEquals(3, mInterceptor.mActivityOptions.getLaunchDisplayId());
}
@Test
public void testInterceptionCallback_singleCallbackReturnsNull() {
- addMockInterceptorCallback(null);
+ addMockInterceptorCallback(null, null);
assertFalse(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
}
@Test
public void testInterceptionCallback_fallbackToSecondCallback() {
- addMockInterceptorCallback(null);
- addMockInterceptorCallback(new Intent("android.test.second"));
+ addMockInterceptorCallback(null, null);
+ addMockInterceptorCallback(new Intent("android.test.second"), null);
assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
assertEquals("android.test.second", mInterceptor.mIntent.getAction());
@@ -334,7 +345,7 @@ public class ActivityStartInterceptorTest {
@Test
public void testActivityLaunchedCallback_singleCallback() {
- addMockInterceptorCallback(null);
+ addMockInterceptorCallback(null, null);
assertEquals(1, mActivityInterceptorCallbacks.size());
final ActivityInterceptorCallback callback = mActivityInterceptorCallbacks.valueAt(0);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index e0072b441577..a2b04c295944 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -53,7 +53,6 @@ import android.app.IApplicationThread;
import android.app.PictureInPictureParams;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.EnterPipRequestedItem;
-import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
@@ -951,7 +950,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
new ActivityInterceptorCallback() {
@Nullable
@Override
- public Intent intercept(ActivityInterceptorInfo info) {
+ public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
return null;
}
});
@@ -963,7 +962,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
new ActivityInterceptorCallback() {
@Nullable
@Override
- public Intent intercept(ActivityInterceptorInfo info) {
+ public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
return null;
}
});
@@ -975,7 +974,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
new ActivityInterceptorCallback() {
@Nullable
@Override
- public Intent intercept(ActivityInterceptorInfo info) {
+ public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
return null;
}
});
@@ -983,7 +982,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
new ActivityInterceptorCallback() {
@Nullable
@Override
- public Intent intercept(ActivityInterceptorInfo info) {
+ public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
return null;
}
});
@@ -997,7 +996,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
new ActivityInterceptorCallback() {
@Nullable
@Override
- public Intent intercept(ActivityInterceptorInfo info) {
+ public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
return null;
}
});
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
index f5b361cff16d..97b1c91d156a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static android.util.DisplayMetrics.DENSITY_DEFAULT;
import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM;
import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT;
@@ -56,7 +55,6 @@ public class DisplayPolicyTestsBase extends WindowTestsBase {
static final int DISPLAY_WIDTH = 500;
static final int DISPLAY_HEIGHT = 1000;
- static final int DISPLAY_DENSITY = 320;
static final int DISPLAY_CUTOUT_HEIGHT = 8;
static final int IME_HEIGHT = 415;
@@ -85,12 +83,7 @@ public class DisplayPolicyTestsBase extends WindowTestsBase {
doReturn(true).when(mDisplayPolicy).hasNavigationBar();
doReturn(true).when(mDisplayPolicy).hasStatusBar();
- final int shortSizeDp =
- Math.min(DISPLAY_WIDTH, DISPLAY_HEIGHT) * DENSITY_DEFAULT / DISPLAY_DENSITY;
- final int longSizeDp =
- Math.min(DISPLAY_WIDTH, DISPLAY_HEIGHT) * DENSITY_DEFAULT / DISPLAY_DENSITY;
- mDisplayContent.getDisplayRotation().configure(
- DISPLAY_WIDTH, DISPLAY_HEIGHT, shortSizeDp, longSizeDp);
+ mDisplayContent.getDisplayRotation().configure(DISPLAY_WIDTH, DISPLAY_HEIGHT);
mDisplayPolicy.onConfigurationChanged();
addWindow(mStatusBarWindow);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index e1aca55762d6..6342183ea28e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -681,7 +681,7 @@ public class DisplayRotationTests {
}
/**
- * Call {@link DisplayRotation#configure(int, int, int, int)} to configure {@link #mTarget}
+ * Call {@link DisplayRotation#configure(int, int)} to configure {@link #mTarget}
* according to given parameters.
*/
private void configureDisplayRotation(int displayOrientation, boolean isCar, boolean isTv) {
@@ -709,9 +709,7 @@ public class DisplayRotationTests {
when(mockPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK))
.thenReturn(isTv);
- final int shortSizeDp = (isCar || isTv) ? 540 : 720;
- final int longSizeDp = 960;
- mTarget.configure(width, height, shortSizeDp, longSizeDp);
+ mTarget.configure(width, height);
}
private void freezeRotation(int rotation) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
index c4cccf022322..19247604ad10 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
@@ -104,7 +104,8 @@ public class RefreshRatePolicyTest extends WindowTestsBase {
mPolicy.addNonHighRefreshRatePackage("com.android.test");
assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow));
assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
- assertEquals(0, mPolicy.getPreferredMinRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
+ assertEquals(LOW_REFRESH_RATE,
+ mPolicy.getPreferredMinRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
assertEquals(LOW_REFRESH_RATE,
mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
mPolicy.removeNonHighRefreshRatePackage("com.android.test");
@@ -165,7 +166,8 @@ public class RefreshRatePolicyTest extends WindowTestsBase {
assertEquals(HI_MODE_ID, mPolicy.getPreferredModeId(overrideWindow));
assertEquals(HI_REFRESH_RATE,
mPolicy.getPreferredRefreshRate(overrideWindow), FLOAT_TOLERANCE);
- assertEquals(0, mPolicy.getPreferredMinRefreshRate(overrideWindow), FLOAT_TOLERANCE);
+ assertEquals(LOW_REFRESH_RATE,
+ mPolicy.getPreferredMinRefreshRate(overrideWindow), FLOAT_TOLERANCE);
assertEquals(LOW_REFRESH_RATE,
mPolicy.getPreferredMaxRefreshRate(overrideWindow), FLOAT_TOLERANCE);
}
@@ -180,7 +182,8 @@ public class RefreshRatePolicyTest extends WindowTestsBase {
assertEquals(0, mPolicy.getPreferredModeId(overrideWindow));
assertEquals(HI_REFRESH_RATE,
mPolicy.getPreferredRefreshRate(overrideWindow), FLOAT_TOLERANCE);
- assertEquals(0, mPolicy.getPreferredMinRefreshRate(overrideWindow), FLOAT_TOLERANCE);
+ assertEquals(LOW_REFRESH_RATE,
+ mPolicy.getPreferredMinRefreshRate(overrideWindow), FLOAT_TOLERANCE);
assertEquals(LOW_REFRESH_RATE,
mPolicy.getPreferredMaxRefreshRate(overrideWindow), FLOAT_TOLERANCE);
}
@@ -257,7 +260,8 @@ public class RefreshRatePolicyTest extends WindowTestsBase {
mPolicy.addNonHighRefreshRatePackage("com.android.test");
assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow));
assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
- assertEquals(0, mPolicy.getPreferredMinRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
+ assertEquals(LOW_REFRESH_RATE,
+ mPolicy.getPreferredMinRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
assertEquals(LOW_REFRESH_RATE,
mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index b7417c4e8dd0..ec6cd9249317 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -488,7 +488,8 @@ public class TransitionTests extends WindowTestsBase {
final TestTransitionPlayer player = registerTestTransitionPlayer();
mDisplayContent.getDisplayRotation().setRotation(mDisplayContent.getRotation() + 1);
- mDisplayContent.requestChangeTransitionIfNeeded(1 /* any changes */);
+ mDisplayContent.requestChangeTransitionIfNeeded(1 /* any changes */,
+ null /* displayChange */);
final FadeRotationAnimationController fadeController =
mDisplayContent.getFadeRotationAnimationController();
assertNotNull(fadeController);
@@ -524,6 +525,43 @@ public class TransitionTests extends WindowTestsBase {
}
@Test
+ public void testAppTransitionWithRotationChange() {
+ final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar");
+ makeWindowVisible(statusBar);
+ mDisplayContent.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs);
+ final ActivityRecord app = createActivityRecord(mDisplayContent);
+ final TestTransitionPlayer player = registerTestTransitionPlayer();
+ final Transition transition = app.mTransitionController.createTransition(TRANSIT_OPEN);
+ app.mTransitionController.requestStartTransition(transition, app.getTask(),
+ null /* remoteTransition */, null /* displayChange */);
+ mDisplayContent.getDisplayRotation().setRotation(mDisplayContent.getRotation() + 1);
+ final int anyChanges = 1;
+ mDisplayContent.requestChangeTransitionIfNeeded(anyChanges, null /* displayChange */);
+ transition.setKnownConfigChanges(mDisplayContent, anyChanges);
+ final FadeRotationAnimationController fadeController =
+ mDisplayContent.getFadeRotationAnimationController();
+ assertNotNull(fadeController);
+ assertTrue(fadeController.shouldFreezeInsetsPosition(statusBar));
+
+ statusBar.setOrientationChanging(true);
+ player.startTransition();
+ // Non-app windows should not be collected.
+ assertFalse(statusBar.mToken.inTransition());
+ assertTrue(app.getTask().inTransition());
+
+ final SurfaceControl.Transaction startTransaction = mock(SurfaceControl.Transaction.class);
+ player.onTransactionReady(startTransaction);
+ // The leash should be unrotated.
+ verify(startTransaction).setMatrix(eq(statusBar.mToken.getAnimationLeash()), any(), any());
+
+ // The redrawn window will be faded in when the transition finishes. And because this test
+ // only use one non-activity window, the fade rotation controller should also be cleared.
+ statusBar.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
+ player.finish();
+ assertNull(mDisplayContent.getFadeRotationAnimationController());
+ }
+
+ @Test
public void testIntermediateVisibility() {
final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class);
final TransitionController controller = new TransitionController(mAtm, snapshotController);
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index 2d3b9286d69a..88725a6ea1b9 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -552,23 +552,26 @@ public class StorageStatsService extends IStorageStatsManager.Stub {
private static final int MSG_PACKAGE_REMOVED = 103;
/**
* By only triggering a re-calculation after the storage has changed sizes, we can avoid
- * recalculating quotas too often. Minimum change delta defines the percentage of change
- * we need to see before we recalculate.
+ * recalculating quotas too often. Minimum change delta high and low define the
+ * percentage of change we need to see before we recalculate quotas when the device has
+ * enough storage space (more than StorageManager.STORAGE_THRESHOLD_PERCENT_HIGH of total
+ * free) and in low storage condition respectively.
*/
- private static final double MINIMUM_CHANGE_DELTA = 0.05;
+ private static final long MINIMUM_CHANGE_DELTA_PERCENT_HIGH = 5;
+ private static final long MINIMUM_CHANGE_DELTA_PERCENT_LOW = 2;
private static final int UNSET = -1;
private static final boolean DEBUG = false;
private final StatFs mStats;
private long mPreviousBytes;
- private double mMinimumThresholdBytes;
+ private long mTotalBytes;
public H(Looper looper) {
super(looper);
// TODO: Handle all private volumes.
mStats = new StatFs(Environment.getDataDirectory().getAbsolutePath());
mPreviousBytes = mStats.getAvailableBytes();
- mMinimumThresholdBytes = mStats.getTotalBytes() * MINIMUM_CHANGE_DELTA;
+ mTotalBytes = mStats.getTotalBytes();
}
public void handleMessage(Message msg) {
@@ -584,7 +587,14 @@ public class StorageStatsService extends IStorageStatsManager.Stub {
case MSG_CHECK_STORAGE_DELTA: {
mStats.restat(Environment.getDataDirectory().getAbsolutePath());
long bytesDelta = Math.abs(mPreviousBytes - mStats.getAvailableBytes());
- if (bytesDelta > mMinimumThresholdBytes) {
+ long bytesDeltaThreshold;
+ if (mStats.getAvailableBytes() > mTotalBytes
+ * StorageManager.STORAGE_THRESHOLD_PERCENT_HIGH / 100) {
+ bytesDeltaThreshold = mTotalBytes * MINIMUM_CHANGE_DELTA_PERCENT_HIGH / 100;
+ } else {
+ bytesDeltaThreshold = mTotalBytes * MINIMUM_CHANGE_DELTA_PERCENT_LOW / 100;
+ }
+ if (bytesDelta > bytesDeltaThreshold) {
mPreviousBytes = mStats.getAvailableBytes();
recalculateQuotas(getInitializedStrategy());
notifySignificantDelta();
diff --git a/telecomm/java/android/telecom/CallAudioState.java b/telecomm/java/android/telecom/CallAudioState.java
index 4b9098504bc7..fccdf76372dd 100644
--- a/telecomm/java/android/telecom/CallAudioState.java
+++ b/telecomm/java/android/telecom/CallAudioState.java
@@ -248,10 +248,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/Connection.java b/telecomm/java/android/telecom/Connection.java
index 467084a716a0..21a180459978 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -3537,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..0f034ad6a45e 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -287,7 +287,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/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java
index 320308c9e926..f412a1825e2a 100644
--- a/telecomm/java/android/telecom/ParcelableCall.java
+++ b/telecomm/java/android/telecom/ParcelableCall.java
@@ -623,9 +623,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,23 +633,23 @@ 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();
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/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/telephony/java/android/service/carrier/CarrierService.java b/telephony/java/android/service/carrier/CarrierService.java
index d06ec11f3e61..ae91d4d9b595 100644
--- a/telephony/java/android/service/carrier/CarrierService.java
+++ b/telephony/java/android/service/carrier/CarrierService.java
@@ -15,6 +15,8 @@
package android.service.carrier;
import android.annotation.CallSuper;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
@@ -22,9 +24,12 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.PersistableBundle;
import android.os.ResultReceiver;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyRegistryManager;
import android.util.Log;
+import com.android.internal.util.ArrayUtils;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -87,7 +92,7 @@ public abstract class CarrierService extends Service {
* PersistableBundle} may be overridden by the system's default configuration service.
* </p>
*
- * @param id contains details about the current carrier that can be used do decide what
+ * @param id contains details about the current carrier that can be used to decide what
* configuration values to return. Instead of using details like MCCMNC to decide
* current carrier, it also contains subscription carrier id
* {@link android.telephony.TelephonyManager#getSimCarrierId()}, a platform-wide
@@ -95,10 +100,61 @@ public abstract class CarrierService extends Service {
* id as the key to look up the carrier info.
* @return a {@link PersistableBundle} object containing the configuration or null if default
* values should be used.
+ * @deprecated use {@link #onLoadConfig(int, CarrierIdentifier)} instead.
*/
+ @Deprecated
public abstract PersistableBundle onLoadConfig(CarrierIdentifier id);
/**
+ * Override this method to set carrier configuration on the given {@code subscriptionId}.
+ * <p>
+ * This method will be called by telephony services to get carrier-specific configuration
+ * values. The returned config will be saved by the system until,
+ * <ol>
+ * <li>The carrier app package is updated, or</li>
+ * <li>The carrier app requests a reload with
+ * {@link android.telephony.CarrierConfigManager#notifyConfigChangedForSubId
+ * notifyConfigChangedForSubId}.</li>
+ * </ol>
+ * This method can be called after a SIM card loads, which may be before or after boot.
+ * </p>
+ * <p>
+ * This method should not block for a long time. If expensive operations (e.g. network access)
+ * are required, this method can schedule the work and return null. Then, use
+ * {@link android.telephony.CarrierConfigManager#notifyConfigChangedForSubId
+ * notifyConfigChangedForSubId} to trigger a reload when the config is ready.
+ * </p>
+ * <p>
+ * Implementations should use the keys defined in {@link android.telephony.CarrierConfigManager
+ * CarrierConfigManager}. Any configuration values not set in the returned {@link
+ * PersistableBundle} may be overridden by the system's default configuration service.
+ * </p>
+ * <p>
+ * By default, this method just calls {@link #onLoadConfig(CarrierIdentifier)} with specified
+ * CarrierIdentifier {@code id}. Carrier app with target SDK
+ * {@link android.os.Build.VERSION_CODES#TIRAMISU} and above should override this method to
+ * load carrier configuration on the given {@code subscriptionId}.
+ * Note that {@link #onLoadConfig(CarrierIdentifier)} is still called prior to
+ * {@link android.os.Build.VERSION_CODES#TIRAMISU}.
+ * </p>
+ *
+ * @param subscriptionId the subscription on which the carrier app should load configuration
+ * @param id contains details about the current carrier that can be used to decide what
+ * configuration values to return. Instead of using details like MCCMNC to decide
+ * current carrier, it also contains subscription carrier id
+ * {@link android.telephony.TelephonyManager#getSimCarrierId()}, a platform-wide
+ * unique identifier for each carrier, CarrierConfigService can directly use carrier
+ * id as the key to look up the carrier info.
+ * @return a {@link PersistableBundle} object containing the configuration or null if default
+ * values should be used.
+ */
+ @SuppressLint("NullableCollection")
+ @Nullable
+ public PersistableBundle onLoadConfig(int subscriptionId, @Nullable CarrierIdentifier id) {
+ return onLoadConfig(id);
+ }
+
+ /**
* Informs the system of an intentional upcoming carrier network change by
* a carrier app. This call is optional and is only used to allow the
* system to provide alternative UI while telephony is performing an action
@@ -115,7 +171,12 @@ public abstract class CarrierService extends Service {
* active. Set this value to true to begin showing
* alternative UI and false to stop.
* @see android.telephony.TelephonyManager#hasCarrierPrivileges
+ * @deprecated use {@link #notifyCarrierNetworkChange(int, boolean)} instead.
+ * With no parameter to specify the subscription, this API will
+ * apply to all subscriptions that the carrier app has carrier
+ * privileges on.
*/
+ @Deprecated
public final void notifyCarrierNetworkChange(boolean active) {
TelephonyRegistryManager telephonyRegistryMgr =
(TelephonyRegistryManager) this.getSystemService(
@@ -126,6 +187,31 @@ public abstract class CarrierService extends Service {
}
/**
+ * Informs the system of an intentional upcoming carrier network change by a carrier app on the
+ * given {@code subscriptionId}. This call is optional and is only used to allow the system to
+ * provide alternative UI while telephony is performing an action that may result in
+ * intentional, temporary network lack of connectivity.
+ *
+ * <p>Based on the active parameter passed in, this method will either show or hide the
+ * alternative UI. There is no timeout associated with showing this UX, so a carrier app must
+ * be sure to call with active set to false sometime after calling with it set to true.
+ *
+ * <p>Requires Permission: calling app has carrier privileges.
+ *
+ * @param subscriptionId the subscription of the carrier network that trigger the change.
+ * @param active whether the carrier network change is or shortly will be active. Set this
+ * value to true to begin showing alternative UI and false to stop.
+ * @see android.telephony.TelephonyManager#hasCarrierPrivileges
+ */
+ public final void notifyCarrierNetworkChange(int subscriptionId, boolean active) {
+ TelephonyRegistryManager telephonyRegistryMgr = this.getSystemService(
+ TelephonyRegistryManager.class);
+ if (telephonyRegistryMgr != null) {
+ telephonyRegistryMgr.notifyCarrierNetworkChange(subscriptionId, active);
+ }
+ }
+
+ /**
* If overriding this method, call through to the super method for any unknown actions.
* {@inheritDoc}
*/
@@ -149,10 +235,16 @@ public abstract class CarrierService extends Service {
public static final String KEY_CONFIG_BUNDLE = "config_bundle";
@Override
- public void getCarrierConfig(CarrierIdentifier id, ResultReceiver result) {
+ public void getCarrierConfig(int phoneId, CarrierIdentifier id, ResultReceiver result) {
try {
+ int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ int[] subIds = SubscriptionManager.getSubId(phoneId);
+ if (!ArrayUtils.isEmpty(subIds)) {
+ // There should be at most one active subscription mapping to the phoneId.
+ subId = subIds[0];
+ }
Bundle data = new Bundle();
- data.putParcelable(KEY_CONFIG_BUNDLE, CarrierService.this.onLoadConfig(id));
+ data.putParcelable(KEY_CONFIG_BUNDLE, CarrierService.this.onLoadConfig(subId, id));
result.send(RESULT_OK, data);
} catch (Exception e) {
Log.e(LOG_TAG, "Error in onLoadConfig: " + e.getMessage(), e);
diff --git a/telephony/java/android/service/carrier/ICarrierService.aidl b/telephony/java/android/service/carrier/ICarrierService.aidl
index ac6f9614d8f5..054a280c3fe8 100644
--- a/telephony/java/android/service/carrier/ICarrierService.aidl
+++ b/telephony/java/android/service/carrier/ICarrierService.aidl
@@ -29,5 +29,5 @@ import android.service.carrier.CarrierIdentifier;
interface ICarrierService {
/** @see android.service.carrier.CarrierService#onLoadConfig */
- oneway void getCarrierConfig(in CarrierIdentifier id, in ResultReceiver result);
+ oneway void getCarrierConfig(in int phoneId, in CarrierIdentifier id, in ResultReceiver result);
}
diff --git a/telephony/java/android/telephony/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java
index 1d7a4761dec6..4469ffc14447 100644
--- a/telephony/java/android/telephony/AccessNetworkConstants.java
+++ b/telephony/java/android/telephony/AccessNetworkConstants.java
@@ -17,6 +17,7 @@
package android.telephony;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.hardware.radio.V1_5.AccessNetwork;
@@ -28,6 +29,8 @@ import java.lang.annotation.RetentionPolicy;
*/
public final class AccessNetworkConstants {
+ private static final String TAG = AccessNetworkConstants.class.getSimpleName();
+
/**
* Wireless transportation type
*
@@ -108,6 +111,21 @@ public final class AccessNetworkConstants {
default: return Integer.toString(type);
}
}
+
+ /** @hide */
+ public static @RadioAccessNetworkType int fromString(@NonNull String str) {
+ switch (str.toUpperCase()) {
+ case "GERAN" : return GERAN;
+ case "UTRAN" : return UTRAN;
+ case "EUTRAN" : return EUTRAN;
+ case "CDMA2000" : return CDMA2000;
+ case "IWLAN" : return IWLAN;
+ case "NGRAN" : return NGRAN;
+ default:
+ Rlog.e(TAG, "Invalid access network type " + str);
+ return UNKNOWN;
+ }
+ }
}
/**
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 7d24b761b814..53261cb0a599 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3776,6 +3776,17 @@ public class CarrierConfigManager {
"esim_max_download_retry_attempts_int";
/**
+ * List of opportunistic carrier-ids associated with CBRS Primary SIM. When CBRS pSIM is
+ * inserted, opportunistic eSIM is download and this configuration is used for grouping pSIM
+ * and opportunistic eSIM. Also when a new CBRS pSIM is inserted, old opportunistic eSIMs are
+ * deleted using the carrier-ids in this configuration.
+ *
+ * @hide
+ */
+ public static final String KEY_OPPORTUNISTIC_CARRIER_IDS_INT_ARRAY =
+ "opportunistic_carrier_ids_int_array";
+
+ /**
* Controls RSRP threshold at which OpportunisticNetworkService will decide whether
* the opportunistic network is good enough for internet data.
*/
@@ -4455,6 +4466,29 @@ public class CarrierConfigManager {
"subscription_group_uuid_string";
/**
+ * Controls the cellular usage setting.
+ *
+ * The usage setting indicates whether a device will remain attached to a network based on
+ * the primary use case for the service. A device will detach and search for a more-preferred
+ * network if the primary use case (voice or data) is not satisfied. Depending on the type
+ * of device, it may operate in a voice or data-centric mode by default.
+ *
+ * <p>Sets the usage setting in accordance with 3gpp 24.301 sec 4.3 and 3gpp 24.501 sec 4.3.
+ * Also refer to "UE's usage setting" as defined in 3gpp 24.301 section 3.1 and 3gpp 23.221
+ * Annex A.
+ *
+ * Either omit this key or pass a value of
+ * {@link SubscriptionManager#USAGE_SETTING_UNKNOWN unknown} to preserve the current setting.
+ *
+ * {@link SubscriptionManager#USAGE_SETTING_DEFAULT default},
+ * {@link SubscriptionManager#USAGE_SETTING_VOICE_CENTRIC voice-centric},
+ * or {@link SubscriptionManager#USAGE_SETTING_DATA_CENTRIC data-centric}.
+ * {@see SubscriptionInfo#getUsageSetting}
+ */
+ public static final String KEY_CELLULAR_USAGE_SETTING_INT =
+ "cellular_usage_setting_int";
+
+ /**
* Data switch validation minimal gap time, in milliseconds.
*
* Which means, if the same subscription on the same network (based on MCC+MNC+TAC+subId)
@@ -5381,6 +5415,34 @@ public class CarrierConfigManager {
public static final String KEY_UNTHROTTLE_DATA_RETRY_WHEN_TAC_CHANGES_BOOL =
"unthrottle_data_retry_when_tac_changes_bool";
+ /**
+ * IWLAN handover rules that determine whether handover is allowed or disallowed between
+ * cellular and IWLAN.
+ *
+ * The handover rules will be matched in the order. Here are some sample rules.
+ * <string-array name="iwlan_handover_rules" num="5">
+ * <!-- Handover from IWLAN to 2G/3G is not allowed -->
+ * <item value="source=IWLAN, target=GERAN|UTRAN, type=disallowed"/>
+ * <!-- Handover from 2G/3G to IWLAN is not allowed -->
+ * <item value="source=GERAN|UTRAN, target:IWLAN, type=disallowed"/>
+ * <!-- Handover from IWLAN to 3G/4G/5G is not allowed if the device is roaming. -->
+ * <item value="source=IWLAN, target=UTRAN|EUTRAN|NGRAN, roaming=true, type=disallowed"/>
+ * <!-- Handover from 4G to IWLAN is not allowed -->
+ * <item value="source=EUTRAN, target=IWLAN, type=disallowed"/>
+ * <!-- Handover is always allowed in any condition. -->
+ * <item value="source=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN,
+ * target=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, type=allowed"/>
+ * </string-array>
+ *
+ * When handover is not allowed, frameworks will tear down the data network on source transport,
+ * and then setup a new one on the target transport when Qualified Network Service changes the
+ * preferred access networks for particular APN types.
+ *
+ * @hide
+ */
+ public static final String KEY_IWLAN_HANDOVER_POLICY_STRING_ARRAY =
+ "iwlan_handover_policy_string_array";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -5906,6 +5968,7 @@ public class CarrierConfigManager {
sDefaults.putString(KEY_SMDP_SERVER_ADDRESS_STRING, "");
sDefaults.putInt(KEY_ESIM_MAX_DOWNLOAD_RETRY_ATTEMPTS_INT, 5);
sDefaults.putInt(KEY_ESIM_DOWNLOAD_RETRY_BACKOFF_TIMER_SEC_INT, 60);
+ sDefaults.putIntArray(KEY_OPPORTUNISTIC_CARRIER_IDS_INT_ARRAY, new int[] {0});
/* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_GOOD */
sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT, -108);
/* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_MODERATE */
@@ -5953,6 +6016,7 @@ public class CarrierConfigManager {
sDefaults.putInt(
KEY_OPPORTUNISTIC_TIME_TO_SCAN_AFTER_CAPABILITY_SWITCH_TO_PRIMARY_LONG,
120000);
+ sDefaults.putAll(ImsServiceEntitlement.getDefaults());
sDefaults.putAll(Gps.getDefaults());
sDefaults.putIntArray(KEY_CDMA_ENHANCED_ROAMING_INDICATOR_FOR_HOME_NETWORK_INT_ARRAY,
new int[] {
@@ -6043,6 +6107,11 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_UNTHROTTLE_DATA_RETRY_WHEN_TAC_CHANGES_BOOL, false);
sDefaults.putBoolean(KEY_VONR_SETTING_VISIBILITY_BOOL, true);
sDefaults.putBoolean(KEY_VONR_ENABLED_BOOL, false);
+ sDefaults.putStringArray(KEY_IWLAN_HANDOVER_POLICY_STRING_ARRAY, new String[]{
+ "source=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, "
+ + "target=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, type=allowed"});
+ sDefaults.putInt(KEY_CELLULAR_USAGE_SETTING_INT,
+ SubscriptionManager.USAGE_SETTING_UNKNOWN);
}
/**
diff --git a/telephony/java/android/telephony/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/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/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/SignalStrengthUpdateRequest.java b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
index 9cb80f1814f9..4884d549e2e0 100644
--- a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
+++ b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
@@ -19,6 +19,7 @@ package android.telephony;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
@@ -130,7 +131,10 @@ public final class SignalStrengthUpdateRequest implements Parcelable {
/**
* Set the builder object if require reporting on the system thresholds when device is idle.
*
- * <p>This can only used by the system caller. Requires permission
+ * <p>This is intended to be used by the system privileged caller only. When setting to
+ * {@code true}, signal strength update request through
+ * {@link TelephonyManager#setSignalStrengthUpdateRequest(SignalStrengthUpdateRequest)}
+ * will require permission
* {@link android.Manifest.permission#LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH}.
*
* @param isSystemThresholdReportingRequestedWhileIdle true if request reporting on the
@@ -138,6 +142,7 @@ public final class SignalStrengthUpdateRequest implements Parcelable {
* @return the builder to facilitate the chaining
* @hide
*/
+ @SystemApi
@RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
public @NonNull Builder setSystemThresholdReportingRequestedWhileIdle(
boolean isSystemThresholdReportingRequestedWhileIdle) {
@@ -191,6 +196,7 @@ public final class SignalStrengthUpdateRequest implements Parcelable {
*
* @hide
*/
+ @SystemApi
public boolean isSystemThresholdReportingRequestedWhileIdle() {
return mIsSystemThresholdReportingRequestedWhileIdle;
}
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index d11ad914061d..c36eb2f9cf65 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -37,6 +37,7 @@ import android.os.Build;
import android.os.Parcel;
import android.os.ParcelUuid;
import android.os.Parcelable;
+import android.telephony.SubscriptionManager.UsageSetting;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -227,6 +228,11 @@ public class SubscriptionInfo implements Parcelable {
private final int mPortIndex;
/**
+ * Subscription's preferred usage setting.
+ */
+ private @UsageSetting int mUsageSetting = SubscriptionManager.USAGE_SETTING_UNKNOWN;
+
+ /**
* Public copy constructor.
* @hide
*/
@@ -284,6 +290,7 @@ public class SubscriptionInfo implements Parcelable {
cardId, isOpportunistic, groupUUID, isGroupDisabled, carrierId, profileClass,
subType, groupOwner, carrierConfigAccessRules, areUiccApplicationsEnabled, 0);
}
+
/**
* @hide
*/
@@ -295,6 +302,24 @@ public class SubscriptionInfo implements Parcelable {
int carrierId, int profileClass, int subType, @Nullable String groupOwner,
@Nullable UiccAccessRule[] carrierConfigAccessRules,
boolean areUiccApplicationsEnabled, int portIndex) {
+ this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
+ roaming, icon, mcc, mnc, countryIso, isEmbedded, nativeAccessRules, cardString,
+ cardId, isOpportunistic, groupUUID, isGroupDisabled, carrierId, profileClass,
+ subType, groupOwner, carrierConfigAccessRules, areUiccApplicationsEnabled,
+ portIndex, SubscriptionManager.USAGE_SETTING_DEFAULT);
+ }
+
+ /**
+ * @hide
+ */
+ public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
+ CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
+ Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
+ @Nullable UiccAccessRule[] nativeAccessRules, String cardString, int cardId,
+ boolean isOpportunistic, @Nullable String groupUUID, boolean isGroupDisabled,
+ int carrierId, int profileClass, int subType, @Nullable String groupOwner,
+ @Nullable UiccAccessRule[] carrierConfigAccessRules,
+ boolean areUiccApplicationsEnabled, int portIndex, @UsageSetting int usageSetting) {
this.mId = id;
this.mIccId = iccId;
this.mSimSlotIndex = simSlotIndex;
@@ -322,6 +347,7 @@ public class SubscriptionInfo implements Parcelable {
this.mCarrierConfigAccessRules = carrierConfigAccessRules;
this.mAreUiccApplicationsEnabled = areUiccApplicationsEnabled;
this.mPortIndex = portIndex;
+ this.mUsageSetting = usageSetting;
}
/**
* @return the subscription ID.
@@ -796,7 +822,18 @@ public class SubscriptionInfo implements Parcelable {
return mAreUiccApplicationsEnabled;
}
- public static final @android.annotation.NonNull Parcelable.Creator<SubscriptionInfo> CREATOR = new Parcelable.Creator<SubscriptionInfo>() {
+ /**
+ * Get the usage setting for this subscription.
+ *
+ * @return the usage setting used for this subscription.
+ */
+ public @UsageSetting int getUsageSetting() {
+ return mUsageSetting;
+ }
+
+ public static final @android.annotation.NonNull
+ Parcelable.Creator<SubscriptionInfo> CREATOR =
+ new Parcelable.Creator<SubscriptionInfo>() {
@Override
public SubscriptionInfo createFromParcel(Parcel source) {
int id = source.readInt();
@@ -828,12 +865,14 @@ public class SubscriptionInfo implements Parcelable {
UiccAccessRule[] carrierConfigAccessRules = source.createTypedArray(
UiccAccessRule.CREATOR);
boolean areUiccApplicationsEnabled = source.readBoolean();
+ int usageSetting = source.readInt();
SubscriptionInfo info = new SubscriptionInfo(id, iccId, simSlotIndex, displayName,
carrierName, nameSource, iconTint, number, dataRoaming, /* icon= */ null,
mcc, mnc, countryIso, isEmbedded, nativeAccessRules, cardString, cardId,
isOpportunistic, groupUUID, isGroupDisabled, carrierid, profileClass, subType,
- groupOwner, carrierConfigAccessRules, areUiccApplicationsEnabled, portId);
+ groupOwner, carrierConfigAccessRules, areUiccApplicationsEnabled,
+ portId, usageSetting);
info.setAssociatedPlmns(ehplmns, hplmns);
return info;
}
@@ -875,6 +914,7 @@ public class SubscriptionInfo implements Parcelable {
dest.writeString(mGroupOwner);
dest.writeTypedArray(mCarrierConfigAccessRules, flags);
dest.writeBoolean(mAreUiccApplicationsEnabled);
+ dest.writeInt(mUsageSetting);
}
@Override
@@ -919,7 +959,8 @@ public class SubscriptionInfo implements Parcelable {
+ " subscriptionType=" + mSubscriptionType
+ " groupOwner=" + mGroupOwner
+ " carrierConfigAccessRules=" + Arrays.toString(mCarrierConfigAccessRules)
- + " areUiccApplicationsEnabled=" + mAreUiccApplicationsEnabled + "}";
+ + " areUiccApplicationsEnabled=" + mAreUiccApplicationsEnabled
+ + " usageSetting=" + mUsageSetting + "}";
}
@Override
@@ -927,7 +968,8 @@ public class SubscriptionInfo implements Parcelable {
return Objects.hash(mId, mSimSlotIndex, mNameSource, mIconTint, mDataRoaming, mIsEmbedded,
mIsOpportunistic, mGroupUUID, mIccId, mNumber, mMcc, mMnc, mCountryIso, mCardString,
mCardId, mDisplayName, mCarrierName, mNativeAccessRules, mIsGroupDisabled,
- mCarrierId, mProfileClass, mGroupOwner, mAreUiccApplicationsEnabled, mPortIndex);
+ mCarrierId, mProfileClass, mGroupOwner, mAreUiccApplicationsEnabled, mPortIndex,
+ mUsageSetting);
}
@Override
@@ -967,6 +1009,7 @@ public class SubscriptionInfo implements Parcelable {
&& Arrays.equals(mNativeAccessRules, toCompare.mNativeAccessRules)
&& mProfileClass == toCompare.mProfileClass
&& Arrays.equals(mEhplmns, toCompare.mEhplmns)
- && Arrays.equals(mHplmns, toCompare.mHplmns);
+ && Arrays.equals(mHplmns, toCompare.mHplmns)
+ && mUsageSetting == toCompare.mUsageSetting;
}
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index f72832438204..4e6a7a36d879 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -941,6 +941,13 @@ public class SubscriptionManager {
public static final String PROFILE_CLASS = SimInfo.COLUMN_PROFILE_CLASS;
/**
+ * TelephonyProvider column name for the port index of the active UICC port.
+ * <P>Type: INTEGER (int)</P>
+ * @hide
+ */
+ public static final String PORT_INDEX = SimInfo.COLUMN_PORT_INDEX;
+
+ /**
* TelephonyProvider column name for VoIMS opt-in status.
*
* <P>Type: INTEGER (int)</P>
@@ -1030,12 +1037,75 @@ public class SubscriptionManager {
public static final String UICC_APPLICATIONS_ENABLED = SimInfo.COLUMN_UICC_APPLICATIONS_ENABLED;
/**
- * Indicate which network type is allowed. By default it's enabled.
+ * Indicate which network type is allowed.
* @hide
*/
public static final String ALLOWED_NETWORK_TYPES =
SimInfo.COLUMN_ALLOWED_NETWORK_TYPES_FOR_REASONS;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"USAGE_SETTING_"},
+ value = {
+ USAGE_SETTING_UNKNOWN,
+ USAGE_SETTING_DEFAULT,
+ USAGE_SETTING_VOICE_CENTRIC,
+ USAGE_SETTING_DATA_CENTRIC})
+ public @interface UsageSetting {}
+
+ /**
+ * The usage setting is unknown.
+ *
+ * This will be the usage setting returned on devices that do not support querying the
+ * or setting the usage setting.
+ *
+ * It may also be provided by a carrier that wishes to provide a value to avoid making any
+ * settings changes.
+ */
+ public static final int USAGE_SETTING_UNKNOWN = -1;
+
+ /**
+ * Subscription uses the default setting.
+ *
+ * The value is based upon device capability and the other properties of the subscription.
+ *
+ * Most subscriptions will default to voice-centric when in a phone.
+ *
+ * An opportunistic subscription will default to data-centric.
+ *
+ * {@see SubscriptionInfo#isOpportunistic}
+ */
+ public static final int USAGE_SETTING_DEFAULT = 0;
+
+ /**
+ * This subscription is forced to voice-centric mode
+ *
+ * <p>Refer to voice-centric mode in 3gpp 24.301 sec 4.3 and 3gpp 24.501 sec 4.3.
+ * Also refer to "UE's usage setting" as defined in 3gpp 24.301 section 3.1 and 3gpp 23.221
+ * Annex A.
+ */
+ public static final int USAGE_SETTING_VOICE_CENTRIC = 1;
+
+ /**
+ * This subscription is forced to data-centric mode
+ *
+ * <p>Refer to data-centric mode in 3gpp 24.301 sec 4.3 and 3gpp 24.501 sec 4.3.
+ * Also refer to "UE's usage setting" as defined in 3gpp 24.301 section 3.1 and 3gpp 23.221
+ * Annex A.
+ */
+ public static final int USAGE_SETTING_DATA_CENTRIC = 2;
+
+ /**
+ * Indicate the preferred usage setting for the subscription.
+ *
+ * 0 - Default - If the value has not been explicitly set, it will be "default"
+ * 1 - Voice-centric
+ * 2 - Data-centric
+ *
+ * @hide
+ */
+ public static final String USAGE_SETTING = SimInfo.COLUMN_USAGE_SETTING;
+
/**
* Broadcast Action: The user has changed one of the default subs related to
* data, phone calls, or sms</p>
@@ -3943,4 +4013,33 @@ public class SubscriptionManager {
throw ex.rethrowAsRuntimeException();
}
}
+
+ /**
+ * Set the preferred usage setting.
+ *
+ * The cellular usage setting is a switch which controls the mode of operation for the cellular
+ * radio to either require or not require voice service. It is not managed via Android’s
+ * Settings.
+ *
+ * @param subscriptionId the subId of the subscription.
+ * @param usageSetting the requested usage setting.
+ *
+ * @throws IllegalStateException if a specific mode or setting the mode is not supported on a
+ * particular device.
+ *
+ * <p>Requires {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+ * or that the calling app has CarrierPrivileges for the given subscription.
+ *
+ * Note: This method will not allow the setting of USAGE_SETTING_UNKNOWN.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ void setUsageSetting(int subscriptionId, @UsageSetting int usageSetting) {
+ if (VDBG) logd("[setUsageSetting]+ setting:" + usageSetting + " subId:" + subscriptionId);
+ setSubscriptionPropertyHelper(subscriptionId, "setUsageSetting",
+ (iSub)-> iSub.setUsageSetting(
+ usageSetting, subscriptionId, mContext.getOpPackageName()));
+ }
}
+
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index cca5e2f164dc..2d16f9e62e25 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -70,6 +70,7 @@ import android.os.SystemProperties;
import android.os.WorkSource;
import android.provider.Settings.SettingNotFoundException;
import android.service.carrier.CarrierIdentifier;
+import android.service.carrier.CarrierService;
import android.sysprop.TelephonyProperties;
import android.telecom.CallScreeningService;
import android.telecom.InCallService;
@@ -320,11 +321,19 @@ public class TelephonyManager {
public static final int UNINITIALIZED_CARD_ID = -2;
/**
- * Default port index for the UICC Card
- * @hide
+ * Default port index for a UICC.
+ *
+ * On physical SIM cards the only available port is 0.
+ * See {@link android.telephony.UiccPortInfo} for more information on ports.
+ *
+ * See {@link android.telephony.euicc.EuiccManager#isSimPortAvailable(int)} for information on
+ * how portIndex is used on eUICCs.
*/
public static final int DEFAULT_PORT_INDEX = 0;
+ /** @hide */
+ public static final int INVALID_PORT_INDEX = -1;
+
private final Context mContext;
private final int mSubId;
@UnsupportedAppUsage
@@ -6766,10 +6775,12 @@ public class TelephonyManager {
* @param p2 P2 parameter (described in ISO 7816-4).
* @return an IccOpenLogicalChannelResponse object.
* @hide
+ * @deprecated instead use {@link #iccOpenLogicalChannelByPort(int, int, String, int)}
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@SystemApi
@Nullable
+ @Deprecated
public IccOpenLogicalChannelResponse iccOpenLogicalChannelBySlot(int slotIndex,
@Nullable String aid, int p2) {
try {
@@ -6790,6 +6801,58 @@ public class TelephonyManager {
}
/**
+ * Opens a logical channel to the ICC card using the physical slot index and port index.
+ *
+ * Use this method when no subscriptions are available on the SIM and the operation must be
+ * performed using the physical slot index and port index.
+ *
+ * This operation wraps two APDU instructions:
+ * <ul>
+ * <li>MANAGE CHANNEL to open a logical channel</li>
+ * <li>SELECT the given {@code AID} using the given {@code p2}</li>
+ * </ul>
+ *
+ * Per Open Mobile API Specification v3.2 section 6.2.7.h, only p2 values of 0x00, 0x04, 0x08,
+ * and 0x0C are guaranteed to be supported.
+ *
+ * If the SELECT command's status word is not '9000', '62xx', or '63xx', the status word will be
+ * considered an error and the channel shall not be opened.
+ *
+ * Input parameters equivalent to TS 27.007 AT+CCHO command.
+ *
+ * @param slotIndex the physical slot index of the ICC card
+ * @param portIndex The port index is an enumeration of the ports available on the UICC.
+ * Use {@link UiccPortInfo#getPortIndex()} to get portIndex.
+ * @param aid Application id. See ETSI 102.221 and 101.220.
+ * @param p2 P2 parameter (described in ISO 7816-4).
+ * @return an IccOpenLogicalChannelResponse object.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ @SystemApi
+ @NonNull
+ public IccOpenLogicalChannelResponse iccOpenLogicalChannelByPort(int slotIndex,
+ int portIndex, @Nullable String aid, int p2) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ IccLogicalChannelRequest request = new IccLogicalChannelRequest();
+ request.slotIndex = slotIndex;
+ request.portIndex = portIndex;
+ request.aid = aid;
+ request.p2 = p2;
+ request.callingPackage = getOpPackageName();
+ request.binder = new Binder();
+ return telephony.iccOpenLogicalChannel(request);
+ } else {
+ throw new IllegalStateException("telephony service is null.");
+ }
+ } catch (RemoteException ex) {
+ throw ex.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
* Opens a logical channel to the ICC card.
*
* This operation wraps two APDU instructions:
@@ -6879,9 +6942,11 @@ public class TelephonyManager {
* iccOpenLogicalChannel.
* @return true if the channel was closed successfully.
* @hide
+ * @deprecated instead use {@link #iccCloseLogicalChannelByPort(int, int, int)}
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@SystemApi
+ @Deprecated
public boolean iccCloseLogicalChannelBySlot(int slotIndex, int channel) {
try {
ITelephony telephony = getITelephony();
@@ -6898,6 +6963,45 @@ public class TelephonyManager {
}
/**
+ * Closes a previously opened logical channel to the ICC card using the physical slot index and
+ * port index.
+ *
+ * Use this method when no subscriptions are available on the SIM and the operation must be
+ * performed using the physical slot index and port index.
+ *
+ * Input parameters equivalent to TS 27.007 AT+CCHC command.
+ *
+ * @param slotIndex the physical slot index of the ICC card
+ * @param portIndex The port index is an enumeration of the ports available on the UICC.
+ * Use {@link UiccPortInfo#getPortIndex()} to get portIndex.
+ * @param channel is the channel id to be closed as returned by a successful
+ * iccOpenLogicalChannel.
+ *
+ * @throws IllegalStateException if the Telephony process is not currently available or modem
+ * currently can't process this command.
+ * @throws IllegalArgumentException if invalid arguments are passed.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ @SystemApi
+ public void iccCloseLogicalChannelByPort(int slotIndex, int portIndex, int channel) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ IccLogicalChannelRequest request = new IccLogicalChannelRequest();
+ request.slotIndex = slotIndex;
+ request.portIndex = portIndex;
+ request.channel = channel;
+ telephony.iccCloseLogicalChannel(request);
+ } else {
+ throw new IllegalStateException("telephony service is null.");
+ }
+ } catch (RemoteException ex) {
+ throw ex.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
* Closes a previously opened logical channel to the ICC card.
*
* Input parameters equivalent to TS 27.007 AT+CCHC command.
@@ -6968,10 +7072,13 @@ public class TelephonyManager {
* @return The APDU response from the ICC card with the status appended at the end, or null if
* there is an issue connecting to the Telephony service.
* @hide
+ * @deprecated instead use
+ * {@link #iccTransmitApduLogicalChannelByPort(int, int, int, int, int, int, int, int, String)}
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@SystemApi
@Nullable
+ @Deprecated
public String iccTransmitApduLogicalChannelBySlot(int slotIndex, int channel, int cla,
int instruction, int p1, int p2, int p3, @Nullable String data) {
try {
@@ -6987,6 +7094,50 @@ public class TelephonyManager {
}
/**
+ * Transmit an APDU to the ICC card over a logical channel using the physical slot index.
+ *
+ * Use this method when no subscriptions are available on the SIM and the operation must be
+ * performed using the physical slot index.
+ *
+ * Input parameters equivalent to TS 27.007 AT+CGLA command.
+ *
+ * @param slotIndex the physical slot index of the ICC card
+ * @param portIndex The port index is an enumeration of the ports available on the UICC.
+ * Use {@link UiccPortInfo#getPortIndex()} to get portIndex.
+ * @param channel is the channel id to be closed as returned by a successful
+ * iccOpenLogicalChannel.
+ * @param cla Class of the APDU command.
+ * @param instruction Instruction of the APDU command.
+ * @param p1 P1 value of the APDU command.
+ * @param p2 P2 value of the APDU command.
+ * @param p3 P3 value of the APDU command. If p3 is negative a 4 byte APDU
+ * is sent to the SIM.
+ * @param data Data to be sent with the APDU.
+ * @return The APDU response from the ICC card with the status appended at the end, or null if
+ * there is an issue connecting to the Telephony service.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ @SystemApi
+ @NonNull
+ public String iccTransmitApduLogicalChannelByPort(int slotIndex, int portIndex, int channel,
+ int cla, int instruction, int p1, int p2, int p3, @Nullable String data) {
+ String response;
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ response = telephony.iccTransmitApduLogicalChannelByPort(slotIndex, portIndex,
+ channel, cla, instruction, p1, p2, p3, data);
+ } else {
+ throw new IllegalStateException("telephony service is null.");
+ }
+ } catch (RemoteException ex) {
+ throw ex.rethrowAsRuntimeException();
+ }
+ return response;
+ }
+
+ /**
* Transmit an APDU to the ICC card over a logical channel.
*
* Input parameters equivalent to TS 27.007 AT+CGLA command.
@@ -7071,10 +7222,13 @@ public class TelephonyManager {
* @return The APDU response from the ICC card with the status appended at
* the end.
* @hide
+ * @deprecated instead use
+ * {@link #iccTransmitApduBasicChannelByPort(int, int, int, int, int, int, int, String)}
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@SystemApi
@NonNull
+ @Deprecated
public String iccTransmitApduBasicChannelBySlot(int slotIndex, int cla, int instruction, int p1,
int p2, int p3, @Nullable String data) {
try {
@@ -7090,6 +7244,47 @@ public class TelephonyManager {
}
/**
+ * Transmit an APDU to the ICC card over the basic channel using the physical slot index.
+ *
+ * Use this method when no subscriptions are available on the SIM and the operation must be
+ * performed using the physical slot index.
+ *
+ * Input parameters equivalent to TS 27.007 AT+CSIM command.
+ *
+ * @param slotIndex the physical slot index of the ICC card to target
+ * @param portIndex The port index is an enumeration of the ports available on the UICC.
+ * Use {@link UiccPortInfo#getPortIndex()} to get portIndex.
+ * @param cla Class of the APDU command.
+ * @param instruction Instruction of the APDU command.
+ * @param p1 P1 value of the APDU command.
+ * @param p2 P2 value of the APDU command.
+ * @param p3 P3 value of the APDU command. If p3 is negative a 4 byte APDU
+ * is sent to the SIM.
+ * @param data Data to be sent with the APDU.
+ * @return The APDU response from the ICC card with the status appended at
+ * the end.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ @SystemApi
+ @NonNull
+ public String iccTransmitApduBasicChannelByPort(int slotIndex, int portIndex, int cla,
+ int instruction, int p1, int p2, int p3, @Nullable String data) {
+ String response;
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ response = telephony.iccTransmitApduBasicChannelByPort(slotIndex, portIndex,
+ getOpPackageName(), cla, instruction, p1, p2, p3, data);
+ } else {
+ throw new IllegalStateException("telephony service is null.");
+ }
+ } catch (RemoteException ex) {
+ throw ex.rethrowAsRuntimeException();
+ }
+ return response;
+ }
+ /**
* Transmit an APDU to the ICC card over the basic channel.
*
* Input parameters equivalent to TS 27.007 AT+CSIM command.
@@ -9408,6 +9603,57 @@ public class TelephonyManager {
return null;
}
+ /**
+ * Returns the package name that provides the {@link CarrierService} implementation for the
+ * current subscription, or {@code null} if no package with carrier privileges declares one.
+ *
+ * <p>If this object has been created with {@link #createForSubscriptionId}, then the provided
+ * subscription ID is used. Otherwise, the default subscription ID will be used.
+ *
+ * @return The system-selected package that provides the {@link CarrierService} implementation
+ * for the current subscription, or {@code null} if none is resolved
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public @Nullable String getCarrierServicePackageName() {
+ // TODO(b/205736323) plumb this through to CarrierPrivilegesTracker, which will cache the
+ // value instead of re-querying every time.
+ List<String> carrierServicePackages =
+ getCarrierPackageNamesForIntent(
+ new Intent(CarrierService.CARRIER_SERVICE_INTERFACE));
+ if (carrierServicePackages != null && !carrierServicePackages.isEmpty()) {
+ return carrierServicePackages.get(0);
+ }
+ return null;
+ }
+
+ /**
+ * Returns the package name that provides the {@link CarrierService} implementation for the
+ * specified {@code logicalSlotIndex}, or {@code null} if no package with carrier privileges
+ * declares one.
+ *
+ * @param logicalSlotIndex The slot index to fetch the {@link CarrierService} package for
+ * @return The system-selected package that provides the {@link CarrierService} implementation
+ * for the slot, or {@code null} if none is resolved
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public @Nullable String getCarrierServicePackageNameForLogicalSlot(int logicalSlotIndex) {
+ // TODO(b/205736323) plumb this through to CarrierPrivilegesTracker, which will cache the
+ // value instead of re-querying every time.
+ List<String> carrierServicePackages =
+ getCarrierPackageNamesForIntentAndPhone(
+ new Intent(CarrierService.CARRIER_SERVICE_INTERFACE), logicalSlotIndex);
+ if (carrierServicePackages != null && !carrierServicePackages.isEmpty()) {
+ return carrierServicePackages.get(0);
+ }
+ return null;
+ }
+
/** @hide */
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public List<String> getPackagesWithCarrierPrivileges() {
@@ -16202,4 +16448,76 @@ public class TelephonyManager {
}
return null;
}
+
+ /**
+ * Callback to listen for when the set of packages with carrier privileges for a SIM changes.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface CarrierPrivilegesListener {
+ /**
+ * Called when the set of packages with carrier privileges has changed.
+ *
+ * <p>Of note, this callback will <b>not</b> be fired if a carrier triggers a SIM profile
+ * switch and the same set of packages remains privileged after the switch.
+ *
+ * <p>At registration, the callback will receive the current set of privileged packages.
+ *
+ * @param privilegedPackageNames The updated set of package names that have carrier
+ * privileges
+ * @param privilegedUids The updated set of UIDs that have carrier privileges
+ */
+ void onCarrierPrivilegesChanged(
+ @NonNull List<String> privilegedPackageNames, @NonNull int[] privilegedUids);
+ }
+
+ /**
+ * Registers a {@link CarrierPrivilegesListener} on the given {@code logicalSlotIndex} to
+ * receive callbacks when the set of packages with carrier privileges changes. The callback will
+ * immediately be called with the latest state.
+ *
+ * @param logicalSlotIndex The SIM slot to listen on
+ * @param executor The executor where {@code listener} will be invoked
+ * @param listener The callback to register
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void addCarrierPrivilegesListener(
+ int logicalSlotIndex,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull CarrierPrivilegesListener listener) {
+ if (mContext == null) {
+ throw new IllegalStateException("Telephony service is null");
+ } else if (executor == null || listener == null) {
+ throw new IllegalArgumentException(
+ "CarrierPrivilegesListener and executor must be non-null");
+ }
+ mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class);
+ if (mTelephonyRegistryMgr == null) {
+ throw new IllegalStateException("Telephony registry service is null");
+ }
+ mTelephonyRegistryMgr.addCarrierPrivilegesListener(logicalSlotIndex, executor, listener);
+ }
+
+ /**
+ * Unregisters an existing {@link CarrierPrivilegesListener}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void removeCarrierPrivilegesListener(@NonNull CarrierPrivilegesListener listener) {
+ if (mContext == null) {
+ throw new IllegalStateException("Telephony service is null");
+ } else if (listener == null) {
+ throw new IllegalArgumentException("CarrierPrivilegesListener must be non-null");
+ }
+ mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class);
+ if (mTelephonyRegistryMgr == null) {
+ throw new IllegalStateException("Telephony registry service is null");
+ }
+ mTelephonyRegistryMgr.removeCarrierPrivilegesListener(listener);
+ }
}
diff --git a/telephony/java/android/telephony/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 8c02ffe12363..9ed230aebfbb 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -1629,7 +1629,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 c1d16a9aaea7..479f057eda9c 100644
--- a/telephony/java/android/telephony/data/DataProfile.java
+++ b/telephony/java/android/telephony/data/DataProfile.java
@@ -105,8 +105,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/DataService.java b/telephony/java/android/telephony/data/DataService.java
index 2f034752ae5f..892eb2937491 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -402,6 +402,21 @@ public abstract class DataService extends Service {
}
/**
+ * Notify the system that a given DataProfile was unthrottled.
+ *
+ * @param dataProfile DataProfile associated with an APN returned from the modem
+ */
+ public final void notifyDataProfileUnthrottled(@NonNull DataProfile dataProfile) {
+ synchronized (mApnUnthrottledCallbacks) {
+ for (IDataServiceCallback callback : mApnUnthrottledCallbacks) {
+ mHandler.obtainMessage(DATA_SERVICE_INDICATION_APN_UNTHROTTLED,
+ mSlotIndex, 0, new ApnUnthrottledIndication(dataProfile,
+ callback)).sendToTarget();
+ }
+ }
+ }
+
+ /**
* Called when the instance of data service is destroyed (e.g. got unbind or binder died)
* or when the data service provider is removed. The extended class should implement this
* method to perform cleanup works.
@@ -496,13 +511,20 @@ public abstract class DataService extends Service {
}
private static final class ApnUnthrottledIndication {
+ public final DataProfile dataProfile;
public final String apn;
public final IDataServiceCallback callback;
ApnUnthrottledIndication(String apn,
IDataServiceCallback callback) {
+ this.dataProfile = null;
this.apn = apn;
this.callback = callback;
}
+ ApnUnthrottledIndication(DataProfile dataProfile, IDataServiceCallback callback) {
+ this.dataProfile = dataProfile;
+ this.apn = null;
+ this.callback = callback;
+ }
}
private class DataServiceHandler extends Handler {
@@ -636,8 +658,13 @@ public abstract class DataService extends Service {
ApnUnthrottledIndication apnUnthrottledIndication =
(ApnUnthrottledIndication) message.obj;
try {
- apnUnthrottledIndication.callback
- .onApnUnthrottled(apnUnthrottledIndication.apn);
+ if (apnUnthrottledIndication.dataProfile != null) {
+ apnUnthrottledIndication.callback
+ .onDataProfileUnthrottled(apnUnthrottledIndication.dataProfile);
+ } else {
+ apnUnthrottledIndication.callback
+ .onApnUnthrottled(apnUnthrottledIndication.apn);
+ }
} catch (RemoteException e) {
loge("Failed to call onApnUnthrottled. " + e);
}
diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java
index d0827159b98d..051d6c5d5ec0 100644
--- a/telephony/java/android/telephony/data/DataServiceCallback.java
+++ b/telephony/java/android/telephony/data/DataServiceCallback.java
@@ -260,8 +260,8 @@ public class DataServiceCallback {
/**
* Unthrottles the APN on the current transport. There is no matching "APN throttle" method.
- * Instead, the APN is throttled for the time specified in
- * {@link DataCallResponse#getRetryDurationMillis}.
+ * Instead, the APN is throttled when {@link IDataService#setupDataCall} fails within
+ * the time specified by {@link DataCallResponse#getRetryDurationMillis}.
* <p/>
* see: {@link DataCallResponse#getRetryDurationMillis}
*
@@ -279,4 +279,27 @@ public class DataServiceCallback {
Rlog.e(TAG, "onApnUnthrottled: callback is null!");
}
}
+
+ /**
+ * Unthrottles the DataProfile on the current transport.
+ * There is no matching "DataProfile throttle" method.
+ * Instead, the DataProfile is throttled when {@link IDataService#setupDataCall} fails within
+ * the time specified by {@link DataCallResponse#getRetryDurationMillis}.
+ * <p/>
+ * see: {@link DataCallResponse#getRetryDurationMillis}
+ *
+ * @param dataProfile DataProfile containing the APN to be throttled
+ */
+ public void onDataProfileUnthrottled(final @NonNull DataProfile dataProfile) {
+ if (mCallback != null) {
+ try {
+ if (DBG) Rlog.d(TAG, "onDataProfileUnthrottled");
+ mCallback.onDataProfileUnthrottled(dataProfile);
+ } catch (RemoteException e) {
+ Rlog.e(TAG, "onDataProfileUnthrottled: remote exception", e);
+ }
+ } else {
+ Rlog.e(TAG, "onDataProfileUnthrottled: callback is null!");
+ }
+ }
}
diff --git a/telephony/java/android/telephony/data/IDataServiceCallback.aidl b/telephony/java/android/telephony/data/IDataServiceCallback.aidl
index 9cc2feac331a..8205b5eb4d37 100644
--- a/telephony/java/android/telephony/data/IDataServiceCallback.aidl
+++ b/telephony/java/android/telephony/data/IDataServiceCallback.aidl
@@ -17,6 +17,7 @@
package android.telephony.data;
import android.telephony.data.DataCallResponse;
+import android.telephony.data.DataProfile;
/**
* The call back interface
@@ -33,4 +34,5 @@ oneway interface IDataServiceCallback
void onHandoverStarted(int result);
void onHandoverCancelled(int result);
void onApnUnthrottled(in String apn);
+ void onDataProfileUnthrottled(in DataProfile dp);
}
diff --git a/telephony/java/android/telephony/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 5f5762b2235b..5a7189f44552 100644
--- a/telephony/java/android/telephony/data/QosBearerFilter.java
+++ b/telephony/java/android/telephony/data/QosBearerFilter.java
@@ -257,11 +257,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/euicc/EuiccCardManager.java b/telephony/java/android/telephony/euicc/EuiccCardManager.java
index ab35d77c0b4d..885244ede4f1 100644
--- a/telephony/java/android/telephony/euicc/EuiccCardManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccCardManager.java
@@ -66,14 +66,15 @@ public class EuiccCardManager {
/** Reason for canceling a profile download session */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = { "CANCEL_REASON_" }, value = {
+ @IntDef(prefix = {"CANCEL_REASON_"}, value = {
CANCEL_REASON_END_USER_REJECTED,
CANCEL_REASON_POSTPONED,
CANCEL_REASON_TIMEOUT,
CANCEL_REASON_PPR_NOT_ALLOWED
})
/** @hide */
- public @interface CancelReason {}
+ public @interface CancelReason {
+ }
/**
* The end user has rejected the download. The profile will be put into the error state and
@@ -96,13 +97,14 @@ public class EuiccCardManager {
/** Options for resetting eUICC memory */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true, prefix = { "RESET_OPTION_" }, value = {
+ @IntDef(flag = true, prefix = {"RESET_OPTION_"}, value = {
RESET_OPTION_DELETE_OPERATIONAL_PROFILES,
RESET_OPTION_DELETE_FIELD_LOADED_TEST_PROFILES,
RESET_OPTION_RESET_DEFAULT_SMDP_ADDRESS
})
/** @hide */
- public @interface ResetOption {}
+ public @interface ResetOption {
+ }
/** Deletes all operational profiles. */
public static final int RESET_OPTION_DELETE_OPERATIONAL_PROFILES = 1;
@@ -124,6 +126,10 @@ public class EuiccCardManager {
/** Result code indicating the caller is not the active LPA. */
public static final int RESULT_CALLER_NOT_ALLOWED = -3;
+
+ /** Result code when the requested profile is not found */
+ public static final int RESULT_PROFILE_NOT_FOUND = -4;
+
/**
* Callback to receive the result of an eUICC card API.
*
@@ -134,9 +140,9 @@ public class EuiccCardManager {
* This method will be called when an eUICC card API call is completed.
*
* @param resultCode This can be {@link #RESULT_OK} or other positive values returned by the
- * eUICC.
- * @param result The result object. It can be null if the {@code resultCode} is not
- * {@link #RESULT_OK}.
+ * eUICC.
+ * @param result The result object. It can be null if the {@code resultCode} is not
+ * {@link #RESULT_OK}.
*/
void onComplete(int resultCode, T result);
}
@@ -159,7 +165,7 @@ public class EuiccCardManager {
/**
* Requests all the profiles on eUicc.
*
- * @param cardId The Id of the eUICC.
+ * @param cardId The Id of the eUICC.
* @param executor The executor through which the callback should be invoked.
* @param callback The callback to get the result code and all the profiles.
*/
@@ -187,8 +193,8 @@ public class EuiccCardManager {
/**
* Requests the profile of the given iccid.
*
- * @param cardId The Id of the eUICC.
- * @param iccid The iccid of the profile.
+ * @param cardId The Id of the eUICC.
+ * @param iccid The iccid of the profile.
* @param executor The executor through which the callback should be invoked.
* @param callback The callback to get the result code and profile.
*/
@@ -214,16 +220,47 @@ public class EuiccCardManager {
}
/**
+ * Requests the enabled profile for a given port on an eUicc.
+ *
+ * @param cardId The Id of the eUICC.
+ * @param portIndex The portIndex to use. The port may be active or inactive. As long as the
+ * ICCID is known, an APDU will be sent through to read the enabled profile.
+ * @param executor The executor through which the callback should be invoked.
+ * @param callback The callback to get the result code and the profile.
+ */
+ public void requestEnabledProfileForPort(@NonNull String cardId, int portIndex,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull ResultCallback<EuiccProfileInfo> callback) {
+ try {
+ getIEuiccCardController().getEnabledProfile(mContext.getOpPackageName(), cardId,
+ portIndex,
+ new IGetProfileCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, EuiccProfileInfo profile) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> callback.onComplete(resultCode, profile));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling requestEnabledProfileForPort", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Disables the profile of the given iccid.
*
- * @param cardId The Id of the eUICC.
- * @param iccid The iccid of the profile.
- * @param refresh Whether sending the REFRESH command to modem.
+ * @param cardId The Id of the eUICC.
+ * @param iccid The iccid of the profile.
+ * @param refresh Whether sending the REFRESH command to modem.
* @param executor The executor through which the callback should be invoked.
* @param callback The callback to get the result code.
- *
* @deprecated instead use {@link #disableProfile(String, String, int, boolean, Executor,
- * ResultCallback)}
+ * ResultCallback)}
*/
@Deprecated
public void disableProfile(String cardId, String iccid, boolean refresh,
@@ -247,15 +284,16 @@ public class EuiccCardManager {
throw e.rethrowFromSystemServer();
}
}
+
/**
* Disables the profile of the given ICCID.
*
- * @param cardId The Id of the eUICC.
- * @param iccid The iccid of the profile.
+ * @param cardId The Id of the eUICC.
+ * @param iccid The iccid of the profile.
* @param portIndex the Port index is the unique index referring to a port.
- * @param refresh Whether sending the REFRESH command to modem.
- * @param executor The executor through which the callback should be invoked.
- * @param callback The callback to get the result code.
+ * @param refresh Whether sending the REFRESH command to modem.
+ * @param executor The executor through which the callback should be invoked.
+ * @param callback The callback to get the result code.
*/
public void disableProfile(@Nullable String cardId, @Nullable String iccid, int portIndex,
boolean refresh, @NonNull @CallbackExecutor Executor executor,
@@ -283,14 +321,13 @@ public class EuiccCardManager {
* Switches from the current profile to another profile. The current profile will be disabled
* and the specified profile will be enabled.
*
- * @param cardId The Id of the eUICC.
- * @param iccid The iccid of the profile to switch to.
- * @param refresh Whether sending the REFRESH command to modem.
+ * @param cardId The Id of the eUICC.
+ * @param iccid The iccid of the profile to switch to.
+ * @param refresh Whether sending the REFRESH command to modem.
* @param executor The executor through which the callback should be invoked.
* @param callback The callback to get the result code and the EuiccProfileInfo enabled.
- *
* @deprecated instead use {@link #switchToProfile(String, String, int, boolean, Executor,
- * ResultCallback)}
+ * ResultCallback)}
*/
@Deprecated
public void switchToProfile(String cardId, String iccid, boolean refresh,
@@ -320,12 +357,12 @@ public class EuiccCardManager {
* and the specified profile will be enabled. Here portIndex specifies on which port the
* profile is to be enabled.
*
- * @param cardId The Id of the eUICC.
- * @param iccid The iccid of the profile to switch to.
+ * @param cardId The Id of the eUICC.
+ * @param iccid The iccid of the profile to switch to.
* @param portIndex The Port index is the unique index referring to a port.
- * @param refresh Whether sending the REFRESH command to modem.
- * @param executor The executor through which the callback should be invoked.
- * @param callback The callback to get the result code and the EuiccProfileInfo enabled.
+ * @param refresh Whether sending the REFRESH command to modem.
+ * @param executor The executor through which the callback should be invoked.
+ * @param callback The callback to get the result code and the EuiccProfileInfo enabled.
*/
public void switchToProfile(@Nullable String cardId, @Nullable String iccid, int portIndex,
boolean refresh, @NonNull @CallbackExecutor Executor executor,
diff --git a/telephony/java/android/telephony/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 486f74632ca2..93e10583fbc0 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -839,7 +839,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/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/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java
index 9ab5aeb9c34c..53dff545b0ce 100644
--- a/telephony/java/android/telephony/ims/ImsService.java
+++ b/telephony/java/android/telephony/ims/ImsService.java
@@ -17,6 +17,7 @@
package android.telephony.ims;
import android.annotation.LongDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
@@ -44,11 +45,18 @@ import android.util.SparseArray;
import com.android.ims.internal.IImsFeatureStatusCallback;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.util.TelephonyUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.function.Supplier;
/**
* Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend
@@ -173,7 +181,21 @@ public class ImsService extends Service {
private final SparseArray<SparseArray<ImsFeature>> mFeaturesBySlot = new SparseArray<>();
private IImsServiceControllerListener mListener;
+ private Executor mExecutor;
+ /**
+ * Create a new ImsService.
+ * <p>
+ * Method stubs called from the framework will be called asynchronously. Vendor specifies the
+ * {@link Executor} that the methods stubs will be called. If mExecutor is set to null by
+ * vendor use Runnable::run.
+ */
+ public ImsService() {
+ mExecutor = ImsService.this.getExecutor();
+ if (mExecutor == null) {
+ mExecutor = Runnable::run;
+ }
+ }
/**
* Listener that notifies the framework of ImsService changes.
@@ -201,78 +223,132 @@ public class ImsService extends Service {
@Override
public IImsMmTelFeature createMmTelFeature(int slotId) {
- return createMmTelFeatureInternal(slotId);
+ return executeMethodAsyncForResult(() -> createMmTelFeatureInternal(slotId),
+ "createMmTelFeature");
}
@Override
public IImsRcsFeature createRcsFeature(int slotId) {
- return createRcsFeatureInternal(slotId);
+ return executeMethodAsyncForResult(() -> createRcsFeatureInternal(slotId),
+ "createRcsFeature");
}
@Override
public void addFeatureStatusCallback(int slotId, int featureType,
IImsFeatureStatusCallback c) {
- ImsService.this.addImsFeatureStatusCallback(slotId, featureType, c);
+ executeMethodAsync(() -> ImsService.this.addImsFeatureStatusCallback(
+ slotId, featureType, c), "addFeatureStatusCallback");
}
@Override
public void removeFeatureStatusCallback(int slotId, int featureType,
IImsFeatureStatusCallback c) {
- ImsService.this.removeImsFeatureStatusCallback(slotId, featureType, c);
+ executeMethodAsync(() -> ImsService.this.removeImsFeatureStatusCallback(
+ slotId, featureType, c), "removeFeatureStatusCallback");
}
@Override
public void removeImsFeature(int slotId, int featureType) {
- ImsService.this.removeImsFeature(slotId, featureType);
+ executeMethodAsync(() -> ImsService.this.removeImsFeature(slotId, featureType),
+ "removeImsFeature");
}
@Override
public ImsFeatureConfiguration querySupportedImsFeatures() {
- return ImsService.this.querySupportedImsFeatures();
+ return executeMethodAsyncForResult(() -> ImsService.this.querySupportedImsFeatures(),
+ "ImsFeatureConfiguration");
}
@Override
public long getImsServiceCapabilities() {
- long caps = ImsService.this.getImsServiceCapabilities();
- long sanitizedCaps = sanitizeCapabilities(caps);
- if (caps != sanitizedCaps) {
- Log.w(LOG_TAG, "removing invalid bits from field: 0x"
- + Long.toHexString(caps ^ sanitizedCaps));
- }
- return sanitizedCaps;
+ return executeMethodAsyncForResult(() -> {
+ long caps = ImsService.this.getImsServiceCapabilities();
+ long sanitizedCaps = sanitizeCapabilities(caps);
+ if (caps != sanitizedCaps) {
+ Log.w(LOG_TAG, "removing invalid bits from field: 0x"
+ + Long.toHexString(caps ^ sanitizedCaps));
+ }
+ return sanitizedCaps;
+ }, "getImsServiceCapabilities");
}
@Override
public void notifyImsServiceReadyForFeatureCreation() {
- ImsService.this.readyForFeatureCreation();
+ executeMethodAsync(() -> ImsService.this.readyForFeatureCreation(),
+ "notifyImsServiceReadyForFeatureCreation");
}
@Override
public IImsConfig getConfig(int slotId) {
- ImsConfigImplBase c = ImsService.this.getConfig(slotId);
- return c != null ? c.getIImsConfig() : null;
+ return executeMethodAsyncForResult(() -> {
+ ImsConfigImplBase c = ImsService.this.getConfig(slotId);
+ if (c != null) {
+ c.setDefaultExecutor(mExecutor);
+ return c.getIImsConfig();
+ } else {
+ return null;
+ }
+ }, "getConfig");
}
@Override
public IImsRegistration getRegistration(int slotId) {
- ImsRegistrationImplBase r = ImsService.this.getRegistration(slotId);
- return r != null ? r.getBinder() : null;
+ return executeMethodAsyncForResult(() -> {
+ ImsRegistrationImplBase r = ImsService.this.getRegistration(slotId);
+ if (r != null) {
+ r.setDefaultExecutor(mExecutor);
+ return r.getBinder();
+ } else {
+ return null;
+ }
+ }, "getRegistration");
}
@Override
public ISipTransport getSipTransport(int slotId) {
- SipTransportImplBase s = ImsService.this.getSipTransport(slotId);
- return s != null ? s.getBinder() : null;
+ return executeMethodAsyncForResult(() -> {
+ SipTransportImplBase s = ImsService.this.getSipTransport(slotId);
+ if (s != null) {
+ s.setDefaultExecutor(mExecutor);
+ return s.getBinder();
+ } else {
+ return null;
+ }
+ }, "getSipTransport");
}
@Override
public void enableIms(int slotId) {
- ImsService.this.enableIms(slotId);
+ executeMethodAsync(() -> ImsService.this.enableIms(slotId), "enableIms");
}
@Override
public void disableIms(int slotId) {
- ImsService.this.disableIms(slotId);
+ executeMethodAsync(() -> ImsService.this.disableIms(slotId), "disableIms");
+ }
+
+ // Call the methods with a clean calling identity on the executor and wait indefinitely for
+ // the future to return.
+ private void executeMethodAsync(Runnable r, String errorLogName) {
+ try {
+ CompletableFuture.runAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+ } catch (CancellationException | CompletionException e) {
+ Log.w(LOG_TAG, "ImsService Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ }
+ }
+
+ private <T> T executeMethodAsyncForResult(Supplier<T> r, String errorLogName) {
+ CompletableFuture<T> future = CompletableFuture.supplyAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
+ try {
+ return future.get();
+ } catch (ExecutionException | InterruptedException e) {
+ Log.w(LOG_TAG, "ImsService Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ return null;
+ }
}
};
@@ -300,6 +376,7 @@ public class ImsService extends Service {
MmTelFeature f = createMmTelFeature(slotId);
if (f != null) {
setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL);
+ f.setDefaultExecutor(mExecutor);
return f.getBinder();
} else {
Log.e(LOG_TAG, "createMmTelFeatureInternal: null feature returned.");
@@ -310,6 +387,7 @@ public class ImsService extends Service {
private IImsRcsFeature createRcsFeatureInternal(int slotId) {
RcsFeature f = createRcsFeature(slotId);
if (f != null) {
+ f.setDefaultExecutor(mExecutor);
setupFeature(f, slotId, ImsFeature.FEATURE_RCS);
return f.getBinder();
} else {
@@ -562,4 +640,15 @@ public class ImsService extends Service {
result.append("}");
return result.toString();
}
+
+ /**
+ * The ImsService will now be able to define an Executor that the ImsService can be used to
+ * execute the methods. By default all ImsService level method calls will use this Executor.
+ * The ImsService has set the default executor as Runnable::run,
+ * Should be override or default executor will be used.
+ * @return an Executor used to execute methods called remotely by the framework.
+ */
+ public @NonNull Executor getExecutor() {
+ return Runnable::run;
+ }
}
diff --git a/telephony/java/android/telephony/ims/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/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/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index 9a3f592480d1..7fdf21b3e5ff 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -40,16 +40,25 @@ import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.telephony.ims.stub.ImsSmsImplBase;
import android.telephony.ims.stub.ImsUtImplBase;
import android.util.ArraySet;
+import android.util.Log;
import com.android.ims.internal.IImsCallSession;
import com.android.ims.internal.IImsEcbm;
import com.android.ims.internal.IImsMultiEndpoint;
import com.android.ims.internal.IImsUt;
+import com.android.internal.telephony.util.TelephonyUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
/**
* Base implementation for Voice and SMS (IR-92) and Video (IR-94) IMS support.
@@ -60,6 +69,7 @@ import java.util.Set;
public class MmTelFeature extends ImsFeature {
private static final String LOG_TAG = "MmTelFeature";
+ private Executor mExecutor;
/**
* @hide
@@ -68,160 +78,261 @@ public class MmTelFeature extends ImsFeature {
public MmTelFeature() {
}
+ /**
+ * Create a new MmTelFeature using the Executor specified for methods being called by the
+ * framework.
+ * @param executor The executor for the framework to use when executing the methods overridden
+ * by the implementation of MmTelFeature.
+ * @hide
+ */
+ @SystemApi
+ public MmTelFeature(@NonNull Executor executor) {
+ super();
+ mExecutor = executor;
+ }
+
private final IImsMmTelFeature mImsMMTelBinder = new IImsMmTelFeature.Stub() {
@Override
public void setListener(IImsMmTelListener l) {
- MmTelFeature.this.setListener(l);
+ executeMethodAsyncNoException(() -> MmTelFeature.this.setListener(l), "setListener");
}
@Override
public int getFeatureState() throws RemoteException {
- try {
- return MmTelFeature.this.getFeatureState();
- } catch (Exception e) {
- throw new RemoteException(e.getMessage());
- }
+ return executeMethodAsyncForResult(() -> MmTelFeature.this.getFeatureState(),
+ "getFeatureState");
}
-
@Override
public ImsCallProfile createCallProfile(int callSessionType, int callType)
throws RemoteException {
- synchronized (mLock) {
- try {
- return MmTelFeature.this.createCallProfile(callSessionType, callType);
- } catch (Exception e) {
- throw new RemoteException(e.getMessage());
- }
- }
+ return executeMethodAsyncForResult(() -> MmTelFeature.this.createCallProfile(
+ callSessionType, callType), "createCallProfile");
}
@Override
public void changeOfferedRtpHeaderExtensionTypes(List<RtpHeaderExtensionType> types)
throws RemoteException {
- synchronized (mLock) {
- try {
- MmTelFeature.this.changeOfferedRtpHeaderExtensionTypes(new ArraySet<>(types));
- } catch (Exception e) {
- throw new RemoteException(e.getMessage());
- }
- }
+ executeMethodAsync(() -> MmTelFeature.this.changeOfferedRtpHeaderExtensionTypes(
+ new ArraySet<>(types)), "changeOfferedRtpHeaderExtensionTypes");
}
@Override
public IImsCallSession createCallSession(ImsCallProfile profile) throws RemoteException {
- synchronized (mLock) {
- return createCallSessionInterface(profile);
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ IImsCallSession result = executeMethodAsyncForResult(() -> {
+ try {
+ return createCallSessionInterface(profile);
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ return null;
+ }
+ }, "createCallSession");
+
+ if (exceptionRef.get() != null) {
+ throw exceptionRef.get();
}
+
+ return result;
}
@Override
public int shouldProcessCall(String[] numbers) {
- synchronized (mLock) {
- return MmTelFeature.this.shouldProcessCall(numbers);
+ Integer result = executeMethodAsyncForResultNoException(() ->
+ MmTelFeature.this.shouldProcessCall(numbers), "shouldProcessCall");
+ if (result != null) {
+ return result.intValue();
+ } else {
+ return PROCESS_CALL_CSFB;
}
}
@Override
public IImsUt getUtInterface() throws RemoteException {
- synchronized (mLock) {
- return MmTelFeature.this.getUtInterface();
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ IImsUt result = executeMethodAsyncForResult(() -> {
+ try {
+ return MmTelFeature.this.getUtInterface();
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ return null;
+ }
+ }, "getUtInterface");
+
+ if (exceptionRef.get() != null) {
+ throw exceptionRef.get();
}
+
+ return result;
}
@Override
public IImsEcbm getEcbmInterface() throws RemoteException {
- synchronized (mLock) {
- return MmTelFeature.this.getEcbmInterface();
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ IImsEcbm result = executeMethodAsyncForResult(() -> {
+ try {
+ return MmTelFeature.this.getEcbmInterface();
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ return null;
+ }
+ }, "getEcbmInterface");
+
+ if (exceptionRef.get() != null) {
+ throw exceptionRef.get();
}
+
+ return result;
}
@Override
public void setUiTtyMode(int uiTtyMode, Message onCompleteMessage) throws RemoteException {
- synchronized (mLock) {
- try {
- MmTelFeature.this.setUiTtyMode(uiTtyMode, onCompleteMessage);
- } catch (Exception e) {
- throw new RemoteException(e.getMessage());
- }
- }
+ executeMethodAsync(() -> MmTelFeature.this.setUiTtyMode(uiTtyMode, onCompleteMessage),
+ "setUiTtyMode");
}
@Override
public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
- synchronized (mLock) {
- return MmTelFeature.this.getMultiEndpointInterface();
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ IImsMultiEndpoint result = executeMethodAsyncForResult(() -> {
+ try {
+ return MmTelFeature.this.getMultiEndpointInterface();
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ return null;
+ }
+ }, "getMultiEndpointInterface");
+
+ if (exceptionRef.get() != null) {
+ throw exceptionRef.get();
}
+
+ return result;
}
@Override
public int queryCapabilityStatus() {
- return MmTelFeature.this.queryCapabilityStatus().mCapabilities;
+ Integer result = executeMethodAsyncForResultNoException(() -> MmTelFeature.this
+ .queryCapabilityStatus().mCapabilities, "queryCapabilityStatus");
+
+ if (result != null) {
+ return result.intValue();
+ } else {
+ return 0;
+ }
}
@Override
public void addCapabilityCallback(IImsCapabilityCallback c) {
- // no need to lock, structure already handles multithreading.
- MmTelFeature.this.addCapabilityCallback(c);
+ executeMethodAsyncNoException(() -> MmTelFeature.this
+ .addCapabilityCallback(c), "addCapabilityCallback");
}
@Override
public void removeCapabilityCallback(IImsCapabilityCallback c) {
- // no need to lock, structure already handles multithreading.
- MmTelFeature.this.removeCapabilityCallback(c);
+ executeMethodAsyncNoException(() -> MmTelFeature.this
+ .removeCapabilityCallback(c), "removeCapabilityCallback");
}
@Override
public void changeCapabilitiesConfiguration(CapabilityChangeRequest request,
IImsCapabilityCallback c) {
- MmTelFeature.this.requestChangeEnabledCapabilities(request, c);
+ executeMethodAsyncNoException(() -> MmTelFeature.this
+ .requestChangeEnabledCapabilities(request, c),
+ "changeCapabilitiesConfiguration");
}
@Override
public void queryCapabilityConfiguration(int capability, int radioTech,
IImsCapabilityCallback c) {
- queryCapabilityConfigurationInternal(capability, radioTech, c);
+ executeMethodAsyncNoException(() -> queryCapabilityConfigurationInternal(
+ capability, radioTech, c), "queryCapabilityConfiguration");
}
@Override
public void setSmsListener(IImsSmsListener l) {
- MmTelFeature.this.setSmsListener(l);
+ executeMethodAsyncNoException(() -> MmTelFeature.this.setSmsListener(l),
+ "setSmsListener");
}
@Override
public void sendSms(int token, int messageRef, String format, String smsc, boolean retry,
byte[] pdu) {
- synchronized (mLock) {
- MmTelFeature.this.sendSms(token, messageRef, format, smsc, retry, pdu);
- }
+ executeMethodAsyncNoException(() -> MmTelFeature.this
+ .sendSms(token, messageRef, format, smsc, retry, pdu), "sendSms");
}
@Override
public void acknowledgeSms(int token, int messageRef, int result) {
- synchronized (mLock) {
- MmTelFeature.this.acknowledgeSms(token, messageRef, result);
- }
+ executeMethodAsyncNoException(() -> MmTelFeature.this
+ .acknowledgeSms(token, messageRef, result), "acknowledgeSms");
}
@Override
public void acknowledgeSmsReport(int token, int messageRef, int result) {
- synchronized (mLock) {
- MmTelFeature.this.acknowledgeSmsReport(token, messageRef, result);
- }
+ executeMethodAsyncNoException(() -> MmTelFeature.this
+ .acknowledgeSmsReport(token, messageRef, result), "acknowledgeSmsReport");
}
@Override
public String getSmsFormat() {
- synchronized (mLock) {
- return MmTelFeature.this.getSmsFormat();
- }
+ return executeMethodAsyncForResultNoException(() -> MmTelFeature.this
+ .getSmsFormat(), "getSmsFormat");
}
@Override
public void onSmsReady() {
- synchronized (mLock) {
- MmTelFeature.this.onSmsReady();
+ executeMethodAsyncNoException(() -> MmTelFeature.this.onSmsReady(),
+ "onSmsReady");
+ }
+
+ // Call the methods with a clean calling identity on the executor and wait indefinitely for
+ // the future to return.
+ private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException {
+ try {
+ CompletableFuture.runAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+ } catch (CancellationException | CompletionException e) {
+ Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ throw new RemoteException(e.getMessage());
+ }
+ }
+
+ private void executeMethodAsyncNoException(Runnable r, String errorLogName) {
+ try {
+ CompletableFuture.runAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+ } catch (CancellationException | CompletionException e) {
+ Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ }
+ }
+
+ private <T> T executeMethodAsyncForResult(Supplier<T> r,
+ String errorLogName) throws RemoteException {
+ CompletableFuture<T> future = CompletableFuture.supplyAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
+ try {
+ return future.get();
+ } catch (ExecutionException | InterruptedException e) {
+ Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ throw new RemoteException(e.getMessage());
+ }
+ }
+
+ private <T> T executeMethodAsyncForResultNoException(Supplier<T> r,
+ String errorLogName) {
+ CompletableFuture<T> future = CompletableFuture.supplyAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
+ try {
+ return future.get();
+ } catch (ExecutionException | InterruptedException e) {
+ Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ return null;
}
}
};
@@ -672,7 +783,12 @@ public class MmTelFeature extends ImsFeature {
public IImsCallSession createCallSessionInterface(ImsCallProfile profile)
throws RemoteException {
ImsCallSessionImplBase s = MmTelFeature.this.createCallSession(profile);
- return s != null ? s.getServiceImpl() : null;
+ if (s != null) {
+ s.setDefaultExecutor(mExecutor);
+ return s.getServiceImpl();
+ } else {
+ return null;
+ }
}
/**
@@ -713,7 +829,12 @@ public class MmTelFeature extends ImsFeature {
*/
protected IImsUt getUtInterface() throws RemoteException {
ImsUtImplBase utImpl = getUt();
- return utImpl != null ? utImpl.getInterface() : null;
+ if (utImpl != null) {
+ utImpl.setDefaultExecutor(mExecutor);
+ return utImpl.getInterface();
+ } else {
+ return null;
+ }
}
/**
@@ -721,7 +842,12 @@ public class MmTelFeature extends ImsFeature {
*/
protected IImsEcbm getEcbmInterface() throws RemoteException {
ImsEcbmImplBase ecbmImpl = getEcbm();
- return ecbmImpl != null ? ecbmImpl.getImsEcbm() : null;
+ if (ecbmImpl != null) {
+ ecbmImpl.setDefaultExecutor(mExecutor);
+ return ecbmImpl.getImsEcbm();
+ } else {
+ return null;
+ }
}
/**
@@ -729,7 +855,12 @@ public class MmTelFeature extends ImsFeature {
*/
public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
ImsMultiEndpointImplBase multiendpointImpl = getMultiEndpoint();
- return multiendpointImpl != null ? multiendpointImpl.getIImsMultiEndpoint() : null;
+ if (multiendpointImpl != null) {
+ multiendpointImpl.setDefaultExecutor(mExecutor);
+ return multiendpointImpl.getIImsMultiEndpoint();
+ } else {
+ return null;
+ }
}
/**
@@ -859,4 +990,16 @@ public class MmTelFeature extends ImsFeature {
public final IImsMmTelFeature getBinder() {
return mImsMMTelBinder;
}
+
+ /**
+ * Set default Executor from ImsService.
+ * @param executor The default executor for the framework to use when executing the methods
+ * overridden by the implementation of MmTelFeature.
+ * @hide
+ */
+ public final void setDefaultExecutor(@NonNull Executor executor) {
+ if (mExecutor == null) {
+ mExecutor = executor;
+ }
+ }
}
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index 18cc37d7fbda..11cf0e3f7855 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -70,7 +70,7 @@ public class RcsFeature extends ImsFeature {
// Reference the outer class in order to have better test coverage metrics instead of
// creating a inner class referencing the outer class directly.
private final RcsFeature mReference;
- private final Executor mExecutor;
+ private Executor mExecutor;
RcsFeatureBinder(RcsFeature classRef, @CallbackExecutor Executor executor) {
mReference = classRef;
@@ -259,7 +259,7 @@ public class RcsFeature extends ImsFeature {
}
}
- private final Executor mExecutor;
+ private Executor mExecutor;
private final RcsFeatureBinder mImsRcsBinder;
private RcsCapabilityExchangeImplBase mCapabilityExchangeImpl;
private CapabilityExchangeEventListener mCapExchangeEventListener;
@@ -270,13 +270,9 @@ public class RcsFeature extends ImsFeature {
* Method stubs called from the framework will be called asynchronously. To specify the
* {@link Executor} that the methods stubs will be called, use
* {@link RcsFeature#RcsFeature(Executor)} instead.
- *
- * @deprecated Use {@link #RcsFeature(Executor)} to create the RcsFeature.
*/
- @Deprecated
public RcsFeature() {
super();
- mExecutor = Runnable::run;
// Run on the Binder threads that call them.
mImsRcsBinder = new RcsFeatureBinder(this, mExecutor);
}
@@ -477,4 +473,17 @@ public class RcsFeature extends ImsFeature {
return mCapabilityExchangeImpl;
}
}
+
+ /**
+ * Set default Executor from ImsService.
+ * @param executor The default executor for the framework to use when executing the methods
+ * overridden by the implementation of RcsFeature.
+ * @hide
+ */
+ public final void setDefaultExecutor(@NonNull Executor executor) {
+ if (mImsRcsBinder.mExecutor == null) {
+ mExecutor = executor;
+ mImsRcsBinder.mExecutor = executor;
+ }
+ }
}
diff --git a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
index 9293a40d9a33..7a1a2af060d2 100644
--- a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
+++ b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
@@ -93,8 +93,8 @@ public interface CapabilityExchangeEventListener {
* Notify the framework that the ImsService has refreshed the PUBLISH
* internally, which has resulted in a new PUBLISH result.
* <p>
- * This method must return both SUCCESS (200 OK) and FAILURE (300+) codes in order to
- * keep the AOSP stack up to date.
+ * This method must be called to notify the framework of SUCCESS (200 OK) and FAILURE (300+)
+ * codes in order to keep the AOSP stack up to date.
* @param reasonCode The SIP response code sent from the network.
* @param reasonPhrase The optional reason response from the network. If the
* network provided no reason with the sip code, the string should be empty.
diff --git a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
index a3a6cb864fa5..e8100957517f 100644
--- a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
@@ -30,12 +30,20 @@ import android.telephony.ims.RtpHeaderExtension;
import android.telephony.ims.RtpHeaderExtensionType;
import android.telephony.ims.aidl.IImsCallSessionListener;
import android.util.ArraySet;
+import android.util.Log;
import com.android.ims.internal.IImsCallSession;
import com.android.ims.internal.IImsVideoCallProvider;
+import com.android.internal.telephony.util.TelephonyUtils;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.function.Supplier;
/**
* Base implementation of IImsCallSession, which implements stub versions of the methods available.
@@ -48,6 +56,8 @@ import java.util.Set;
// DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
// will break other implementations of ImsCallSession maintained by other ImsServices.
public class ImsCallSessionImplBase implements AutoCloseable {
+
+ private static final String LOG_TAG = "ImsCallSessionImplBase";
/**
* Notify USSD Mode.
*/
@@ -110,185 +120,235 @@ public class ImsCallSessionImplBase implements AutoCloseable {
}
}
+ private Executor mExecutor = Runnable::run;
+
// Non-final for injection by tests
private IImsCallSession mServiceImpl = new IImsCallSession.Stub() {
@Override
public void close() {
- ImsCallSessionImplBase.this.close();
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.close(), "close");
}
@Override
public String getCallId() {
- return ImsCallSessionImplBase.this.getCallId();
+ return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.getCallId(),
+ "getCallId");
}
@Override
public ImsCallProfile getCallProfile() {
- return ImsCallSessionImplBase.this.getCallProfile();
+ return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.getCallProfile(),
+ "getCallProfile");
}
@Override
public ImsCallProfile getLocalCallProfile() {
- return ImsCallSessionImplBase.this.getLocalCallProfile();
+ return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this
+ .getLocalCallProfile(), "getLocalCallProfile");
}
@Override
public ImsCallProfile getRemoteCallProfile() {
- return ImsCallSessionImplBase.this.getRemoteCallProfile();
+ return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this
+ .getRemoteCallProfile(), "getRemoteCallProfile");
}
@Override
public String getProperty(String name) {
- return ImsCallSessionImplBase.this.getProperty(name);
+ return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.getProperty(name),
+ "getProperty");
}
@Override
public int getState() {
- return ImsCallSessionImplBase.this.getState();
+ return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.getState(),
+ "getState");
}
@Override
public boolean isInCall() {
- return ImsCallSessionImplBase.this.isInCall();
+ return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.isInCall(),
+ "isInCall");
}
@Override
public void setListener(IImsCallSessionListener listener) {
- ImsCallSessionImplBase.this.setListener(new ImsCallSessionListener(listener));
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.setListener(
+ new ImsCallSessionListener(listener)), "setListener");
}
@Override
public void setMute(boolean muted) {
- ImsCallSessionImplBase.this.setMute(muted);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.setMute(muted), "setMute");
}
@Override
public void start(String callee, ImsCallProfile profile) {
- ImsCallSessionImplBase.this.start(callee, profile);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.start(callee, profile), "start");
}
@Override
public void startConference(String[] participants, ImsCallProfile profile) throws
RemoteException {
- ImsCallSessionImplBase.this.startConference(participants, profile);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.startConference(participants,
+ profile), "startConference");
}
@Override
public void accept(int callType, ImsStreamMediaProfile profile) {
- ImsCallSessionImplBase.this.accept(callType, profile);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.accept(callType, profile),
+ "accept");
}
@Override
public void deflect(String deflectNumber) {
- ImsCallSessionImplBase.this.deflect(deflectNumber);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.deflect(deflectNumber),
+ "deflect");
}
@Override
public void reject(int reason) {
- ImsCallSessionImplBase.this.reject(reason);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.reject(reason), "reject");
}
@Override
public void transfer(@NonNull String number, boolean isConfirmationRequired) {
- ImsCallSessionImplBase.this.transfer(number, isConfirmationRequired);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.transfer(number,
+ isConfirmationRequired), "transfer");
}
@Override
public void consultativeTransfer(@NonNull IImsCallSession transferToSession) {
- ImsCallSessionImplBase otherSession = new ImsCallSessionImplBase();
- otherSession.setServiceImpl(transferToSession);
- ImsCallSessionImplBase.this.transfer(otherSession);
+ executeMethodAsync(() -> {
+ ImsCallSessionImplBase otherSession = new ImsCallSessionImplBase();
+ otherSession.setServiceImpl(transferToSession);
+ ImsCallSessionImplBase.this.transfer(otherSession);
+ }, "consultativeTransfer");
}
@Override
public void terminate(int reason) {
- ImsCallSessionImplBase.this.terminate(reason);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.terminate(reason), "terminate");
}
@Override
public void hold(ImsStreamMediaProfile profile) {
- ImsCallSessionImplBase.this.hold(profile);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.hold(profile), "hold");
}
@Override
public void resume(ImsStreamMediaProfile profile) {
- ImsCallSessionImplBase.this.resume(profile);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.resume(profile), "resume");
}
@Override
public void merge() {
- ImsCallSessionImplBase.this.merge();
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.merge(), "merge");
}
@Override
public void update(int callType, ImsStreamMediaProfile profile) {
- ImsCallSessionImplBase.this.update(callType, profile);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.update(callType, profile),
+ "update");
}
@Override
public void extendToConference(String[] participants) {
- ImsCallSessionImplBase.this.extendToConference(participants);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.extendToConference(participants),
+ "extendToConference");
}
@Override
public void inviteParticipants(String[] participants) {
- ImsCallSessionImplBase.this.inviteParticipants(participants);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.inviteParticipants(participants),
+ "inviteParticipants");
}
@Override
public void removeParticipants(String[] participants) {
- ImsCallSessionImplBase.this.removeParticipants(participants);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.removeParticipants(participants),
+ "removeParticipants");
}
@Override
public void sendDtmf(char c, Message result) {
- ImsCallSessionImplBase.this.sendDtmf(c, result);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.sendDtmf(c, result), "sendDtmf");
}
@Override
public void startDtmf(char c) {
- ImsCallSessionImplBase.this.startDtmf(c);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.startDtmf(c), "startDtmf");
}
@Override
public void stopDtmf() {
- ImsCallSessionImplBase.this.stopDtmf();
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.stopDtmf(), "stopDtmf");
}
@Override
public void sendUssd(String ussdMessage) {
- ImsCallSessionImplBase.this.sendUssd(ussdMessage);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.sendUssd(ussdMessage), "sendUssd");
}
@Override
public IImsVideoCallProvider getVideoCallProvider() {
- return ImsCallSessionImplBase.this.getVideoCallProvider();
+ return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this
+ .getVideoCallProvider(), "getVideoCallProvider");
}
@Override
public boolean isMultiparty() {
- return ImsCallSessionImplBase.this.isMultiparty();
+ return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.isMultiparty(),
+ "isMultiparty");
}
@Override
public void sendRttModifyRequest(ImsCallProfile toProfile) {
- ImsCallSessionImplBase.this.sendRttModifyRequest(toProfile);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.sendRttModifyRequest(toProfile),
+ "sendRttModifyRequest");
}
@Override
public void sendRttModifyResponse(boolean status) {
- ImsCallSessionImplBase.this.sendRttModifyResponse(status);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.sendRttModifyResponse(status),
+ "sendRttModifyResponse");
}
@Override
public void sendRttMessage(String rttMessage) {
- ImsCallSessionImplBase.this.sendRttMessage(rttMessage);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.sendRttMessage(rttMessage),
+ "sendRttMessage");
}
@Override
public void sendRtpHeaderExtensions(@NonNull List<RtpHeaderExtension> extensions) {
- ImsCallSessionImplBase.this.sendRtpHeaderExtensions(
- new ArraySet<RtpHeaderExtension>(extensions));
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.sendRtpHeaderExtensions(
+ new ArraySet<RtpHeaderExtension>(extensions)), "sendRtpHeaderExtensions");
+ }
+
+ // Call the methods with a clean calling identity on the executor and wait indefinitely for
+ // the future to return.
+ private void executeMethodAsync(Runnable r, String errorLogName) {
+ try {
+ CompletableFuture.runAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+ } catch (CancellationException | CompletionException e) {
+ Log.w(LOG_TAG, "ImsCallSessionImplBase Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ }
+ }
+
+ private <T> T executeMethodAsyncForResult(Supplier<T> r,
+ String errorLogName) {
+ CompletableFuture<T> future = CompletableFuture.supplyAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
+ try {
+ return future.get();
+ } catch (ExecutionException | InterruptedException e) {
+ Log.w(LOG_TAG, "ImsCallSessionImplBase Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ return null;
+ }
}
};
@@ -674,4 +734,14 @@ public class ImsCallSessionImplBase implements AutoCloseable {
public void setServiceImpl(IImsCallSession serviceImpl) {
mServiceImpl = serviceImpl;
}
+
+ /**
+ * Set default Executor from MmTelFeature.
+ * @param executor The default executor for the framework to use when executing the methods
+ * overridden by the implementation of ImsCallSession.
+ * @hide
+ */
+ public final void setDefaultExecutor(@NonNull Executor executor) {
+ mExecutor = executor;
+ }
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
index d75da9035124..11fc328a42c7 100644
--- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
@@ -33,12 +33,21 @@ import android.util.Log;
import com.android.ims.ImsConfig;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.util.RemoteCallbackListExt;
+import com.android.internal.telephony.util.TelephonyUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
+
/**
* Controls the modification of IMS specific configurations. For more information on the supported
@@ -81,21 +90,48 @@ public class ImsConfigImplBase {
WeakReference<ImsConfigImplBase> mImsConfigImplBaseWeakReference;
private HashMap<Integer, Integer> mProvisionedIntValue = new HashMap<>();
private HashMap<Integer, String> mProvisionedStringValue = new HashMap<>();
+ private final Object mLock = new Object();
+ private Executor mExecutor;
@VisibleForTesting
- public ImsConfigStub(ImsConfigImplBase imsConfigImplBase) {
+ public ImsConfigStub(ImsConfigImplBase imsConfigImplBase, Executor executor) {
+ mExecutor = executor;
mImsConfigImplBaseWeakReference =
new WeakReference<ImsConfigImplBase>(imsConfigImplBase);
}
@Override
public void addImsConfigCallback(IImsConfigCallback c) throws RemoteException {
- getImsConfigImpl().addImsConfigCallback(c);
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ executeMethodAsync(()-> {
+ try {
+ getImsConfigImpl().addImsConfigCallback(c);
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ }
+ }, "addImsConfigCallback");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception addImsConfigCallback");
+ throw exceptionRef.get();
+ }
}
@Override
public void removeImsConfigCallback(IImsConfigCallback c) throws RemoteException {
- getImsConfigImpl().removeImsConfigCallback(c);
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ executeMethodAsync(()-> {
+ try {
+ getImsConfigImpl().removeImsConfigCallback(c);
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ }
+ }, "removeImsConfigCallback");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception removeImsConfigCallback");
+ throw exceptionRef.get();
+ }
}
/**
@@ -108,16 +144,34 @@ public class ImsConfigImplBase {
* unavailable.
*/
@Override
- public synchronized int getConfigInt(int item) throws RemoteException {
- if (mProvisionedIntValue.containsKey(item)) {
- return mProvisionedIntValue.get(item);
- } else {
- int retVal = getImsConfigImpl().getConfigInt(item);
- if (retVal != ImsConfig.OperationStatusConstants.UNKNOWN) {
- updateCachedValue(item, retVal, false);
+ public int getConfigInt(int item) throws RemoteException {
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ int retVal = executeMethodAsyncForResult(()-> {
+ int returnVal = ImsConfig.OperationStatusConstants.UNKNOWN;
+ synchronized (mLock) {
+ if (mProvisionedIntValue.containsKey(item)) {
+ return mProvisionedIntValue.get(item);
+ } else {
+ try {
+ returnVal = getImsConfigImpl().getConfigInt(item);
+ if (returnVal != ImsConfig.OperationStatusConstants.UNKNOWN) {
+ mProvisionedIntValue.put(item, returnVal);
+ }
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ return returnVal;
+ }
+ }
}
- return retVal;
+ return returnVal;
+ }, "getConfigInt");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception getConfigString");
+ throw exceptionRef.get();
}
+
+ return retVal;
}
/**
@@ -129,16 +183,34 @@ public class ImsConfigImplBase {
* @return value in String format.
*/
@Override
- public synchronized String getConfigString(int item) throws RemoteException {
- if (mProvisionedStringValue.containsKey(item)) {
- return mProvisionedStringValue.get(item);
- } else {
- String retVal = getImsConfigImpl().getConfigString(item);
- if (retVal != null) {
- updateCachedValue(item, retVal, false);
+ public String getConfigString(int item) throws RemoteException {
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ String retVal = executeMethodAsyncForResult(()-> {
+ String returnVal = null;
+ synchronized (mLock) {
+ if (mProvisionedStringValue.containsKey(item)) {
+ returnVal = mProvisionedStringValue.get(item);
+ } else {
+ try {
+ returnVal = getImsConfigImpl().getConfigString(item);
+ if (returnVal != null) {
+ mProvisionedStringValue.put(item, returnVal);
+ }
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ return returnVal;
+ }
+ }
}
- return retVal;
+ return returnVal;
+ }, "getConfigString");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception getConfigString");
+ throw exceptionRef.get();
}
+
+ return retVal;
}
/**
@@ -153,14 +225,32 @@ public class ImsConfigImplBase {
* {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}.
*/
@Override
- public synchronized int setConfigInt(int item, int value) throws RemoteException {
- mProvisionedIntValue.remove(item);
- int retVal = getImsConfigImpl().setConfig(item, value);
- if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) {
- updateCachedValue(item, value, true);
- } else {
- Log.d(TAG, "Set provision value of " + item +
- " to " + value + " failed with error code " + retVal);
+ public int setConfigInt(int item, int value) throws RemoteException {
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ int retVal = executeMethodAsyncForResult(()-> {
+ int returnVal = ImsConfig.OperationStatusConstants.UNKNOWN;
+ try {
+ synchronized (mLock) {
+ mProvisionedIntValue.remove(item);
+ returnVal = getImsConfigImpl().setConfig(item, value);
+ if (returnVal == ImsConfig.OperationStatusConstants.SUCCESS) {
+ mProvisionedIntValue.put(item, value);
+ } else {
+ Log.d(TAG, "Set provision value of " + item
+ + " to " + value + " failed with error code " + returnVal);
+ }
+ }
+ notifyImsConfigChanged(item, value);
+ return returnVal;
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ return returnVal;
+ }
+ }, "setConfigInt");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception setConfigInt");
+ throw exceptionRef.get();
}
return retVal;
@@ -178,12 +268,30 @@ public class ImsConfigImplBase {
* {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}.
*/
@Override
- public synchronized int setConfigString(int item, String value)
+ public int setConfigString(int item, String value)
throws RemoteException {
- mProvisionedStringValue.remove(item);
- int retVal = getImsConfigImpl().setConfig(item, value);
- if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) {
- updateCachedValue(item, value, true);
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ int retVal = executeMethodAsyncForResult(()-> {
+ int returnVal = ImsConfig.OperationStatusConstants.UNKNOWN;
+ try {
+ synchronized (mLock) {
+ mProvisionedStringValue.remove(item);
+ returnVal = getImsConfigImpl().setConfig(item, value);
+ if (returnVal == ImsConfig.OperationStatusConstants.SUCCESS) {
+ mProvisionedStringValue.put(item, value);
+ }
+ }
+ notifyImsConfigChanged(item, value);
+ return returnVal;
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ return returnVal;
+ }
+ }, "setConfigString");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception setConfigInt");
+ throw exceptionRef.get();
}
return retVal;
@@ -191,7 +299,19 @@ public class ImsConfigImplBase {
@Override
public void updateImsCarrierConfigs(PersistableBundle bundle) throws RemoteException {
- getImsConfigImpl().updateImsCarrierConfigs(bundle);
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ executeMethodAsync(()-> {
+ try {
+ getImsConfigImpl().updateImsCarrierConfigs(bundle);
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ }
+ }, "updateImsCarrierConfigs");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception updateImsCarrierConfigs");
+ throw exceptionRef.get();
+ }
}
private ImsConfigImplBase getImsConfigImpl() throws RemoteException {
@@ -206,13 +326,37 @@ public class ImsConfigImplBase {
@Override
public void notifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed)
throws RemoteException {
- getImsConfigImpl().onNotifyRcsAutoConfigurationReceived(config, isCompressed);
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ executeMethodAsync(()-> {
+ try {
+ getImsConfigImpl().onNotifyRcsAutoConfigurationReceived(config, isCompressed);
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ }
+ }, "notifyRcsAutoConfigurationReceived");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception notifyRcsAutoConfigurationReceived");
+ throw exceptionRef.get();
+ }
}
@Override
public void notifyRcsAutoConfigurationRemoved()
throws RemoteException {
- getImsConfigImpl().onNotifyRcsAutoConfigurationRemoved();
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ executeMethodAsync(()-> {
+ try {
+ getImsConfigImpl().onNotifyRcsAutoConfigurationRemoved();
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ }
+ }, "notifyRcsAutoConfigurationRemoved");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception notifyRcsAutoConfigurationRemoved");
+ throw exceptionRef.get();
+ }
}
private void notifyImsConfigChanged(int item, int value) throws RemoteException {
@@ -223,50 +367,144 @@ public class ImsConfigImplBase {
getImsConfigImpl().notifyConfigChanged(item, value);
}
- protected synchronized void updateCachedValue(int item, int value, boolean notifyChange)
- throws RemoteException {
- mProvisionedIntValue.put(item, value);
- if (notifyChange) {
- notifyImsConfigChanged(item, value);
+ protected void updateCachedValue(int item, int value) {
+ synchronized (mLock) {
+ mProvisionedIntValue.put(item, value);
}
}
- protected synchronized void updateCachedValue(int item, String value,
- boolean notifyChange) throws RemoteException {
- mProvisionedStringValue.put(item, value);
- if (notifyChange) {
- notifyImsConfigChanged(item, value);
+ protected void updateCachedValue(int item, String value) {
+ synchronized (mLock) {
+ mProvisionedStringValue.put(item, value);
}
}
@Override
public void addRcsConfigCallback(IRcsConfigCallback c) throws RemoteException {
- getImsConfigImpl().addRcsConfigCallback(c);
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ executeMethodAsync(()-> {
+ try {
+ getImsConfigImpl().addRcsConfigCallback(c);
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ }
+ }, "addRcsConfigCallback");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception addRcsConfigCallback");
+ throw exceptionRef.get();
+ }
}
@Override
public void removeRcsConfigCallback(IRcsConfigCallback c) throws RemoteException {
- getImsConfigImpl().removeRcsConfigCallback(c);
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ executeMethodAsync(()-> {
+ try {
+ getImsConfigImpl().removeRcsConfigCallback(c);
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ }
+ }, "removeRcsConfigCallback");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception removeRcsConfigCallback");
+ throw exceptionRef.get();
+ }
}
@Override
public void triggerRcsReconfiguration() throws RemoteException {
- getImsConfigImpl().triggerAutoConfiguration();
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ executeMethodAsync(()-> {
+ try {
+ getImsConfigImpl().triggerAutoConfiguration();
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ }
+ }, "triggerRcsReconfiguration");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception triggerRcsReconfiguration");
+ throw exceptionRef.get();
+ }
}
@Override
public void setRcsClientConfiguration(RcsClientConfiguration rcc) throws RemoteException {
- getImsConfigImpl().setRcsClientConfiguration(rcc);
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ executeMethodAsync(()-> {
+ try {
+ getImsConfigImpl().setRcsClientConfiguration(rcc);
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ }
+ }, "setRcsClientConfiguration");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception setRcsClientConfiguration");
+ throw exceptionRef.get();
+ }
}
@Override
public void notifyIntImsConfigChanged(int item, int value) throws RemoteException {
- notifyImsConfigChanged(item, value);
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ executeMethodAsync(()-> {
+ try {
+ notifyImsConfigChanged(item, value);
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ }
+ }, "notifyIntImsConfigChanged");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception notifyIntImsConfigChanged");
+ throw exceptionRef.get();
+ }
}
@Override
public void notifyStringImsConfigChanged(int item, String value) throws RemoteException {
- notifyImsConfigChanged(item, value);
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ executeMethodAsync(()-> {
+ try {
+ notifyImsConfigChanged(item, value);
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ }
+ }, "notifyStringImsConfigChanged");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception notifyStringImsConfigChanged");
+ throw exceptionRef.get();
+ }
+ }
+
+ // Call the methods with a clean calling identity on the executor and wait indefinitely for
+ // the future to return.
+ private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException {
+ try {
+ CompletableFuture.runAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+ } catch (CancellationException | CompletionException e) {
+ Log.w(TAG, "ImsConfigImplBase Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ throw new RemoteException(e.getMessage());
+ }
+ }
+
+ private <T> T executeMethodAsyncForResult(Supplier<T> r,
+ String errorLogName) throws RemoteException {
+ CompletableFuture<T> future = CompletableFuture.supplyAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
+ try {
+ return future.get();
+ } catch (ExecutionException | InterruptedException e) {
+ Log.w(TAG, "ImsConfigImplBase Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ throw new RemoteException(e.getMessage());
+ }
}
}
@@ -303,15 +541,24 @@ public class ImsConfigImplBase {
ImsConfigStub mImsConfigStub;
/**
- * Used for compatibility between older versions of the ImsService.
+ * Create a ImsConfig using the Executor specified for methods being called by the
+ * framework.
+ * @param executor The executor for the framework to use when executing the methods overridden
+ * by the implementation of ImsConfig.
+ */
+ public ImsConfigImplBase(@NonNull Executor executor) {
+ mImsConfigStub = new ImsConfigStub(this, executor);
+ }
+
+ /**
* @hide
*/
- public ImsConfigImplBase(Context context) {
- mImsConfigStub = new ImsConfigStub(this);
+ public ImsConfigImplBase(@NonNull Context context) {
+ mImsConfigStub = new ImsConfigStub(this, null);
}
public ImsConfigImplBase() {
- mImsConfigStub = new ImsConfigStub(this);
+ mImsConfigStub = new ImsConfigStub(this, null);
}
/**
@@ -427,8 +674,10 @@ public class ImsConfigImplBase {
* @param value in Integer format.
*/
public final void notifyProvisionedValueChanged(int item, int value) {
+ mImsConfigStub.updateCachedValue(item, value);
+
try {
- mImsConfigStub.updateCachedValue(item, value, true);
+ mImsConfigStub.notifyImsConfigChanged(item, value);
} catch (RemoteException e) {
Log.w(TAG, "notifyProvisionedValueChanged(int): Framework connection is dead.");
}
@@ -443,8 +692,10 @@ public class ImsConfigImplBase {
* @param value in String format.
*/
public final void notifyProvisionedValueChanged(int item, String value) {
+ mImsConfigStub.updateCachedValue(item, value);
+
try {
- mImsConfigStub.updateCachedValue(item, value, true);
+ mImsConfigStub.notifyImsConfigChanged(item, value);
} catch (RemoteException e) {
Log.w(TAG, "notifyProvisionedValueChanged(string): Framework connection is dead.");
}
@@ -582,4 +833,16 @@ public class ImsConfigImplBase {
}
});
}
+
+ /**
+ * Set default Executor from ImsService.
+ * @param executor The default executor for the framework to use when executing the methods
+ * overridden by the implementation of ImsConfig.
+ * @hide
+ */
+ public final void setDefaultExecutor(@NonNull Executor executor) {
+ if (mImsConfigStub.mExecutor == null) {
+ mImsConfigStub.mExecutor = executor;
+ }
+ }
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
index 8ad40ed1032c..84b2253e1b27 100644
--- a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
@@ -16,14 +16,21 @@
package android.telephony.ims.stub;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.RemoteException;
import android.util.Log;
import com.android.ims.internal.IImsEcbm;
import com.android.ims.internal.IImsEcbmListener;
+import com.android.internal.telephony.util.TelephonyUtils;
import java.util.Objects;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.Executor;
+
/**
* Base implementation of ImsEcbm, which implements stub versions of the methods
@@ -40,10 +47,12 @@ public class ImsEcbmImplBase {
private final Object mLock = new Object();
private IImsEcbmListener mListener;
+ private Executor mExecutor = Runnable::run;
+
private final IImsEcbm mImsEcbm = new IImsEcbm.Stub() {
@Override
public void setListener(IImsEcbmListener listener) {
- synchronized (mLock) {
+ executeMethodAsync(() -> {
if (mListener != null && !mListener.asBinder().isBinderAlive()) {
Log.w(TAG, "setListener: discarding dead Binder");
mListener = null;
@@ -62,12 +71,25 @@ public class ImsEcbmImplBase {
+ "listener");
mListener = listener;
}
- }
+ }, "setListener");
}
@Override
public void exitEmergencyCallbackMode() {
- ImsEcbmImplBase.this.exitEmergencyCallbackMode();
+ executeMethodAsync(() -> ImsEcbmImplBase.this.exitEmergencyCallbackMode(),
+ "exitEmergencyCallbackMode");
+ }
+
+ // Call the methods with a clean calling identity on the executor and wait indefinitely for
+ // the future to return.
+ private void executeMethodAsync(Runnable r, String errorLogName) {
+ try {
+ CompletableFuture.runAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+ } catch (CancellationException | CompletionException e) {
+ Log.w(TAG, "ImsEcbmImplBase Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ }
}
};
@@ -123,4 +145,14 @@ public class ImsEcbmImplBase {
}
}
}
+
+ /**
+ * Set default Executor from MmTelFeature.
+ * @param executor The default executor for the framework to use when executing the methods
+ * overridden by the implementation of ImsEcbm.
+ * @hide
+ */
+ public final void setDefaultExecutor(@NonNull Executor executor) {
+ mExecutor = executor;
+ }
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
index ec1c7b3a92a8..a723cd8b118c 100644
--- a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
@@ -16,6 +16,7 @@
package android.telephony.ims.stub;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.RemoteException;
import android.telephony.ims.ImsExternalCallState;
@@ -23,9 +24,14 @@ import android.util.Log;
import com.android.ims.internal.IImsExternalCallStateListener;
import com.android.ims.internal.IImsMultiEndpoint;
+import com.android.internal.telephony.util.TelephonyUtils;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.Executor;
/**
* Base implementation of ImsMultiEndpoint, which implements stub versions of the methods
@@ -43,11 +49,13 @@ public class ImsMultiEndpointImplBase {
private IImsExternalCallStateListener mListener;
private final Object mLock = new Object();
+ private Executor mExecutor = Runnable::run;
+
private final IImsMultiEndpoint mImsMultiEndpoint = new IImsMultiEndpoint.Stub() {
@Override
public void setListener(IImsExternalCallStateListener listener) throws RemoteException {
- synchronized (mLock) {
+ executeMethodAsync(() -> {
if (mListener != null && !mListener.asBinder().isBinderAlive()) {
Log.w(TAG, "setListener: discarding dead Binder");
mListener = null;
@@ -67,12 +75,25 @@ public class ImsMultiEndpointImplBase {
+ "listener");
mListener = listener;
}
- }
+ }, "setListener");
}
@Override
public void requestImsExternalCallStateInfo() throws RemoteException {
- ImsMultiEndpointImplBase.this.requestImsExternalCallStateInfo();
+ executeMethodAsync(() -> ImsMultiEndpointImplBase.this
+ .requestImsExternalCallStateInfo(), "requestImsExternalCallStateInfo");
+ }
+
+ // Call the methods with a clean calling identity on the executor and wait indefinitely for
+ // the future to return.
+ private void executeMethodAsync(Runnable r, String errorLogName) {
+ try {
+ CompletableFuture.runAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+ } catch (CancellationException | CompletionException e) {
+ Log.w(TAG, "ImsMultiEndpointImplBase Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ }
}
};
@@ -108,4 +129,14 @@ public class ImsMultiEndpointImplBase {
public void requestImsExternalCallStateInfo() {
Log.d(TAG, "requestImsExternalCallStateInfo() not implemented");
}
+
+ /**
+ * Set default Executor from MmTelFeature.
+ * @param executor The default executor for the framework to use when executing the methods
+ * overridden by the implementation of ImsMultiEndpoint.
+ * @hide
+ */
+ public final void setDefaultExecutor(@NonNull Executor executor) {
+ mExecutor = executor;
+ }
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index 02bcdec621c1..3b151a422b57 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -31,10 +31,19 @@ import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.util.Log;
import com.android.internal.telephony.util.RemoteCallbackListExt;
+import com.android.internal.telephony.util.TelephonyUtils;
import com.android.internal.util.ArrayUtils;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
/**
* Controls IMS registration for this ImsService and notifies the framework when the IMS
@@ -92,39 +101,114 @@ public class ImsRegistrationImplBase {
// yet.
private static final int REGISTRATION_STATE_UNKNOWN = -1;
+ private Executor mExecutor;
+
+ /**
+ * Create a new ImsRegistration.
+ * <p>
+ * Method stubs called from the framework will be called asynchronously. To specify the
+ * {@link Executor} that the methods stubs will be called, use
+ * {@link ImsRegistrationImplBase#ImsRegistrationImplBase(Executor)} instead.
+ */
+ public ImsRegistrationImplBase() {
+ super();
+ }
+
+ /**
+ * Create a ImsRegistration using the Executor specified for methods being called by the
+ * framework.
+ * @param executor The executor for the framework to use when executing the methods overridden
+ * by the implementation of ImsRegistration.
+ */
+ public ImsRegistrationImplBase(@NonNull Executor executor) {
+ super();
+ mExecutor = executor;
+ }
+
private final IImsRegistration mBinder = new IImsRegistration.Stub() {
@Override
public @ImsRegistrationTech int getRegistrationTechnology() throws RemoteException {
- synchronized (mLock) {
- return (mRegistrationAttributes == null) ? REGISTRATION_TECH_NONE
- : mRegistrationAttributes.getRegistrationTechnology();
- }
+ return executeMethodAsyncForResult(() -> (mRegistrationAttributes == null)
+ ? REGISTRATION_TECH_NONE : mRegistrationAttributes.getRegistrationTechnology(),
+ "getRegistrationTechnology");
}
@Override
public void addRegistrationCallback(IImsRegistrationCallback c) throws RemoteException {
- ImsRegistrationImplBase.this.addRegistrationCallback(c);
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ executeMethodAsync(() -> {
+ try {
+ ImsRegistrationImplBase.this.addRegistrationCallback(c);
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ }
+ }, "addRegistrationCallback");
+
+ if (exceptionRef.get() != null) {
+ throw exceptionRef.get();
+ }
}
@Override
public void removeRegistrationCallback(IImsRegistrationCallback c) throws RemoteException {
- ImsRegistrationImplBase.this.removeRegistrationCallback(c);
+ executeMethodAsync(() -> ImsRegistrationImplBase.this.removeRegistrationCallback(c),
+ "removeRegistrationCallback");
}
@Override
public void triggerFullNetworkRegistration(int sipCode, String sipReason) {
- ImsRegistrationImplBase.this.triggerFullNetworkRegistration(sipCode, sipReason);
+ executeMethodAsyncNoException(() -> ImsRegistrationImplBase.this
+ .triggerFullNetworkRegistration(sipCode, sipReason),
+ "triggerFullNetworkRegistration");
}
@Override
public void triggerUpdateSipDelegateRegistration() {
- ImsRegistrationImplBase.this.updateSipDelegateRegistration();
+ executeMethodAsyncNoException(() -> ImsRegistrationImplBase.this
+ .updateSipDelegateRegistration(), "triggerUpdateSipDelegateRegistration");
}
@Override
public void triggerSipDelegateDeregistration() {
- ImsRegistrationImplBase.this.triggerSipDelegateDeregistration();
+ executeMethodAsyncNoException(() -> ImsRegistrationImplBase.this
+ .triggerSipDelegateDeregistration(), "triggerSipDelegateDeregistration");
+ }
+
+ // Call the methods with a clean calling identity on the executor and wait indefinitely for
+ // the future to return.
+ private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException {
+ try {
+ CompletableFuture.runAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+ } catch (CancellationException | CompletionException e) {
+ Log.w(LOG_TAG, "ImsRegistrationImplBase Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ throw new RemoteException(e.getMessage());
+ }
+ }
+
+ private void executeMethodAsyncNoException(Runnable r, String errorLogName) {
+ try {
+ CompletableFuture.runAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+ } catch (CancellationException | CompletionException e) {
+ Log.w(LOG_TAG, "ImsRegistrationImplBase Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ }
+ }
+
+ private <T> T executeMethodAsyncForResult(Supplier<T> r,
+ String errorLogName) throws RemoteException {
+ CompletableFuture<T> future = CompletableFuture.supplyAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
+ try {
+ return future.get();
+ } catch (ExecutionException | InterruptedException e) {
+ Log.w(LOG_TAG, "ImsRegistrationImplBase Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ throw new RemoteException(e.getMessage());
+ }
}
};
@@ -394,4 +478,16 @@ public class ImsRegistrationImplBase {
onSubscriberAssociatedUriChanged(c, uris);
}
}
+
+ /**
+ * Set default Executor from ImsService.
+ * @param executor The default executor for the framework to use when executing the methods
+ * overridden by the implementation of Registration.
+ * @hide
+ */
+ public final void setDefaultExecutor(@NonNull Executor executor) {
+ if (mExecutor == null) {
+ mExecutor = executor;
+ }
+ }
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
index eb3e8ed5a8e4..11cdeed10c5a 100644
--- a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
@@ -27,10 +27,17 @@ import android.util.Log;
import com.android.ims.internal.IImsUt;
import com.android.ims.internal.IImsUtListener;
+import com.android.internal.telephony.util.TelephonyUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.function.Supplier;
/**
* Base implementation of IMS UT interface, which implements stubs. Override these methods to
@@ -119,96 +126,108 @@ public class ImsUtImplBase {
*/
public static final int INVALID_RESULT = -1;
+ private Executor mExecutor = Runnable::run;
+
private final IImsUt.Stub mServiceImpl = new IImsUt.Stub() {
private final Object mLock = new Object();
private ImsUtListener mUtListener;
@Override
public void close() throws RemoteException {
- ImsUtImplBase.this.close();
+ executeMethodAsync(() ->ImsUtImplBase.this.close(), "close");
}
@Override
public int queryCallBarring(int cbType) throws RemoteException {
- return ImsUtImplBase.this.queryCallBarring(cbType);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCallBarring(cbType),
+ "queryCallBarring");
}
@Override
public int queryCallForward(int condition, String number) throws RemoteException {
- return ImsUtImplBase.this.queryCallForward(condition, number);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCallForward(
+ condition, number), "queryCallForward");
}
@Override
public int queryCallWaiting() throws RemoteException {
- return ImsUtImplBase.this.queryCallWaiting();
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCallWaiting(),
+ "queryCallWaiting");
}
@Override
public int queryCLIR() throws RemoteException {
- return ImsUtImplBase.this.queryCLIR();
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCLIR(), "queryCLIR");
}
@Override
public int queryCLIP() throws RemoteException {
- return ImsUtImplBase.this.queryCLIP();
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCLIP(), "queryCLIP");
}
@Override
public int queryCOLR() throws RemoteException {
- return ImsUtImplBase.this.queryCOLR();
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCOLR(), "queryCOLR");
}
@Override
public int queryCOLP() throws RemoteException {
- return ImsUtImplBase.this.queryCOLP();
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCOLP(), "queryCOLP");
}
@Override
public int transact(Bundle ssInfo) throws RemoteException {
- return ImsUtImplBase.this.transact(ssInfo);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.transact(ssInfo),
+ "transact");
}
@Override
public int updateCallBarring(int cbType, int action, String[] barrList) throws
RemoteException {
- return ImsUtImplBase.this.updateCallBarring(cbType, action, barrList);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCallBarring(
+ cbType, action, barrList), "updateCallBarring");
}
@Override
public int updateCallForward(int action, int condition, String number, int serviceClass,
int timeSeconds) throws RemoteException {
- return ImsUtImplBase.this.updateCallForward(action, condition, number, serviceClass,
- timeSeconds);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCallForward(
+ action, condition, number, serviceClass, timeSeconds), "updateCallForward");
}
@Override
public int updateCallWaiting(boolean enable, int serviceClass) throws RemoteException {
- return ImsUtImplBase.this.updateCallWaiting(enable, serviceClass);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCallWaiting(
+ enable, serviceClass), "updateCallWaiting");
}
@Override
public int updateCLIR(int clirMode) throws RemoteException {
- return ImsUtImplBase.this.updateCLIR(clirMode);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCLIR(clirMode),
+ "updateCLIR");
}
@Override
public int updateCLIP(boolean enable) throws RemoteException {
- return ImsUtImplBase.this.updateCLIP(enable);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCLIP(enable),
+ "updateCLIP");
}
@Override
public int updateCOLR(int presentation) throws RemoteException {
- return ImsUtImplBase.this.updateCOLR(presentation);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCOLR(presentation),
+ "updateCOLR");
}
@Override
public int updateCOLP(boolean enable) throws RemoteException {
- return ImsUtImplBase.this.updateCOLP(enable);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCOLP(enable),
+ "updateCOLP");
}
@Override
public void setListener(IImsUtListener listener) throws RemoteException {
- synchronized (mLock) {
+ executeMethodAsync(() -> {
if (mUtListener != null
&& !mUtListener.getListenerInterface().asBinder().isBinderAlive()) {
Log.w(TAG, "setListener: discarding dead Binder");
@@ -229,29 +248,59 @@ public class ImsUtImplBase {
+ "listener");
mUtListener = new ImsUtListener(listener);
}
- }
- ImsUtImplBase.this.setListener(mUtListener);
+ ImsUtImplBase.this.setListener(mUtListener);
+ }, "setListener");
}
@Override
public int queryCallBarringForServiceClass(int cbType, int serviceClass)
throws RemoteException {
- return ImsUtImplBase.this.queryCallBarringForServiceClass(cbType, serviceClass);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this
+ .queryCallBarringForServiceClass(cbType, serviceClass),
+ "queryCallBarringForServiceClass");
}
@Override
public int updateCallBarringForServiceClass(int cbType, int action,
String[] barrList, int serviceClass) throws RemoteException {
- return ImsUtImplBase.this.updateCallBarringForServiceClass(
- cbType, action, barrList, serviceClass);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this
+ .updateCallBarringForServiceClass(cbType, action, barrList, serviceClass),
+ "updateCallBarringForServiceClass");
}
@Override
public int updateCallBarringWithPassword(int cbType, int action, String[] barrList,
int serviceClass, String password) throws RemoteException {
- return ImsUtImplBase.this.updateCallBarringWithPassword(
- cbType, action, barrList, serviceClass, password);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this
+ .updateCallBarringWithPassword(cbType, action, barrList, serviceClass,
+ password), "updateCallBarringWithPassword");
+ }
+
+ // Call the methods with a clean calling identity on the executor and wait indefinitely for
+ // the future to return.
+ private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException {
+ try {
+ CompletableFuture.runAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+ } catch (CancellationException | CompletionException e) {
+ Log.w(TAG, "ImsUtImplBase Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ throw new RemoteException(e.getMessage());
+ }
+ }
+
+ private <T> T executeMethodAsyncForResult(Supplier<T> r,
+ String errorLogName) throws RemoteException {
+ CompletableFuture<T> future = CompletableFuture.supplyAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
+ try {
+ return future.get();
+ } catch (ExecutionException | InterruptedException e) {
+ Log.w(TAG, "ImsUtImplBase Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ throw new RemoteException(e.getMessage());
+ }
}
};
@@ -470,4 +519,14 @@ public class ImsUtImplBase {
public IImsUt getInterface() {
return mServiceImpl;
}
+
+ /**
+ * Set default Executor from MmTelFeature.
+ * @param executor The default executor for the framework to use when executing the methods
+ * overridden by the implementation of ImsUT.
+ * @hide
+ */
+ public final void setDefaultExecutor(@NonNull Executor executor) {
+ mExecutor = executor;
+ }
}
diff --git a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
index 13ea99735ab4..52538cb4e2df 100644
--- a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
@@ -86,10 +86,21 @@ public class SipTransportImplBase {
}
};
- private final Executor mBinderExecutor;
+ private Executor mBinderExecutor;
private final ArrayList<SipDelegateAidlWrapper> mDelegates = new ArrayList<>();
/**
+ * Create a new SipTransport.
+ * <p>
+ * Method stubs called from the framework will be called asynchronously. To specify the
+ * {@link Executor} that the methods stubs will be called, use
+ * {@link SipTransportImplBase#SipTransportImplBase(Executor)} instead.
+ */
+ public SipTransportImplBase() {
+ super();
+ }
+
+ /**
* Create an implementation of SipTransportImplBase.
*
* @param executor The executor that remote calls from the framework will be called on. This
@@ -212,4 +223,16 @@ public class SipTransportImplBase {
public ISipTransport getBinder() {
return mSipTransportImpl;
}
+
+ /**
+ * Set default Executor from ImsService.
+ * @param executor The default executor for the framework to use when executing the methods
+ * overridden by the implementation of SipTransport.
+ * @hide
+ */
+ public final void setDefaultExecutor(@NonNull Executor executor) {
+ if (mBinderExecutor == null) {
+ mBinderExecutor = executor;
+ }
+ }
}
diff --git a/telephony/java/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/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index a900c84c1819..1e38b9215d76 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -313,4 +313,15 @@ interface ISub {
void setPhoneNumber(int subId, int source, String number,
String callingPackage, String callingFeatureId);
+
+ /**
+ * Set the Usage Setting for this subscription.
+ *
+ * @param usageSetting the usage setting for this subscription
+ * @param subId the unique SubscriptionInfo index in database
+ * @param callingPackage The package making the IPC.
+ *
+ * @throws SecurityException if doesn't have MODIFY_PHONE_STATE or Carrier Privileges
+ */
+ int setUsageSetting(int usageSetting, int subId, String callingPackage);
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index f317c921be75..be54cecbe3ba 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2513,4 +2513,14 @@ interface ITelephony {
/** Check if telephony new data stack is enabled. */
boolean isUsingNewDataStack();
+
+ /**
+ * @return true if the modem service is set successfully, false otherwise.
+ */
+ boolean setModemService(in String serviceName);
+
+ /**
+ * @return the service name of the modem service which bind to.
+ */
+ String getModemService();
}
diff --git a/telephony/java/com/android/internal/telephony/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/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 866fd2c7394e..ba9584112b75 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -530,6 +530,8 @@ public interface RILConstants {
int RIL_REQUEST_GET_SLICING_CONFIG = 224;
int RIL_REQUEST_ENABLE_VONR = 225;
int RIL_REQUEST_IS_VONR_ENABLED = 226;
+ int RIL_REQUEST_SET_USAGE_SETTING = 227;
+ int RIL_REQUEST_GET_USAGE_SETTING = 228;
/* Responses begin */
int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl
index c717c092cbed..1734c982ffb4 100644
--- a/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl
@@ -45,6 +45,8 @@ interface IEuiccCardController {
in IGetAllProfilesCallback callback);
oneway void getProfile(String callingPackage, String cardId, String iccid,
in IGetProfileCallback callback);
+ oneway void getEnabledProfile(String callingPackage, String cardId, int portIndex,
+ in IGetProfileCallback callback);
oneway void disableProfile(String callingPackage, String cardId, String iccid, int portIndex,
boolean refresh, in IDisableProfileCallback callback);
oneway void switchToProfile(String callingPackage, String cardId, String iccid, int portIndex,
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 fe434268fbc9..32feccda0c47 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
@@ -99,7 +99,11 @@ class OpenAppColdTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSp
/** {@inheritDoc} */
@Presubmit
@Test
- override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
+ override fun appLayerReplacesLauncher() {
+ // This test doesn't work in shell transitions because of b/206094140
+ assumeFalse(isShellTransitionsEnabled)
+ super.appLayerReplacesLauncher()
+ }
/** {@inheritDoc} */
@Presubmit
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 5d0d718e15c3..3914ef6633b3 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
@@ -231,7 +231,7 @@ class OpenAppNonResizeableTest(testSpec: FlickerTestParameter) : OpenAppTransiti
@Test
fun screenLockedStart() {
testSpec.assertLayersStart {
- isVisible(colorFadComponent)
+ isEmpty()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
index 4db01bf53833..619f5d218a14 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
@@ -119,7 +119,11 @@ class OpenAppWarmTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSp
/** {@inheritDoc} */
@Presubmit
@Test
- override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
+ override fun appLayerReplacesLauncher() {
+ // This test doesn't work in shell transitions because of b/206094140
+ assumeFalse(isShellTransitionsEnabled)
+ super.appLayerReplacesLauncher()
+ }
/** {@inheritDoc} */
@Presubmit
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
index ded80a0b2509..8f8951b7379f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
@@ -32,12 +32,14 @@ import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.navBarLayerIsVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.common.FlickerComponentName
+import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -128,6 +130,8 @@ class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTestPara
@Presubmit
@Test
fun startsWithApp1WindowsCoverFullScreen() {
+ // This test doesn't work in shell transitions because of b/209936664
+ Assume.assumeFalse(isShellTransitionsEnabled)
testSpec.assertWmStart {
this.frameRegion(testApp1.component).coversExactly(startDisplayBounds)
}
@@ -140,6 +144,8 @@ class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTestPara
@Presubmit
@Test
fun startsWithApp1LayersCoverFullScreen() {
+ // This test doesn't work in shell transitions because of b/209936664
+ Assume.assumeFalse(isShellTransitionsEnabled)
testSpec.assertLayersStart {
this.visibleRegion(testApp1.component).coversExactly(startDisplayBounds)
}
@@ -151,6 +157,8 @@ class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTestPara
@Presubmit
@Test
fun startsWithApp1WindowBeingOnTop() {
+ // This test doesn't work in shell transitions because of b/209936664
+ Assume.assumeFalse(isShellTransitionsEnabled)
testSpec.assertWmStart {
this.isAppWindowOnTop(testApp1.component)
}
@@ -163,6 +171,8 @@ class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTestPara
@Presubmit
@Test
fun endsWithApp2WindowsCoveringFullScreen() {
+ // This test doesn't work in shell transitions because of b/209936664
+ Assume.assumeFalse(isShellTransitionsEnabled)
testSpec.assertWmEnd {
this.frameRegion(testApp2.component).coversExactly(startDisplayBounds)
}
@@ -175,6 +185,8 @@ class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTestPara
@Presubmit
@Test
fun endsWithApp2LayersCoveringFullScreen() {
+ // This test doesn't work in shell transitions because of b/209936664
+ Assume.assumeFalse(isShellTransitionsEnabled)
testSpec.assertLayersEnd {
this.visibleRegion(testApp2.component).coversExactly(startDisplayBounds)
}
@@ -187,6 +199,8 @@ class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTestPara
@Presubmit
@Test
fun endsWithApp2BeingOnTop() {
+ // This test doesn't work in shell transitions because of b/209936664
+ Assume.assumeFalse(isShellTransitionsEnabled)
testSpec.assertWmEnd {
this.isAppWindowOnTop(testApp2.component)
}
@@ -199,6 +213,8 @@ class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTestPara
@Presubmit
@Test
fun app2WindowBecomesAndStaysVisible() {
+ // This test doesn't work in shell transitions because of b/209936664
+ Assume.assumeFalse(isShellTransitionsEnabled)
testSpec.assertWm {
this.isAppWindowInvisible(testApp2.component)
.then()
@@ -215,6 +231,8 @@ class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTestPara
@Presubmit
@Test
fun app2LayerBecomesAndStaysVisible() {
+ // This test doesn't work in shell transitions because of b/209936664
+ Assume.assumeFalse(isShellTransitionsEnabled)
testSpec.assertLayers {
this.isInvisible(testApp2.component)
.then()
@@ -229,6 +247,8 @@ class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTestPara
@Presubmit
@Test
fun app1WindowBecomesAndStaysInvisible() {
+ // This test doesn't work in shell transitions because of b/209936664
+ Assume.assumeFalse(isShellTransitionsEnabled)
testSpec.assertWm {
this.isAppWindowVisible(testApp1.component)
.then()
@@ -243,6 +263,8 @@ class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTestPara
@Presubmit
@Test
fun app1LayerBecomesAndStaysInvisible() {
+ // This test doesn't work in shell transitions because of b/209936664
+ Assume.assumeFalse(isShellTransitionsEnabled)
testSpec.assertLayers {
this.isVisible(testApp1.component)
.then()
@@ -258,6 +280,8 @@ class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTestPara
@Presubmit
@Test
fun app2WindowIsVisibleOnceApp1WindowIsInvisible() {
+ // This test doesn't work in shell transitions because of b/209936664
+ Assume.assumeFalse(isShellTransitionsEnabled)
testSpec.assertWm {
this.isAppWindowVisible(testApp1.component)
.then()
@@ -277,6 +301,8 @@ class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTestPara
@Presubmit
@Test
fun app2LayerIsVisibleOnceApp1LayerIsInvisible() {
+ // This test doesn't work in shell transitions because of b/209936664
+ Assume.assumeFalse(isShellTransitionsEnabled)
testSpec.assertLayers {
this.isVisible(testApp1.component)
.then()
@@ -293,14 +319,22 @@ class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTestPara
*/
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsVisible()
+ fun navBarWindowIsAlwaysVisible() {
+ // This test doesn't work in shell transitions because of b/209936664
+ Assume.assumeFalse(isShellTransitionsEnabled)
+ testSpec.navBarWindowIsVisible()
+ }
/**
* Checks that the navbar layer is visible throughout the entire transition.
*/
@Presubmit
@Test
- fun navBarLayerAlwaysIsVisible() = testSpec.navBarLayerIsVisible()
+ fun navBarLayerAlwaysIsVisible() {
+ // This test doesn't work in shell transitions because of b/209936664
+ Assume.assumeFalse(isShellTransitionsEnabled)
+ testSpec.navBarLayerIsVisible()
+ }
/**
* Checks that the navbar is always in the right position and covers the expected region.
@@ -309,21 +343,33 @@ class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTestPara
*/
@Presubmit
@Test
- fun navbarIsAlwaysInRightPosition() = testSpec.navBarLayerRotatesAndScales()
+ fun navbarIsAlwaysInRightPosition() {
+ // This test doesn't work in shell transitions because of b/209936664
+ Assume.assumeFalse(isShellTransitionsEnabled)
+ testSpec.navBarLayerRotatesAndScales()
+ }
/**
* Checks that the status bar window is visible throughout the entire transition.
*/
@Presubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsVisible()
+ fun statusBarWindowIsAlwaysVisible() {
+ // This test doesn't work in shell transitions because of b/209936664
+ Assume.assumeFalse(isShellTransitionsEnabled)
+ testSpec.statusBarWindowIsVisible()
+ }
/**
* Checks that the status bar layer is visible throughout the entire transition.
*/
@Presubmit
@Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsVisible()
+ fun statusBarLayerIsAlwaysVisible() {
+ // This test doesn't work in shell transitions because of b/209936664
+ Assume.assumeFalse(isShellTransitionsEnabled)
+ testSpec.statusBarLayerIsVisible()
+ }
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/tests/MultiUser/Android.bp b/tests/MultiUser/Android.bp
new file mode 100644
index 000000000000..bde309fe3015
--- /dev/null
+++ b/tests/MultiUser/Android.bp
@@ -0,0 +1,27 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "MultiUserTests",
+ platform_apis: true,
+ srcs: ["src/**/*.java"],
+
+ static_libs: [
+ "androidx.test.rules",
+ "androidx.test.runner",
+ "services.core",
+ ],
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ "android.test.mock",
+ ],
+ certificate: "platform",
+ test_suites: ["device-tests"],
+}
diff --git a/tests/MultiUser/AndroidManifest.xml b/tests/MultiUser/AndroidManifest.xml
new file mode 100644
index 000000000000..9a25c0990de4
--- /dev/null
+++ b/tests/MultiUser/AndroidManifest.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test.multiuser">
+ <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+ <uses-permission android:name="android.permission.MANAGE_USERS" />
+
+
+ <application android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.test.multiuser">
+ </instrumentation>
+</manifest>
diff --git a/tests/MultiUser/TEST_MAPPING b/tests/MultiUser/TEST_MAPPING
new file mode 100644
index 000000000000..0dbef6cbc6ff
--- /dev/null
+++ b/tests/MultiUser/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "MultiUserTests"
+ }
+ ]
+}
diff --git a/tests/MultiUser/src/com/android/test/multiuser/MultiUserSettingsTests.java b/tests/MultiUser/src/com/android/test/multiuser/MultiUserSettingsTests.java
new file mode 100644
index 000000000000..1521cc617873
--- /dev/null
+++ b/tests/MultiUser/src/com/android/test/multiuser/MultiUserSettingsTests.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.test.multiuser;
+
+import static android.provider.Settings.Secure.FONT_WEIGHT_ADJUSTMENT;
+import static android.provider.Settings.System.FONT_SCALE;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.os.UserManager;
+import android.provider.Settings;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class MultiUserSettingsTests {
+ private final Context mContext = getInstrumentation().getTargetContext();
+ private final ContentResolver mContentResolver = mContext.getContentResolver();
+
+ private static void waitForBroadcastIdle() throws InterruptedException {
+ final int sleepDuration = 1000;
+ final String cmdAmWaitForBroadcastIdle = "am wait-for-broadcast-idle";
+
+ Thread.sleep(sleepDuration);
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .executeShellCommand(cmdAmWaitForBroadcastIdle);
+ Thread.sleep(sleepDuration);
+ }
+
+ private float getGlobalFontScale() {
+ return mContext.getResources().getConfiguration().fontScale;
+ }
+
+ private int getGlobalFontWeight() {
+ return mContext.getResources().getConfiguration().fontWeightAdjustment;
+ }
+
+ private float getFontScaleOfUser(int userId) {
+ return Settings.System.getFloatForUser(mContentResolver, FONT_SCALE, 1, userId);
+ }
+
+ private int getFontWeightOfUser(int userId) {
+ return Settings.Secure.getIntForUser(mContentResolver, FONT_WEIGHT_ADJUSTMENT, 1, userId);
+ }
+
+ private void setFontScaleOfUser(float fontScale, int userId) throws InterruptedException {
+ Settings.System.putFloatForUser(mContentResolver, FONT_SCALE, fontScale, userId);
+ waitForBroadcastIdle();
+ }
+
+ private void setFontWeightOfUser(int fontWeight, int userId) throws InterruptedException {
+ Settings.Secure.putIntForUser(mContentResolver, FONT_WEIGHT_ADJUSTMENT, fontWeight, userId);
+ waitForBroadcastIdle();
+ }
+
+ @Test
+ public void testChangingFontScaleOfABackgroundUser_shouldNotAffectUI()
+ throws InterruptedException {
+
+ Assume.assumeTrue(UserManager.supportsMultipleUsers());
+
+ UserManager userManager = UserManager.get(mContext);
+
+ final int backgroundUserId = userManager.createUser("test_user",
+ UserManager.USER_TYPE_FULL_SECONDARY, 0).id;
+ final float oldFontScaleOfBgUser = getFontScaleOfUser(backgroundUserId);
+ final float oldGlobalFontScale = getGlobalFontScale();
+ final float newFontScaleOfBgUser = 1 + Math.max(oldGlobalFontScale, oldFontScaleOfBgUser);
+
+ try {
+ setFontScaleOfUser(newFontScaleOfBgUser, backgroundUserId);
+ final float newGlobalFontScale = getGlobalFontScale();
+ assertEquals(oldGlobalFontScale, newGlobalFontScale, 0);
+ } finally {
+ setFontScaleOfUser(oldFontScaleOfBgUser, backgroundUserId);
+ userManager.removeUser(backgroundUserId);
+ }
+ }
+
+ @Test
+ public void testChangingFontWeightOfABackgroundUser_shouldNotAffectUI()
+ throws InterruptedException {
+
+ Assume.assumeTrue(UserManager.supportsMultipleUsers());
+
+ UserManager userManager = UserManager.get(mContext);
+
+ final int backgroundUserId = userManager.createUser("test_user",
+ UserManager.USER_TYPE_FULL_SECONDARY, 0).id;
+ final int oldFontWeightOfBgUser = getFontWeightOfUser(backgroundUserId);
+ final int oldGlobalFontWeight = getGlobalFontWeight();
+ final int newFontWeightOfBgUser = 2 * Math.max(oldGlobalFontWeight, oldFontWeightOfBgUser);
+
+ try {
+ setFontWeightOfUser(newFontWeightOfBgUser, backgroundUserId);
+ final int newGlobalFontWeight = getGlobalFontWeight();
+ assertEquals(oldGlobalFontWeight, newGlobalFontWeight);
+ } finally {
+ setFontWeightOfUser(oldFontWeightOfBgUser, backgroundUserId);
+ userManager.removeUser(backgroundUserId);
+ }
+ }
+
+ @Test
+ public void testChangingFontScaleOfTheForegroundUser_shouldAffectUI()
+ throws InterruptedException {
+
+ Assume.assumeTrue(UserManager.supportsMultipleUsers());
+
+ final int currentUserId = mContext.getUserId();
+ final float oldFontScale = getFontScaleOfUser(currentUserId);
+ final float newFontScale = 1 + oldFontScale;
+
+ try {
+ setFontScaleOfUser(newFontScale, currentUserId);
+ final float globalFontScale = getGlobalFontScale();
+ assertEquals(newFontScale, globalFontScale, 0);
+ } finally {
+ setFontScaleOfUser(oldFontScale, currentUserId);
+ }
+ }
+
+ @Test
+ public void testChangingFontWeightOfTheForegroundUser_shouldAffectUI()
+ throws InterruptedException {
+
+ Assume.assumeTrue(UserManager.supportsMultipleUsers());
+
+ final int currentUserId = mContext.getUserId();
+ final int oldFontWeight = getFontWeightOfUser(currentUserId);
+ final int newFontWeight = 2 * oldFontWeight;
+
+ try {
+ setFontWeightOfUser(newFontWeight, currentUserId);
+ final int globalFontWeight = getGlobalFontWeight();
+ assertEquals(newFontWeight, globalFontWeight);
+ } finally {
+ setFontWeightOfUser(oldFontWeight, currentUserId);
+ }
+ }
+}
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index d204190d59dc..d432341a8cde 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -588,10 +588,21 @@ static bool RenameManifestPackage(const StringPiece& package_override, xml::Elem
child_el->name == "provider" || child_el->name == "receiver" ||
child_el->name == "service") {
FullyQualifyClassName(original_package, xml::kSchemaAndroid, "name", child_el);
+ continue;
}
if (child_el->name == "activity-alias") {
FullyQualifyClassName(original_package, xml::kSchemaAndroid, "targetActivity", child_el);
+ continue;
+ }
+
+ if (child_el->name == "processes") {
+ for (xml::Element* grand_child_el : child_el->GetChildElements()) {
+ if (grand_child_el->name == "process") {
+ FullyQualifyClassName(original_package, xml::kSchemaAndroid, "name", grand_child_el);
+ }
+ }
+ continue;
}
}
}