summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp5
-rw-r--r--StubLibraries.bp27
-rw-r--r--apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java5
-rw-r--r--apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java2
-rw-r--r--apex/Android.bp6
-rw-r--r--apex/blobstore/framework/java/android/app/blob/BlobInfo.java16
-rw-r--r--apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java45
-rw-r--r--apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl4
-rw-r--r--apex/blobstore/framework/java/android/app/blob/LeaseInfo.aidl (renamed from core/java/android/os/incremental/IncrementalSignature.aidl)18
-rw-r--r--apex/blobstore/framework/java/android/app/blob/LeaseInfo.java (renamed from apex/blobstore/framework/java/android/app/blob/AccessorInfo.java)53
-rw-r--r--apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java20
-rw-r--r--apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java75
-rw-r--r--apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java9
-rw-r--r--apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java6
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java2
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java55
-rw-r--r--apex/media/framework/java/android/media/MediaParser.java11
-rw-r--r--api/current.txt44
-rw-r--r--api/module-lib-current.txt2
-rwxr-xr-xapi/system-current.txt327
-rw-r--r--api/system-lint-baseline.txt6
-rw-r--r--api/test-current.txt162
-rw-r--r--cmds/statsd/src/atoms.proto88
-rw-r--r--cmds/statsd/src/logd/LogEvent.cpp87
-rw-r--r--cmds/statsd/src/logd/LogEvent.h17
-rw-r--r--cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp1204
-rw-r--r--cmds/statsd/tests/statsd_test_util.cpp121
-rw-r--r--cmds/statsd/tests/statsd_test_util.h23
-rw-r--r--core/java/android/app/ActivityManager.java5
-rw-r--r--core/java/android/app/ActivityManagerInternal.java7
-rw-r--r--core/java/android/app/AppOpsManager.java157
-rw-r--r--core/java/android/app/ApplicationPackageManager.java21
-rw-r--r--core/java/android/app/ContextImpl.java5
-rw-r--r--core/java/android/app/Notification.java8
-rw-r--r--core/java/android/app/NotificationManager.java11
-rw-r--r--core/java/android/app/StatusBarManager.java1
-rw-r--r--core/java/android/app/SystemServiceRegistry.java16
-rw-r--r--core/java/android/app/TaskInfo.java24
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java32
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl4
-rw-r--r--core/java/android/app/usage/NetworkStatsManager.java53
-rw-r--r--core/java/android/app/usage/UsageStatsManager.java41
-rw-r--r--core/java/android/companion/CompanionDeviceManager.java19
-rw-r--r--core/java/android/content/PermissionChecker.java17
-rw-r--r--core/java/android/content/integrity/AppIntegrityManager.java19
-rw-r--r--core/java/android/content/integrity/IAppIntegrityManager.aidl2
-rw-r--r--core/java/android/content/pm/ILauncherApps.aidl3
-rw-r--r--core/java/android/content/pm/LauncherApps.java82
-rw-r--r--core/java/android/content/pm/PackageInstaller.java48
-rw-r--r--core/java/android/content/pm/PackageManager.java50
-rw-r--r--core/java/android/content/pm/ShortcutInfo.java49
-rw-r--r--core/java/android/content/pm/ShortcutServiceInternal.java6
-rw-r--r--core/java/android/hardware/SensorEvent.java4
-rw-r--r--core/java/android/hardware/camera2/CameraCharacteristics.java4
-rw-r--r--core/java/android/hardware/camera2/CameraDevice.java2
-rw-r--r--core/java/android/hardware/camera2/CameraManager.java26
-rw-r--r--core/java/android/hardware/camera2/params/MandatoryStreamCombination.java2
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java5
-rw-r--r--core/java/android/net/ConnectivityDiagnosticsManager.java19
-rw-r--r--core/java/android/net/ConnectivityManager.java28
-rw-r--r--core/java/android/net/IConnectivityDiagnosticsCallback.aidl2
-rw-r--r--core/java/android/net/ITestNetworkManager.aidl2
-rw-r--r--core/java/android/net/Network.java1
-rw-r--r--core/java/android/net/NetworkAgent.java4
-rw-r--r--core/java/android/net/NetworkAgentConfig.java6
-rw-r--r--core/java/android/net/NetworkCapabilities.java1
-rw-r--r--core/java/android/net/NetworkPolicyManager.java10
-rw-r--r--core/java/android/net/NetworkProvider.java2
-rw-r--r--core/java/android/net/NetworkStats.java71
-rw-r--r--core/java/android/net/TestNetworkManager.java40
-rw-r--r--core/java/android/net/netstats/provider/AbstractNetworkStatsProvider.java70
-rw-r--r--core/java/android/net/netstats/provider/INetworkStatsProvider.aidl6
-rw-r--r--core/java/android/net/netstats/provider/INetworkStatsProviderCallback.aidl6
-rw-r--r--core/java/android/net/netstats/provider/NetworkStatsProvider.java195
-rw-r--r--core/java/android/net/netstats/provider/NetworkStatsProviderCallback.java98
-rw-r--r--core/java/android/net/netstats/provider/NetworkStatsProviderWrapper.java48
-rw-r--r--core/java/android/os/BugreportManager.java14
-rw-r--r--core/java/android/os/Environment.java94
-rw-r--r--core/java/android/os/Process.java19
-rw-r--r--core/java/android/os/VibrationEffect.java15
-rw-r--r--core/java/android/os/ZygoteProcess.java14
-rw-r--r--core/java/android/os/incremental/IncrementalNewFileParams.aidl4
-rw-r--r--core/java/android/os/incremental/IncrementalStorage.java51
-rw-r--r--core/java/android/os/incremental/V4Signature.java232
-rw-r--r--core/java/android/os/storage/StorageManagerInternal.java7
-rw-r--r--core/java/android/permission/IPermissionManager.aidl4
-rw-r--r--core/java/android/permission/Permissions.md78
-rw-r--r--core/java/android/provider/Settings.java1
-rw-r--r--core/java/android/service/autofill/InlineSuggestionRenderService.java8
-rw-r--r--core/java/android/service/autofill/InlineSuggestionRoot.java5
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java22
-rw-r--r--core/java/android/service/quickaccesswallet/GetWalletCardsCallbackImpl.java67
-rw-r--r--core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java25
-rw-r--r--core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java106
-rw-r--r--core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java2
-rw-r--r--core/java/android/service/quickaccesswallet/WalletCard.java2
-rw-r--r--core/java/android/service/textclassifier/TextClassifierService.java29
-rw-r--r--core/java/android/telephony/PhoneStateListener.java1
-rw-r--r--core/java/android/util/FeatureFlagUtils.java2
-rw-r--r--core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java18
-rw-r--r--core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java143
-rw-r--r--core/java/android/util/apk/ApkSignatureVerifier.java36
-rw-r--r--core/java/android/util/apk/SourceStampVerifier.java81
-rw-r--r--core/java/android/view/Display.java9
-rw-r--r--core/java/android/view/ImeFocusController.java10
-rw-r--r--core/java/android/view/InsetsController.java62
-rw-r--r--core/java/android/view/PendingInsetsController.java30
-rw-r--r--core/java/android/view/SurfaceControl.java2
-rw-r--r--core/java/android/view/SurfaceControlViewHost.java16
-rw-r--r--core/java/android/view/WindowContainerTransaction.java26
-rw-r--r--core/java/android/view/WindowInsetsAnimationControlListener.java1
-rw-r--r--core/java/android/view/WindowInsetsController.java9
-rw-r--r--core/java/android/view/WindowManager.java37
-rw-r--r--core/java/android/view/inline/InlinePresentationSpec.java16
-rw-r--r--core/java/android/view/inputmethod/InlineSuggestionsRequest.java10
-rw-r--r--core/java/android/view/textclassifier/ActionsModelParamsSupplier.java210
-rw-r--r--core/java/android/view/textclassifier/ActionsSuggestionsHelper.java234
-rw-r--r--core/java/android/view/textclassifier/ExtrasUtils.java202
-rw-r--r--core/java/android/view/textclassifier/GenerateLinksLogger.java160
-rw-r--r--core/java/android/view/textclassifier/Log.java2
-rw-r--r--core/java/android/view/textclassifier/ModelFileManager.java301
-rw-r--r--core/java/android/view/textclassifier/SelectionEvent.java4
-rw-r--r--core/java/android/view/textclassifier/SelectionSessionLogger.java261
-rw-r--r--core/java/android/view/textclassifier/SystemTextClassifier.java2
-rw-r--r--core/java/android/view/textclassifier/TextClassification.java54
-rw-r--r--core/java/android/view/textclassifier/TextClassificationConstants.java279
-rw-r--r--core/java/android/view/textclassifier/TextClassificationContext.java7
-rw-r--r--core/java/android/view/textclassifier/TextClassificationManager.java101
-rw-r--r--core/java/android/view/textclassifier/TextClassifier.java21
-rw-r--r--core/java/android/view/textclassifier/TextClassifierEventTronLogger.java187
-rw-r--r--core/java/android/view/textclassifier/TextClassifierImpl.java911
-rw-r--r--core/java/android/view/textclassifier/intent/ClassificationIntentFactory.java58
-rw-r--r--core/java/android/view/textclassifier/intent/LabeledIntent.java219
-rw-r--r--core/java/android/view/textclassifier/intent/LegacyClassificationIntentFactory.java280
-rw-r--r--core/java/android/view/textclassifier/intent/TemplateClassificationIntentFactory.java81
-rw-r--r--core/java/android/view/textclassifier/intent/TemplateIntentFactory.java156
-rw-r--r--core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java599
-rw-r--r--core/java/android/widget/Editor.java16
-rw-r--r--core/java/android/widget/RemoteViews.java6
-rw-r--r--core/java/android/widget/SelectionActionModeHelper.java3
-rw-r--r--core/java/android/widget/WidgetFlags.java19
-rw-r--r--core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java11
-rw-r--r--core/java/com/android/internal/app/AbstractResolverComparator.java6
-rw-r--r--core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java11
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java39
-rw-r--r--core/java/com/android/internal/app/ChooserListAdapter.java160
-rw-r--r--core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java4
-rw-r--r--core/java/com/android/internal/app/IntentForwarderActivity.java9
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java29
-rw-r--r--core/java/com/android/internal/app/ResolverListAdapter.java8
-rw-r--r--core/java/com/android/internal/app/ResolverListController.java8
-rw-r--r--core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java4
-rw-r--r--core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java10
-rw-r--r--core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java11
-rw-r--r--core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java11
-rw-r--r--core/java/com/android/internal/os/Zygote.java20
-rw-r--r--core/java/com/android/internal/os/ZygoteArguments.java7
-rw-r--r--core/java/com/android/internal/os/ZygoteConnection.java2
-rw-r--r--core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java78
-rwxr-xr-xcore/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java5
-rw-r--r--core/java/com/android/internal/widget/ConversationLayout.java43
-rw-r--r--core/java/com/android/internal/widget/MessagingGroup.java26
-rw-r--r--core/java/com/android/internal/widget/MessagingLayout.java1
-rw-r--r--core/jni/android_util_Process.cpp17
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp40
-rw-r--r--core/proto/android/app/enums.proto105
-rw-r--r--core/proto/android/os/incident.proto3
-rw-r--r--core/proto/android/stats/devicepolicy/device_policy_enums.proto2
-rw-r--r--core/proto/android/util/quotatracker.proto90
-rw-r--r--core/res/AndroidManifest.xml10
-rw-r--r--core/res/res/drawable/tab_indicator_resolver.xml2
-rw-r--r--core/res/res/layout/notification_template_material_conversation.xml7
-rw-r--r--core/res/res/layout/notification_template_messaging_group.xml3
-rw-r--r--core/res/res/layout/resolver_empty_states.xml8
-rw-r--r--core/res/res/values-night/colors.xml1
-rw-r--r--core/res/res/values/colors.xml2
-rw-r--r--core/res/res/values/config.xml6
-rw-r--r--core/res/res/values/dimens.xml7
-rw-r--r--core/res/res/values/public.xml8
-rw-r--r--core/res/res/values/strings.xml2
-rw-r--r--core/res/res/values/symbols.xml8
-rw-r--r--core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java2
-rw-r--r--core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java4
-rw-r--r--core/tests/coretests/src/android/util/apk/SourceStampVerifierTest.java44
-rw-r--r--core/tests/coretests/src/android/view/InsetsControllerTest.java33
-rw-r--r--core/tests/coretests/src/android/view/PendingInsetsControllerTest.java21
-rw-r--r--core/tests/coretests/src/android/view/textclassifier/ActionsModelParamsSupplierTest.java95
-rw-r--r--core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java331
-rw-r--r--core/tests/coretests/src/android/view/textclassifier/ModelFileManagerTest.java350
-rw-r--r--core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java68
-rw-r--r--core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java52
-rw-r--r--core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java719
-rw-r--r--core/tests/coretests/src/android/view/textclassifier/intent/LabeledIntentTest.java200
-rw-r--r--core/tests/coretests/src/android/view/textclassifier/intent/LegacyIntentClassificationFactoryTest.java122
-rw-r--r--core/tests/coretests/src/android/view/textclassifier/intent/TemplateClassificationIntentFactoryTest.java243
-rw-r--r--core/tests/coretests/src/android/view/textclassifier/intent/TemplateIntentFactoryTest.java270
-rw-r--r--core/tests/coretests/src/android/view/textclassifier/logging/GenerateLinksLoggerTest.java116
-rw-r--r--core/tests/coretests/src/android/view/textclassifier/logging/SmartSelectionEventTrackerTest.java43
-rw-r--r--core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java107
-rw-r--r--core/tests/coretests/src/com/android/internal/app/AbstractResolverComparatorTest.java5
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java9
-rw-r--r--core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java30
-rw-r--r--data/etc/com.android.launcher3.xml1
-rw-r--r--data/etc/privapp-permissions-platform.xml4
-rw-r--r--keystore/java/android/security/keystore/KeymasterUtils.java103
-rw-r--r--libs/androidfw/LocaleDataTables.cpp2717
-rw-r--r--media/java/android/media/MediaCas.java19
-rw-r--r--media/java/android/media/MediaRoute2ProviderService.java4
-rw-r--r--media/java/android/media/MediaRouter2.java21
-rw-r--r--media/java/android/media/MediaRouter2Manager.java6
-rw-r--r--media/java/android/media/audiofx/Visualizer.java180
-rw-r--r--media/java/android/media/tv/tuner/Descrambler.java2
-rw-r--r--media/java/android/media/tv/tuner/Lnb.java2
-rw-r--r--media/java/android/media/tv/tuner/Tuner.java198
-rw-r--r--media/java/android/media/tv/tuner/TunerConstants.java122
-rw-r--r--media/java/android/media/tv/tuner/TunerUtils.java14
-rw-r--r--media/java/android/media/tv/tuner/dvr/DvrPlayback.java2
-rw-r--r--media/java/android/media/tv/tuner/dvr/DvrRecorder.java2
-rw-r--r--media/java/android/media/tv/tuner/filter/Filter.java2
-rw-r--r--media/java/android/media/tv/tuner/filter/TimeFilter.java27
-rw-r--r--media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java19
-rw-r--r--media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java19
-rw-r--r--media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java19
-rw-r--r--media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java39
-rw-r--r--media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java19
-rw-r--r--media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java19
-rw-r--r--media/java/android/media/tv/tuner/frontend/FrontendSettings.java25
-rw-r--r--media/java/android/media/tv/tuner/frontend/FrontendStatus.java2
-rw-r--r--media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java19
-rw-r--r--media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java19
-rw-r--r--media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java19
-rw-r--r--media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java26
-rw-r--r--media/jni/android_media_tv_Tuner.cpp19
-rw-r--r--media/jni/audioeffect/Visualizer.cpp3
-rw-r--r--media/jni/audioeffect/android_media_Visualizer.cpp6
-rw-r--r--media/tests/MediaRouter/AndroidManifest.xml2
-rw-r--r--media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java30
-rw-r--r--media/tests/MediaRouter/src/com/android/mediaroutertest/StubMediaRoute2ProviderService.java (renamed from media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java)6
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java6
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java6
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarTest.java50
-rw-r--r--packages/SettingsLib/res/values/strings.xml2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java8
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java33
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java17
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java86
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java19
-rw-r--r--packages/SystemUI/res/drawable/ic_bubble_overflow_button.xml15
-rw-r--r--packages/SystemUI/res/layout/global_screenshot.xml6
-rw-r--r--packages/SystemUI/res/values/config.xml8
-rw-r--r--packages/SystemUI/res/values/strings.xml4
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java8
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestReceiver.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/ScreenDecorations.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistManager.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java157
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java45
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java57
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java81
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java75
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java71
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSHost.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanel.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java113
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java51
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt51
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java132
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewBarn.kt63
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewManager.kt200
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java289
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SimpleNotificationListContainer.kt43
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java171
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java41
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/FakePipelineConsumer.java85
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollection.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifEvent.kt93
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt41
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt338
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java134
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/DungeonRow.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListItem.java66
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java228
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java72
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java84
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java72
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java170
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt117
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java111
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinatorTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java191
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java5
-rw-r--r--packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java41
-rw-r--r--packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java2
-rw-r--r--packages/Tethering/res/values/config.xml45
-rw-r--r--packages/Tethering/res/values/overlayable.xml40
-rw-r--r--packages/Tethering/res/values/strings.xml12
-rw-r--r--packages/Tethering/src/android/net/ip/IpServer.java7
-rw-r--r--packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java47
-rw-r--r--packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java120
-rw-r--r--packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java9
-rw-r--r--packages/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java198
-rw-r--r--packages/Tethering/tests/unit/src/android/net/TetheredClientTest.kt13
-rw-r--r--packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/ConnectedClientsTrackerTest.kt17
-rw-r--r--packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java86
-rw-r--r--packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java51
-rw-r--r--packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/config.xml3
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java2
-rw-r--r--services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java36
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java2
-rw-r--r--services/core/Android.bp3
-rw-r--r--services/core/java/android/content/pm/PackageManagerInternal.java5
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java17
-rw-r--r--services/core/java/com/android/server/DynamicSystemService.java59
-rw-r--r--services/core/java/com/android/server/PackageWatchdog.java4
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java166
-rw-r--r--services/core/java/com/android/server/TestNetworkService.java13
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java40
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java37
-rw-r--r--services/core/java/com/android/server/am/BugReportHandlerUtil.java32
-rw-r--r--services/core/java/com/android/server/am/CoreSettingsObserver.java4
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java12
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java51
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java1
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java80
-rw-r--r--services/core/java/com/android/server/hdmi/TEST_MAPPING34
-rw-r--r--services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java30
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java13
-rw-r--r--services/core/java/com/android/server/media/BluetoothRouteProvider.java2
-rw-r--r--services/core/java/com/android/server/media/SystemMediaRoute2Provider.java82
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java6
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java2
-rw-r--r--services/core/java/com/android/server/net/NetworkStatsFactory.java6
-rw-r--r--services/core/java/com/android/server/net/NetworkStatsService.java48
-rw-r--r--services/core/java/com/android/server/notification/BubbleExtractor.java219
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java453
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecordLogger.java70
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java16
-rw-r--r--services/core/java/com/android/server/notification/ShortcutHelper.java196
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java4
-rw-r--r--services/core/java/com/android/server/os/BugreportManagerServiceImpl.java8
-rw-r--r--services/core/java/com/android/server/pm/ApexManager.java24
-rw-r--r--services/core/java/com/android/server/pm/AppsFilter.java33
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java12
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java94
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java43
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java10
-rw-r--r--services/core/java/com/android/server/pm/ShortcutBitmapSaver.java4
-rw-r--r--services/core/java/com/android/server/pm/ShortcutPackage.java25
-rw-r--r--services/core/java/com/android/server/pm/ShortcutParser.java1
-rw-r--r--services/core/java/com/android/server/pm/ShortcutService.java85
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java49
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java84
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java4
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java6
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java8
-rw-r--r--services/core/java/com/android/server/power/batterysaver/BatterySaverController.java34
-rw-r--r--services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java12
-rw-r--r--services/core/java/com/android/server/security/TEST_MAPPING8
-rw-r--r--services/core/java/com/android/server/security/VerityUtils.java16
-rw-r--r--services/core/java/com/android/server/stats/pull/StatsPullAtomService.java44
-rw-r--r--services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java4
-rw-r--r--services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java126
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java86
-rw-r--r--services/core/java/com/android/server/wm/ActivityStack.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityStackSupervisor.java5
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java46
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java61
-rw-r--r--services/core/java/com/android/server/wm/RecentTasks.java7
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java4
-rw-r--r--services/core/java/com/android/server/wm/SurfaceAnimator.java15
-rw-r--r--services/core/java/com/android/server/wm/SurfaceFreezer.java8
-rw-r--r--services/core/java/com/android/server/wm/Task.java71
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java7
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotController.java14
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotPersister.java14
-rw-r--r--services/core/java/com/android/server/wm/WindowAnimator.java154
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java21
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java15
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java4
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java47
-rw-r--r--services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp59
-rw-r--r--services/core/jni/com_android_server_power_PowerManagerService.cpp161
-rw-r--r--services/core/jni/com_android_server_security_VerityUtils.cpp39
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java45
-rw-r--r--services/incremental/BinderIncrementalService.cpp10
-rw-r--r--services/incremental/IncrementalService.cpp2
-rw-r--r--services/robotests/Android.bp2
-rw-r--r--services/robotests/backup/Android.bp2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java3
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/FakeSettings.java15
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java18
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java27
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java22
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java131
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java182
-rw-r--r--services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java119
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java11
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java102
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/BubbleCheckerTest.java257
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java31
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java13
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java224
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java24
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java141
-rw-r--r--services/tests/wmtests/Android.bp1
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java34
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java57
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java24
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java35
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java76
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java12
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java29
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java13
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsDatabase.java4
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java5
-rw-r--r--services/usb/java/com/android/server/usb/UsbDeviceManager.java15
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java4
-rw-r--r--telephony/common/com/android/internal/telephony/CarrierAppUtils.java3
-rw-r--r--telephony/common/com/android/internal/telephony/GsmAlphabet.java10
-rw-r--r--telephony/common/com/google/android/mms/util/SqliteWrapper.java3
-rw-r--r--telephony/java/android/telephony/Annotation.java42
-rw-r--r--telephony/java/android/telephony/CallForwardingInfo.java57
-rw-r--r--telephony/java/android/telephony/ImsManager.java4
-rw-r--r--telephony/java/android/telephony/NetworkRegistrationInfo.java10
-rw-r--r--telephony/java/android/telephony/PreciseDataConnectionState.java1
-rw-r--r--telephony/java/android/telephony/ServiceState.java1
-rw-r--r--telephony/java/android/telephony/SmsManager.java10
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java68
-rw-r--r--telephony/java/android/telephony/ims/ImsMmTelManager.java5
-rw-r--r--telephony/java/android/telephony/ims/ProvisioningManager.java72
-rw-r--r--telephony/java/android/telephony/ims/RcsContactUceCapability.java4
-rw-r--r--telephony/java/android/telephony/ims/RegistrationManager.java6
-rw-r--r--telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java7
-rw-r--r--telephony/java/android/telephony/ims/stub/ImsUtImplBase.java12
-rw-r--r--tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java77
-rw-r--r--tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java78
-rw-r--r--tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java14
-rw-r--r--tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostTest.java2
-rw-r--r--tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java6
-rw-r--r--tests/net/AndroidManifest.xml1
-rw-r--r--tests/net/common/java/android/net/LinkAddressTest.java6
-rw-r--r--tests/net/common/java/android/net/NetworkAgentConfigTest.kt26
-rw-r--r--tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java6
-rw-r--r--tests/net/java/android/net/NetworkStatsTest.java278
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java67
-rw-r--r--tests/net/java/com/android/server/net/NetworkStatsObserversTest.java26
-rw-r--r--tests/net/java/com/android/server/net/NetworkStatsServiceTest.java157
-rw-r--r--wifi/Android.bp17
-rw-r--r--wifi/java/android/net/wifi/ScanResult.java46
-rwxr-xr-xwifi/java/android/net/wifi/WifiMigration.java164
-rw-r--r--wifi/java/android/net/wifi/WifiNetworkSuggestion.java24
-rw-r--r--wifi/java/android/net/wifi/hotspot2/pps/Credential.java13
-rw-r--r--wifi/tests/src/android/net/wifi/ScanResultTest.java6
-rw-r--r--wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java22
-rw-r--r--wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java65
548 files changed, 14299 insertions, 14529 deletions
diff --git a/Android.bp b/Android.bp
index 3c512563f31c..da5d62429035 100644
--- a/Android.bp
+++ b/Android.bp
@@ -791,10 +791,6 @@ java_library {
"libphonenumber-platform",
"tagsoup",
"rappor",
- "libtextclassifier-java",
- ],
- required: [
- "libtextclassifier",
],
dxflags: ["--core-library"],
}
@@ -986,7 +982,6 @@ filegroup {
srcs: [
"core/java/android/os/incremental/IIncrementalService.aidl",
"core/java/android/os/incremental/IncrementalNewFileParams.aidl",
- "core/java/android/os/incremental/IncrementalSignature.aidl",
],
path: "core/java",
}
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 7f23df74d2da..da9f1654d60f 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -41,7 +41,7 @@ packages_to_document = [
]
stubs_defaults {
- name: "metalava-non-updatable-api-stubs-default",
+ name: "metalava-base-api-stubs-default",
srcs: [
":framework-non-updatable-sources",
"core/java/**/*.logtags",
@@ -70,12 +70,18 @@ stubs_defaults {
}
stubs_defaults {
- name: "metalava-api-stubs-default",
- defaults: ["metalava-non-updatable-api-stubs-default"],
+ name: "metalava-full-api-stubs-default",
+ defaults: ["metalava-base-api-stubs-default"],
srcs: [":framework-updatable-sources"],
sdk_version: "core_platform",
}
+stubs_defaults {
+ name: "metalava-non-updatable-api-stubs-default",
+ defaults: ["metalava-base-api-stubs-default"],
+ sdk_version: "system_current",
+}
+
/////////////////////////////////////////////////////////////////////
// *-api-stubs-docs modules providing source files for the stub libraries
/////////////////////////////////////////////////////////////////////
@@ -85,7 +91,7 @@ stubs_defaults {
// modules
droidstubs {
name: "api-stubs-docs",
- defaults: ["metalava-api-stubs-default"],
+ defaults: ["metalava-full-api-stubs-default"],
api_filename: "public_api.txt",
private_api_filename: "private.txt",
removed_api_filename: "removed.txt",
@@ -124,7 +130,7 @@ module_libs = " " +
droidstubs {
name: "system-api-stubs-docs",
- defaults: ["metalava-api-stubs-default"],
+ defaults: ["metalava-full-api-stubs-default"],
api_tag_name: "SYSTEM",
api_filename: "system-api.txt",
private_api_filename: "system-private.txt",
@@ -155,7 +161,7 @@ droidstubs {
droidstubs {
name: "test-api-stubs-docs",
- defaults: ["metalava-api-stubs-default"],
+ defaults: ["metalava-full-api-stubs-default"],
api_tag_name: "TEST",
api_filename: "test-api.txt",
removed_api_filename: "test-removed.txt",
@@ -188,7 +194,7 @@ droidstubs {
droidstubs {
name: "module-lib-api",
- defaults: ["metalava-api-stubs-default"],
+ defaults: ["metalava-full-api-stubs-default"],
arg_files: ["core/res/AndroidManifest.xml"],
args: metalava_framework_docs_args + module_libs,
check_api: {
@@ -216,7 +222,7 @@ droidstubs {
droidstubs {
name: "module-lib-api-stubs-docs",
- defaults: ["metalava-api-stubs-default"],
+ defaults: ["metalava-non-updatable-api-stubs-default"],
arg_files: ["core/res/AndroidManifest.xml"],
args: metalava_framework_docs_args + priv_apps + module_libs,
}
@@ -266,6 +272,7 @@ java_library_static {
name: "android_module_lib_stubs_current",
srcs: [ ":module-lib-api-stubs-docs" ],
defaults: ["framework-stubs-default"],
+ libs: ["android_system_stubs_current"],
}
/////////////////////////////////////////////////////////////////////
@@ -317,7 +324,7 @@ java_library_static {
droidstubs {
name: "hiddenapi-lists-docs",
- defaults: ["metalava-api-stubs-default"],
+ defaults: ["metalava-full-api-stubs-default"],
arg_files: [
"core/res/AndroidManifest.xml",
],
@@ -332,7 +339,7 @@ droidstubs {
droidstubs {
name: "hiddenapi-mappings",
- defaults: ["metalava-api-stubs-default"],
+ defaults: ["metalava-full-api-stubs-default"],
srcs: [
":opt-telephony-common-srcs",
],
diff --git a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
index 02df5e2b6c31..23f025b0a759 100644
--- a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
+++ b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
@@ -121,8 +121,9 @@ public class BlobStorePerfTests {
}
private DummyBlobData prepareDataBlob(int fileSizeInMb) throws Exception {
- final DummyBlobData blobData = new DummyBlobData(mContext,
- fileSizeInMb * 1024 * 1024 /* bytes */);
+ final DummyBlobData blobData = new DummyBlobData.Builder(mContext)
+ .setFileSize(fileSizeInMb * 1024 * 1024 /* bytes */)
+ .build();
blobData.prepare();
return blobData;
}
diff --git a/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java b/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java
index f61ea8549236..46250d74a4e3 100644
--- a/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java
+++ b/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java
@@ -77,7 +77,6 @@ public class TextClassificationManagerPerfTest {
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
textClassificationManager.getTextClassifier();
- textClassificationManager.invalidateForTesting();
}
}
@@ -90,7 +89,6 @@ public class TextClassificationManagerPerfTest {
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
textClassificationManager.getTextClassifier();
- textClassificationManager.invalidateForTesting();
}
}
diff --git a/apex/Android.bp b/apex/Android.bp
index 151091137c9c..39137fb908d2 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -49,7 +49,7 @@ stubs_defaults {
stubs_defaults {
name: "framework-module-stubs-defaults-systemapi",
args: mainline_stubs_args + priv_apps,
- srcs: [":framework-annotations"],
+ libs: ["framework-annotations-lib"],
installable: false,
sdk_version: "system_current",
}
@@ -62,7 +62,7 @@ stubs_defaults {
stubs_defaults {
name: "framework-module-api-defaults-module_libs_api",
args: mainline_stubs_args + module_libs,
- srcs: [":framework-annotations"],
+ libs: ["framework-annotations-lib"],
installable: false,
sdk_version: "module_current",
}
@@ -70,7 +70,7 @@ stubs_defaults {
stubs_defaults {
name: "framework-module-stubs-defaults-module_libs_api",
args: mainline_stubs_args + module_libs + priv_apps,
- srcs: [":framework-annotations"],
+ libs: ["framework-annotations-lib"],
installable: false,
sdk_version: "module_current",
}
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobInfo.java b/apex/blobstore/framework/java/android/app/blob/BlobInfo.java
index 9746dd023002..80062d5d245f 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobInfo.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobInfo.java
@@ -32,21 +32,21 @@ public final class BlobInfo implements Parcelable {
private final long mId;
private final long mExpiryTimeMs;
private final CharSequence mLabel;
- private final List<AccessorInfo> mAccessors;
+ private final List<LeaseInfo> mLeaseInfos;
public BlobInfo(long id, long expiryTimeMs, CharSequence label,
- List<AccessorInfo> accessors) {
+ List<LeaseInfo> leaseInfos) {
mId = id;
mExpiryTimeMs = expiryTimeMs;
mLabel = label;
- mAccessors = accessors;
+ mLeaseInfos = leaseInfos;
}
private BlobInfo(Parcel in) {
mId = in.readLong();
mExpiryTimeMs = in.readLong();
mLabel = in.readCharSequence();
- mAccessors = in.readArrayList(null /* classloader */);
+ mLeaseInfos = in.readArrayList(null /* classloader */);
}
public long getId() {
@@ -61,8 +61,8 @@ public final class BlobInfo implements Parcelable {
return mLabel;
}
- public List<AccessorInfo> getAccessors() {
- return Collections.unmodifiableList(mAccessors);
+ public List<LeaseInfo> getLeases() {
+ return Collections.unmodifiableList(mLeaseInfos);
}
@Override
@@ -70,7 +70,7 @@ public final class BlobInfo implements Parcelable {
dest.writeLong(mId);
dest.writeLong(mExpiryTimeMs);
dest.writeCharSequence(mLabel);
- dest.writeList(mAccessors);
+ dest.writeList(mLeaseInfos);
}
@Override
@@ -83,7 +83,7 @@ public final class BlobInfo implements Parcelable {
+ "id: " + mId + ","
+ "expiryMs: " + mExpiryTimeMs + ","
+ "label: " + mLabel + ","
- + "accessors: " + AccessorInfo.toShortString(mAccessors) + ","
+ + "leases: " + LeaseInfo.toShortString(mLeaseInfos) + ","
+ "}";
}
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
index 814ab6dbd7fd..c339351759cd 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
@@ -21,6 +21,7 @@ import android.annotation.CurrentTimeMillisLong;
import android.annotation.IdRes;
import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.content.Context;
@@ -522,6 +523,50 @@ public class BlobStoreManager {
}
/**
+ * Return the {@link BlobHandle BlobHandles} corresponding to the data blobs that
+ * the calling app has acquired a lease on using {@link #acquireLease(BlobHandle, int)} or
+ * one of it's other variants.
+ *
+ * @hide
+ */
+ @TestApi
+ @NonNull
+ public List<BlobHandle> getLeasedBlobs() throws IOException {
+ try {
+ return mService.getLeasedBlobs(mContext.getOpPackageName());
+ } catch (ParcelableException e) {
+ e.maybeRethrow(IOException.class);
+ throw new RuntimeException(e);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Return {@link LeaseInfo} representing a lease acquired using
+ * {@link #acquireLease(BlobHandle, int)} or one of it's other variants,
+ * or {@code null} if there is no lease acquired.
+ *
+ * @throws SecurityException when the blob represented by the {@code blobHandle} does not
+ * exist or the caller does not have access to it.
+ * @throws IllegalArgumentException when {@code blobHandle} is invalid.
+ *
+ * @hide
+ */
+ @TestApi
+ @Nullable
+ public LeaseInfo getLeaseInfo(@NonNull BlobHandle blobHandle) throws IOException {
+ try {
+ return mService.getLeaseInfo(blobHandle, mContext.getOpPackageName());
+ } catch (ParcelableException e) {
+ e.maybeRethrow(IOException.class);
+ throw new RuntimeException(e);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Represents an ongoing session of a blob's contribution to the blob store managed by the
* system.
*
diff --git a/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl b/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl
index e78381359b41..20c15ab57496 100644
--- a/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl
+++ b/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl
@@ -18,6 +18,7 @@ package android.app.blob;
import android.app.blob.BlobHandle;
import android.app.blob.BlobInfo;
import android.app.blob.IBlobStoreSession;
+import android.app.blob.LeaseInfo;
import android.os.RemoteCallback;
/** {@hide} */
@@ -35,4 +36,7 @@ interface IBlobStoreManager {
List<BlobInfo> queryBlobsForUser(int userId);
void deleteBlob(long blobId);
+
+ List<BlobHandle> getLeasedBlobs(in String packageName);
+ LeaseInfo getLeaseInfo(in BlobHandle blobHandle, in String packageName);
} \ No newline at end of file
diff --git a/core/java/android/os/incremental/IncrementalSignature.aidl b/apex/blobstore/framework/java/android/app/blob/LeaseInfo.aidl
index 729e8e5556a8..908885731bb1 100644
--- a/core/java/android/os/incremental/IncrementalSignature.aidl
+++ b/apex/blobstore/framework/java/android/app/blob/LeaseInfo.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,19 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-package android.os.incremental;
+package android.app.blob;
/** {@hide} */
-parcelable IncrementalSignature {
- /*
- * Stable AIDL doesn't support constants, but here's the possible values
- * const int HASH_ALGO_NONE = 0;
- * const int HASH_ALGO_SHA256 = 1;
- */
-
- int hashAlgorithm = 0;
- byte[] rootHash;
- byte[] additionalData;
- byte[] signature;
-}
+parcelable LeaseInfo; \ No newline at end of file
diff --git a/apex/blobstore/framework/java/android/app/blob/AccessorInfo.java b/apex/blobstore/framework/java/android/app/blob/LeaseInfo.java
index 3725ad4a6c09..fef50c9e8dba 100644
--- a/apex/blobstore/framework/java/android/app/blob/AccessorInfo.java
+++ b/apex/blobstore/framework/java/android/app/blob/LeaseInfo.java
@@ -16,50 +16,61 @@
package android.app.blob;
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.IdRes;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.List;
/**
- * Class to provide information about an accessor of a shared blob.
+ * Class to provide information about a lease (acquired using
+ * {@link BlobStoreManager#acquireLease(BlobHandle, int)} or one of it's variants)
+ * for a shared blob.
*
* @hide
*/
-public final class AccessorInfo implements Parcelable {
+@TestApi
+public final class LeaseInfo implements Parcelable {
private final String mPackageName;
- private final long mExpiryTimeMs;
+ private final long mExpiryTimeMillis;
private final int mDescriptionResId;
private final CharSequence mDescription;
- public AccessorInfo(String packageName, long expiryTimeMs,
- int descriptionResId, CharSequence description) {
+ public LeaseInfo(@NonNull String packageName, @CurrentTimeMillisLong long expiryTimeMs,
+ @IdRes int descriptionResId, @Nullable CharSequence description) {
mPackageName = packageName;
- mExpiryTimeMs = expiryTimeMs;
+ mExpiryTimeMillis = expiryTimeMs;
mDescriptionResId = descriptionResId;
mDescription = description;
}
- private AccessorInfo(Parcel in) {
+ private LeaseInfo(Parcel in) {
mPackageName = in.readString();
- mExpiryTimeMs = in.readLong();
+ mExpiryTimeMillis = in.readLong();
mDescriptionResId = in.readInt();
mDescription = in.readCharSequence();
}
+ @NonNull
public String getPackageName() {
return mPackageName;
}
- public long getExpiryTimeMs() {
- return mExpiryTimeMs;
+ @CurrentTimeMillisLong
+ public long getExpiryTimeMillis() {
+ return mExpiryTimeMillis;
}
+ @IdRes
public int getDescriptionResId() {
return mDescriptionResId;
}
+ @Nullable
public CharSequence getDescription() {
return mDescription;
}
@@ -67,16 +78,16 @@ public final class AccessorInfo implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(mPackageName);
- dest.writeLong(mExpiryTimeMs);
+ dest.writeLong(mExpiryTimeMillis);
dest.writeInt(mDescriptionResId);
dest.writeCharSequence(mDescription);
}
@Override
public String toString() {
- return "AccessorInfo {"
+ return "LeaseInfo {"
+ "package: " + mPackageName + ","
- + "expiryMs: " + mExpiryTimeMs + ","
+ + "expiryMs: " + mExpiryTimeMillis + ","
+ "descriptionResId: " + mDescriptionResId + ","
+ "description: " + mDescription + ","
+ "}";
@@ -86,11 +97,11 @@ public final class AccessorInfo implements Parcelable {
return mPackageName;
}
- public static String toShortString(List<AccessorInfo> accessors) {
+ static String toShortString(List<LeaseInfo> leaseInfos) {
final StringBuilder sb = new StringBuilder();
sb.append("[");
- for (int i = 0, size = accessors.size(); i < size; ++i) {
- sb.append(accessors.get(i).toShortString());
+ for (int i = 0, size = leaseInfos.size(); i < size; ++i) {
+ sb.append(leaseInfos.get(i).toShortString());
sb.append(",");
}
sb.append("]");
@@ -103,17 +114,17 @@ public final class AccessorInfo implements Parcelable {
}
@NonNull
- public static final Creator<AccessorInfo> CREATOR = new Creator<AccessorInfo>() {
+ public static final Creator<LeaseInfo> CREATOR = new Creator<LeaseInfo>() {
@Override
@NonNull
- public AccessorInfo createFromParcel(Parcel source) {
- return new AccessorInfo(source);
+ public LeaseInfo createFromParcel(Parcel source) {
+ return new LeaseInfo(source);
}
@Override
@NonNull
- public AccessorInfo[] newArray(int size) {
- return new AccessorInfo[size];
+ public LeaseInfo[] newArray(int size) {
+ return new LeaseInfo[size];
}
};
}
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
index 970766d2c8a6..8b640ca75698 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
@@ -38,6 +38,7 @@ import static com.android.server.blob.BlobStoreUtils.getPackageResources;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.blob.BlobHandle;
+import android.app.blob.LeaseInfo;
import android.content.Context;
import android.content.res.ResourceId;
import android.content.res.Resources;
@@ -281,6 +282,25 @@ class BlobMetadata {
return false;
}
+ @Nullable
+ LeaseInfo getLeaseInfo(@NonNull String packageName, int uid) {
+ synchronized (mMetadataLock) {
+ for (int i = 0, size = mLeasees.size(); i < size; ++i) {
+ final Leasee leasee = mLeasees.valueAt(i);
+ if (leasee.uid == uid && leasee.packageName.equals(packageName)) {
+ final int descriptionResId = leasee.descriptionResEntryName == null
+ ? Resources.ID_NULL
+ : BlobStoreUtils.getDescriptionResourceId(
+ mContext, leasee.descriptionResEntryName, leasee.packageName,
+ UserHandle.getUserId(leasee.uid));
+ return new LeaseInfo(packageName, leasee.expiryTimeMillis,
+ descriptionResId, leasee.description);
+ }
+ }
+ }
+ return null;
+ }
+
void forEachLeasee(Consumer<Leasee> consumer) {
mLeasees.forEach(consumer);
}
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
index 53a97cefa59b..f4b8f0f39e85 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -45,11 +45,11 @@ import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
-import android.app.blob.AccessorInfo;
import android.app.blob.BlobHandle;
import android.app.blob.BlobInfo;
import android.app.blob.IBlobStoreManager;
import android.app.blob.IBlobStoreSession;
+import android.app.blob.LeaseInfo;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -454,17 +454,17 @@ public class BlobStoreManagerService extends SystemService {
return packageResources;
};
getUserBlobsLocked(userId).forEach((blobHandle, blobMetadata) -> {
- final ArrayList<AccessorInfo> accessorInfos = new ArrayList<>();
+ final ArrayList<LeaseInfo> leaseInfos = new ArrayList<>();
blobMetadata.forEachLeasee(leasee -> {
final int descriptionResId = leasee.descriptionResEntryName == null
? Resources.ID_NULL
: getDescriptionResourceId(resourcesGetter.apply(leasee.packageName),
leasee.descriptionResEntryName, leasee.packageName);
- accessorInfos.add(new AccessorInfo(leasee.packageName, leasee.expiryTimeMillis,
+ leaseInfos.add(new LeaseInfo(leasee.packageName, leasee.expiryTimeMillis,
descriptionResId, leasee.description));
});
blobInfos.add(new BlobInfo(blobMetadata.getBlobId(),
- blobHandle.getExpiryTimeMillis(), blobHandle.getLabel(), accessorInfos));
+ blobHandle.getExpiryTimeMillis(), blobHandle.getLabel(), leaseInfos));
});
}
return blobInfos;
@@ -482,6 +482,31 @@ public class BlobStoreManagerService extends SystemService {
}
}
+ private List<BlobHandle> getLeasedBlobsInternal(int callingUid,
+ @NonNull String callingPackage) {
+ final ArrayList<BlobHandle> leasedBlobs = new ArrayList<>();
+ forEachBlobInUser(blobMetadata -> {
+ if (blobMetadata.isALeasee(callingPackage, callingUid)) {
+ leasedBlobs.add(blobMetadata.getBlobHandle());
+ }
+ }, UserHandle.getUserId(callingUid));
+ return leasedBlobs;
+ }
+
+ private LeaseInfo getLeaseInfoInternal(BlobHandle blobHandle,
+ int callingUid, @NonNull String callingPackage) {
+ synchronized (mBlobsLock) {
+ final BlobMetadata blobMetadata = getUserBlobsLocked(UserHandle.getUserId(callingUid))
+ .get(blobHandle);
+ if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller(
+ callingPackage, callingUid)) {
+ throw new SecurityException("Caller not allowed to access " + blobHandle
+ + "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
+ }
+ return blobMetadata.getLeaseInfo(callingPackage, callingUid);
+ }
+ }
+
private void verifyCallingPackage(int callingUid, String callingPackage) {
if (mPackageManagerInternal.getPackageUid(
callingPackage, 0, UserHandle.getUserId(callingUid)) != callingUid) {
@@ -1267,6 +1292,12 @@ public class BlobStoreManagerService extends SystemService {
final int callingUid = Binder.getCallingUid();
verifyCallingPackage(callingUid, packageName);
+ if (Process.isIsolated(callingUid) || mPackageManagerInternal.isInstantApp(
+ packageName, UserHandle.getUserId(callingUid))) {
+ throw new SecurityException("Caller not allowed to open blob; "
+ + "callingUid=" + callingUid + ", callingPackage=" + packageName);
+ }
+
try {
acquireLeaseInternal(blobHandle, descriptionResId, description,
leaseExpiryTimeMillis, callingUid, packageName);
@@ -1284,6 +1315,12 @@ public class BlobStoreManagerService extends SystemService {
final int callingUid = Binder.getCallingUid();
verifyCallingPackage(callingUid, packageName);
+ if (Process.isIsolated(callingUid) || mPackageManagerInternal.isInstantApp(
+ packageName, UserHandle.getUserId(callingUid))) {
+ throw new SecurityException("Caller not allowed to open blob; "
+ + "callingUid=" + callingUid + ", callingPackage=" + packageName);
+ }
+
releaseLeaseInternal(blobHandle, callingUid, packageName);
}
@@ -1320,6 +1357,36 @@ public class BlobStoreManagerService extends SystemService {
}
@Override
+ @NonNull
+ public List<BlobHandle> getLeasedBlobs(@NonNull String packageName) {
+ Objects.requireNonNull(packageName, "packageName must not be null");
+
+ final int callingUid = Binder.getCallingUid();
+ verifyCallingPackage(callingUid, packageName);
+
+ return getLeasedBlobsInternal(callingUid, packageName);
+ }
+
+ @Override
+ @Nullable
+ public LeaseInfo getLeaseInfo(@NonNull BlobHandle blobHandle, @NonNull String packageName) {
+ Objects.requireNonNull(blobHandle, "blobHandle must not be null");
+ blobHandle.assertIsValid();
+ Objects.requireNonNull(packageName, "packageName must not be null");
+
+ final int callingUid = Binder.getCallingUid();
+ verifyCallingPackage(callingUid, packageName);
+
+ if (Process.isIsolated(callingUid) || mPackageManagerInternal.isInstantApp(
+ packageName, UserHandle.getUserId(callingUid))) {
+ throw new SecurityException("Caller not allowed to open blob; "
+ + "callingUid=" + callingUid + ", callingPackage=" + packageName);
+ }
+
+ return getLeaseInfoInternal(blobHandle, callingUid, packageName);
+ }
+
+ @Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer,
@Nullable String[] args) {
// TODO: add proto-based version of this.
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java
index 6af540acd6a4..fabce766c237 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java
@@ -47,4 +47,13 @@ class BlobStoreUtils {
@NonNull String resourceEntryName, @NonNull String packageName) {
return resources.getIdentifier(resourceEntryName, DESC_RES_TYPE_STRING, packageName);
}
+
+ @IdRes
+ static int getDescriptionResourceId(@NonNull Context context,
+ @NonNull String resourceEntryName, @NonNull String packageName, int userId) {
+ final Resources resources = getPackageResources(context, packageName, userId);
+ return resources == null
+ ? Resources.ID_NULL
+ : getDescriptionResourceId(resources, resourceEntryName, packageName);
+ }
}
diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
index f8b598a43db6..6d9e3eddf616 100644
--- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
@@ -5,6 +5,7 @@ import android.annotation.UserIdInt;
import android.app.usage.AppStandbyInfo;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManager.StandbyBuckets;
+import android.app.usage.UsageStatsManager.SystemForcedReasons;
import android.content.Context;
import android.os.Looper;
@@ -123,9 +124,10 @@ public interface AppStandbyInternal {
* appropriate time.
*
* @param restrictReason The restrictReason for restricting the app. Should be one of the
- * UsageStatsManager.REASON_SUB_RESTRICT_* reasons.
+ * UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_* reasons.
*/
- void restrictApp(@NonNull String packageName, int userId, int restrictReason);
+ void restrictApp(@NonNull String packageName, int userId,
+ @SystemForcedReasons int restrictReason);
void addActiveDeviceAdmin(String adminPkg, int userId);
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 fc29c9cf1035..819f253e0c19 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -974,7 +974,7 @@ public class JobSchedulerService extends com.android.server.SystemService
if (!mQuotaTracker.isWithinQuota(userId, pkg, QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG)) {
Slog.e(TAG, userId + "-" + pkg + " has called schedule() too many times");
mAppStandbyInternal.restrictApp(
- pkg, userId, UsageStatsManager.REASON_SUB_RESTRICT_BUGGY);
+ pkg, userId, UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY);
if (mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION) {
final boolean isDebuggable;
synchronized (mLock) {
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 249bc524c379..e14ca990a2e5 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -58,6 +58,7 @@ import android.app.AppGlobals;
import android.app.usage.AppStandbyInfo;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManager.StandbyBuckets;
+import android.app.usage.UsageStatsManager.SystemForcedReasons;
import android.appwidget.AppWidgetManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -1153,6 +1154,13 @@ public class AppStandbyController implements AppStandbyInternal {
}
}
+ @VisibleForTesting
+ int getAppStandbyBucketReason(String packageName, int userId, long elapsedRealtime) {
+ synchronized (mAppIdleLock) {
+ return mAppIdleHistory.getAppStandbyReason(packageName, userId, elapsedRealtime);
+ }
+ }
+
@Override
public List<AppStandbyInfo> getAppStandbyBuckets(int userId) {
synchronized (mAppIdleLock) {
@@ -1161,7 +1169,8 @@ public class AppStandbyController implements AppStandbyInternal {
}
@Override
- public void restrictApp(@NonNull String packageName, int userId, int restrictReason) {
+ public void restrictApp(@NonNull String packageName, int userId,
+ @SystemForcedReasons int restrictReason) {
// If the package is not installed, don't allow the bucket to be set.
if (!mInjector.isPackageInstalled(packageName, 0, userId)) {
Slog.e(TAG, "Tried to restrict uninstalled app: " + packageName);
@@ -1248,30 +1257,52 @@ public class AppStandbyController implements AppStandbyInternal {
// Don't allow changing bucket if higher than ACTIVE
if (app.currentBucket < STANDBY_BUCKET_ACTIVE) return;
- // Don't allow prediction to change from/to NEVER or from RESTRICTED.
- if ((app.currentBucket == STANDBY_BUCKET_NEVER
- || app.currentBucket == STANDBY_BUCKET_RESTRICTED
- || newBucket == STANDBY_BUCKET_NEVER)
+ // Don't allow prediction to change from/to NEVER.
+ if ((app.currentBucket == STANDBY_BUCKET_NEVER || newBucket == STANDBY_BUCKET_NEVER)
&& predicted) {
return;
}
+ final boolean wasForcedBySystem =
+ (app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_SYSTEM;
+
// If the bucket was forced, don't allow prediction to override
if (predicted
&& ((app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_USER
- || (app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_SYSTEM)) {
+ || wasForcedBySystem)) {
+ return;
+ }
+
+ final boolean isForcedBySystem =
+ (reason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_SYSTEM;
+
+ if (app.currentBucket == newBucket && wasForcedBySystem && isForcedBySystem) {
+ mAppIdleHistory
+ .noteRestrictionAttempt(packageName, userId, elapsedRealtime, reason);
+ // Keep track of all restricting reasons
+ reason = REASON_MAIN_FORCED_BY_SYSTEM
+ | (app.bucketingReason & REASON_SUB_MASK)
+ | (reason & REASON_SUB_MASK);
+ mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime,
+ newBucket, reason, resetTimeout);
return;
}
final boolean isForcedByUser =
(reason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_USER;
- // If the current bucket is RESTRICTED, only user force or usage should bring it out,
- // unless the app was put into the bucket due to timing out.
- if (app.currentBucket == STANDBY_BUCKET_RESTRICTED && !isUserUsage(reason)
- && !isForcedByUser
- && (app.bucketingReason & REASON_MAIN_MASK) != REASON_MAIN_TIMEOUT) {
- return;
+ if (app.currentBucket == STANDBY_BUCKET_RESTRICTED) {
+ if ((app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_TIMEOUT) {
+ if (predicted && newBucket >= STANDBY_BUCKET_RARE) {
+ // Predicting into RARE or below means we don't expect the user to use the
+ // app anytime soon, so don't elevate it from RESTRICTED.
+ return;
+ }
+ } else if (!isUserUsage(reason) && !isForcedByUser) {
+ // If the current bucket is RESTRICTED, only user force or usage should bring
+ // it out, unless the app was put into the bucket due to timing out.
+ return;
+ }
}
if (newBucket == STANDBY_BUCKET_RESTRICTED) {
diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
index 29c48adb999d..0d9cbf0edf03 100644
--- a/apex/media/framework/java/android/media/MediaParser.java
+++ b/apex/media/framework/java/android/media/MediaParser.java
@@ -25,6 +25,7 @@ import android.util.SparseArray;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
+import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.extractor.DefaultExtractorInput;
import com.google.android.exoplayer2.extractor.Extractor;
import com.google.android.exoplayer2.extractor.ExtractorInput;
@@ -431,6 +432,14 @@ public final class MediaParser {
}
}
+ /** Thrown when an error occurs while parsing a media stream. */
+ public static final class ParsingException extends IOException {
+
+ private ParsingException(ParserException cause) {
+ super(cause);
+ }
+ }
+
// Public constants.
/**
@@ -768,6 +777,8 @@ public final class MediaParser {
int result = 0;
try {
result = mExtractor.read(mExtractorInput, mPositionHolder);
+ } catch (ParserException e) {
+ throw new ParsingException(e);
} catch (InterruptedException e) {
// TODO: Remove this exception replacement once we update the ExoPlayer version.
throw new InterruptedIOException();
diff --git a/api/current.txt b/api/current.txt
index 716735a66135..05c7cbcf5c84 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5995,7 +5995,7 @@ package android.app {
method public android.service.notification.StatusBarNotification[] getActiveNotifications();
method public android.app.AutomaticZenRule getAutomaticZenRule(String);
method public java.util.Map<java.lang.String,android.app.AutomaticZenRule> getAutomaticZenRules();
- method @Nullable public android.app.NotificationManager.Policy getConsolidatedNotificationPolicy();
+ method @NonNull public android.app.NotificationManager.Policy getConsolidatedNotificationPolicy();
method public final int getCurrentInterruptionFilter();
method public int getImportance();
method public android.app.NotificationChannel getNotificationChannel(String);
@@ -6912,7 +6912,6 @@ package android.app.admin {
method @Nullable public java.util.List<java.lang.String> getPermittedCrossProfileNotificationListeners(@NonNull android.content.ComponentName);
method @Nullable public java.util.List<java.lang.String> getPermittedInputMethods(@NonNull android.content.ComponentName);
method public int getPersonalAppsSuspendedReasons(@NonNull android.content.ComponentName);
- method @NonNull public java.util.List<java.lang.String> getProtectedPackages(@NonNull android.content.ComponentName);
method public long getRequiredStrongAuthTimeout(@Nullable android.content.ComponentName);
method public boolean getScreenCaptureDisabled(@Nullable android.content.ComponentName);
method public java.util.List<android.os.UserHandle> getSecondaryUsers(@NonNull android.content.ComponentName);
@@ -6923,6 +6922,7 @@ package android.app.admin {
method @Nullable public android.app.admin.SystemUpdatePolicy getSystemUpdatePolicy();
method @Nullable public android.os.PersistableBundle getTransferOwnershipBundle();
method @Nullable public java.util.List<android.os.PersistableBundle> getTrustAgentConfiguration(@Nullable android.content.ComponentName, @NonNull android.content.ComponentName);
+ method @NonNull public java.util.List<java.lang.String> getUserControlDisabledPackages(@NonNull android.content.ComponentName);
method @NonNull public android.os.Bundle getUserRestrictions(@NonNull android.content.ComponentName);
method @Nullable public String getWifiMacAddress(@NonNull android.content.ComponentName);
method public boolean grantKeyPairToApp(@Nullable android.content.ComponentName, @NonNull String, @NonNull String);
@@ -7042,7 +7042,6 @@ package android.app.admin {
method public void setPersonalAppsSuspended(@NonNull android.content.ComponentName, boolean);
method public void setProfileEnabled(@NonNull android.content.ComponentName);
method public void setProfileName(@NonNull android.content.ComponentName, String);
- method public void setProtectedPackages(@NonNull android.content.ComponentName, @NonNull java.util.List<java.lang.String>);
method public void setRecommendedGlobalProxy(@NonNull android.content.ComponentName, @Nullable android.net.ProxyInfo);
method public void setRequiredStrongAuthTimeout(@NonNull android.content.ComponentName, long);
method public boolean setResetPasswordToken(android.content.ComponentName, byte[]);
@@ -7061,6 +7060,7 @@ package android.app.admin {
method public boolean setTimeZone(@NonNull android.content.ComponentName, String);
method public void setTrustAgentConfiguration(@NonNull android.content.ComponentName, @NonNull android.content.ComponentName, android.os.PersistableBundle);
method public void setUninstallBlocked(@Nullable android.content.ComponentName, String, boolean);
+ method public void setUserControlDisabledPackages(@NonNull android.content.ComponentName, @NonNull java.util.List<java.lang.String>);
method public void setUserIcon(@NonNull android.content.ComponentName, android.graphics.Bitmap);
method public int startUserInBackground(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
method public int stopUser(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
@@ -11721,6 +11721,7 @@ package android.content.pm {
ctor public LauncherApps.ShortcutQuery();
method public android.content.pm.LauncherApps.ShortcutQuery setActivity(@Nullable android.content.ComponentName);
method public android.content.pm.LauncherApps.ShortcutQuery setChangedSince(long);
+ method @NonNull public android.content.pm.LauncherApps.ShortcutQuery setLocusIds(@Nullable java.util.List<android.content.LocusId>);
method public android.content.pm.LauncherApps.ShortcutQuery setPackage(@Nullable String);
method public android.content.pm.LauncherApps.ShortcutQuery setQueryFlags(int);
method public android.content.pm.LauncherApps.ShortcutQuery setShortcutIds(@Nullable java.util.List<java.lang.String>);
@@ -11896,6 +11897,7 @@ package android.content.pm {
method public void setAppIcon(@Nullable android.graphics.Bitmap);
method public void setAppLabel(@Nullable CharSequence);
method public void setAppPackageName(@Nullable String);
+ method public void setAutoRevokePermissionsMode(boolean);
method public void setInstallLocation(int);
method public void setInstallReason(int);
method public void setMultiPackage();
@@ -12025,6 +12027,7 @@ package android.content.pm {
method public boolean hasSigningCertificate(int, @NonNull byte[], int);
method public abstract boolean hasSystemFeature(@NonNull String);
method public abstract boolean hasSystemFeature(@NonNull String, int);
+ method @RequiresPermission(value="android.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS", conditional=true) public boolean isAutoRevokeWhitelisted(@NonNull String);
method public boolean isAutoRevokeWhitelisted();
method public boolean isDefaultApplicationIcon(@NonNull android.graphics.drawable.Drawable);
method public boolean isDeviceUpgrading();
@@ -12050,6 +12053,7 @@ package android.content.pm {
method @Nullable public abstract android.content.pm.ResolveInfo resolveService(@NonNull android.content.Intent, int);
method public abstract void setApplicationCategoryHint(@NonNull String, int);
method @RequiresPermission(value=android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE, conditional=true) public abstract void setApplicationEnabledSetting(@NonNull String, int, int);
+ method @RequiresPermission(value="android.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS", conditional=true) public boolean setAutoRevokeWhitelisted(@NonNull String, boolean);
method @RequiresPermission(value=android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE, conditional=true) public abstract void setComponentEnabledSetting(@NonNull android.content.ComponentName, int, int);
method public abstract void setInstallerPackageName(@NonNull String, @Nullable String);
method public void setMimeGroup(@NonNull String, @NonNull java.util.Set<java.lang.String>);
@@ -17356,7 +17360,7 @@ package android.hardware.camera2 {
public final class CameraManager {
method @NonNull public android.hardware.camera2.CameraCharacteristics getCameraCharacteristics(@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>> getConcurrentStreamingCameraIds() throws android.hardware.camera2.CameraAccessException;
+ method @NonNull public java.util.Set<java.util.Set<java.lang.String>> getConcurrentCameraIds() 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;
@@ -20098,8 +20102,7 @@ package android.icu.number {
method public T numberFormatterSecond(android.icu.number.UnlocalizedNumberFormatter);
}
- public abstract class Precision implements java.lang.Cloneable {
- method public Object clone();
+ public abstract class Precision {
method public static android.icu.number.CurrencyPrecision currency(android.icu.util.Currency.CurrencyUsage);
method public static android.icu.number.FractionPrecision fixedFraction(int);
method public static android.icu.number.Precision fixedSignificantDigits(int);
@@ -20122,8 +20125,7 @@ package android.icu.number {
method public static android.icu.number.Scale powerOfTen(int);
}
- public class ScientificNotation extends android.icu.number.Notation implements java.lang.Cloneable {
- method public Object clone();
+ public class ScientificNotation extends android.icu.number.Notation {
method public android.icu.number.ScientificNotation withExponentSignDisplay(android.icu.number.NumberFormatter.SignDisplay);
method public android.icu.number.ScientificNotation withMinExponentDigits(int);
}
@@ -26460,6 +26462,9 @@ package android.media {
method public void onTrackDataFound(int, @NonNull android.media.MediaParser.TrackData);
}
+ public static final class MediaParser.ParsingException extends java.io.IOException {
+ }
+
public static final class MediaParser.SeekMap {
method public long getDurationMicros();
method @NonNull public android.util.Pair<android.media.MediaParser.SeekPoint,android.media.MediaParser.SeekPoint> getSeekPoints(long);
@@ -27050,7 +27055,7 @@ package android.media {
method public abstract void onVolumeUpdateRequest(android.media.MediaRouter.RouteInfo, int);
}
- public class MediaRouter2 {
+ public final class MediaRouter2 {
method @NonNull public java.util.List<android.media.MediaRouter2.RoutingController> getControllers();
method @NonNull public static android.media.MediaRouter2 getInstance(@NonNull android.content.Context);
method @NonNull public java.util.List<android.media.MediaRoute2Info> getRoutes();
@@ -29817,7 +29822,7 @@ package android.net {
public abstract static class ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback {
ctor public ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback();
- method public void onConnectivityReport(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityReport);
+ method public void onConnectivityReportAvailable(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityReport);
method public void onDataStallSuspected(@NonNull android.net.ConnectivityDiagnosticsManager.DataStallReport);
method public void onNetworkConnectivityReported(@NonNull android.net.Network, boolean);
}
@@ -37159,12 +37164,10 @@ package android.os {
method @Nullable public android.os.VibrationEffect.Composition addPrimitive(int, @FloatRange(from=0.0f, to=1.0f) float, @IntRange(from=0) int);
method @NonNull public android.os.VibrationEffect compose();
field public static final int PRIMITIVE_CLICK = 1; // 0x1
- field public static final int PRIMITIVE_LIGHT_TICK = 7; // 0x7
field public static final int PRIMITIVE_QUICK_FALL = 6; // 0x6
field public static final int PRIMITIVE_QUICK_RISE = 4; // 0x4
field public static final int PRIMITIVE_SLOW_RISE = 5; // 0x5
- field public static final int PRIMITIVE_SPIN = 3; // 0x3
- field public static final int PRIMITIVE_THUD = 2; // 0x2
+ field public static final int PRIMITIVE_TICK = 7; // 0x7
}
public abstract class Vibrator {
@@ -47834,6 +47837,7 @@ package android.telephony {
method @Deprecated public void sendMultimediaMessage(android.content.Context, android.net.Uri, String, android.os.Bundle, android.app.PendingIntent);
method public void sendMultipartTextMessage(String, String, java.util.ArrayList<java.lang.String>, java.util.ArrayList<android.app.PendingIntent>, java.util.ArrayList<android.app.PendingIntent>);
method public void sendMultipartTextMessage(@NonNull String, @Nullable String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, long);
+ method public void sendMultipartTextMessage(@NonNull String, @Nullable String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, @NonNull String);
method public void sendTextMessage(String, String, String, android.app.PendingIntent, android.app.PendingIntent);
method public void sendTextMessage(@NonNull String, @Nullable String, @NonNull String, @Nullable android.app.PendingIntent, @Nullable android.app.PendingIntent, long);
method @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.SEND_SMS}) public void sendTextMessageWithoutPersisting(String, String, String, android.app.PendingIntent, android.app.PendingIntent);
@@ -48210,7 +48214,6 @@ package android.telephony {
method @Deprecated public String iccTransmitApduBasicChannel(int, int, int, int, int, String);
method @Deprecated public String iccTransmitApduLogicalChannel(int, int, int, int, int, int, String);
method public boolean isConcurrentVoiceAndDataSupported();
- method public boolean isDataCapable();
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean isDataEnabled();
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataRoamingEnabled();
method public boolean isEmergencyNumber(@NonNull String);
@@ -48999,8 +49002,8 @@ package android.telephony.ims {
ctor public RegistrationManager.RegistrationCallback();
method public void onRegistered(int);
method public void onRegistering(int);
- method public void onTechnologyChangeFailed(int, @Nullable android.telephony.ims.ImsReasonInfo);
- method public void onUnregistered(@Nullable android.telephony.ims.ImsReasonInfo);
+ method public void onTechnologyChangeFailed(int, @NonNull android.telephony.ims.ImsReasonInfo);
+ method public void onUnregistered(@NonNull android.telephony.ims.ImsReasonInfo);
}
}
@@ -53608,10 +53611,11 @@ package android.view {
public class SurfaceControlViewHost {
ctor public SurfaceControlViewHost(@NonNull android.content.Context, @NonNull android.view.Display, @Nullable android.os.IBinder);
- method public void addView(@NonNull android.view.View, int, int);
method @Nullable public android.view.SurfaceControlViewHost.SurfacePackage getSurfacePackage();
+ method @Nullable public android.view.View getView();
method public void relayout(int, int);
method public void release();
+ method public void setView(@NonNull android.view.View, int, int);
}
public static final class SurfaceControlViewHost.SurfacePackage implements android.os.Parcelable {
@@ -55586,7 +55590,7 @@ package android.view {
public interface WindowInsetsController {
method public void addOnControllableInsetsChangedListener(@NonNull android.view.WindowInsetsController.OnControllableInsetsChangedListener);
- method @NonNull public android.os.CancellationSignal controlWindowInsetsAnimation(int, long, @Nullable android.view.animation.Interpolator, @NonNull android.view.WindowInsetsAnimationControlListener);
+ method public void controlWindowInsetsAnimation(int, long, @Nullable android.view.animation.Interpolator, @Nullable android.os.CancellationSignal, @NonNull android.view.WindowInsetsAnimationControlListener);
method public int getSystemBarsAppearance();
method public int getSystemBarsBehavior();
method public void hide(int);
@@ -56810,7 +56814,7 @@ package android.view.inline {
public static final class InlinePresentationSpec.Builder {
ctor public InlinePresentationSpec.Builder(@NonNull android.util.Size, @NonNull android.util.Size);
method @NonNull public android.view.inline.InlinePresentationSpec build();
- method @NonNull public android.view.inline.InlinePresentationSpec.Builder setStyle(@Nullable android.os.Bundle);
+ method @NonNull public android.view.inline.InlinePresentationSpec.Builder setStyle(@NonNull android.os.Bundle);
}
}
@@ -57022,7 +57026,7 @@ package android.view.inputmethod {
ctor public InlineSuggestionsRequest.Builder(@NonNull java.util.List<android.view.inline.InlinePresentationSpec>);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder addPresentationSpecs(@NonNull android.view.inline.InlinePresentationSpec);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest build();
- method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setExtras(@Nullable android.os.Bundle);
+ method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setExtras(@NonNull android.os.Bundle);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setMaxSuggestionCount(int);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setSupportedLocales(@NonNull android.os.LocaleList);
}
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index 69b52693aa46..ee997f154299 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -29,7 +29,7 @@ package android.net {
field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient.AddressInfo> CREATOR;
}
- public class TetheringConstants {
+ public final class TetheringConstants {
field public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
field public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
field public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType";
diff --git a/api/system-current.txt b/api/system-current.txt
index 80de67a0a017..76d230a7628b 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -148,6 +148,7 @@ package android {
field public static final String NETWORK_SETUP_WIZARD = "android.permission.NETWORK_SETUP_WIZARD";
field public static final String NETWORK_SIGNAL_STRENGTH_WAKEUP = "android.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP";
field public static final String NETWORK_STACK = "android.permission.NETWORK_STACK";
+ field public static final String NETWORK_STATS_PROVIDER = "android.permission.NETWORK_STATS_PROVIDER";
field public static final String NOTIFICATION_DURING_SETUP = "android.permission.NOTIFICATION_DURING_SETUP";
field public static final String NOTIFY_TV_INPUTS = "android.permission.NOTIFY_TV_INPUTS";
field public static final String OBSERVE_APP_USAGE = "android.permission.OBSERVE_APP_USAGE";
@@ -235,6 +236,7 @@ package android {
field public static final String UPGRADE_RUNTIME_PERMISSIONS = "android.permission.UPGRADE_RUNTIME_PERMISSIONS";
field public static final String USER_ACTIVITY = "android.permission.USER_ACTIVITY";
field public static final String USE_RESERVED_DISK = "android.permission.USE_RESERVED_DISK";
+ field public static final String WHITELIST_AUTO_REVOKE_PERMISSIONS = "android.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS";
field public static final String WHITELIST_RESTRICTED_PERMISSIONS = "android.permission.WHITELIST_RESTRICTED_PERMISSIONS";
field public static final String WIFI_SET_DEVICE_MOBILITY_STATE = "android.permission.WIFI_SET_DEVICE_MOBILITY_STATE";
field public static final String WIFI_UPDATE_USABILITY_STATS_SCORE = "android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE";
@@ -251,9 +253,6 @@ package android {
public static final class R.array {
field public static final int config_keySystemUuidMapping = 17235973; // 0x1070005
- field public static final int config_restrictedPreinstalledCarrierApps = 17235975; // 0x1070007
- field public static final int config_sms_enabled_locking_shift_tables = 17235977; // 0x1070009
- field public static final int config_sms_enabled_single_shift_tables = 17235976; // 0x1070008
field public static final int simColors = 17235974; // 0x1070006
}
@@ -306,7 +305,6 @@ package android {
field public static final int config_helpPackageNameKey = 17039387; // 0x104001b
field public static final int config_helpPackageNameValue = 17039388; // 0x104001c
field public static final int config_systemGallery = 17039402; // 0x104002a
- field public static final int low_memory = 17039403; // 0x104002b
}
public static final class R.style {
@@ -353,7 +351,6 @@ package android.app {
method @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS) public static void setPersistentVrThread(int);
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean switchUser(@NonNull android.os.UserHandle);
method public void unregisterHomeVisibilityObserver(@NonNull android.app.HomeVisibilityObserver);
- method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public boolean updateMccMncConfiguration(@NonNull String, @NonNull String);
}
public static interface ActivityManager.OnUidImportanceListener {
@@ -407,7 +404,6 @@ package android.app {
field public static final String OPSTR_POST_NOTIFICATION = "android:post_notification";
field public static final String OPSTR_PROJECT_MEDIA = "android:project_media";
field public static final String OPSTR_READ_CLIPBOARD = "android:read_clipboard";
- field public static final String OPSTR_READ_DEVICE_IDENTIFIERS = "android:read_device_identifiers";
field public static final String OPSTR_READ_ICC_SMS = "android:read_icc_sms";
field public static final String OPSTR_READ_MEDIA_AUDIO = "android:read_media_audio";
field public static final String OPSTR_READ_MEDIA_IMAGES = "android:read_media_images";
@@ -757,7 +753,6 @@ package android.app {
public class StatusBarManager {
method @NonNull @RequiresPermission(android.Manifest.permission.STATUS_BAR) public android.app.StatusBarManager.DisableInfo getDisableInfo();
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSetup(boolean);
- method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSimNetworkLock(boolean);
}
public static final class StatusBarManager.DisableInfo {
@@ -1415,7 +1410,8 @@ package android.app.usage {
}
public class NetworkStatsManager {
- method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.net.netstats.provider.NetworkStatsProviderCallback registerNetworkStatsProvider(@NonNull String, @NonNull android.net.netstats.provider.AbstractNetworkStatsProvider);
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STATS_PROVIDER, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void registerNetworkStatsProvider(@NonNull String, @NonNull android.net.netstats.provider.NetworkStatsProvider);
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STATS_PROVIDER, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void unregisterNetworkStatsProvider(@NonNull android.net.netstats.provider.NetworkStatsProvider);
}
public static final class UsageEvents.Event {
@@ -2107,10 +2103,6 @@ package android.content.pm {
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.LauncherApps.AppUsageLimit> CREATOR;
}
- public static class LauncherApps.ShortcutQuery {
- method @NonNull public android.content.pm.LauncherApps.ShortcutQuery setLocusIds(@Nullable java.util.List<android.content.LocusId>);
- }
-
public class PackageInstaller {
method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setPermissionsResult(int, boolean);
field public static final int DATA_LOADER_TYPE_INCREMENTAL = 2; // 0x2
@@ -2132,6 +2124,7 @@ package android.content.pm {
public static class PackageInstaller.SessionInfo implements android.os.Parcelable {
method public boolean getAllocateAggressive();
method @Deprecated public boolean getAllowDowngrade();
+ method public int getAutoRevokePermissionsMode();
method public boolean getDontKillApp();
method public boolean getEnableRollback();
method @Nullable public String[] getGrantedRuntimePermissions();
@@ -2280,7 +2273,6 @@ package android.content.pm {
field @Deprecated public static final int MASK_PERMISSION_FLAGS = 255; // 0xff
field public static final int MATCH_ANY_USER = 4194304; // 0x400000
field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000
- field public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS = 536870912; // 0x20000000
field public static final int MATCH_INSTANT = 8388608; // 0x800000
field public static final int MODULE_APEX_NAME = 1; // 0x1
field public static final int RESTRICTION_HIDE_FROM_SUGGESTIONS = 1; // 0x1
@@ -4894,16 +4886,10 @@ package android.media.tv.tuner {
method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void shareFrontendFromTuner(@NonNull android.media.tv.tuner.Tuner);
method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int tune(@NonNull android.media.tv.tuner.frontend.FrontendSettings);
method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void updateResourcePriority(int, int);
- }
-
- public static interface Tuner.OnResourceLostListener {
- method public void onResourceLost(@NonNull android.media.tv.tuner.Tuner);
- }
-
- public final class TunerConstants {
field public static final int INVALID_AV_SYNC_ID = -1; // 0xffffffff
field public static final int INVALID_FILTER_ID = -1; // 0xffffffff
field public static final int INVALID_STREAM_ID = 65535; // 0xffff
+ field public static final long INVALID_TIMESTAMP = -1L; // 0xffffffffffffffffL
field public static final int INVALID_TS_PID = 65535; // 0xffff
field public static final int RESULT_INVALID_ARGUMENT = 4; // 0x4
field public static final int RESULT_INVALID_STATE = 3; // 0x3
@@ -4917,6 +4903,10 @@ package android.media.tv.tuner {
field public static final int SCAN_TYPE_UNDEFINED = 0; // 0x0
}
+ public static interface Tuner.OnResourceLostListener {
+ method public void onResourceLost(@NonNull android.media.tv.tuner.Tuner);
+ }
+
}
package android.media.tv.tuner.dvr {
@@ -5277,7 +5267,6 @@ package android.media.tv.tuner.filter {
method public long getSourceTime();
method public long getTimeStamp();
method public int setCurrentTimestamp(long);
- field public static final long TIMESTAMP_UNAVAILABLE = -1L; // 0xffffffffffffffffL
}
public final class TlvFilterConfiguration extends android.media.tv.tuner.filter.FilterConfiguration {
@@ -5364,8 +5353,9 @@ package android.media.tv.tuner.frontend {
field public static final int SIGNAL_TYPE_UNDEFINED = 0; // 0x0
}
- public static class AnalogFrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder> {
+ public static class AnalogFrontendSettings.Builder {
method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings build();
+ method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setFrequency(int);
method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setSifStandard(int);
method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setSignalType(int);
}
@@ -5429,10 +5419,11 @@ package android.media.tv.tuner.frontend {
field public static final int TIME_INTERLEAVE_MODE_UNDEFINED = 0; // 0x0
}
- public static class Atsc3FrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.Atsc3FrontendSettings.Builder> {
+ public static class Atsc3FrontendSettings.Builder {
method @NonNull public android.media.tv.tuner.frontend.Atsc3FrontendSettings build();
method @NonNull public android.media.tv.tuner.frontend.Atsc3FrontendSettings.Builder setBandwidth(int);
method @NonNull public android.media.tv.tuner.frontend.Atsc3FrontendSettings.Builder setDemodOutputFormat(int);
+ method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.Atsc3FrontendSettings.Builder setFrequency(int);
method @NonNull public android.media.tv.tuner.frontend.Atsc3FrontendSettings.Builder setPlpSettings(@NonNull android.media.tv.tuner.frontend.Atsc3PlpSettings[]);
}
@@ -5473,8 +5464,9 @@ package android.media.tv.tuner.frontend {
field public static final int MODULATION_UNDEFINED = 0; // 0x0
}
- public static class AtscFrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.AtscFrontendSettings.Builder> {
+ public static class AtscFrontendSettings.Builder {
method @NonNull public android.media.tv.tuner.frontend.AtscFrontendSettings build();
+ method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.AtscFrontendSettings.Builder setFrequency(int);
method @NonNull public android.media.tv.tuner.frontend.AtscFrontendSettings.Builder setModulation(int);
}
@@ -5487,7 +5479,7 @@ package android.media.tv.tuner.frontend {
public class DvbcFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings {
method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public static android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder builder(@NonNull android.content.Context);
method public int getAnnex();
- method public long getFec();
+ method public long getInnerFec();
method public int getModulation();
method public int getOuterFec();
method public int getSpectralInversion();
@@ -5512,10 +5504,11 @@ package android.media.tv.tuner.frontend {
field public static final int SPECTRAL_INVERSION_UNDEFINED = 0; // 0x0
}
- public static class DvbcFrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder> {
+ public static class DvbcFrontendSettings.Builder {
method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings build();
method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setAnnex(int);
- method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setFec(long);
+ method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setFrequency(int);
+ method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setInnerFec(long);
method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setModulation(int);
method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setOuterFec(int);
method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setSpectralInversion(int);
@@ -5590,9 +5583,10 @@ package android.media.tv.tuner.frontend {
field public static final int VCM_MODE_UNDEFINED = 0; // 0x0
}
- public static class DvbsFrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder> {
+ public static class DvbsFrontendSettings.Builder {
method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings build();
method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setCodeRate(@Nullable android.media.tv.tuner.frontend.DvbsCodeRate);
+ method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setFrequency(int);
method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setInputStreamId(int);
method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setModulation(int);
method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setPilot(int);
@@ -5689,10 +5683,11 @@ package android.media.tv.tuner.frontend {
field public static final int TRANSMISSION_MODE_UNDEFINED = 0; // 0x0
}
- public static class DvbtFrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder> {
+ public static class DvbtFrontendSettings.Builder {
method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings build();
method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setBandwidth(int);
method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setConstellation(int);
+ method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setFrequency(int);
method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setGuardInterval(int);
method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setHierarchy(int);
method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setHighPriority(boolean);
@@ -5773,17 +5768,13 @@ package android.media.tv.tuner.frontend {
field public static final int TYPE_UNDEFINED = 0; // 0x0
}
- public abstract static class FrontendSettings.Builder<T extends android.media.tv.tuner.frontend.FrontendSettings.Builder<T>> {
- method @IntRange(from=1) @NonNull public T setFrequency(int);
- }
-
public class FrontendStatus {
method public int getAgc();
method @NonNull public android.media.tv.tuner.frontend.FrontendStatus.Atsc3PlpInfo[] getAtsc3PlpInfo();
method public int getBer();
- method public long getFec();
method public int getFreqOffset();
method public int getHierarchy();
+ method public long getInnerFec();
method @NonNull public boolean[] getLayerErrors();
method public int getLnbVoltage();
method public int getMer();
@@ -5868,9 +5859,10 @@ package android.media.tv.tuner.frontend {
field public static final int ROLLOFF_UNDEFINED = 0; // 0x0
}
- public static class Isdbs3FrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.Isdbs3FrontendSettings.Builder> {
+ public static class Isdbs3FrontendSettings.Builder {
method @NonNull public android.media.tv.tuner.frontend.Isdbs3FrontendSettings build();
method @NonNull public android.media.tv.tuner.frontend.Isdbs3FrontendSettings.Builder setCodeRate(int);
+ method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.Isdbs3FrontendSettings.Builder setFrequency(int);
method @NonNull public android.media.tv.tuner.frontend.Isdbs3FrontendSettings.Builder setModulation(int);
method @NonNull public android.media.tv.tuner.frontend.Isdbs3FrontendSettings.Builder setRolloff(int);
method @NonNull public android.media.tv.tuner.frontend.Isdbs3FrontendSettings.Builder setStreamId(int);
@@ -5910,9 +5902,10 @@ package android.media.tv.tuner.frontend {
field public static final int STREAM_ID_TYPE_RELATIVE_NUMBER = 1; // 0x1
}
- public static class IsdbsFrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.IsdbsFrontendSettings.Builder> {
+ public static class IsdbsFrontendSettings.Builder {
method @NonNull public android.media.tv.tuner.frontend.IsdbsFrontendSettings build();
method @NonNull public android.media.tv.tuner.frontend.IsdbsFrontendSettings.Builder setCodeRate(int);
+ method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.IsdbsFrontendSettings.Builder setFrequency(int);
method @NonNull public android.media.tv.tuner.frontend.IsdbsFrontendSettings.Builder setModulation(int);
method @NonNull public android.media.tv.tuner.frontend.IsdbsFrontendSettings.Builder setRolloff(int);
method @NonNull public android.media.tv.tuner.frontend.IsdbsFrontendSettings.Builder setStreamId(int);
@@ -5955,10 +5948,11 @@ package android.media.tv.tuner.frontend {
field public static final int MODULATION_UNDEFINED = 0; // 0x0
}
- public static class IsdbtFrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder> {
+ public static class IsdbtFrontendSettings.Builder {
method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings build();
method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setBandwidth(int);
method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setCodeRate(int);
+ method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setFrequency(int);
method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setGuardInterval(int);
method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setMode(int);
method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setModulation(int);
@@ -6089,7 +6083,7 @@ package android.net {
method @Deprecated @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String getCaptivePortalServerUrl();
method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEntitlementResultListener);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
- method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public int registerNetworkProvider(@NonNull android.net.NetworkProvider);
+ method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_FACTORY}) public int registerNetworkProvider(@NonNull android.net.NetworkProvider);
method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
method @Deprecated public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, int, int, @NonNull android.os.Handler);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_AIRPLANE_MODE, android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean);
@@ -6098,7 +6092,7 @@ package android.net {
method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler);
method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int);
- method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void unregisterNetworkProvider(@NonNull android.net.NetworkProvider);
+ method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_FACTORY}) public void unregisterNetworkProvider(@NonNull android.net.NetworkProvider);
method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void unregisterTetheringEventCallback(@NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC";
field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT";
@@ -6286,7 +6280,6 @@ package android.net {
method @Nullable public android.net.Network getNetwork();
method public void onAddKeepalivePacketFilter(int, @NonNull android.net.KeepalivePacketData);
method public void onAutomaticReconnectDisabled();
- method public void onBandwidthUpdateRequested();
method public void onNetworkUnwanted();
method public void onRemoveKeepalivePacketFilter(int);
method public void onSaveAcceptUnvalidated(boolean);
@@ -6300,23 +6293,17 @@ package android.net {
method public void sendNetworkScore(int);
method public void sendSocketKeepaliveEvent(int, int);
method public void setConnected();
- method @Deprecated public void setLegacyExtraInfo(@Nullable String);
- method @Deprecated public void setLegacySubtype(int, @NonNull String);
method public void unregister();
field public static final int VALIDATION_STATUS_NOT_VALID = 2; // 0x2
field public static final int VALIDATION_STATUS_VALID = 1; // 0x1
- field public final int providerId;
}
public final class NetworkAgentConfig implements android.os.Parcelable {
method public int describeContents();
method public int getLegacyType();
method @NonNull public String getLegacyTypeName();
- method @Nullable public String getSubscriberId();
method public boolean isExplicitlySelected();
- method public boolean isNat64DetectionEnabled();
method public boolean isPartialConnectivityAcceptable();
- method public boolean isProvisioningNotificationEnabled();
method public boolean isUnvalidatedConnectivityAcceptable();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkAgentConfig> CREATOR;
@@ -6325,18 +6312,14 @@ package android.net {
public static class NetworkAgentConfig.Builder {
ctor public NetworkAgentConfig.Builder();
method @NonNull public android.net.NetworkAgentConfig build();
- method @NonNull public android.net.NetworkAgentConfig.Builder disableNat64Detection();
- method @NonNull public android.net.NetworkAgentConfig.Builder disableProvisioningNotification();
method @NonNull public android.net.NetworkAgentConfig.Builder setExplicitlySelected(boolean);
method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyType(int);
method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyTypeName(@NonNull String);
method @NonNull public android.net.NetworkAgentConfig.Builder setPartialConnectivityAcceptable(boolean);
- method @NonNull public android.net.NetworkAgentConfig.Builder setSubscriberId(@Nullable String);
method @NonNull public android.net.NetworkAgentConfig.Builder setUnvalidatedConnectivityAcceptable(boolean);
}
public final class NetworkCapabilities implements android.os.Parcelable {
- method public boolean deduceRestrictedCapability();
method @NonNull public java.util.List<java.lang.Integer> getAdministratorUids();
method @Nullable public String getSSID();
method @NonNull public int[] getTransportTypes();
@@ -6361,27 +6344,9 @@ package android.net {
field public final android.net.WifiKey wifiKey;
}
- public class NetworkPolicyManager {
- method @NonNull public android.telephony.SubscriptionPlan[] getSubscriptionPlans(int, @NonNull String);
- method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void registerSubscriptionCallback(@NonNull android.net.NetworkPolicyManager.SubscriptionCallback);
- method public void setSubscriptionOverride(int, int, int, long, @NonNull String);
- method public void setSubscriptionPlans(int, @NonNull android.telephony.SubscriptionPlan[], @NonNull String);
- method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void unregisterSubscriptionCallback(@NonNull android.net.NetworkPolicyManager.SubscriptionCallback);
- field public static final int SUBSCRIPTION_OVERRIDE_CONGESTED = 2; // 0x2
- field public static final int SUBSCRIPTION_OVERRIDE_UNMETERED = 1; // 0x1
- }
-
- public static class NetworkPolicyManager.SubscriptionCallback {
- ctor public NetworkPolicyManager.SubscriptionCallback();
- method public void onSubscriptionOverride(int, int, int);
- method public void onSubscriptionPlansChanged(int, @NonNull android.telephony.SubscriptionPlan[]);
- }
-
public class NetworkProvider {
ctor public NetworkProvider(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String);
method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void declareNetworkRequestUnfulfillable(@NonNull android.net.NetworkRequest);
- method @Nullable public android.os.Messenger getMessenger();
- method @NonNull public String getName();
method public int getProviderId();
method public void onNetworkRequested(@NonNull android.net.NetworkRequest, int, int);
method public void onRequestWithdrawn(@NonNull android.net.NetworkRequest);
@@ -6443,14 +6408,13 @@ package android.net {
public final class NetworkStats implements android.os.Parcelable {
ctor public NetworkStats(long, int);
method @NonNull public android.net.NetworkStats add(@NonNull android.net.NetworkStats);
- method @NonNull public android.net.NetworkStats addValues(@NonNull android.net.NetworkStats.Entry);
+ method @NonNull public android.net.NetworkStats addEntry(@NonNull android.net.NetworkStats.Entry);
method public int describeContents();
method @NonNull public android.net.NetworkStats subtract(@NonNull android.net.NetworkStats);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkStats> CREATOR;
field public static final int DEFAULT_NETWORK_NO = 0; // 0x0
field public static final int DEFAULT_NETWORK_YES = 1; // 0x1
- field @Nullable public static final String IFACE_ALL;
field public static final String IFACE_VT = "vt_data0";
field public static final int METERED_NO = 0; // 0x0
field public static final int METERED_YES = 1; // 0x1
@@ -6850,21 +6814,17 @@ package android.net.metrics {
package android.net.netstats.provider {
- public abstract class AbstractNetworkStatsProvider {
- ctor public AbstractNetworkStatsProvider();
- method public abstract void requestStatsUpdate(int);
- method public abstract void setAlert(long);
- method public abstract void setLimit(@NonNull String, long);
+ public abstract class NetworkStatsProvider {
+ ctor public NetworkStatsProvider();
+ method public void notifyAlertReached();
+ method public void notifyLimitReached();
+ method public void notifyStatsUpdated(int, @NonNull android.net.NetworkStats, @NonNull android.net.NetworkStats);
+ method public abstract void onRequestStatsUpdate(int);
+ method public abstract void onSetAlert(long);
+ method public abstract void onSetLimit(@NonNull String, long);
field public static final int QUOTA_UNLIMITED = -1; // 0xffffffff
}
- public class NetworkStatsProviderCallback {
- method public void onAlertReached();
- method public void onLimitReached();
- method public void onStatsUpdated(int, @NonNull android.net.NetworkStats, @NonNull android.net.NetworkStats);
- method public void unregister();
- }
-
}
package android.net.sip {
@@ -7488,24 +7448,7 @@ package android.net.wifi {
}
public final class WifiMigration {
- method @Nullable public static android.net.wifi.WifiMigration.ConfigStoreMigrationData loadFromConfigStore();
method @NonNull public static android.net.wifi.WifiMigration.SettingsMigrationData loadFromSettings(@NonNull android.content.Context);
- method public static void removeConfigStore();
- }
-
- public static final class WifiMigration.ConfigStoreMigrationData implements android.os.Parcelable {
- method public int describeContents();
- method @Nullable public java.util.List<android.net.wifi.WifiConfiguration> getUserSavedNetworkConfigurations();
- method @Nullable public android.net.wifi.SoftApConfiguration getUserSoftApConfiguration();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiMigration.ConfigStoreMigrationData> CREATOR;
- }
-
- public static final class WifiMigration.ConfigStoreMigrationData.Builder {
- ctor public WifiMigration.ConfigStoreMigrationData.Builder();
- method @NonNull public android.net.wifi.WifiMigration.ConfigStoreMigrationData build();
- method @NonNull public android.net.wifi.WifiMigration.ConfigStoreMigrationData.Builder setUserSavedNetworkConfigurations(@NonNull java.util.List<android.net.wifi.WifiConfiguration>);
- method @NonNull public android.net.wifi.WifiMigration.ConfigStoreMigrationData.Builder setUserSoftApConfiguration(@NonNull android.net.wifi.SoftApConfiguration);
}
public static final class WifiMigration.SettingsMigrationData implements android.os.Parcelable {
@@ -9369,7 +9312,6 @@ package android.provider {
field public static final String AUTOFILL_USER_DATA_MAX_VALUE_LENGTH = "autofill_user_data_max_value_length";
field public static final String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH = "autofill_user_data_min_value_length";
field public static final String AUTO_REVOKE_DISABLED = "auto_revoke_disabled";
- field public static final String CARRIER_APPS_HANDLED = "carrier_apps_handled";
field public static final String COMPLETED_CATEGORY_PREFIX = "suggested.completed_category.";
field public static final String DOZE_ALWAYS_ON = "doze_always_on";
field public static final String HUSH_GESTURE_USED = "hush_gesture_used";
@@ -10765,27 +10707,6 @@ package android.telephony {
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallAttributes> CREATOR;
}
- public final class CallForwardingInfo implements android.os.Parcelable {
- ctor public CallForwardingInfo(int, int, @Nullable String, int);
- method public int describeContents();
- method @Nullable public String getNumber();
- method public int getReason();
- method public int getStatus();
- method public int getTimeoutSeconds();
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallForwardingInfo> CREATOR;
- field public static final int REASON_ALL = 4; // 0x4
- field public static final int REASON_ALL_CONDITIONAL = 5; // 0x5
- field public static final int REASON_BUSY = 1; // 0x1
- field public static final int REASON_NOT_REACHABLE = 3; // 0x3
- field public static final int REASON_NO_REPLY = 2; // 0x2
- field public static final int REASON_UNCONDITIONAL = 0; // 0x0
- field public static final int STATUS_ACTIVE = 1; // 0x1
- field public static final int STATUS_FDN_CHECK_FAILURE = 2; // 0x2
- field public static final int STATUS_INACTIVE = 0; // 0x0
- field public static final int STATUS_NOT_SUPPORTED = 4; // 0x4
- field public static final int STATUS_UNKNOWN_ERROR = 3; // 0x3
- }
-
public final class CallQuality implements android.os.Parcelable {
ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int);
ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int, boolean, boolean, boolean);
@@ -11166,7 +11087,6 @@ package android.telephony {
method public void onRadioPowerStateChanged(int);
method public void onSrvccStateChanged(int);
method public void onVoiceActivationStateChanged(int);
- field @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) public static final int LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH = 512; // 0x200
field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000
field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000
field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000
@@ -11197,7 +11117,6 @@ package android.telephony {
}
public final class PreciseDataConnectionState implements android.os.Parcelable {
- ctor public PreciseDataConnectionState(int, int, int, @NonNull String, @Nullable android.net.LinkProperties, int, @Nullable android.telephony.data.ApnSetting);
method @Deprecated @NonNull public String getDataConnectionApn();
method @Deprecated public int getDataConnectionApnTypeBitMask();
method @Deprecated public int getDataConnectionFailCause();
@@ -11305,7 +11224,6 @@ package android.telephony {
public class ServiceState implements android.os.Parcelable {
method public void fillInNotifierBundle(@NonNull android.os.Bundle);
- method public int getDataNetworkType();
method @Nullable public android.telephony.NetworkRegistrationInfo getNetworkRegistrationInfo(int, int);
method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoListForDomain(int);
method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoListForTransportType(int);
@@ -11451,7 +11369,6 @@ package android.telephony {
method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_MESSAGES_ON_ICC) public java.util.List<android.telephony.SmsMessage> getMessagesFromIcc();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getPremiumSmsConsent(@NonNull String);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getSmsCapacityOnIcc();
- method public void sendMultipartTextMessage(@NonNull String, @Nullable String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, @NonNull String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void sendMultipartTextMessageWithoutPersisting(String, String, java.util.List<java.lang.String>, java.util.List<android.app.PendingIntent>, java.util.List<android.app.PendingIntent>);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setPremiumSmsConsent(@NonNull String, int);
field public static final int PREMIUM_SMS_CONSENT_ALWAYS_ALLOW = 3; // 0x3
@@ -11540,7 +11457,6 @@ package android.telephony {
public class TelephonyManager {
method @Deprecated @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void call(String, String);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int changeIccLockPassword(@NonNull String, @NonNull String);
method public int checkCarrierPrivilegesForPackage(String);
method public int checkCarrierPrivilegesForPackageAnyPhone(String);
method public void dial(String);
@@ -11551,8 +11467,7 @@ package android.telephony {
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getAidForAppType(int);
method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getAllowedNetworkTypes();
- method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CallForwardingInfo getCallForwarding(int);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCallWaitingStatus();
+ method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.ComponentName getAndUpdateDefaultRespondViaMessageApplication();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int);
method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
method public java.util.List<java.lang.String> getCarrierPackageNamesForIntentAndPhone(android.content.Intent, int);
@@ -11564,18 +11479,16 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin(int);
method public String getCdmaPrlVersion();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCdmaRoamingMode();
method public int getCurrentPhoneType();
method public int getCurrentPhoneType(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getDataActivationState();
method @Deprecated public boolean getDataEnabled();
method @Deprecated public boolean getDataEnabled(int);
- method @Nullable public static android.content.ComponentName getDefaultRespondViaMessageApplication(@NonNull android.content.Context, boolean);
+ method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.ComponentName getDefaultRespondViaMessageApplication();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDeviceSoftwareVersion(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getEmergencyCallbackMode();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEmergencyNumberDbVersion();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimDomain();
- method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String[] getIsimImpu();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimIst();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Map<java.lang.Integer,java.lang.Integer> getLogicalToPhysicalSlotMapping();
method public int getMaxNumberOfSimultaneouslyActiveSims();
@@ -11602,12 +11515,10 @@ package android.telephony {
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);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataAllowedInVoiceCall();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataConnectionAllowed();
method public boolean isDataConnectivityPossible();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataEnabledForApn(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isEmergencyAssistanceEnabled();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean isIccLockEnabled();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isInEmergencySmsMode();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isLteCdmaEvdoGsmWcdmaEnabled();
@@ -11637,17 +11548,11 @@ package android.telephony {
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setAllowedNetworkTypes(long);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setAlwaysAllowMmsData(boolean);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCallForwarding(@NonNull android.telephony.CallForwardingInfo);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCallWaitingStatus(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCarrierDataEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setCarrierRestrictionRules(@NonNull android.telephony.CarrierRestrictionRules);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCdmaRoamingMode(int);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCdmaSubscriptionMode(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setDataAllowedDuringVoiceCall(boolean);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setIccLockEnabled(boolean, @NonNull String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setPreferredNetworkTypeBitmask(long);
@@ -11686,10 +11591,6 @@ package android.telephony {
field public static final String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.telephony.action.SIM_APPLICATION_STATE_CHANGED";
field public static final String ACTION_SIM_CARD_STATE_CHANGED = "android.telephony.action.SIM_CARD_STATE_CHANGED";
field public static final String ACTION_SIM_SLOT_STATUS_CHANGED = "android.telephony.action.SIM_SLOT_STATUS_CHANGED";
- field public static final int CALL_WAITING_STATUS_ACTIVE = 1; // 0x1
- field public static final int CALL_WAITING_STATUS_INACTIVE = 2; // 0x2
- field public static final int CALL_WAITING_STATUS_NOT_SUPPORTED = 4; // 0x4
- field public static final int CALL_WAITING_STATUS_UNKNOWN_ERROR = 3; // 0x3
field public static final int CARD_POWER_DOWN = 0; // 0x0
field public static final int CARD_POWER_UP = 1; // 0x1
field public static final int CARD_POWER_UP_PASS_THROUGH = 2; // 0x2
@@ -11697,10 +11598,6 @@ package android.telephony {
field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff
- field public static final int CDMA_SUBSCRIPTION_NV = 1; // 0x1
- field public static final int CDMA_SUBSCRIPTION_RUIM_SIM = 0; // 0x0
- field public static final int CDMA_SUBSCRIPTION_UNKNOWN = -1; // 0xffffffff
- field public static final int CHANGE_ICC_LOCK_SUCCESS = 2147483647; // 0x7fffffff
field public static final String EXTRA_ANOMALY_DESCRIPTION = "android.telephony.extra.ANOMALY_DESCRIPTION";
field public static final String EXTRA_ANOMALY_ID = "android.telephony.extra.ANOMALY_ID";
field @Deprecated public static final String EXTRA_APN_PROTOCOL = "apnProto";
@@ -12322,10 +12219,6 @@ package android.telephony.ims {
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsExternalCallState> CREATOR;
}
- public class ImsManager {
- field public static final String ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION = "com.android.internal.intent.action.ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION";
- }
-
public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
method @Deprecated @NonNull @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getFeatureState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>) throws android.telephony.ims.ImsException;
@@ -12591,82 +12484,12 @@ package android.telephony.ims {
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback);
- field public static final int KEY_1X_EPDG_TIMER_SEC = 64; // 0x40
- field public static final int KEY_1X_THRESHOLD = 59; // 0x3b
- field public static final int KEY_AMR_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 50; // 0x32
- field public static final int KEY_AMR_CODEC_MODE_SET_VALUES = 0; // 0x0
- field public static final int KEY_AMR_DEFAULT_ENCODING_MODE = 53; // 0x35
- field public static final int KEY_AMR_OCTET_ALIGNED_PAYLOAD_TYPE = 49; // 0x31
- field public static final int KEY_AMR_WB_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 48; // 0x30
- field public static final int KEY_AMR_WB_CODEC_MODE_SET_VALUES = 1; // 0x1
- field public static final int KEY_AMR_WB_OCTET_ALIGNED_PAYLOAD_TYPE = 47; // 0x2f
- field public static final int KEY_DTMF_NB_PAYLOAD_TYPE = 52; // 0x34
- field public static final int KEY_DTMF_WB_PAYLOAD_TYPE = 51; // 0x33
- field public static final int KEY_EAB_PROVISIONING_STATUS = 25; // 0x19
- field public static final int KEY_ENABLE_SILENT_REDIAL = 6; // 0x6
- field public static final int KEY_LOCAL_BREAKOUT_PCSCF_ADDRESS = 31; // 0x1f
- field public static final int KEY_LTE_EPDG_TIMER_SEC = 62; // 0x3e
- field public static final int KEY_LTE_THRESHOLD_1 = 56; // 0x38
- field public static final int KEY_LTE_THRESHOLD_2 = 57; // 0x39
- field public static final int KEY_LTE_THRESHOLD_3 = 58; // 0x3a
- field public static final int KEY_MINIMUM_SIP_SESSION_EXPIRATION_TIMER_SEC = 3; // 0x3
- field public static final int KEY_MOBILE_DATA_ENABLED = 29; // 0x1d
- field public static final int KEY_MULTIENDPOINT_ENABLED = 65; // 0x41
- field public static final int KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC = 19; // 0x13
- field public static final int KEY_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC = 18; // 0x12
- field public static final int KEY_RCS_CAPABILITIES_POLL_INTERVAL_SEC = 20; // 0x14
- field public static final int KEY_RCS_CAPABILITY_DISCOVERY_ENABLED = 17; // 0x11
- field public static final int KEY_RCS_CAPABILITY_POLL_LIST_SUB_EXP_SEC = 23; // 0x17
- field public static final int KEY_RCS_MAX_NUM_ENTRIES_IN_RCL = 22; // 0x16
- field public static final int KEY_RCS_PUBLISH_OFFLINE_AVAILABILITY_TIMER_SEC = 16; // 0x10
- field public static final int KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS = 21; // 0x15
- field public static final int KEY_RCS_PUBLISH_TIMER_SEC = 15; // 0xf
- field public static final int KEY_REGISTRATION_DOMAIN_NAME = 12; // 0xc
- field public static final int KEY_REGISTRATION_RETRY_BASE_TIME_SEC = 33; // 0x21
- field public static final int KEY_REGISTRATION_RETRY_MAX_TIME_SEC = 34; // 0x22
- field public static final int KEY_RTP_SPEECH_END_PORT = 36; // 0x24
- field public static final int KEY_RTP_SPEECH_START_PORT = 35; // 0x23
- field public static final int KEY_RTT_ENABLED = 66; // 0x42
- field public static final int KEY_SIP_ACK_RECEIPT_WAIT_TIME_MS = 43; // 0x2b
- field public static final int KEY_SIP_ACK_RETRANSMIT_WAIT_TIME_MS = 44; // 0x2c
- field public static final int KEY_SIP_INVITE_ACK_WAIT_TIME_MS = 38; // 0x26
- field public static final int KEY_SIP_INVITE_CANCELLATION_TIMER_MS = 4; // 0x4
- field public static final int KEY_SIP_INVITE_REQUEST_TRANSMIT_INTERVAL_MS = 37; // 0x25
- field public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_INTERVAL_MS = 42; // 0x2a
- field public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_WAIT_TIME_MS = 39; // 0x27
- field public static final int KEY_SIP_KEEP_ALIVE_ENABLED = 32; // 0x20
- field public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMISSION_WAIT_TIME_MS = 45; // 0x2d
- field public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMIT_INTERVAL_MS = 40; // 0x28
- field public static final int KEY_SIP_NON_INVITE_RESPONSE_RETRANSMISSION_WAIT_TIME_MS = 46; // 0x2e
- field public static final int KEY_SIP_NON_INVITE_TRANSACTION_TIMEOUT_TIMER_MS = 41; // 0x29
- field public static final int KEY_SIP_SESSION_TIMER_SEC = 2; // 0x2
- field public static final int KEY_SMS_FORMAT = 13; // 0xd
- field public static final int KEY_SMS_OVER_IP_ENABLED = 14; // 0xe
- field public static final int KEY_SMS_PUBLIC_SERVICE_IDENTITY = 54; // 0x36
- field public static final int KEY_T1_TIMER_VALUE_MS = 7; // 0x7
- field public static final int KEY_T2_TIMER_VALUE_MS = 8; // 0x8
- field public static final int KEY_TF_TIMER_VALUE_MS = 9; // 0x9
- field public static final int KEY_TRANSITION_TO_LTE_DELAY_MS = 5; // 0x5
- field public static final int KEY_USE_GZIP_FOR_LIST_SUBSCRIPTION = 24; // 0x18
- field public static final int KEY_VIDEO_QUALITY = 55; // 0x37
- field public static final int KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE = 28; // 0x1c
field public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; // 0x1b
field public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; // 0x1a
- field public static final int KEY_VOLTE_PROVISIONING_STATUS = 10; // 0xa
- field public static final int KEY_VOLTE_USER_OPT_IN_STATUS = 30; // 0x1e
- field public static final int KEY_VT_PROVISIONING_STATUS = 11; // 0xb
- field public static final int KEY_WIFI_EPDG_TIMER_SEC = 63; // 0x3f
- field public static final int KEY_WIFI_THRESHOLD_A = 60; // 0x3c
- field public static final int KEY_WIFI_THRESHOLD_B = 61; // 0x3d
- field public static final int PROVISIONING_RESULT_UNKNOWN = -1; // 0xffffffff
field public static final int PROVISIONING_VALUE_DISABLED = 0; // 0x0
field public static final int PROVISIONING_VALUE_ENABLED = 1; // 0x1
- field public static final int SMS_FORMAT_3GPP = 1; // 0x1
- field public static final int SMS_FORMAT_3GPP2 = 0; // 0x0
field public static final String STRING_QUERY_RESULT_ERROR_GENERIC = "STRING_QUERY_RESULT_ERROR_GENERIC";
field public static final String STRING_QUERY_RESULT_ERROR_NOT_READY = "STRING_QUERY_RESULT_ERROR_NOT_READY";
- field public static final int VIDEO_QUALITY_HIGH = 1; // 0x1
- field public static final int VIDEO_QUALITY_LOW = 0; // 0x0
}
public static class ProvisioningManager.Callback {
@@ -12675,56 +12498,6 @@ package android.telephony.ims {
method public void onProvisioningStringChanged(int, @NonNull String);
}
- public final class RcsContactUceCapability implements android.os.Parcelable {
- method public int describeContents();
- method @NonNull public java.util.List<java.lang.String> getCapableExtensionTags();
- method @NonNull public android.net.Uri getContactUri();
- method @Nullable public android.net.Uri getServiceUri(long);
- method public boolean isCapable(long);
- method public boolean isCapable(@NonNull String);
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field public static final int CAPABILITY_CALL_COMPOSER = 4194304; // 0x400000
- field public static final int CAPABILITY_CHAT_BOT = 67108864; // 0x4000000
- field public static final int CAPABILITY_CHAT_BOT_ROLE = 134217728; // 0x8000000
- field public static final int CAPABILITY_CHAT_SESSION = 2; // 0x2
- field public static final int CAPABILITY_CHAT_SESSION_STORE_FORWARD = 4; // 0x4
- field public static final int CAPABILITY_CHAT_STANDALONE = 1; // 0x1
- field public static final int CAPABILITY_DISCOVERY_VIA_PRESENCE = 4096; // 0x1000
- field public static final int CAPABILITY_FILE_TRANSFER = 8; // 0x8
- field public static final int CAPABILITY_FILE_TRANSFER_HTTP = 64; // 0x40
- field public static final int CAPABILITY_FILE_TRANSFER_SMS = 128; // 0x80
- field public static final int CAPABILITY_FILE_TRANSFER_STORE_FORWARD = 32; // 0x20
- field public static final int CAPABILITY_FILE_TRANSFER_THUMBNAIL = 16; // 0x10
- field public static final int CAPABILITY_GEOLOCATION_PULL = 131072; // 0x20000
- field public static final int CAPABILITY_GEOLOCATION_PULL_FILE_TRANSFER = 262144; // 0x40000
- field public static final int CAPABILITY_GEOLOCATION_PUSH = 32768; // 0x8000
- field public static final int CAPABILITY_GEOLOCATION_PUSH_SMS = 65536; // 0x10000
- field public static final int CAPABILITY_IMAGE_SHARE = 256; // 0x100
- field public static final int CAPABILITY_IP_VIDEO_CALL = 16384; // 0x4000
- field public static final int CAPABILITY_IP_VOICE_CALL = 8192; // 0x2000
- field public static final int CAPABILITY_MMTEL_CALL_COMPOSER = 1073741824; // 0x40000000
- field public static final int CAPABILITY_PLUG_IN = 268435456; // 0x10000000
- field public static final int CAPABILITY_POST_CALL = 8388608; // 0x800000
- field public static final int CAPABILITY_RCS_VIDEO_CALL = 1048576; // 0x100000
- field public static final int CAPABILITY_RCS_VIDEO_ONLY_CALL = 2097152; // 0x200000
- field public static final int CAPABILITY_RCS_VOICE_CALL = 524288; // 0x80000
- field public static final int CAPABILITY_SHARED_MAP = 16777216; // 0x1000000
- field public static final int CAPABILITY_SHARED_SKETCH = 33554432; // 0x2000000
- field public static final int CAPABILITY_SOCIAL_PRESENCE = 2048; // 0x800
- field public static final int CAPABILITY_STANDALONE_CHAT_BOT = 536870912; // 0x20000000
- field public static final int CAPABILITY_VIDEO_SHARE = 1024; // 0x400
- field public static final int CAPABILITY_VIDEO_SHARE_DURING_CS_CALL = 512; // 0x200
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsContactUceCapability> CREATOR;
- }
-
- public static class RcsContactUceCapability.Builder {
- ctor public RcsContactUceCapability.Builder(@NonNull android.net.Uri);
- method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(long, @NonNull android.net.Uri);
- method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(long);
- method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(@NonNull String);
- method @NonNull public android.telephony.ims.RcsContactUceCapability build();
- }
-
public class RcsUceAdapter {
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
}
@@ -12971,24 +12744,12 @@ package android.telephony.ims.stub {
method public int transact(android.os.Bundle);
method public int updateCallBarring(int, int, String[]);
method public int updateCallBarringForServiceClass(int, int, String[], int);
- method public int updateCallBarringWithPassword(int, int, @Nullable String[], int, @NonNull String);
method public int updateCallForward(int, int, String, int, int);
method public int updateCallWaiting(boolean, int);
method public int updateClip(boolean);
method public int updateClir(int);
method public int updateColp(boolean);
method public int updateColr(int);
- field public static final int CALL_BARRING_ALL = 7; // 0x7
- field public static final int CALL_BARRING_ALL_INCOMING = 1; // 0x1
- field public static final int CALL_BARRING_ALL_OUTGOING = 2; // 0x2
- field public static final int CALL_BARRING_ANONYMOUS_INCOMING = 6; // 0x6
- field public static final int CALL_BARRING_INCOMING_ALL_SERVICES = 9; // 0x9
- field public static final int CALL_BARRING_OUTGOING_ALL_SERVICES = 8; // 0x8
- field public static final int CALL_BARRING_OUTGOING_INTL = 3; // 0x3
- field public static final int CALL_BARRING_OUTGOING_INTL_EXCL_HOME = 4; // 0x4
- field public static final int CALL_BARRING_SPECIFIC_INCOMING_CALLS = 10; // 0xa
- field public static final int CALL_BLOCKING_INCOMING_WHEN_ROAMING = 5; // 0x5
- field public static final int INVALID_RESULT = -1; // 0xffffffff
}
}
diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt
index 55333cf06ffc..10c96a330bf4 100644
--- a/api/system-lint-baseline.txt
+++ b/api/system-lint-baseline.txt
@@ -246,12 +246,6 @@ ProtectedMember: android.service.notification.NotificationAssistantService#attac
-ResourceValueFieldName: android.R.array#config_sms_enabled_locking_shift_tables:
- Expected resource name in `android.R.array` to be in the `fooBarBaz` style, was `config_sms_enabled_locking_shift_tables`
-ResourceValueFieldName: android.R.array#config_sms_enabled_single_shift_tables:
- Expected resource name in `android.R.array` to be in the `fooBarBaz` style, was `config_sms_enabled_single_shift_tables`
-
-
SamShouldBeLast: android.accounts.AccountManager#addAccount(String, String, String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler):
SamShouldBeLast: android.accounts.AccountManager#addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean):
diff --git a/api/test-current.txt b/api/test-current.txt
index 1403b69afca3..5bd1f89ec081 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -83,7 +83,6 @@ package android.app {
method public static void resumeAppSwitches() throws android.os.RemoteException;
method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public void scheduleApplicationInfoChanged(java.util.List<java.lang.String>, int);
method @RequiresPermission("android.permission.MANAGE_USERS") public boolean switchUser(@NonNull android.os.UserHandle);
- method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public boolean updateMccMncConfiguration(@NonNull String, @NonNull String);
field public static final int PROCESS_CAPABILITY_ALL = 7; // 0x7
field public static final int PROCESS_CAPABILITY_ALL_EXPLICIT = 1; // 0x1
field public static final int PROCESS_CAPABILITY_ALL_IMPLICIT = 6; // 0x6
@@ -597,9 +596,22 @@ package android.app.backup {
package android.app.blob {
public class BlobStoreManager {
+ method @Nullable public android.app.blob.LeaseInfo getLeaseInfo(@NonNull android.app.blob.BlobHandle) throws java.io.IOException;
+ method @NonNull public java.util.List<android.app.blob.BlobHandle> getLeasedBlobs() throws java.io.IOException;
method public void waitForIdle(long) throws java.lang.InterruptedException, java.util.concurrent.TimeoutException;
}
+ public final class LeaseInfo implements android.os.Parcelable {
+ ctor public LeaseInfo(@NonNull String, long, @IdRes int, @Nullable CharSequence);
+ method public int describeContents();
+ method @Nullable public CharSequence getDescription();
+ method @IdRes public int getDescriptionResId();
+ method public long getExpiryTimeMillis();
+ method @NonNull public String getPackageName();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.blob.LeaseInfo> CREATOR;
+ }
+
}
package android.app.prediction {
@@ -844,6 +856,7 @@ package android.content.integrity {
method @NonNull public android.content.integrity.RuleSet getCurrentRuleSet();
method @NonNull public String getCurrentRuleSetProvider();
method @NonNull public String getCurrentRuleSetVersion();
+ method @NonNull public java.util.List<java.lang.String> getWhitelistedRuleProviders();
method public void updateRuleSet(@NonNull android.content.integrity.RuleSet, @NonNull android.content.IntentSender);
field public static final String EXTRA_STATUS = "android.content.integrity.extra.STATUS";
field public static final int STATUS_FAILURE = 1; // 0x1
@@ -920,6 +933,7 @@ package android.content.pm {
}
public static class PackageInstaller.SessionInfo implements android.os.Parcelable {
+ method public int getAutoRevokePermissionsMode();
method public int getRollbackDataPolicy();
method @NonNull public java.util.Set<java.lang.String> getWhitelistedRestrictedPermissions();
}
@@ -3420,13 +3434,15 @@ package android.service.notification {
package android.service.quickaccesswallet {
- public interface QuickAccessWalletClient {
+ public interface QuickAccessWalletClient extends java.io.Closeable {
method public void addWalletServiceEventListener(@NonNull android.service.quickaccesswallet.QuickAccessWalletClient.WalletServiceEventListener);
+ method public void addWalletServiceEventListener(@NonNull java.util.concurrent.Executor, @NonNull android.service.quickaccesswallet.QuickAccessWalletClient.WalletServiceEventListener);
method @NonNull public static android.service.quickaccesswallet.QuickAccessWalletClient create(@NonNull android.content.Context);
method @Nullable public android.content.Intent createWalletIntent();
method @Nullable public android.content.Intent createWalletSettingsIntent();
method public void disconnect();
method public void getWalletCards(@NonNull android.service.quickaccesswallet.GetWalletCardsRequest, @NonNull android.service.quickaccesswallet.QuickAccessWalletClient.OnWalletCardsRetrievedCallback);
+ method public void getWalletCards(@NonNull java.util.concurrent.Executor, @NonNull android.service.quickaccesswallet.GetWalletCardsRequest, @NonNull android.service.quickaccesswallet.QuickAccessWalletClient.OnWalletCardsRetrievedCallback);
method public boolean isWalletFeatureAvailable();
method public boolean isWalletFeatureAvailableWhenDeviceLocked();
method public boolean isWalletServiceAvailable();
@@ -3721,7 +3737,6 @@ package android.telephony {
public final class SmsManager {
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int checkSmsShortCodeDestination(String, String);
- method public void sendMultipartTextMessage(@NonNull String, @Nullable String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, @NonNull String);
field public static final int SMS_CATEGORY_FREE_SHORT_CODE = 1; // 0x1
field public static final int SMS_CATEGORY_NOT_SHORT_CODE = 0; // 0x0
field public static final int SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE = 3; // 0x3
@@ -3742,9 +3757,10 @@ package android.telephony {
public class TelephonyManager {
method public int addDevicePolicyOverrideApn(@NonNull android.content.Context, @NonNull android.telephony.data.ApnSetting);
method public int checkCarrierPrivilegesForPackage(String);
+ method @Nullable @RequiresPermission("android.permission.INTERACT_ACROSS_USERS") public android.content.ComponentName getAndUpdateDefaultRespondViaMessageApplication();
method public int getCarrierIdListVersion();
method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
- method @Nullable public static android.content.ComponentName getDefaultRespondViaMessageApplication(@NonNull android.content.Context, boolean);
+ method @Nullable @RequiresPermission("android.permission.INTERACT_ACROSS_USERS") public android.content.ComponentName getDefaultRespondViaMessageApplication();
method @NonNull public java.util.List<android.telephony.data.ApnSetting> getDevicePolicyOverrideApns(@NonNull android.content.Context);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public int getEmergencyNumberDbVersion();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getLine1AlphaTag();
@@ -4020,10 +4036,6 @@ package android.telephony.ims {
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsExternalCallState> CREATOR;
}
- public class ImsManager {
- field public static final String ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION = "com.android.internal.intent.action.ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION";
- }
-
public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
method @Deprecated @NonNull @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getFeatureState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>) throws android.telephony.ims.ImsException;
@@ -4285,82 +4297,12 @@ package android.telephony.ims {
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback);
- field public static final int KEY_1X_EPDG_TIMER_SEC = 64; // 0x40
- field public static final int KEY_1X_THRESHOLD = 59; // 0x3b
- field public static final int KEY_AMR_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 50; // 0x32
- field public static final int KEY_AMR_CODEC_MODE_SET_VALUES = 0; // 0x0
- field public static final int KEY_AMR_DEFAULT_ENCODING_MODE = 53; // 0x35
- field public static final int KEY_AMR_OCTET_ALIGNED_PAYLOAD_TYPE = 49; // 0x31
- field public static final int KEY_AMR_WB_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 48; // 0x30
- field public static final int KEY_AMR_WB_CODEC_MODE_SET_VALUES = 1; // 0x1
- field public static final int KEY_AMR_WB_OCTET_ALIGNED_PAYLOAD_TYPE = 47; // 0x2f
- field public static final int KEY_DTMF_NB_PAYLOAD_TYPE = 52; // 0x34
- field public static final int KEY_DTMF_WB_PAYLOAD_TYPE = 51; // 0x33
- field public static final int KEY_EAB_PROVISIONING_STATUS = 25; // 0x19
- field public static final int KEY_ENABLE_SILENT_REDIAL = 6; // 0x6
- field public static final int KEY_LOCAL_BREAKOUT_PCSCF_ADDRESS = 31; // 0x1f
- field public static final int KEY_LTE_EPDG_TIMER_SEC = 62; // 0x3e
- field public static final int KEY_LTE_THRESHOLD_1 = 56; // 0x38
- field public static final int KEY_LTE_THRESHOLD_2 = 57; // 0x39
- field public static final int KEY_LTE_THRESHOLD_3 = 58; // 0x3a
- field public static final int KEY_MINIMUM_SIP_SESSION_EXPIRATION_TIMER_SEC = 3; // 0x3
- field public static final int KEY_MOBILE_DATA_ENABLED = 29; // 0x1d
- field public static final int KEY_MULTIENDPOINT_ENABLED = 65; // 0x41
- field public static final int KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC = 19; // 0x13
- field public static final int KEY_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC = 18; // 0x12
- field public static final int KEY_RCS_CAPABILITIES_POLL_INTERVAL_SEC = 20; // 0x14
- field public static final int KEY_RCS_CAPABILITY_DISCOVERY_ENABLED = 17; // 0x11
- field public static final int KEY_RCS_CAPABILITY_POLL_LIST_SUB_EXP_SEC = 23; // 0x17
- field public static final int KEY_RCS_MAX_NUM_ENTRIES_IN_RCL = 22; // 0x16
- field public static final int KEY_RCS_PUBLISH_OFFLINE_AVAILABILITY_TIMER_SEC = 16; // 0x10
- field public static final int KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS = 21; // 0x15
- field public static final int KEY_RCS_PUBLISH_TIMER_SEC = 15; // 0xf
- field public static final int KEY_REGISTRATION_DOMAIN_NAME = 12; // 0xc
- field public static final int KEY_REGISTRATION_RETRY_BASE_TIME_SEC = 33; // 0x21
- field public static final int KEY_REGISTRATION_RETRY_MAX_TIME_SEC = 34; // 0x22
- field public static final int KEY_RTP_SPEECH_END_PORT = 36; // 0x24
- field public static final int KEY_RTP_SPEECH_START_PORT = 35; // 0x23
- field public static final int KEY_RTT_ENABLED = 66; // 0x42
- field public static final int KEY_SIP_ACK_RECEIPT_WAIT_TIME_MS = 43; // 0x2b
- field public static final int KEY_SIP_ACK_RETRANSMIT_WAIT_TIME_MS = 44; // 0x2c
- field public static final int KEY_SIP_INVITE_ACK_WAIT_TIME_MS = 38; // 0x26
- field public static final int KEY_SIP_INVITE_CANCELLATION_TIMER_MS = 4; // 0x4
- field public static final int KEY_SIP_INVITE_REQUEST_TRANSMIT_INTERVAL_MS = 37; // 0x25
- field public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_INTERVAL_MS = 42; // 0x2a
- field public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_WAIT_TIME_MS = 39; // 0x27
- field public static final int KEY_SIP_KEEP_ALIVE_ENABLED = 32; // 0x20
- field public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMISSION_WAIT_TIME_MS = 45; // 0x2d
- field public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMIT_INTERVAL_MS = 40; // 0x28
- field public static final int KEY_SIP_NON_INVITE_RESPONSE_RETRANSMISSION_WAIT_TIME_MS = 46; // 0x2e
- field public static final int KEY_SIP_NON_INVITE_TRANSACTION_TIMEOUT_TIMER_MS = 41; // 0x29
- field public static final int KEY_SIP_SESSION_TIMER_SEC = 2; // 0x2
- field public static final int KEY_SMS_FORMAT = 13; // 0xd
- field public static final int KEY_SMS_OVER_IP_ENABLED = 14; // 0xe
- field public static final int KEY_SMS_PUBLIC_SERVICE_IDENTITY = 54; // 0x36
- field public static final int KEY_T1_TIMER_VALUE_MS = 7; // 0x7
- field public static final int KEY_T2_TIMER_VALUE_MS = 8; // 0x8
- field public static final int KEY_TF_TIMER_VALUE_MS = 9; // 0x9
- field public static final int KEY_TRANSITION_TO_LTE_DELAY_MS = 5; // 0x5
- field public static final int KEY_USE_GZIP_FOR_LIST_SUBSCRIPTION = 24; // 0x18
- field public static final int KEY_VIDEO_QUALITY = 55; // 0x37
- field public static final int KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE = 28; // 0x1c
field public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; // 0x1b
field public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; // 0x1a
- field public static final int KEY_VOLTE_PROVISIONING_STATUS = 10; // 0xa
- field public static final int KEY_VOLTE_USER_OPT_IN_STATUS = 30; // 0x1e
- field public static final int KEY_VT_PROVISIONING_STATUS = 11; // 0xb
- field public static final int KEY_WIFI_EPDG_TIMER_SEC = 63; // 0x3f
- field public static final int KEY_WIFI_THRESHOLD_A = 60; // 0x3c
- field public static final int KEY_WIFI_THRESHOLD_B = 61; // 0x3d
- field public static final int PROVISIONING_RESULT_UNKNOWN = -1; // 0xffffffff
field public static final int PROVISIONING_VALUE_DISABLED = 0; // 0x0
field public static final int PROVISIONING_VALUE_ENABLED = 1; // 0x1
- field public static final int SMS_FORMAT_3GPP = 1; // 0x1
- field public static final int SMS_FORMAT_3GPP2 = 0; // 0x0
field public static final String STRING_QUERY_RESULT_ERROR_GENERIC = "STRING_QUERY_RESULT_ERROR_GENERIC";
field public static final String STRING_QUERY_RESULT_ERROR_NOT_READY = "STRING_QUERY_RESULT_ERROR_NOT_READY";
- field public static final int VIDEO_QUALITY_HIGH = 1; // 0x1
- field public static final int VIDEO_QUALITY_LOW = 0; // 0x0
}
public static class ProvisioningManager.Callback {
@@ -4369,56 +4311,6 @@ package android.telephony.ims {
method public void onProvisioningStringChanged(int, @NonNull String);
}
- public final class RcsContactUceCapability implements android.os.Parcelable {
- method public int describeContents();
- method @NonNull public java.util.List<java.lang.String> getCapableExtensionTags();
- method @NonNull public android.net.Uri getContactUri();
- method @Nullable public android.net.Uri getServiceUri(long);
- method public boolean isCapable(long);
- method public boolean isCapable(@NonNull String);
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field public static final int CAPABILITY_CALL_COMPOSER = 4194304; // 0x400000
- field public static final int CAPABILITY_CHAT_BOT = 67108864; // 0x4000000
- field public static final int CAPABILITY_CHAT_BOT_ROLE = 134217728; // 0x8000000
- field public static final int CAPABILITY_CHAT_SESSION = 2; // 0x2
- field public static final int CAPABILITY_CHAT_SESSION_STORE_FORWARD = 4; // 0x4
- field public static final int CAPABILITY_CHAT_STANDALONE = 1; // 0x1
- field public static final int CAPABILITY_DISCOVERY_VIA_PRESENCE = 4096; // 0x1000
- field public static final int CAPABILITY_FILE_TRANSFER = 8; // 0x8
- field public static final int CAPABILITY_FILE_TRANSFER_HTTP = 64; // 0x40
- field public static final int CAPABILITY_FILE_TRANSFER_SMS = 128; // 0x80
- field public static final int CAPABILITY_FILE_TRANSFER_STORE_FORWARD = 32; // 0x20
- field public static final int CAPABILITY_FILE_TRANSFER_THUMBNAIL = 16; // 0x10
- field public static final int CAPABILITY_GEOLOCATION_PULL = 131072; // 0x20000
- field public static final int CAPABILITY_GEOLOCATION_PULL_FILE_TRANSFER = 262144; // 0x40000
- field public static final int CAPABILITY_GEOLOCATION_PUSH = 32768; // 0x8000
- field public static final int CAPABILITY_GEOLOCATION_PUSH_SMS = 65536; // 0x10000
- field public static final int CAPABILITY_IMAGE_SHARE = 256; // 0x100
- field public static final int CAPABILITY_IP_VIDEO_CALL = 16384; // 0x4000
- field public static final int CAPABILITY_IP_VOICE_CALL = 8192; // 0x2000
- field public static final int CAPABILITY_MMTEL_CALL_COMPOSER = 1073741824; // 0x40000000
- field public static final int CAPABILITY_PLUG_IN = 268435456; // 0x10000000
- field public static final int CAPABILITY_POST_CALL = 8388608; // 0x800000
- field public static final int CAPABILITY_RCS_VIDEO_CALL = 1048576; // 0x100000
- field public static final int CAPABILITY_RCS_VIDEO_ONLY_CALL = 2097152; // 0x200000
- field public static final int CAPABILITY_RCS_VOICE_CALL = 524288; // 0x80000
- field public static final int CAPABILITY_SHARED_MAP = 16777216; // 0x1000000
- field public static final int CAPABILITY_SHARED_SKETCH = 33554432; // 0x2000000
- field public static final int CAPABILITY_SOCIAL_PRESENCE = 2048; // 0x800
- field public static final int CAPABILITY_STANDALONE_CHAT_BOT = 536870912; // 0x20000000
- field public static final int CAPABILITY_VIDEO_SHARE = 1024; // 0x400
- field public static final int CAPABILITY_VIDEO_SHARE_DURING_CS_CALL = 512; // 0x200
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsContactUceCapability> CREATOR;
- }
-
- public static class RcsContactUceCapability.Builder {
- ctor public RcsContactUceCapability.Builder(@NonNull android.net.Uri);
- method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(long, @NonNull android.net.Uri);
- method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(long);
- method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(@NonNull String);
- method @NonNull public android.telephony.ims.RcsContactUceCapability build();
- }
-
public class RcsUceAdapter {
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
}
@@ -4665,24 +4557,12 @@ package android.telephony.ims.stub {
method public int transact(android.os.Bundle);
method public int updateCallBarring(int, int, String[]);
method public int updateCallBarringForServiceClass(int, int, String[], int);
- method public int updateCallBarringWithPassword(int, int, @Nullable String[], int, @NonNull String);
method public int updateCallForward(int, int, String, int, int);
method public int updateCallWaiting(boolean, int);
method public int updateClip(boolean);
method public int updateClir(int);
method public int updateColp(boolean);
method public int updateColr(int);
- field public static final int CALL_BARRING_ALL = 7; // 0x7
- field public static final int CALL_BARRING_ALL_INCOMING = 1; // 0x1
- field public static final int CALL_BARRING_ALL_OUTGOING = 2; // 0x2
- field public static final int CALL_BARRING_ANONYMOUS_INCOMING = 6; // 0x6
- field public static final int CALL_BARRING_INCOMING_ALL_SERVICES = 9; // 0x9
- field public static final int CALL_BARRING_OUTGOING_ALL_SERVICES = 8; // 0x8
- field public static final int CALL_BARRING_OUTGOING_INTL = 3; // 0x3
- field public static final int CALL_BARRING_OUTGOING_INTL_EXCL_HOME = 4; // 0x4
- field public static final int CALL_BARRING_SPECIFIC_INCOMING_CALLS = 10; // 0xa
- field public static final int CALL_BLOCKING_INCOMING_WHEN_ROAMING = 5; // 0x5
- field public static final int INVALID_RESULT = -1; // 0xffffffff
}
}
@@ -4954,8 +4834,8 @@ package android.view {
}
public class SurfaceControlViewHost {
- method public void addView(@NonNull android.view.View, android.view.WindowManager.LayoutParams);
method public void relayout(android.view.WindowManager.LayoutParams);
+ method public void setView(@NonNull android.view.View, @NonNull android.view.WindowManager.LayoutParams);
}
@UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback {
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index bd5bdc6c4f2f..1cbe1508e6bb 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -398,6 +398,8 @@ message Atom {
256 [(module) = "framework"];
DisplayJankReported display_jank_reported = 257;
AppStandbyBucketChanged app_standby_bucket_changed = 258 [(module) = "framework"];
+ SharesheetStarted sharesheet_started = 259 [(module) = "framework"];
+ RankingSelected ranking_selected = 260 [(module) = "framework"];
SdkExtensionStatus sdk_extension_status = 354;
}
@@ -3332,16 +3334,12 @@ message ForegroundServiceAppOpSessionEnded {
optional int32 uid = 1 [(is_uid) = true];
// The operation's name.
- // To the extent possible, preserve the mapping from AppOpsManager.OP_ constants.
- // Only these named ops are actually logged.
- enum AppOpName {
- OP_NONE = -1; // Also represents UNKNOWN.
- OP_COARSE_LOCATION = 0;
- OP_FINE_LOCATION = 1;
- OP_CAMERA = 26;
- OP_RECORD_AUDIO = 27;
- }
- optional AppOpName app_op_name = 2 [default = OP_NONE];
+ // Only following four ops are logged
+ // COARSE_LOCATION = 0
+ // FINE_LOCATION = 1
+ // CAMERA = 26
+ // RECORD_AUDIO = 27
+ optional android.app.AppOpEnum app_op_name = 2 [default = APP_OP_NONE];
// The uid's permission mode for accessing the AppOp during this fgs session.
enum Mode {
@@ -7571,8 +7569,8 @@ message AppOps {
// Name of the package performing the op
optional string package_name = 2;
- // operation id; maps to the OP_* constants in AppOpsManager.java
- optional int32 op_id = 3;
+ // operation id
+ optional android.app.AppOpEnum op_id = 3 [default = APP_OP_NONE];
// The number of times the op was granted while the app was in the
// foreground (only for trusted requests)
@@ -7617,8 +7615,8 @@ message AttributedAppOps {
// above.
optional string tag = 3;
- // operation id; maps to the OPSTR_* constants in AppOpsManager.java
- optional string op = 4;
+ // operation id
+ optional android.app.AppOpEnum op = 4 [default = APP_OP_NONE];
// The number of times the op was granted while the app was in the
// foreground (only for trusted requests)
@@ -8490,6 +8488,7 @@ message RuntimeAppOpAccess {
DEFAULT = 0;
UNIFORM = 1;
RARELY_USED = 2;
+ BOOT_TIME_SAMPLING = 3;
}
// sampling strategy used to collect this message
@@ -8917,3 +8916,64 @@ message AppStandbyBucketChanged {
// UsageStatsManager.java.
optional int32 sub_reason = 5;
}
+
+/**
+* Reports a started sharesheet transaction.
+*
+* Logged from:
+* frameworks/base/core/java/com/android/internal/app/ChooserActivity.java
+*/
+message SharesheetStarted {
+ // The event_id (as for UiEventReported).
+ optional int32 event_id = 1;
+ // The calling app's package name.
+ optional string package_name = 2;
+ // An identifier to tie together multiple logs relating to the same share event
+ optional int32 instance_id = 3;
+ // The mime type of the share
+ optional string mime_type = 4;
+ // The number of direct targets the calling app is providing that will be shown.
+ optional int32 num_app_provided_direct_targets = 5;
+ // The number of app targets the calling app is providing that will be shown.
+ optional int32 num_app_provided_app_targets = 6;
+ // True if the share originates from the workprofile
+ optional bool is_workprofile = 7;
+
+ enum SharesheetPreviewType { // Constants from ChooserActivity.java
+ CONTENT_PREVIEW_IMAGE = 1; // The preview shown in the sharesheet is an image.
+ CONTENT_PREVIEW_FILE = 2; // The preview shown in the sharesheet is a file.
+ CONTENT_PREVIEW_TEXT = 3; // The preview shown in the sharesheet is text.
+ }
+ // How the sharesheet preview is presented.
+ optional SharesheetPreviewType previewType = 8;
+
+ enum ResolverActivityIntent { // Intents handled by ResolverActivity.java
+ INTENT_DEFAULT = 0;
+ INTENT_ACTION_VIEW = 1;
+ INTENT_ACTION_EDIT = 2;
+ INTENT_ACTION_SEND = 3;
+ INTENT_ACTION_SENDTO = 4;
+ INTENT_ACTION_SEND_MULTIPLE = 5;
+ INTENT_ACTION_IMAGE_CAPTURE = 6;
+ INTENT_ACTION_MAIN = 7;
+ }
+ // The intent being processed (only SEND and SEND_MULTIPLE are system sharesheet)
+ optional ResolverActivityIntent intentType = 9;
+}
+
+/**
+ * Reports a ranking selection event.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/app/ChooserActivity.java (sharesheet)
+ */
+message RankingSelected {
+ // The event_id (as for UiEventReported).
+ optional int32 event_id = 1;
+ // The relevant app's package name (can be source or picked package).
+ optional string package_name = 2;
+ // An identifier to tie together multiple logs relating to the same share event.
+ optional int32 instance_id = 3;
+ // Which of the ranked targets got picked, default starting position 0.
+ optional int32 position_picked = 4;
+}
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 258f84d0fb79..b515d0a5b72f 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -350,17 +350,19 @@ bool LogEvent::write(const AttributionNodeInternal& node) {
return false;
}
-void LogEvent::parseInt32(int32_t* pos, int32_t depth, bool* last) {
+void LogEvent::parseInt32(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
int32_t value = readNextValue<int32_t>();
addToValues(pos, depth, value, last);
+ parseAnnotations(numAnnotations);
}
-void LogEvent::parseInt64(int32_t* pos, int32_t depth, bool* last) {
+void LogEvent::parseInt64(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
int64_t value = readNextValue<int64_t>();
addToValues(pos, depth, value, last);
+ parseAnnotations(numAnnotations);
}
-void LogEvent::parseString(int32_t* pos, int32_t depth, bool* last) {
+void LogEvent::parseString(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
int32_t numBytes = readNextValue<int32_t>();
if ((uint32_t)numBytes > mRemainingLen) {
mValid = false;
@@ -371,20 +373,23 @@ void LogEvent::parseString(int32_t* pos, int32_t depth, bool* last) {
mBuf += numBytes;
mRemainingLen -= numBytes;
addToValues(pos, depth, value, last);
+ parseAnnotations(numAnnotations);
}
-void LogEvent::parseFloat(int32_t* pos, int32_t depth, bool* last) {
+void LogEvent::parseFloat(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
float value = readNextValue<float>();
addToValues(pos, depth, value, last);
+ parseAnnotations(numAnnotations);
}
-void LogEvent::parseBool(int32_t* pos, int32_t depth, bool* last) {
+void LogEvent::parseBool(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
// cast to int32_t because FieldValue does not support bools
int32_t value = (int32_t)readNextValue<uint8_t>();
addToValues(pos, depth, value, last);
+ parseAnnotations(numAnnotations);
}
-void LogEvent::parseByteArray(int32_t* pos, int32_t depth, bool* last) {
+void LogEvent::parseByteArray(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
int32_t numBytes = readNextValue<int32_t>();
if ((uint32_t)numBytes > mRemainingLen) {
mValid = false;
@@ -395,9 +400,10 @@ void LogEvent::parseByteArray(int32_t* pos, int32_t depth, bool* last) {
mBuf += numBytes;
mRemainingLen -= numBytes;
addToValues(pos, depth, value, last);
+ parseAnnotations(numAnnotations);
}
-void LogEvent::parseKeyValuePairs(int32_t* pos, int32_t depth, bool* last) {
+void LogEvent::parseKeyValuePairs(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
int32_t numPairs = readNextValue<uint8_t>();
for (pos[1] = 1; pos[1] <= numPairs; pos[1]++) {
@@ -405,56 +411,79 @@ void LogEvent::parseKeyValuePairs(int32_t* pos, int32_t depth, bool* last) {
// parse key
pos[2] = 1;
- parseInt32(pos, 2, last);
+ parseInt32(pos, /*depth=*/2, last, /*numAnnotations=*/0);
// parse value
last[2] = true;
- uint8_t typeId = getTypeId(readNextValue<uint8_t>());
- switch (typeId) {
+
+ uint8_t typeInfo = readNextValue<uint8_t>();
+ switch (getTypeId(typeInfo)) {
case INT32_TYPE:
pos[2] = 2; // pos[2] determined by index of type in KeyValuePair in atoms.proto
- parseInt32(pos, 2, last);
+ parseInt32(pos, /*depth=*/2, last, /*numAnnotations=*/0);
break;
case INT64_TYPE:
pos[2] = 3;
- parseInt64(pos, 2, last);
+ parseInt64(pos, /*depth=*/2, last, /*numAnnotations=*/0);
break;
case STRING_TYPE:
pos[2] = 4;
- parseString(pos, 2, last);
+ parseString(pos, /*depth=*/2, last, /*numAnnotations=*/0);
break;
case FLOAT_TYPE:
pos[2] = 5;
- parseFloat(pos, 2, last);
+ parseFloat(pos, /*depth=*/2, last, /*numAnnotations=*/0);
break;
default:
mValid = false;
}
}
+ parseAnnotations(numAnnotations);
+
pos[1] = pos[2] = 1;
last[1] = last[2] = false;
}
-void LogEvent::parseAttributionChain(int32_t* pos, int32_t depth, bool* last) {
+void LogEvent::parseAttributionChain(int32_t* pos, int32_t depth, bool* last,
+ uint8_t numAnnotations) {
int32_t numNodes = readNextValue<uint8_t>();
for (pos[1] = 1; pos[1] <= numNodes; pos[1]++) {
last[1] = (pos[1] == numNodes);
// parse uid
pos[2] = 1;
- parseInt32(pos, 2, last);
+ parseInt32(pos, /*depth=*/2, last, /*numAnnotations=*/0);
// parse tag
pos[2] = 2;
last[2] = true;
- parseString(pos, 2, last);
+ parseString(pos, /*depth=*/2, last, /*numAnnotations=*/0);
}
+ parseAnnotations(numAnnotations);
+
pos[1] = pos[2] = 1;
last[1] = last[2] = false;
}
+// TODO(b/151109630): store annotation information within LogEvent
+void LogEvent::parseAnnotations(uint8_t numAnnotations) {
+ for (uint8_t i = 0; i < numAnnotations; i++) {
+ /*uint8_t annotationId = */ readNextValue<uint8_t>();
+ uint8_t annotationType = readNextValue<uint8_t>();
+ switch (annotationType) {
+ case BOOL_TYPE:
+ /*bool annotationValue = */ readNextValue<uint8_t>();
+ break;
+ case INT32_TYPE:
+ /*int32_t annotationValue =*/ readNextValue<int32_t>();
+ break;
+ default:
+ mValid = false;
+ }
+ }
+}
// This parsing logic is tied to the encoding scheme used in StatsEvent.java and
// stats_event.c
@@ -475,6 +504,7 @@ bool LogEvent::parseBuffer(uint8_t* buf, size_t len) {
typeInfo = readNextValue<uint8_t>();
if (getTypeId(typeInfo) != INT64_TYPE) mValid = false;
mElapsedTimestampNs = readNextValue<int64_t>();
+ parseAnnotations(getNumAnnotations(typeInfo)); // atom-level annotations
numElements--;
typeInfo = readNextValue<uint8_t>();
@@ -484,37 +514,36 @@ bool LogEvent::parseBuffer(uint8_t* buf, size_t len) {
for (pos[0] = 1; pos[0] <= numElements && mValid; pos[0]++) {
+ last[0] = (pos[0] == numElements);
+
typeInfo = readNextValue<uint8_t>();
uint8_t typeId = getTypeId(typeInfo);
- last[0] = (pos[0] == numElements);
-
// TODO(b/144373276): handle errors passed to the socket
- // TODO(b/144373257): parse annotations
switch(typeId) {
case BOOL_TYPE:
- parseBool(pos, 0, last);
+ parseBool(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
break;
case INT32_TYPE:
- parseInt32(pos, 0, last);
+ parseInt32(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
break;
case INT64_TYPE:
- parseInt64(pos, 0, last);
+ parseInt64(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
break;
case FLOAT_TYPE:
- parseFloat(pos, 0, last);
+ parseFloat(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
break;
case BYTE_ARRAY_TYPE:
- parseByteArray(pos, 0, last);
+ parseByteArray(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
break;
case STRING_TYPE:
- parseString(pos, 0, last);
+ parseString(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
break;
case KEY_VALUE_PAIRS_TYPE:
- parseKeyValuePairs(pos, 0, last);
+ parseKeyValuePairs(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
break;
case ATTRIBUTION_CHAIN_TYPE:
- parseAttributionChain(pos, 0, last);
+ parseAttributionChain(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
break;
default:
mValid = false;
@@ -531,7 +560,7 @@ uint8_t LogEvent::getTypeId(uint8_t typeInfo) {
}
uint8_t LogEvent::getNumAnnotations(uint8_t typeInfo) {
- return (typeInfo >> 4) & 0x0F;
+ return (typeInfo >> 4) & 0x0F; // num annotations in upper 4 bytes
}
int64_t LogEvent::GetLong(size_t key, status_t* err) const {
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index b68eeb8d6499..6537f13c4089 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -232,14 +232,15 @@ private:
*/
LogEvent(const LogEvent&);
- void parseInt32(int32_t* pos, int32_t depth, bool* last);
- void parseInt64(int32_t* pos, int32_t depth, bool* last);
- void parseString(int32_t* pos, int32_t depth, bool* last);
- void parseFloat(int32_t* pos, int32_t depth, bool* last);
- void parseBool(int32_t* pos, int32_t depth, bool* last);
- void parseByteArray(int32_t* pos, int32_t depth, bool* last);
- void parseKeyValuePairs(int32_t* pos, int32_t depth, bool* last);
- void parseAttributionChain(int32_t* pos, int32_t depth, bool* last);
+ void parseInt32(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
+ void parseInt64(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
+ void parseString(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
+ void parseFloat(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
+ void parseBool(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
+ void parseByteArray(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
+ void parseKeyValuePairs(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
+ void parseAttributionChain(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
+ void parseAnnotations(uint8_t numAnnotations);
/**
* The below three variables are only valid during the execution of
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index e48f37856f15..7b0467c5d5a0 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -79,420 +79,363 @@ static void assertPastBucketValuesSingleKey(
}
}
-// TODO(b/149590301): Update these tests to use new socket schema.
-//class ValueMetricProducerTestHelper {
-//
-// public:
-// static shared_ptr<LogEvent> createEvent(int64_t eventTimeNs, int64_t value) {
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, eventTimeNs);
-// event->write(tagId);
-// event->write(value);
-// event->write(value);
-// event->init();
-// return event;
-// }
-//
-// static sp<ValueMetricProducer> createValueProducerNoConditions(
-// sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric) {
-// UidMap uidMap;
-// SimpleAtomMatcher atomMatcher;
-// atomMatcher.set_atom_id(tagId);
-// sp<EventMatcherWizard> eventMatcherWizard =
-// new EventMatcherWizard({new SimpleLogMatchingTracker(
-// atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-// EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-// EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
-//
-// sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
-// kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-// logEventMatcherIndex, eventMatcherWizard, tagId,
-// bucketStartTimeNs, bucketStartTimeNs, pullerManager);
-// return valueProducer;
-// }
-//
-// static sp<ValueMetricProducer> createValueProducerWithCondition(
-// sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric) {
-// UidMap uidMap;
-// SimpleAtomMatcher atomMatcher;
-// atomMatcher.set_atom_id(tagId);
-// sp<EventMatcherWizard> eventMatcherWizard =
-// new EventMatcherWizard({new SimpleLogMatchingTracker(
-// atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-// EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-// EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
-//
-// sp<ValueMetricProducer> valueProducer =
-// new ValueMetricProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
-// eventMatcherWizard, tagId, bucketStartTimeNs,
-// bucketStartTimeNs, pullerManager);
-// valueProducer->mCondition = ConditionState::kFalse;
-// return valueProducer;
-// }
-//
-// static sp<ValueMetricProducer> createValueProducerWithNoInitialCondition(
-// sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric) {
-// UidMap uidMap;
-// SimpleAtomMatcher atomMatcher;
-// atomMatcher.set_atom_id(tagId);
-// sp<EventMatcherWizard> eventMatcherWizard =
-// new EventMatcherWizard({new SimpleLogMatchingTracker(
-// atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-// EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-// EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
-//
-// sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
-// kConfigKey, metric, 1, wizard, logEventMatcherIndex, eventMatcherWizard, tagId,
-// bucketStartTimeNs, bucketStartTimeNs, pullerManager);
-// return valueProducer;
-// }
-//
-// static sp<ValueMetricProducer> createValueProducerWithState(
-// sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric,
-// vector<int32_t> slicedStateAtoms,
-// unordered_map<int, unordered_map<int, int64_t>> stateGroupMap) {
-// UidMap uidMap;
-// SimpleAtomMatcher atomMatcher;
-// atomMatcher.set_atom_id(tagId);
-// sp<EventMatcherWizard> eventMatcherWizard =
-// new EventMatcherWizard({new SimpleLogMatchingTracker(
-// atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-// EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-// EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
-// sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
-// kConfigKey, metric, -1 /* no condition */, wizard, logEventMatcherIndex,
-// eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager, {},
-// {}, slicedStateAtoms, stateGroupMap);
-// return valueProducer;
-// }
-//
-// static ValueMetric createMetric() {
-// ValueMetric metric;
-// metric.set_id(metricId);
-// metric.set_bucket(ONE_MINUTE);
-// metric.mutable_value_field()->set_field(tagId);
-// metric.mutable_value_field()->add_child()->set_field(2);
-// metric.set_max_pull_delay_sec(INT_MAX);
-// return metric;
-// }
-//
-// static ValueMetric createMetricWithCondition() {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-// metric.set_condition(StringToId("SCREEN_ON"));
-// return metric;
-// }
-//
-// static ValueMetric createMetricWithState(string state) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-// metric.add_slice_by_state(StringToId(state));
-// return metric;
-// }
-//};
-//
-///*
-// * Tests that the first bucket works correctly
-// */
-//TEST(ValueMetricProducerTest, TestCalcPreviousBucketEndTime) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-//
-// int64_t startTimeBase = 11;
-// UidMap uidMap;
-// SimpleAtomMatcher atomMatcher;
-// atomMatcher.set_atom_id(tagId);
-// sp<EventMatcherWizard> eventMatcherWizard =
-// new EventMatcherWizard({new SimpleLogMatchingTracker(
-// atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-//
-// // statsd started long ago.
-// // The metric starts in the middle of the bucket
-// ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-// logEventMatcherIndex, eventMatcherWizard, -1, startTimeBase,
-// 22, pullerManager);
-//
-// EXPECT_EQ(startTimeBase, valueProducer.calcPreviousBucketEndTime(60 * NS_PER_SEC + 10));
-// EXPECT_EQ(startTimeBase, valueProducer.calcPreviousBucketEndTime(60 * NS_PER_SEC + 10));
-// EXPECT_EQ(60 * NS_PER_SEC + startTimeBase,
-// valueProducer.calcPreviousBucketEndTime(2 * 60 * NS_PER_SEC));
-// EXPECT_EQ(2 * 60 * NS_PER_SEC + startTimeBase,
-// valueProducer.calcPreviousBucketEndTime(3 * 60 * NS_PER_SEC));
-//}
-//
-///*
-// * Tests that the first bucket works correctly
-// */
-//TEST(ValueMetricProducerTest, TestFirstBucket) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-//
-// UidMap uidMap;
-// SimpleAtomMatcher atomMatcher;
-// atomMatcher.set_atom_id(tagId);
-// sp<EventMatcherWizard> eventMatcherWizard =
-// new EventMatcherWizard({new SimpleLogMatchingTracker(
-// atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-//
-// // statsd started long ago.
-// // The metric starts in the middle of the bucket
-// ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-// logEventMatcherIndex, eventMatcherWizard, -1, 5,
-// 600 * NS_PER_SEC + NS_PER_SEC / 2, pullerManager);
-//
-// EXPECT_EQ(600500000000, valueProducer.mCurrentBucketStartTimeNs);
-// EXPECT_EQ(10, valueProducer.mCurrentBucketNum);
-// EXPECT_EQ(660000000005, valueProducer.getCurrentBucketEndTimeNs());
-//}
-//
-///*
-// * Tests pulled atoms with no conditions
-// */
-//TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// EXPECT_CALL(*pullerManager, Pull(tagId, _))
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
-// event->write(tagId);
-// event->write(3);
-// event->init();
-// data->push_back(event);
-// return true;
-// }));
-//
-// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
-//
-// vector<shared_ptr<LogEvent>> allData;
-// allData.clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
-// event->write(tagId);
-// event->write(11);
-// event->init();
-// allData.push_back(event);
-//
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-// // has one slice
-// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-// ValueMetricProducer::Interval curInterval =
-// valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-//
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(11, curBaseInfo.base.long_value);
-// EXPECT_EQ(false, curInterval.hasValue);
-// EXPECT_EQ(8, curInterval.value.long_value);
-// EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
-// EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
-// EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
-//
-// allData.clear();
-// event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
-// event->write(tagId);
-// event->write(23);
-// event->init();
-// allData.push_back(event);
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
-// // has one slice
-// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-// curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-//
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(23, curBaseInfo.base.long_value);
-// EXPECT_EQ(false, curInterval.hasValue);
-// EXPECT_EQ(12, curInterval.value.long_value);
-// EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
-// EXPECT_EQ(2UL, valueProducer->mPastBuckets.begin()->second.size());
-// EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
-// EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
-// EXPECT_EQ(12, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value);
-// EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second.back().mConditionTrueNs);
-//
-// allData.clear();
-// event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
-// event->write(tagId);
-// event->write(36);
-// event->init();
-// allData.push_back(event);
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
-// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-// curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-//
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(36, curBaseInfo.base.long_value);
-// EXPECT_EQ(false, curInterval.hasValue);
-// EXPECT_EQ(13, curInterval.value.long_value);
-// EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
-// EXPECT_EQ(3UL, valueProducer->mPastBuckets.begin()->second.size());
-// EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
-// EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
-// EXPECT_EQ(12, valueProducer->mPastBuckets.begin()->second[1].values[0].long_value);
-// EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[1].mConditionTrueNs);
-// EXPECT_EQ(13, valueProducer->mPastBuckets.begin()->second[2].values[0].long_value);
-// EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[2].mConditionTrueNs);
-//}
-//
-//TEST(ValueMetricProducerTest, TestPartialBucketCreated) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// EXPECT_CALL(*pullerManager, Pull(tagId, _))
-// // Initialize bucket.
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
-// event->write(tagId);
-// event->write(1);
-// event->init();
-// data->push_back(event);
-// return true;
-// }))
-// // Partial bucket.
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10);
-// event->write(tagId);
-// event->write(5);
-// event->init();
-// data->push_back(event);
-// return true;
-// }));
-//
-// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
-//
-// // First bucket ends.
-// vector<shared_ptr<LogEvent>> allData;
-// allData.clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10);
-// event->write(tagId);
-// event->write(2);
-// event->init();
-// allData.push_back(event);
-// valueProducer->onDataPulled(allData, /** success */ true, bucket2StartTimeNs);
-//
-// // Partial buckets created in 2nd bucket.
-// valueProducer->notifyAppUpgrade(bucket2StartTimeNs + 2, "com.foo", 10000, 1);
-//
-// // One full bucket and one partial bucket.
-// EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
-// vector<ValueBucket> buckets = valueProducer->mPastBuckets.begin()->second;
-// EXPECT_EQ(2UL, buckets.size());
-// // Full bucket (2 - 1)
-// EXPECT_EQ(1, buckets[0].values[0].long_value);
-// EXPECT_EQ(bucketSizeNs, buckets[0].mConditionTrueNs);
-// // Full bucket (5 - 3)
-// EXPECT_EQ(3, buckets[1].values[0].long_value);
-// // partial bucket [bucket2StartTimeNs, bucket2StartTimeNs + 2]
-// EXPECT_EQ(2, buckets[1].mConditionTrueNs);
-//}
-//
-///*
-// * Tests pulled atoms with filtering
-// */
-//TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-//
-// UidMap uidMap;
-// SimpleAtomMatcher atomMatcher;
-// atomMatcher.set_atom_id(tagId);
-// auto keyValue = atomMatcher.add_field_value_matcher();
-// keyValue->set_field(1);
-// keyValue->set_eq_int(3);
-// sp<EventMatcherWizard> eventMatcherWizard =
-// new EventMatcherWizard({new SimpleLogMatchingTracker(
-// atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-// EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-// EXPECT_CALL(*pullerManager, Pull(tagId, _))
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
-// event->write(3);
-// event->write(3);
-// event->init();
-// data->push_back(event);
-// return true;
-// }));
-//
-// sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
-// kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, logEventMatcherIndex,
-// eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
-//
-// vector<shared_ptr<LogEvent>> allData;
-// allData.clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
-// event->write(3);
-// event->write(11);
-// event->init();
-// allData.push_back(event);
-//
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-// // has one slice
-// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-// ValueMetricProducer::Interval curInterval =
-// valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-//
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(11, curBaseInfo.base.long_value);
-// EXPECT_EQ(false, curInterval.hasValue);
-// EXPECT_EQ(8, curInterval.value.long_value);
-// EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
-// EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
-// EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
-//
-// allData.clear();
-// event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
-// event->write(4);
-// event->write(23);
-// event->init();
-// allData.push_back(event);
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
-// // No new data seen, so data has been cleared.
-// EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
-//
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(11, curBaseInfo.base.long_value);
-// EXPECT_EQ(false, curInterval.hasValue);
-// EXPECT_EQ(8, curInterval.value.long_value);
-// EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
-// EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
-// EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
-//
-// allData.clear();
-// event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
-// event->write(3);
-// event->write(36);
-// event->init();
-// allData.push_back(event);
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
-// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-// curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-//
-// // the base was reset
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(36, curBaseInfo.base.long_value);
-// EXPECT_EQ(false, curInterval.hasValue);
-// EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
-// EXPECT_EQ(1UL, valueProducer->mPastBuckets.begin()->second.size());
-// EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value);
-// EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second.back().mConditionTrueNs);
-//}
-//
+class ValueMetricProducerTestHelper {
+public:
+ static sp<ValueMetricProducer> createValueProducerNoConditions(
+ sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric) {
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+
+ sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
+ kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, logEventMatcherIndex,
+ eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+ return valueProducer;
+ }
+
+ static sp<ValueMetricProducer> createValueProducerWithCondition(
+ sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric) {
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+
+ sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
+ kConfigKey, metric, 1, wizard, logEventMatcherIndex, eventMatcherWizard, tagId,
+ bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+ valueProducer->mCondition = ConditionState::kFalse;
+ return valueProducer;
+ }
+
+ static sp<ValueMetricProducer> createValueProducerWithNoInitialCondition(
+ sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric) {
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+
+ sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
+ kConfigKey, metric, 1, wizard, logEventMatcherIndex, eventMatcherWizard, tagId,
+ bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+ return valueProducer;
+ }
+
+ static sp<ValueMetricProducer> createValueProducerWithState(
+ sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric,
+ vector<int32_t> slicedStateAtoms,
+ unordered_map<int, unordered_map<int, int64_t>> stateGroupMap) {
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+ sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
+ kConfigKey, metric, -1 /* no condition */, wizard, logEventMatcherIndex,
+ eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager, {},
+ {}, slicedStateAtoms, stateGroupMap);
+ return valueProducer;
+ }
+
+ static ValueMetric createMetric() {
+ ValueMetric metric;
+ metric.set_id(metricId);
+ metric.set_bucket(ONE_MINUTE);
+ metric.mutable_value_field()->set_field(tagId);
+ metric.mutable_value_field()->add_child()->set_field(2);
+ metric.set_max_pull_delay_sec(INT_MAX);
+ return metric;
+ }
+
+ static ValueMetric createMetricWithCondition() {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+ metric.set_condition(StringToId("SCREEN_ON"));
+ return metric;
+ }
+
+ static ValueMetric createMetricWithState(string state) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+ metric.add_slice_by_state(StringToId(state));
+ return metric;
+ }
+};
+
+/*
+ * Tests that the first bucket works correctly
+ */
+TEST(ValueMetricProducerTest, TestCalcPreviousBucketEndTime) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+
+ int64_t startTimeBase = 11;
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+ // statsd started long ago.
+ // The metric starts in the middle of the bucket
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+ logEventMatcherIndex, eventMatcherWizard, -1, startTimeBase,
+ 22, pullerManager);
+
+ EXPECT_EQ(startTimeBase, valueProducer.calcPreviousBucketEndTime(60 * NS_PER_SEC + 10));
+ EXPECT_EQ(startTimeBase, valueProducer.calcPreviousBucketEndTime(60 * NS_PER_SEC + 10));
+ EXPECT_EQ(60 * NS_PER_SEC + startTimeBase,
+ valueProducer.calcPreviousBucketEndTime(2 * 60 * NS_PER_SEC));
+ EXPECT_EQ(2 * 60 * NS_PER_SEC + startTimeBase,
+ valueProducer.calcPreviousBucketEndTime(3 * 60 * NS_PER_SEC));
+}
+
+/*
+ * Tests that the first bucket works correctly
+ */
+TEST(ValueMetricProducerTest, TestFirstBucket) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+ // statsd started long ago.
+ // The metric starts in the middle of the bucket
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+ logEventMatcherIndex, eventMatcherWizard, -1, 5,
+ 600 * NS_PER_SEC + NS_PER_SEC / 2, pullerManager);
+
+ EXPECT_EQ(600500000000, valueProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(10, valueProducer.mCurrentBucketNum);
+ EXPECT_EQ(660000000005, valueProducer.getCurrentBucketEndTimeNs());
+}
+
+/*
+ * Tests pulled atoms with no conditions
+ */
+TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
+ return true;
+ }));
+
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
+
+ vector<shared_ptr<LogEvent>> allData;
+ allData.clear();
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 11));
+
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval =
+ valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(11, curBaseInfo.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(8, curInterval.value.long_value);
+ EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+ EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
+ EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
+
+ allData.clear();
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 23));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(23, curBaseInfo.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(12, curInterval.value.long_value);
+ EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+ EXPECT_EQ(2UL, valueProducer->mPastBuckets.begin()->second.size());
+ EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
+ EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
+ EXPECT_EQ(12, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value);
+ EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second.back().mConditionTrueNs);
+
+ allData.clear();
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
+ EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(36, curBaseInfo.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(13, curInterval.value.long_value);
+ EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+ EXPECT_EQ(3UL, valueProducer->mPastBuckets.begin()->second.size());
+ EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
+ EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
+ EXPECT_EQ(12, valueProducer->mPastBuckets.begin()->second[1].values[0].long_value);
+ EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[1].mConditionTrueNs);
+ EXPECT_EQ(13, valueProducer->mPastBuckets.begin()->second[2].values[0].long_value);
+ EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[2].mConditionTrueNs);
+}
+
+TEST(ValueMetricProducerTest, TestPartialBucketCreated) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ // Initialize bucket.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 1));
+ return true;
+ }))
+ // Partial bucket.
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 10, 5));
+ return true;
+ }));
+
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
+
+ // First bucket ends.
+ vector<shared_ptr<LogEvent>> allData;
+ allData.clear();
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 10, 2));
+ valueProducer->onDataPulled(allData, /** success */ true, bucket2StartTimeNs);
+
+ // Partial buckets created in 2nd bucket.
+ valueProducer->notifyAppUpgrade(bucket2StartTimeNs + 2, "com.foo", 10000, 1);
+
+ // One full bucket and one partial bucket.
+ EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+ vector<ValueBucket> buckets = valueProducer->mPastBuckets.begin()->second;
+ EXPECT_EQ(2UL, buckets.size());
+ // Full bucket (2 - 1)
+ EXPECT_EQ(1, buckets[0].values[0].long_value);
+ EXPECT_EQ(bucketSizeNs, buckets[0].mConditionTrueNs);
+ // Full bucket (5 - 3)
+ EXPECT_EQ(3, buckets[1].values[0].long_value);
+ // partial bucket [bucket2StartTimeNs, bucket2StartTimeNs + 2]
+ EXPECT_EQ(2, buckets[1].mConditionTrueNs);
+}
+
+/*
+ * Tests pulled atoms with filtering
+ */
+TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ auto keyValue = atomMatcher.add_field_value_matcher();
+ keyValue->set_field(1);
+ keyValue->set_eq_int(3);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 3, 3));
+ return true;
+ }));
+
+ sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
+ kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, logEventMatcherIndex,
+ eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+
+ vector<shared_ptr<LogEvent>> allData;
+ allData.clear();
+ allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 3, 11));
+
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval =
+ valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(11, curBaseInfo.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(8, curInterval.value.long_value);
+ EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+ EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
+ EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
+
+ allData.clear();
+ allData.push_back(CreateTwoValueLogEvent(tagId, bucket3StartTimeNs + 1, 4, 23));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
+ // No new data seen, so data has been cleared.
+ EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(11, curBaseInfo.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(8, curInterval.value.long_value);
+ EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+ EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
+ EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
+
+ allData.clear();
+ allData.push_back(CreateTwoValueLogEvent(tagId, bucket4StartTimeNs + 1, 3, 36));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
+ EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+
+ // the base was reset
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(36, curBaseInfo.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+ EXPECT_EQ(1UL, valueProducer->mPastBuckets.begin()->second.size());
+ EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value);
+ EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second.back().mConditionTrueNs);
+}
+
///*
// * Tests pulled atoms with no conditions and take absolute value after reset
// */
-//TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset) {
+// TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
// metric.set_use_absolute_value_on_reset(true);
//
@@ -512,8 +455,9 @@ static void assertPastBucketValuesSingleKey(
// valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
// // has one slice
// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-// ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+// ValueMetricProducer::Interval curInterval =
+// valueProducer->mCurrentSlicedBucket.begin()->second[0]; ValueMetricProducer::BaseInfo
+// curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
//
// EXPECT_EQ(true, curBaseInfo.hasBase);
// EXPECT_EQ(11, curBaseInfo.base.long_value);
@@ -564,7 +508,7 @@ static void assertPastBucketValuesSingleKey(
///*
// * Tests pulled atoms with no conditions and take zero value after reset
// */
-//TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) {
+// TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
// EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(false));
@@ -582,8 +526,9 @@ static void assertPastBucketValuesSingleKey(
// valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
// // has one slice
// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-// ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+// ValueMetricProducer::Interval curInterval =
+// valueProducer->mCurrentSlicedBucket.begin()->second[0]; ValueMetricProducer::BaseInfo
+// curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
//
// EXPECT_EQ(true, curBaseInfo.hasBase);
// EXPECT_EQ(11, curBaseInfo.base.long_value);
@@ -628,7 +573,7 @@ static void assertPastBucketValuesSingleKey(
///*
// * Test pulled event with non sliced condition.
// */
-//TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) {
+// TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -663,14 +608,16 @@ static void assertPastBucketValuesSingleKey(
// }));
//
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
//
// valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
//
// // has one slice
// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-// ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+// ValueMetricProducer::Interval curInterval =
+// valueProducer->mCurrentSlicedBucket.begin()->second[0]; ValueMetricProducer::BaseInfo
+// curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
// // startUpdated:false sum:0 start:100
// EXPECT_EQ(true, curBaseInfo.hasBase);
// EXPECT_EQ(100, curBaseInfo.base.long_value);
@@ -711,7 +658,7 @@ static void assertPastBucketValuesSingleKey(
// assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10, 20}, {bucketSizeNs - 8, 1});
//}
//
-//TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade) {
+// TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
//
// UidMap uidMap;
@@ -723,8 +670,8 @@ static void assertPastBucketValuesSingleKey(
// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
// ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
-// eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-// pullerManager);
+// eventMatcherWizard, -1, bucketStartTimeNs,
+// bucketStartTimeNs, pullerManager);
//
// shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
// event1->write(1);
@@ -737,25 +684,21 @@ static void assertPastBucketValuesSingleKey(
// EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
// EXPECT_EQ(bucketStartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs);
//
-// shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 59 * NS_PER_SEC);
-// event2->write(1);
-// event2->write(10);
-// event2->init();
+// shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 59 *
+// NS_PER_SEC); event2->write(1); event2->write(10); event2->init();
// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
// EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
// EXPECT_EQ(bucketStartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs);
//
// // Next value should create a new bucket.
-// shared_ptr<LogEvent> event3 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 65 * NS_PER_SEC);
-// event3->write(1);
-// event3->write(10);
-// event3->init();
+// shared_ptr<LogEvent> event3 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 65 *
+// NS_PER_SEC); event3->write(1); event3->write(10); event3->init();
// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3);
// EXPECT_EQ(2UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
// EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, valueProducer.mCurrentBucketStartTimeNs);
//}
//
-//TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) {
+// TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
//
// UidMap uidMap;
@@ -772,10 +715,8 @@ static void assertPastBucketValuesSingleKey(
// .WillOnce(Return(true))
// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 149);
-// event->write(tagId);
-// event->write(120);
-// event->init();
+// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs +
+// 149); event->write(tagId); event->write(120); event->init();
// data->push_back(event);
// return true;
// }));
@@ -814,7 +755,7 @@ static void assertPastBucketValuesSingleKey(
// {150, bucketSizeNs - 150});
//}
//
-//TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled) {
+// TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
// metric.set_split_bucket_for_app_upgrade(false);
//
@@ -849,7 +790,7 @@ static void assertPastBucketValuesSingleKey(
// EXPECT_EQ(bucket2StartTimeNs, valueProducer.mCurrentBucketStartTimeNs);
//}
//
-//TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse) {
+// TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -865,15 +806,14 @@ static void assertPastBucketValuesSingleKey(
// }))
// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs - 100);
-// event->write(tagId);
-// event->write(120);
-// event->init();
+// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs -
+// 100); event->write(tagId); event->write(120); event->init();
// data->push_back(event);
// return true;
// }));
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
//
// valueProducer->onConditionChanged(true, bucketStartTimeNs + 1);
//
@@ -891,7 +831,7 @@ static void assertPastBucketValuesSingleKey(
// EXPECT_FALSE(valueProducer->mCondition);
//}
//
-//TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) {
+// TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
//
// UidMap uidMap;
@@ -904,8 +844,8 @@ static void assertPastBucketValuesSingleKey(
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
//
// ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
-// eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-// pullerManager);
+// eventMatcherWizard, -1, bucketStartTimeNs,
+// bucketStartTimeNs, pullerManager);
//
// shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
// event1->write(1);
@@ -918,10 +858,10 @@ static void assertPastBucketValuesSingleKey(
// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
// // has one slice
// EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-// ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(10, curInterval.value.long_value);
-// EXPECT_EQ(true, curInterval.hasValue);
+// ValueMetricProducer::Interval curInterval =
+// valueProducer.mCurrentSlicedBucket.begin()->second[0]; ValueMetricProducer::BaseInfo
+// curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(10,
+// curInterval.value.long_value); EXPECT_EQ(true, curInterval.hasValue);
//
// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
//
@@ -934,7 +874,7 @@ static void assertPastBucketValuesSingleKey(
// assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {30}, {bucketSizeNs});
//}
//
-//TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) {
+// TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
//
// UidMap uidMap;
@@ -947,8 +887,8 @@ static void assertPastBucketValuesSingleKey(
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
//
// ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
-// eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-// pullerManager);
+// eventMatcherWizard, -1, bucketStartTimeNs,
+// bucketStartTimeNs, pullerManager);
// valueProducer.mCondition = ConditionState::kFalse;
//
// shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
@@ -1000,7 +940,7 @@ static void assertPastBucketValuesSingleKey(
// assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {50}, {20});
//}
//
-//TEST(ValueMetricProducerTest, TestAnomalyDetection) {
+// TEST(ValueMetricProducerTest, TestAnomalyDetection) {
// sp<AlarmMonitor> alarmMonitor;
// Alert alert;
// alert.set_id(101);
@@ -1053,7 +993,8 @@ static void assertPastBucketValuesSingleKey(
// event5->write(150); // value of interest
// event5->init();
// shared_ptr<LogEvent> event6
-// = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 10 * NS_PER_SEC);
+// = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 10 *
+// NS_PER_SEC);
// event6->write(25);
// event6->write(160); // value of interest
// event6->init();
@@ -1086,7 +1027,7 @@ static void assertPastBucketValuesSingleKey(
//}
//
//// Test value metric no condition, the pull on bucket boundary come in time and too late
-//TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) {
+// TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
// EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(true));
@@ -1107,7 +1048,8 @@ static void assertPastBucketValuesSingleKey(
// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
// ValueMetricProducer::Interval curInterval =
// valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+// ValueMetricProducer::BaseInfo curBaseInfo =
+// valueProducer->mCurrentBaseInfo.begin()->second[0];
//
// // startUpdated:true sum:0 start:11
// EXPECT_EQ(true, curBaseInfo.hasBase);
@@ -1158,7 +1100,7 @@ static void assertPastBucketValuesSingleKey(
// * Test pulled event with non sliced condition. The pull on boundary come late because the alarm
// * was delivered late.
// */
-//TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) {
+// TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -1184,7 +1126,8 @@ static void assertPastBucketValuesSingleKey(
// return true;
// }));
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
//
// valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
//
@@ -1192,8 +1135,8 @@ static void assertPastBucketValuesSingleKey(
// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
// ValueMetricProducer::Interval curInterval =
// valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(true, curBaseInfo.hasBase);
+// ValueMetricProducer::BaseInfo curBaseInfo =
+// valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(true, curBaseInfo.hasBase);
// EXPECT_EQ(100, curBaseInfo.base.long_value);
// EXPECT_EQ(false, curInterval.hasValue);
// EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
@@ -1219,10 +1162,11 @@ static void assertPastBucketValuesSingleKey(
//}
//
///*
-// * Test pulled event with non sliced condition. The pull on boundary come late, after the condition
+// * Test pulled event with non sliced condition. The pull on boundary come late, after the
+// condition
// * change to false, and then true again. This is due to alarm delivered late.
// */
-//TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) {
+// TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -1250,16 +1194,15 @@ static void assertPastBucketValuesSingleKey(
// // condition becomes true again
// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 25);
-// event->write(tagId);
-// event->write(130);
-// event->init();
+// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs +
+// 25); event->write(tagId); event->write(130); event->init();
// data->push_back(event);
// return true;
// }));
//
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
//
// valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
//
@@ -1267,7 +1210,8 @@ static void assertPastBucketValuesSingleKey(
// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
// ValueMetricProducer::Interval curInterval =
// valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+// ValueMetricProducer::BaseInfo curBaseInfo =
+// valueProducer->mCurrentBaseInfo.begin()->second[0];
// // startUpdated:false sum:0 start:100
// EXPECT_EQ(true, curBaseInfo.hasBase);
// EXPECT_EQ(100, curBaseInfo.base.long_value);
@@ -1313,7 +1257,7 @@ static void assertPastBucketValuesSingleKey(
// {bucketSizeNs - 8, bucketSizeNs - 24});
//}
//
-//TEST(ValueMetricProducerTest, TestPushedAggregateMin) {
+// TEST(ValueMetricProducerTest, TestPushedAggregateMin) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
// metric.set_aggregation_type(ValueMetric::MIN);
//
@@ -1327,8 +1271,8 @@ static void assertPastBucketValuesSingleKey(
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
//
// ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
-// eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-// pullerManager);
+// eventMatcherWizard, -1, bucketStartTimeNs,
+// bucketStartTimeNs, pullerManager);
//
// shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
// event1->write(1);
@@ -1357,7 +1301,7 @@ static void assertPastBucketValuesSingleKey(
// assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10}, {bucketSizeNs});
//}
//
-//TEST(ValueMetricProducerTest, TestPushedAggregateMax) {
+// TEST(ValueMetricProducerTest, TestPushedAggregateMax) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
// metric.set_aggregation_type(ValueMetric::MAX);
//
@@ -1371,8 +1315,8 @@ static void assertPastBucketValuesSingleKey(
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
//
// ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
-// eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-// pullerManager);
+// eventMatcherWizard, -1, bucketStartTimeNs,
+// bucketStartTimeNs, pullerManager);
//
// shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
// event1->write(1);
@@ -1403,7 +1347,7 @@ static void assertPastBucketValuesSingleKey(
// /* EXPECT_EQ(20, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value); */
//}
//
-//TEST(ValueMetricProducerTest, TestPushedAggregateAvg) {
+// TEST(ValueMetricProducerTest, TestPushedAggregateAvg) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
// metric.set_aggregation_type(ValueMetric::AVG);
//
@@ -1417,8 +1361,8 @@ static void assertPastBucketValuesSingleKey(
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
//
// ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
-// eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-// pullerManager);
+// eventMatcherWizard, -1, bucketStartTimeNs,
+// bucketStartTimeNs, pullerManager);
//
// shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
// event1->write(1);
@@ -1449,11 +1393,12 @@ static void assertPastBucketValuesSingleKey(
// EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
// EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
//
-// EXPECT_TRUE(std::abs(valueProducer.mPastBuckets.begin()->second.back().values[0].double_value -
+// EXPECT_TRUE(std::abs(valueProducer.mPastBuckets.begin()->second.back().values[0].double_value
+// -
// 12.5) < epsilon);
//}
//
-//TEST(ValueMetricProducerTest, TestPushedAggregateSum) {
+// TEST(ValueMetricProducerTest, TestPushedAggregateSum) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
// metric.set_aggregation_type(ValueMetric::SUM);
//
@@ -1467,8 +1412,8 @@ static void assertPastBucketValuesSingleKey(
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
//
// ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
-// eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-// pullerManager);
+// eventMatcherWizard, -1, bucketStartTimeNs,
+// bucketStartTimeNs, pullerManager);
//
// shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
// event1->write(1);
@@ -1497,7 +1442,7 @@ static void assertPastBucketValuesSingleKey(
// assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {25}, {bucketSizeNs});
//}
//
-//TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) {
+// TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
// metric.set_aggregation_type(ValueMetric::MIN);
// metric.set_use_diff(true);
@@ -1512,8 +1457,8 @@ static void assertPastBucketValuesSingleKey(
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
//
// ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
-// eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-// pullerManager);
+// eventMatcherWizard, -1, bucketStartTimeNs,
+// bucketStartTimeNs, pullerManager);
//
// shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
// event1->write(1);
@@ -1572,7 +1517,7 @@ static void assertPastBucketValuesSingleKey(
// assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {5}, {bucketSizeNs});
//}
//
-//TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) {
+// TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
// metric.mutable_value_field()->add_child()->set_field(3);
// metric.set_aggregation_type(ValueMetric::MIN);
@@ -1588,8 +1533,8 @@ static void assertPastBucketValuesSingleKey(
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
//
// ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
-// eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-// pullerManager);
+// eventMatcherWizard, -1, bucketStartTimeNs,
+// bucketStartTimeNs, pullerManager);
//
// shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
// event1->write(1);
@@ -1687,7 +1632,7 @@ static void assertPastBucketValuesSingleKey(
///*
// * Tests zero default base.
// */
-//TEST(ValueMetricProducerTest, TestUseZeroDefaultBase) {
+// TEST(ValueMetricProducerTest, TestUseZeroDefaultBase) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
// metric.mutable_dimensions_in_what()->set_field(tagId);
// metric.mutable_dimensions_in_what()->add_child()->set_field(1);
@@ -1774,7 +1719,7 @@ static void assertPastBucketValuesSingleKey(
///*
// * Tests using zero default base with failed pull.
// */
-//TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) {
+// TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
// metric.mutable_dimensions_in_what()->set_field(tagId);
// metric.mutable_dimensions_in_what()->add_child()->set_field(1);
@@ -1885,8 +1830,9 @@ static void assertPastBucketValuesSingleKey(
// it2 = std::next(valueProducer->mCurrentSlicedBucket.begin());
// interval1 = it->second[0];
// interval2 = it2->second[0];
-// baseInfo1 = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat())->second[0];
-// baseInfo2 = valueProducer->mCurrentBaseInfo.find(it2->first.getDimensionKeyInWhat())->second[0];
+// baseInfo1 =
+// valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat())->second[0]; baseInfo2
+// = valueProducer->mCurrentBaseInfo.find(it2->first.getDimensionKeyInWhat())->second[0];
//
// EXPECT_EQ(true, baseInfo1.hasBase);
// EXPECT_EQ(5, baseInfo1.base.long_value);
@@ -1905,7 +1851,7 @@ static void assertPastBucketValuesSingleKey(
///*
// * Tests trim unused dimension key if no new data is seen in an entire bucket.
// */
-//TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) {
+// TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
// metric.mutable_dimensions_in_what()->set_field(tagId);
// metric.mutable_dimensions_in_what()->add_child()->set_field(1);
@@ -2023,7 +1969,7 @@ static void assertPastBucketValuesSingleKey(
// EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs);
//}
//
-//TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfBucket) {
+// TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfBucket) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -2040,15 +1986,16 @@ static void assertPastBucketValuesSingleKey(
// }));
//
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
//
// valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
// // has one slice
// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
// ValueMetricProducer::Interval& curInterval =
// valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo& curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(true, curBaseInfo.hasBase);
+// ValueMetricProducer::BaseInfo& curBaseInfo =
+// valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(true, curBaseInfo.hasBase);
// EXPECT_EQ(100, curBaseInfo.base.long_value);
// EXPECT_EQ(false, curInterval.hasValue);
//
@@ -2060,7 +2007,7 @@ static void assertPastBucketValuesSingleKey(
// EXPECT_EQ(false, valueProducer->mHasGlobalBase);
//}
//
-//TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange) {
+// TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -2077,7 +2024,8 @@ static void assertPastBucketValuesSingleKey(
// .WillOnce(Return(false));
//
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
//
// valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
//
@@ -2085,8 +2033,8 @@ static void assertPastBucketValuesSingleKey(
// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
// ValueMetricProducer::Interval& curInterval =
// valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo& curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(true, curBaseInfo.hasBase);
+// ValueMetricProducer::BaseInfo& curBaseInfo =
+// valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(true, curBaseInfo.hasBase);
// EXPECT_EQ(100, curBaseInfo.base.long_value);
// EXPECT_EQ(false, curInterval.hasValue);
// EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
@@ -2100,7 +2048,7 @@ static void assertPastBucketValuesSingleKey(
// EXPECT_EQ(false, valueProducer->mHasGlobalBase);
//}
//
-//TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange) {
+// TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -2125,7 +2073,8 @@ static void assertPastBucketValuesSingleKey(
// }));
//
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
//
// // Don't directly set mCondition; the real code never does that. Go through regular code path
// // to avoid unexpected behaviors.
@@ -2138,13 +2087,13 @@ static void assertPastBucketValuesSingleKey(
// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
// ValueMetricProducer::Interval& curInterval =
// valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(false, curBaseInfo.hasBase);
+// ValueMetricProducer::BaseInfo curBaseInfo =
+// valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(false, curBaseInfo.hasBase);
// EXPECT_EQ(false, curInterval.hasValue);
// EXPECT_EQ(false, valueProducer->mHasGlobalBase);
//}
//
-//TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded) {
+// TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
// metric.set_condition(StringToId("SCREEN_ON"));
// metric.set_max_pull_delay_sec(0);
@@ -2162,7 +2111,8 @@ static void assertPastBucketValuesSingleKey(
// }));
//
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
//
// valueProducer->mCondition = ConditionState::kFalse;
//
@@ -2171,7 +2121,7 @@ static void assertPastBucketValuesSingleKey(
// EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
//}
//
-//TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate) {
+// TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// UidMap uidMap;
@@ -2196,7 +2146,7 @@ static void assertPastBucketValuesSingleKey(
// EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size());
//}
//
-//TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange) {
+// TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -2212,7 +2162,8 @@ static void assertPastBucketValuesSingleKey(
// }));
//
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
//
// valueProducer->mCondition = ConditionState::kFalse;
// valueProducer->mHasGlobalBase = false;
@@ -2222,8 +2173,8 @@ static void assertPastBucketValuesSingleKey(
// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
// ValueMetricProducer::Interval& curInterval =
// valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(true, curBaseInfo.hasBase);
+// ValueMetricProducer::BaseInfo curBaseInfo =
+// valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(true, curBaseInfo.hasBase);
// EXPECT_EQ(100, curBaseInfo.base.long_value);
// EXPECT_EQ(false, curInterval.hasValue);
// EXPECT_EQ(true, valueProducer->mHasGlobalBase);
@@ -2232,7 +2183,7 @@ static void assertPastBucketValuesSingleKey(
///*
// * Tests that a bucket is marked invalid when a condition change pull fails.
// */
-//TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenOneConditionFailed) {
+// TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenOneConditionFailed) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -2251,7 +2202,8 @@ static void assertPastBucketValuesSingleKey(
// }));
//
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
//
// valueProducer->mCondition = ConditionState::kTrue;
//
@@ -2286,8 +2238,8 @@ static void assertPastBucketValuesSingleKey(
// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
// ValueMetricProducer::Interval& curInterval =
// valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(true, curBaseInfo.hasBase);
+// ValueMetricProducer::BaseInfo curBaseInfo =
+// valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(true, curBaseInfo.hasBase);
// EXPECT_EQ(140, curBaseInfo.base.long_value);
// EXPECT_EQ(false, curInterval.hasValue);
// EXPECT_EQ(true, valueProducer->mHasGlobalBase);
@@ -2317,7 +2269,7 @@ static void assertPastBucketValuesSingleKey(
///*
// * Tests that a bucket is marked invalid when the guardrail is hit.
// */
-//TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenGuardRailHit) {
+// TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenGuardRailHit) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
// metric.mutable_dimensions_in_what()->set_field(tagId);
// metric.mutable_dimensions_in_what()->add_child()->set_field(1);
@@ -2339,7 +2291,8 @@ static void assertPastBucketValuesSingleKey(
// }));
//
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
// valueProducer->mCondition = ConditionState::kFalse;
//
// valueProducer->onConditionChanged(true, bucketStartTimeNs + 2);
@@ -2385,7 +2338,7 @@ static void assertPastBucketValuesSingleKey(
///*
// * Tests that a bucket is marked invalid when the bucket's initial pull fails.
// */
-//TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenInitialPullFailed) {
+// TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenInitialPullFailed) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -2412,7 +2365,8 @@ static void assertPastBucketValuesSingleKey(
// }));
//
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
//
// valueProducer->mCondition = ConditionState::kTrue;
//
@@ -2445,8 +2399,8 @@ static void assertPastBucketValuesSingleKey(
// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
// ValueMetricProducer::Interval& curInterval =
// valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(true, curBaseInfo.hasBase);
+// ValueMetricProducer::BaseInfo curBaseInfo =
+// valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(true, curBaseInfo.hasBase);
// EXPECT_EQ(140, curBaseInfo.base.long_value);
// EXPECT_EQ(false, curInterval.hasValue);
// EXPECT_EQ(true, valueProducer->mHasGlobalBase);
@@ -2477,7 +2431,7 @@ static void assertPastBucketValuesSingleKey(
// * Tests that a bucket is marked invalid when the bucket's final pull fails
// * (i.e. failed pull on bucket boundary).
// */
-//TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenLastPullFailed) {
+// TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenLastPullFailed) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -2504,7 +2458,8 @@ static void assertPastBucketValuesSingleKey(
// }));
//
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
//
// valueProducer->mCondition = ConditionState::kTrue;
//
@@ -2537,8 +2492,8 @@ static void assertPastBucketValuesSingleKey(
// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
// ValueMetricProducer::Interval& curInterval =
// valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(false, curBaseInfo.hasBase);
+// ValueMetricProducer::BaseInfo curBaseInfo =
+// valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(false, curBaseInfo.hasBase);
// EXPECT_EQ(false, curInterval.hasValue);
// EXPECT_EQ(false, valueProducer->mHasGlobalBase);
//
@@ -2564,7 +2519,7 @@ static void assertPastBucketValuesSingleKey(
// EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), dropEvent.drop_time_millis());
//}
//
-//TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled) {
+// TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
// EXPECT_CALL(*pullerManager, Pull(tagId, _))
@@ -2605,7 +2560,7 @@ static void assertPastBucketValuesSingleKey(
// EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
//}
//
-//TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged) {
+// TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -2626,14 +2581,15 @@ static void assertPastBucketValuesSingleKey(
// }));
//
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
//
// valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
// ValueMetricProducer::Interval& curInterval =
// valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(true, curBaseInfo.hasBase);
+// ValueMetricProducer::BaseInfo curBaseInfo =
+// valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(true, curBaseInfo.hasBase);
// EXPECT_EQ(false, curInterval.hasValue);
// EXPECT_EQ(true, valueProducer->mHasGlobalBase);
//
@@ -2647,7 +2603,7 @@ static void assertPastBucketValuesSingleKey(
// EXPECT_EQ(false, valueProducer->mHasGlobalBase);
//}
//
-//TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary) {
+// TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -2682,7 +2638,8 @@ static void assertPastBucketValuesSingleKey(
// }));
//
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
//
// valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
// valueProducer->onConditionChanged(false, bucketStartTimeNs + 11);
@@ -2690,8 +2647,8 @@ static void assertPastBucketValuesSingleKey(
// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
// ValueMetricProducer::Interval& curInterval =
// valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(true, curBaseInfo.hasBase);
+// ValueMetricProducer::BaseInfo curBaseInfo =
+// valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(true, curBaseInfo.hasBase);
// EXPECT_EQ(true, curInterval.hasValue);
// EXPECT_EQ(true, valueProducer->mHasGlobalBase);
//
@@ -2712,7 +2669,7 @@ static void assertPastBucketValuesSingleKey(
// assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {1}, {bucketSizeNs - 12 + 1});
//}
//
-//TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries) {
+// TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
// metric.mutable_dimensions_in_what()->set_field(tagId);
// metric.mutable_dimensions_in_what()->add_child()->set_field(1);
@@ -2733,7 +2690,8 @@ static void assertPastBucketValuesSingleKey(
// }));
//
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
//
// valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
@@ -2764,7 +2722,7 @@ static void assertPastBucketValuesSingleKey(
// EXPECT_EQ(true, valueProducer->mHasGlobalBase);
//}
//
-//TEST(ValueMetricProducerTest, TestFullBucketResetWhenLastBucketInvalid) {
+// TEST(ValueMetricProducerTest, TestFullBucketResetWhenLastBucketInvalid) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -2795,7 +2753,7 @@ static void assertPastBucketValuesSingleKey(
// ASSERT_EQ(0UL, valueProducer->mCurrentFullBucket.size());
//}
//
-//TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange) {
+// TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
// EXPECT_CALL(*pullerManager, Pull(tagId, _))
@@ -2815,7 +2773,8 @@ static void assertPastBucketValuesSingleKey(
// }));
//
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
// valueProducer->mCondition = ConditionState::kUnknown;
//
// valueProducer->onConditionChanged(false, bucketStartTimeNs);
@@ -2841,7 +2800,7 @@ static void assertPastBucketValuesSingleKey(
// assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}, {bucketSizeNs - 10});
//}
//
-//TEST(ValueMetricProducerTest, TestLateOnDataPulledWithoutDiff) {
+// TEST(ValueMetricProducerTest, TestLateOnDataPulledWithoutDiff) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
// metric.set_use_diff(false);
//
@@ -2861,7 +2820,7 @@ static void assertPastBucketValuesSingleKey(
// assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs});
//}
//
-//TEST(ValueMetricProducerTest, TestLateOnDataPulledWithDiff) {
+// TEST(ValueMetricProducerTest, TestLateOnDataPulledWithDiff) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -2888,7 +2847,7 @@ static void assertPastBucketValuesSingleKey(
// assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {19}, {bucketSizeNs});
//}
//
-//TEST(ValueMetricProducerTest, TestBucketBoundariesOnAppUpgrade) {
+// TEST(ValueMetricProducerTest, TestBucketBoundariesOnAppUpgrade) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -2916,7 +2875,7 @@ static void assertPastBucketValuesSingleKey(
// assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9}, {bucketSizeNs});
//}
//
-//TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged) {
+// TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -2935,7 +2894,8 @@ static void assertPastBucketValuesSingleKey(
// }));
//
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
//
// valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
// valueProducer->onConditionChanged(false, bucketStartTimeNs + 10);
@@ -2955,7 +2915,7 @@ static void assertPastBucketValuesSingleKey(
//}
//
//// TODO: b/145705635 fix or delete this test
-//TEST(ValueMetricProducerTest, TestBucketInvalidIfGlobalBaseIsNotSet) {
+// TEST(ValueMetricProducerTest, TestBucketInvalidIfGlobalBaseIsNotSet) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -2969,18 +2929,19 @@ static void assertPastBucketValuesSingleKey(
// // 2nd condition change.
// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
// data->clear();
-// data->push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs, 1));
-// return true;
+// data->push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs,
+// 1)); return true;
// }))
// // 3rd condition change.
// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
// data->clear();
-// data->push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs, 1));
-// return true;
+// data->push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs,
+// 1)); return true;
// }));
//
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
// valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10);
//
// vector<shared_ptr<LogEvent>> allData;
@@ -3002,7 +2963,7 @@ static void assertPastBucketValuesSingleKey(
// assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {});
//}
//
-//TEST(ValueMetricProducerTest, TestPullNeededFastDump) {
+// TEST(ValueMetricProducerTest, TestPullNeededFastDump) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
//
// UidMap uidMap;
@@ -3043,7 +3004,7 @@ static void assertPastBucketValuesSingleKey(
// EXPECT_EQ(0, report.value_metrics().data_size());
//}
//
-//TEST(ValueMetricProducerTest, TestFastDumpWithoutCurrentBucket) {
+// TEST(ValueMetricProducerTest, TestFastDumpWithoutCurrentBucket) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
//
// UidMap uidMap;
@@ -3095,7 +3056,7 @@ static void assertPastBucketValuesSingleKey(
// EXPECT_EQ(0, report.value_metrics().data(0).bucket_info(0).bucket_num());
//}
//
-//TEST(ValueMetricProducerTest, TestPullNeededNoTimeConstraints) {
+// TEST(ValueMetricProducerTest, TestPullNeededNoTimeConstraints) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
//
// UidMap uidMap;
@@ -3147,7 +3108,7 @@ static void assertPastBucketValuesSingleKey(
// EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long());
//}
//
-//TEST(ValueMetricProducerTest, TestPulledData_noDiff_withoutCondition) {
+// TEST(ValueMetricProducerTest, TestPulledData_noDiff_withoutCondition) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
// metric.set_use_diff(false);
//
@@ -3163,7 +3124,7 @@ static void assertPastBucketValuesSingleKey(
// assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs});
//}
//
-//TEST(ValueMetricProducerTest, TestPulledData_noDiff_withMultipleConditionChanges) {
+// TEST(ValueMetricProducerTest, TestPulledData_noDiff_withMultipleConditionChanges) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
// metric.set_use_diff(false);
//
@@ -3184,7 +3145,8 @@ static void assertPastBucketValuesSingleKey(
// return true;
// }));
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
// valueProducer->mCondition = ConditionState::kFalse;
//
// valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
@@ -3193,8 +3155,8 @@ static void assertPastBucketValuesSingleKey(
// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
// ValueMetricProducer::Interval curInterval =
// valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(false, curBaseInfo.hasBase);
+// ValueMetricProducer::BaseInfo curBaseInfo =
+// valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(false, curBaseInfo.hasBase);
// EXPECT_EQ(true, curInterval.hasValue);
// EXPECT_EQ(20, curInterval.value.long_value);
//
@@ -3210,7 +3172,7 @@ static void assertPastBucketValuesSingleKey(
// EXPECT_EQ(false, curInterval.hasValue);
//}
//
-//TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryTrue) {
+// TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryTrue) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
// metric.set_use_diff(false);
//
@@ -3224,7 +3186,8 @@ static void assertPastBucketValuesSingleKey(
// return true;
// }));
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
// valueProducer->mCondition = ConditionState::kFalse;
//
// valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
@@ -3237,18 +3200,19 @@ static void assertPastBucketValuesSingleKey(
// assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs - 8});
// ValueMetricProducer::Interval curInterval =
// valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(false, curBaseInfo.hasBase);
+// ValueMetricProducer::BaseInfo curBaseInfo =
+// valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(false, curBaseInfo.hasBase);
// EXPECT_EQ(false, curInterval.hasValue);
//}
//
-//TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryFalse) {
+// TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryFalse) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
// metric.set_use_diff(false);
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
// valueProducer->mCondition = ConditionState::kFalse;
//
// // Now the alarm is delivered. Condition is off though.
@@ -3260,7 +3224,7 @@ static void assertPastBucketValuesSingleKey(
// assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {});
//}
//
-//TEST(ValueMetricProducerTest, TestPulledData_noDiff_withFailure) {
+// TEST(ValueMetricProducerTest, TestPulledData_noDiff_withFailure) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
// metric.set_use_diff(false);
//
@@ -3275,7 +3239,8 @@ static void assertPastBucketValuesSingleKey(
// }))
// .WillOnce(Return(false));
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
// valueProducer->mCondition = ConditionState::kFalse;
//
// valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
@@ -3300,7 +3265,7 @@ static void assertPastBucketValuesSingleKey(
// * - the dump latency must be FAST
// */
//
-//TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenDumpReportRequested) {
+// TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenDumpReportRequested) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -3317,7 +3282,8 @@ static void assertPastBucketValuesSingleKey(
// }));
//
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
//
// // Condition change event.
// valueProducer->onConditionChanged(true, bucketStartTimeNs + 20);
@@ -3348,7 +3314,7 @@ static void assertPastBucketValuesSingleKey(
// * Test that EVENT_IN_WRONG_BUCKET dump reason is logged for a late condition
// * change event (i.e. the condition change occurs in the wrong bucket).
// */
-//TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenConditionEventWrongBucket) {
+// TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenConditionEventWrongBucket) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -3365,7 +3331,8 @@ static void assertPastBucketValuesSingleKey(
// }));
//
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
//
// // Condition change event.
// valueProducer->onConditionChanged(true, bucketStartTimeNs + 50);
@@ -3408,7 +3375,7 @@ static void assertPastBucketValuesSingleKey(
// * Test that EVENT_IN_WRONG_BUCKET dump reason is logged for a late accumulate
// * event (i.e. the accumulate events call occurs in the wrong bucket).
// */
-//TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenAccumulateEventWrongBucket) {
+// TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenAccumulateEventWrongBucket) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -3426,16 +3393,15 @@ static void assertPastBucketValuesSingleKey(
// // Dump report requested.
// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 100);
-// event->write("field1");
-// event->write(15);
-// event->init();
+// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs +
+// 100); event->write("field1"); event->write(15); event->init();
// data->push_back(event);
// return true;
// }));
//
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
//
// // Condition change event.
// valueProducer->onConditionChanged(true, bucketStartTimeNs + 50);
@@ -3485,7 +3451,7 @@ static void assertPastBucketValuesSingleKey(
// * Test that CONDITION_UNKNOWN dump reason is logged due to an unknown condition
// * when a metric is initialized.
// */
-//TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenConditionUnknown) {
+// TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenConditionUnknown) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -3503,10 +3469,8 @@ static void assertPastBucketValuesSingleKey(
// // Dump report requested.
// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 100);
-// event->write("field1");
-// event->write(15);
-// event->init();
+// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs +
+// 100); event->write("field1"); event->write(15); event->init();
// data->push_back(event);
// return true;
// }));
@@ -3545,7 +3509,7 @@ static void assertPastBucketValuesSingleKey(
// * Test that PULL_FAILED dump reason is logged due to a pull failure in
// * #pullAndMatchEventsLocked.
// */
-//TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenPullFailed) {
+// TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenPullFailed) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -3564,7 +3528,8 @@ static void assertPastBucketValuesSingleKey(
// .WillOnce(Return(false));
//
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
//
// // Condition change event.
// valueProducer->onConditionChanged(true, bucketStartTimeNs + 50);
@@ -3596,7 +3561,7 @@ static void assertPastBucketValuesSingleKey(
// * Test that MULTIPLE_BUCKETS_SKIPPED dump reason is logged when a log event
// * skips over more than one bucket.
// */
-//TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenMultipleBucketsSkipped) {
+// TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenMultipleBucketsSkipped) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -3624,7 +3589,8 @@ static void assertPastBucketValuesSingleKey(
// }));
//
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
//
// // Condition change event.
// valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
@@ -3635,7 +3601,8 @@ static void assertPastBucketValuesSingleKey(
// // Check dump report.
// ProtoOutputStream output;
// std::set<string> strSet;
-// valueProducer->onDumpReport(bucket4StartTimeNs + 1000, true /* include recent buckets */, true,
+// valueProducer->onDumpReport(bucket4StartTimeNs + 1000, true /* include recent buckets */,
+// true,
// NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
//
// StatsLogReport report = outputStreamToProto(&output);
@@ -3658,7 +3625,7 @@ static void assertPastBucketValuesSingleKey(
// * Test that BUCKET_TOO_SMALL dump reason is logged when a flushed bucket size
// * is smaller than the "min_bucket_size_nanos" specified in the metric config.
// */
-//TEST(ValueMetricProducerTest_BucketDrop, TestBucketDropWhenBucketTooSmall) {
+// TEST(ValueMetricProducerTest_BucketDrop, TestBucketDropWhenBucketTooSmall) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
// metric.set_min_bucket_size_nanos(10000000000); // 10 seconds
//
@@ -3687,7 +3654,8 @@ static void assertPastBucketValuesSingleKey(
// }));
//
// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+// metric);
//
// // Condition change event.
// valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
@@ -3718,7 +3686,7 @@ static void assertPastBucketValuesSingleKey(
///*
// * Test multiple bucket drop events in the same bucket.
// */
-//TEST(ValueMetricProducerTest_BucketDrop, TestMultipleBucketDropEvents) {
+// TEST(ValueMetricProducerTest_BucketDrop, TestMultipleBucketDropEvents) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -3772,7 +3740,7 @@ static void assertPastBucketValuesSingleKey(
// * Test that the number of logged bucket drop events is capped at the maximum.
// * The maximum is currently 10 and is set in MetricProducer::maxDropEventsReached().
// */
-//TEST(ValueMetricProducerTest_BucketDrop, TestMaxBucketDropEvents) {
+// TEST(ValueMetricProducerTest_BucketDrop, TestMaxBucketDropEvents) {
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
//
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -3800,10 +3768,8 @@ static void assertPastBucketValuesSingleKey(
// .WillOnce(Return(false))
// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 220);
-// event->write("field1");
-// event->write(10);
-// event->init();
+// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs +
+// 220); event->write("field1"); event->write(10); event->init();
// data->push_back(event);
// return true;
// }));
@@ -3896,7 +3862,7 @@ static void assertPastBucketValuesSingleKey(
// * - Using diff
// * - Second field is value field
// */
-//TEST(ValueMetricProducerTest, TestSlicedState) {
+// TEST(ValueMetricProducerTest, TestSlicedState) {
// // Set up ValueMetricProducer.
// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("SCREEN_STATE");
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -3987,7 +3953,8 @@ static void assertPastBucketValuesSingleKey(
// EXPECT_EQ(2, it->second[0].value.long_value);
//
// // Bucket status after screen state change ON->OFF.
-// screenEvent = CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
+// screenEvent =
+// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
// bucketStartTimeNs + 10);
// StateManager::getInstance().onLogEvent(*screenEvent);
// EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
@@ -4067,9 +4034,10 @@ static void assertPastBucketValuesSingleKey(
// * - Using diff
// * - Second field is value field
// */
-//TEST(ValueMetricProducerTest, TestSlicedStateWithMap) {
+// TEST(ValueMetricProducerTest, TestSlicedStateWithMap) {
// // Set up ValueMetricProducer.
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("SCREEN_STATE_ONOFF");
+// ValueMetric metric =
+// ValueMetricProducerTestHelper::createMetricWithState("SCREEN_STATE_ONOFF");
// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
// EXPECT_CALL(*pullerManager, Pull(tagId, _))
// // ValueMetricProducer initialized.
@@ -4189,7 +4157,8 @@ static void assertPastBucketValuesSingleKey(
// EXPECT_EQ(2, it->second[0].value.long_value);
//
// // Bucket status after screen state change VR->OFF.
-// screenEvent = CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
+// screenEvent =
+// CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
// bucketStartTimeNs + 15);
// StateManager::getInstance().onLogEvent(*screenEvent);
// EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
@@ -4243,9 +4212,10 @@ static void assertPastBucketValuesSingleKey(
// * - Using diff
// * - Second field is value field
// */
-//TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
+// TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
// // Set up ValueMetricProducer.
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("UID_PROCESS_STATE");
+// ValueMetric metric =
+// ValueMetricProducerTestHelper::createMetricWithState("UID_PROCESS_STATE");
// metric.mutable_dimensions_in_what()->set_field(tagId);
// metric.mutable_dimensions_in_what()->add_child()->set_field(1);
//
@@ -4311,10 +4281,8 @@ static void assertPastBucketValuesSingleKey(
// // Uid 1 process state change from Foreground -> Background
// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 20);
-// event->write(1 /* uid */);
-// event->write(13);
-// event->init();
+// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs +
+// 20); event->write(1 /* uid */); event->write(13); event->init();
// data->push_back(event);
//
// // This event should be skipped.
@@ -4328,10 +4296,8 @@ static void assertPastBucketValuesSingleKey(
// // Uid 1 process state change from Background -> Foreground
// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 40);
-// event->write(1 /* uid */);
-// event->write(17);
-// event->init();
+// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs +
+// 40); event->write(1 /* uid */); event->write(17); event->init();
// data->push_back(event);
//
// // This event should be skipped.
@@ -4345,10 +4311,8 @@ static void assertPastBucketValuesSingleKey(
// // Dump report pull.
// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 50);
-// event->write(2 /* uid */);
-// event->write(20);
-// event->init();
+// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs +
+// 50); event->write(2 /* uid */); event->write(20); event->init();
// data->push_back(event);
//
// event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 50);
@@ -4393,7 +4357,8 @@ static void assertPastBucketValuesSingleKey(
//
// // Bucket status after uid 1 process state change kStateUnknown -> Foreground.
// auto uidProcessEvent = CreateUidProcessStateChangedEvent(
-// 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, bucketStartTimeNs + 20);
+// 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, bucketStartTimeNs +
+// 20);
// StateManager::getInstance().onLogEvent(*uidProcessEvent);
// EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
// // Base for dimension key {uid 1}.
@@ -4420,7 +4385,8 @@ static void assertPastBucketValuesSingleKey(
//
// // Bucket status after uid 2 process state change kStateUnknown -> Background.
// uidProcessEvent = CreateUidProcessStateChangedEvent(
-// 2 /* uid */, android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, bucketStartTimeNs + 40);
+// 2 /* uid */, android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, bucketStartTimeNs +
+// 40);
// StateManager::getInstance().onLogEvent(*uidProcessEvent);
// EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
// // Base for dimension key {uid 1}.
@@ -4499,7 +4465,8 @@ static void assertPastBucketValuesSingleKey(
//
// // Bucket status after uid 1 process state change from Foreground -> Background.
// uidProcessEvent = CreateUidProcessStateChangedEvent(
-// 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, bucket2StartTimeNs + 20);
+// 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, bucket2StartTimeNs +
+// 20);
// StateManager::getInstance().onLogEvent(*uidProcessEvent);
//
// EXPECT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size());
@@ -4533,7 +4500,8 @@ static void assertPastBucketValuesSingleKey(
//
// // Bucket status after uid 1 process state change Background->Foreground.
// uidProcessEvent = CreateUidProcessStateChangedEvent(
-// 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, bucket2StartTimeNs + 40);
+// 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, bucket2StartTimeNs +
+// 40);
// StateManager::getInstance().onLogEvent(*uidProcessEvent);
//
// EXPECT_EQ(5UL, valueProducer->mCurrentSlicedBucket.size());
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index e2eee032a43b..58eeed3ba054 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -410,6 +410,127 @@ FieldMatcher CreateDimensions(const int atomId, const std::vector<int>& fields)
return dimensions;
}
+shared_ptr<LogEvent> CreateTwoValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1,
+ int32_t value2) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
+
+ AStatsEvent_writeInt32(statsEvent, value1);
+ AStatsEvent_writeInt32(statsEvent, value2);
+ AStatsEvent_build(statsEvent);
+
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+ shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
+ logEvent->parseBuffer(buf, size);
+ AStatsEvent_release(statsEvent);
+
+ return logEvent;
+}
+//
+void CreateTwoValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, int32_t value1,
+ int32_t value2) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
+
+ AStatsEvent_writeInt32(statsEvent, value1);
+ AStatsEvent_writeInt32(statsEvent, value2);
+ AStatsEvent_build(statsEvent);
+
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+ logEvent->parseBuffer(buf, size);
+ AStatsEvent_release(statsEvent);
+}
+
+shared_ptr<LogEvent> CreateThreeValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1,
+ int32_t value2, int32_t value3) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
+
+ AStatsEvent_writeInt32(statsEvent, value1);
+ AStatsEvent_writeInt32(statsEvent, value2);
+ AStatsEvent_writeInt32(statsEvent, value3);
+ AStatsEvent_build(statsEvent);
+
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+ shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
+ logEvent->parseBuffer(buf, size);
+ AStatsEvent_release(statsEvent);
+
+ return logEvent;
+}
+
+void CreateThreeValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, int32_t value1,
+ int32_t value2, int32_t value3) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
+
+ AStatsEvent_writeInt32(statsEvent, value1);
+ AStatsEvent_writeInt32(statsEvent, value2);
+ AStatsEvent_writeInt32(statsEvent, value3);
+ AStatsEvent_build(statsEvent);
+
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+ logEvent->parseBuffer(buf, size);
+ AStatsEvent_release(statsEvent);
+}
+
+shared_ptr<LogEvent> CreateRepeatedValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
+
+ AStatsEvent_writeInt32(statsEvent, value);
+ AStatsEvent_writeInt32(statsEvent, value);
+ AStatsEvent_build(statsEvent);
+
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+ shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
+ logEvent->parseBuffer(buf, size);
+ AStatsEvent_release(statsEvent);
+
+ return logEvent;
+}
+
+void CreateRepeatedValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs,
+ int32_t value) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
+
+ AStatsEvent_writeInt32(statsEvent, value);
+ AStatsEvent_writeInt32(statsEvent, value);
+ AStatsEvent_build(statsEvent);
+
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+ logEvent->parseBuffer(buf, size);
+ AStatsEvent_release(statsEvent);
+}
+
+shared_ptr<LogEvent> CreateNoValuesLogEvent(int atomId, int64_t eventTimeNs) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
+ AStatsEvent_build(statsEvent);
+
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+ shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
+ logEvent->parseBuffer(buf, size);
+ AStatsEvent_release(statsEvent);
+
+ return logEvent;
+}
+
std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
uint64_t timestampNs, const android::view::DisplayStateEnum state) {
AStatsEvent* statsEvent = AStatsEvent_obtain();
diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h
index 437101578397..4c25ca32020e 100644
--- a/cmds/statsd/tests/statsd_test_util.h
+++ b/cmds/statsd/tests/statsd_test_util.h
@@ -164,6 +164,29 @@ FieldMatcher CreateAttributionUidAndTagDimensions(const int atomId,
FieldMatcher CreateAttributionUidDimensions(const int atomId,
const std::vector<Position>& positions);
+shared_ptr<LogEvent> CreateTwoValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1,
+ int32_t value2);
+
+void CreateTwoValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, int32_t value1,
+ int32_t value2);
+
+shared_ptr<LogEvent> CreateThreeValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1,
+ int32_t value2, int32_t value3);
+
+void CreateThreeValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, int32_t value1,
+ int32_t value2, int32_t value3);
+
+// The repeated value log event helpers create a log event with two int fields, both
+// set to the same value. This is useful for testing metrics that are only interested
+// in the value of the second field but still need the first field to be populated.
+std::shared_ptr<LogEvent> CreateRepeatedValueLogEvent(int atomId, int64_t eventTimeNs,
+ int32_t value);
+
+void CreateRepeatedValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs,
+ int32_t value);
+
+std::shared_ptr<LogEvent> CreateNoValuesLogEvent(int atomId, int64_t eventTimeNs);
+
// Create log event for screen state changed.
std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
uint64_t timestampNs, const android::view::DisplayStateEnum state);
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 1a92b758ab9f..f4ee8faaf9bf 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1315,7 +1315,8 @@ public class ActivityManager {
/**
* @return The in-memory or loaded icon that represents the current state of this task.
- * @deprecated This call is no longer supported.
+ * @deprecated This call is no longer supported. The caller should keep track of any icons
+ * it sets for the task descriptions internally.
*/
@Deprecated
public Bitmap getIcon() {
@@ -4242,8 +4243,6 @@ public class ActivityManager {
* {@code false} otherwise.
* @hide
*/
- @SystemApi
- @TestApi
@RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION)
public boolean updateMccMncConfiguration(@NonNull String mcc, @NonNull String mnc) {
if (mcc == null || mnc == null) {
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index ec110435d95c..489a0de0518e 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -34,6 +34,7 @@ import android.os.TransactionTooLargeException;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
/**
* Activity manager local system service interface.
@@ -124,6 +125,12 @@ public abstract class ActivityManagerInternal {
public abstract int getUidProcessState(int uid);
/**
+ * Get a map of pid and package name that process of that pid Android/data and Android/obb
+ * directory is not mounted to lowerfs.
+ */
+ public abstract Map<Integer, String> getProcessesWithPendingBindMounts(int userId);
+
+ /**
* @return {@code true} if system is ready, {@code false} otherwise.
*/
public abstract boolean isSystemReady();
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index f6a79cd767da..fa4aa19d846e 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -696,6 +696,10 @@ public class AppOpsManager {
public static final int SAMPLING_STRATEGY_RARELY_USED =
FrameworkStatsLog.RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__RARELY_USED;
+ /** @hide */
+ public static final int SAMPLING_STRATEGY_BOOT_TIME_SAMPLING =
+ FrameworkStatsLog.RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__BOOT_TIME_SAMPLING;
+
/**
* Strategies used for message sampling
* @hide
@@ -704,7 +708,8 @@ public class AppOpsManager {
@IntDef(prefix = {"SAMPLING_STRATEGY_"}, value = {
SAMPLING_STRATEGY_DEFAULT,
SAMPLING_STRATEGY_UNIFORM,
- SAMPLING_STRATEGY_RARELY_USED
+ SAMPLING_STRATEGY_RARELY_USED,
+ SAMPLING_STRATEGY_BOOT_TIME_SAMPLING
})
public @interface SamplingStrategy {}
@@ -1071,9 +1076,17 @@ public class AppOpsManager {
/** @hide Auto-revoke app permissions if app is unused for an extended period */
public static final int OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED = 97;
+ /**
+ * Whether {@link #OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED} is allowed to be changed by
+ * the installer
+ *
+ * @hide
+ */
+ public static final int OP_AUTO_REVOKE_MANAGED_BY_INSTALLER = 98;
+
/** @hide */
@UnsupportedAppUsage
- public static final int _NUM_OP = 98;
+ public static final int _NUM_OP = 99;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1356,7 +1369,6 @@ public class AppOpsManager {
@SystemApi
public static final String OPSTR_ACCESS_ACCESSIBILITY = "android:access_accessibility";
/** @hide Read device identifiers */
- @SystemApi
public static final String OPSTR_READ_DEVICE_IDENTIFIERS = "android:read_device_identifiers";
/** @hide Query all packages on device */
public static final String OPSTR_QUERY_ALL_PACKAGES = "android:query_all_packages";
@@ -1373,6 +1385,10 @@ public class AppOpsManager {
public static final String OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED =
"android:auto_revoke_permissions_if_unused";
+ /** @hide Auto-revoke app permissions if app is unused for an extended period */
+ public static final String OPSTR_AUTO_REVOKE_MANAGED_BY_INSTALLER =
+ "android:auto_revoke_managed_by_installer";
+
/** @hide Communicate cross-profile within the same profile group. */
@SystemApi
public static final String OPSTR_INTERACT_ACROSS_PROFILES = "android:interact_across_profiles";
@@ -1463,6 +1479,7 @@ public class AppOpsManager {
OP_LOADER_USAGE_STATS,
OP_ACCESS_CALL_AUDIO,
OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
+ OP_AUTO_REVOKE_MANAGED_BY_INSTALLER,
};
/**
@@ -1572,6 +1589,7 @@ public class AppOpsManager {
OP_LOADER_USAGE_STATS, // LOADER_USAGE_STATS
OP_ACCESS_CALL_AUDIO, // ACCESS_CALL_AUDIO
OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, //AUTO_REVOKE_PERMISSIONS_IF_UNUSED
+ OP_AUTO_REVOKE_MANAGED_BY_INSTALLER, //OP_AUTO_REVOKE_MANAGED_BY_INSTALLER
};
/**
@@ -1676,6 +1694,7 @@ public class AppOpsManager {
OPSTR_LOADER_USAGE_STATS,
OPSTR_ACCESS_CALL_AUDIO,
OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
+ OPSTR_AUTO_REVOKE_MANAGED_BY_INSTALLER,
};
/**
@@ -1781,6 +1800,7 @@ public class AppOpsManager {
"LOADER_USAGE_STATS",
"ACCESS_CALL_AUDIO",
"AUTO_REVOKE_PERMISSIONS_IF_UNUSED",
+ "AUTO_REVOKE_MANAGED_BY_INSTALLER",
};
/**
@@ -1887,6 +1907,7 @@ public class AppOpsManager {
android.Manifest.permission.LOADER_USAGE_STATS,
Manifest.permission.ACCESS_CALL_AUDIO,
null, // no permission for OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED
+ null, // no permission for OP_AUTO_REVOKE_MANAGED_BY_INSTALLER
};
/**
@@ -1993,6 +2014,7 @@ public class AppOpsManager {
null, // LOADER_USAGE_STATS
null, // ACCESS_CALL_AUDIO
null, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED
+ null, // AUTO_REVOKE_MANAGED_BY_INSTALLER
};
/**
@@ -2098,6 +2120,7 @@ public class AppOpsManager {
null, // LOADER_USAGE_STATS
null, // ACCESS_CALL_AUDIO
null, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED
+ null, // AUTO_REVOKE_MANAGED_BY_INSTALLER
};
/**
@@ -2202,6 +2225,7 @@ public class AppOpsManager {
AppOpsManager.MODE_DEFAULT, // LOADER_USAGE_STATS
AppOpsManager.MODE_DEFAULT, // ACCESS_CALL_AUDIO
AppOpsManager.MODE_DEFAULT, // OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED
+ AppOpsManager.MODE_ALLOWED, // OP_AUTO_REVOKE_MANAGED_BY_INSTALLER
};
/**
@@ -2310,9 +2334,118 @@ public class AppOpsManager {
false, // LOADER_USAGE_STATS
false, // ACCESS_CALL_AUDIO
false, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED
+ false, // AUTO_REVOKE_MANAGED_BY_INSTALLER
};
/**
+ * This maps each operation to its statsd logging code.
+ */
+ private static int[] sOpToLoggingId = new int[]{
+ AppProtoEnums.APP_OP_COARSE_LOCATION, // OP_COARSE_LOCATION
+ AppProtoEnums.APP_OP_FINE_LOCATION, // OP_FINE_LOCATION
+ AppProtoEnums.APP_OP_GPS, // OP_ID__GPS
+ AppProtoEnums.APP_OP_VIBRATE, // OP_VIBRATE
+ AppProtoEnums.APP_OP_READ_CONTACTS, // OP_READ_CONTACTS
+ AppProtoEnums.APP_OP_WRITE_CONTACTS, // OP_WRITE_CONTACTS
+ AppProtoEnums.APP_OP_READ_CALL_LOG, // OP_READ_CALL_LOG
+ AppProtoEnums.APP_OP_WRITE_CALL_LOG, // OP_WRITE_CALL_LOG
+ AppProtoEnums.APP_OP_READ_CALENDAR, // OP_READ_CALENDAR
+ AppProtoEnums.APP_OP_WRITE_CALENDAR, // OP_WRITE_CALENDAR
+ AppProtoEnums.APP_OP_WIFI_SCAN, // OP_WIFI_SCAN
+ AppProtoEnums.APP_OP_POST_NOTIFICATION, // OP_POST_NOTIFICATION
+ AppProtoEnums.APP_OP_NEIGHBORING_CELLS, // OP_NEIGHBORING_CELLS
+ AppProtoEnums.APP_OP_CALL_PHONE, // OP_CALL_PHONE
+ AppProtoEnums.APP_OP_READ_SMS, // OP_READ_SMS
+ AppProtoEnums.APP_OP_WRITE_SMS, // OP_WRITE_SMS
+ AppProtoEnums.APP_OP_RECEIVE_SMS, // OP_RECEIVE_SMS
+ AppProtoEnums.APP_OP_RECEIVE_EMERGENCY_SMS, // OP_RECEIVE_EMERGENCY_SMS
+ AppProtoEnums.APP_OP_RECEIVE_MMS, // OP_RECEIVE_MMS
+ AppProtoEnums.APP_OP_RECEIVE_WAP_PUSH, // OP_RECEIVE_WAP_PUSH
+ AppProtoEnums.APP_OP_SEND_SMS, // OP_SEND_SMS
+ AppProtoEnums.APP_OP_READ_ICC_SMS, // OP_READ_ICC_SMS
+ AppProtoEnums.APP_OP_WRITE_ICC_SMS, // OP_WRITE_ICC_SMS
+ AppProtoEnums.APP_OP_WRITE_SETTINGS, // OP_WRITE_SETTINGS
+ AppProtoEnums.APP_OP_SYSTEM_ALERT_WINDOW, // OP_SYSTEM_ALERT_WINDOW
+ AppProtoEnums.APP_OP_ACCESS_NOTIFICATIONS, // OP_ACCESS_NOTIFICATIONS
+ AppProtoEnums.APP_OP_CAMERA, // OP_CAMERA
+ AppProtoEnums.APP_OP_RECORD_AUDIO, // OP_RECORD_AUDIO
+ AppProtoEnums.APP_OP_PLAY_AUDIO, // OP_PLAY_AUDIO
+ AppProtoEnums.APP_OP_READ_CLIPBOARD, // OP_READ_CLIPBOARD
+ AppProtoEnums.APP_OP_WRITE_CLIPBOARD, // OP_WRITE_CLIPBOARD
+ AppProtoEnums.APP_OP_TAKE_MEDIA_BUTTONS, // OP_TAKE_MEDIA_BUTTONS
+ AppProtoEnums.APP_OP_TAKE_AUDIO_FOCUS, // OP_TAKE_AUDIO_FOCUS
+ AppProtoEnums.APP_OP_AUDIO_MASTER_VOLUME, // OP_AUDIO_MASTER_VOLUME
+ AppProtoEnums.APP_OP_AUDIO_VOICE_VOLUME, // OP_AUDIO_VOICE_VOLUME
+ AppProtoEnums.APP_OP_AUDIO_RING_VOLUME, // OP_AUDIO_RING_VOLUME
+ AppProtoEnums.APP_OP_AUDIO_MEDIA_VOLUME, // OP_AUDIO_MEDIA_VOLUME
+ AppProtoEnums.APP_OP_AUDIO_ALARM_VOLUME, // OP_AUDIO_ALARM_VOLUME
+ AppProtoEnums.APP_OP_AUDIO_NOTIFICATION_VOLUME, // OP_AUDIO_NOTIFICATION_VOLUME
+ AppProtoEnums.APP_OP_AUDIO_BLUETOOTH_VOLUME, // OP_AUDIO_BLUETOOTH_VOLUME
+ AppProtoEnums.APP_OP_WAKE_LOCK, // OP_WAKE_LOCK
+ AppProtoEnums.APP_OP_MONITOR_LOCATION, // OP_MONITOR_LOCATION
+ AppProtoEnums.APP_OP_MONITOR_HIGH_POWER_LOCATION, // OP_MONITOR_HIGH_POWER_LOCATION
+ AppProtoEnums.APP_OP_GET_USAGE_STATS, // OP_GET_USAGE_STATS
+ AppProtoEnums.APP_OP_MUTE_MICROPHONE, //OP_MUTE_MICROPHONE
+ AppProtoEnums.APP_OP_TOAST_WINDOW, // OP_TOAST_WINDOW
+ AppProtoEnums.APP_OP_PROJECT_MEDIA, // OP_PROJECT_MEDIA
+ AppProtoEnums.APP_OP_ACTIVATE_VPN, // OP_ACTIVATE_VPN
+ AppProtoEnums.APP_OP_WRITE_WALLPAPER, // OP_WRITE_WALLPAPER
+ AppProtoEnums.APP_OP_ASSIST_STRUCTURE, // OP_ASSIST_STRUCTURE
+ AppProtoEnums.APP_OP_ASSIST_SCREENSHOT, // OP_ASSIST_SCREENSHOT
+ AppProtoEnums.APP_OP_READ_PHONE_STATE, // OP_READ_PHONE_STATE
+ AppProtoEnums.APP_OP_ADD_VOICEMAIL, // OP_ADD_VOICEMAIL
+ AppProtoEnums.APP_OP_USE_SIP, // OP_USE_SIP
+ AppProtoEnums.APP_OP_PROCESS_OUTGOING_CALLS, // OP_PROCESS_OUTGOING_CALLS
+ AppProtoEnums.APP_OP_USE_FINGERPRINT, // OP_USE_FINGERPRINT
+ AppProtoEnums.APP_OP_BODY_SENSORS, // OP_BODY_SENSORS
+ AppProtoEnums.APP_OP_READ_CELL_BROADCASTS, // OP_READ_CELL_BROADCASTS
+ AppProtoEnums.APP_OP_MOCK_LOCATION, // OP_MOCK_LOCATION
+ AppProtoEnums.APP_OP_READ_EXTERNAL_STORAGE, // OP_READ_EXTERNAL_STORAGE
+ AppProtoEnums.APP_OP_WRITE_EXTERNAL_STORAGE, // OP_WRITE_EXTERNAL_STORAGE
+ AppProtoEnums.APP_OP_TURN_SCREEN_ON, // OP_TURN_SCREEN_ON
+ AppProtoEnums.APP_OP_GET_ACCOUNTS, // OP_GET_ACCOUNTS
+ AppProtoEnums.APP_OP_RUN_IN_BACKGROUND, // OP_RUN_IN_BACKGROUND
+ AppProtoEnums.APP_OP_AUDIO_ACCESSIBILITY_VOLUME, // OP_AUDIO_ACCESSIBILITY_VOLUME
+ AppProtoEnums.APP_OP_READ_PHONE_NUMBERS, // OP_READ_PHONE_NUMBERS
+ AppProtoEnums.APP_OP_REQUEST_INSTALL_PACKAGES, // OP_REQUEST_INSTALL_PACKAGES
+ AppProtoEnums.APP_OP_PICTURE_IN_PICTURE, // OP_PICTURE_IN_PICTURE
+ AppProtoEnums.APP_OP_INSTANT_APP_START_FOREGROUND, // OP_INSTANT_APP_START_FOREGROUND
+ AppProtoEnums.APP_OP_ANSWER_PHONE_CALLS, // OP_ANSWER_PHONE_CALLS
+ AppProtoEnums.APP_OP_RUN_ANY_IN_BACKGROUND, // OP_RUN_ANY_IN_BACKGROUND
+ AppProtoEnums.APP_OP_CHANGE_WIFI_STATE, // OP_CHANGE_WIFI_STATE
+ AppProtoEnums.APP_OP_REQUEST_DELETE_PACKAGES, // OP_REQUEST_DELETE_PACKAGES
+ AppProtoEnums.APP_OP_BIND_ACCESSIBILITY_SERVICE, // OP_BIND_ACCESSIBILITY_SERVICE
+ AppProtoEnums.APP_OP_ACCEPT_HANDOVER, // OP_ACCEPT_HANDOVER
+ AppProtoEnums.APP_OP_MANAGE_IPSEC_TUNNELS, // OP_MANAGE_IPSEC_TUNNELS
+ AppProtoEnums.APP_OP_START_FOREGROUND, // OP_START_FOREGROUND
+ AppProtoEnums.APP_OP_BLUETOOTH_SCAN, // OP_BLUETOOTH_SCAN
+ AppProtoEnums.APP_OP_USE_BIOMETRIC, // OP_USE_BIOMETRIC
+ AppProtoEnums.APP_OP_ACTIVITY_RECOGNITION, // OP_ACTIVITY_RECOGNITION
+ AppProtoEnums.APP_OP_SMS_FINANCIAL_TRANSACTIONS, // OP_SMS_FINANCIAL_TRANSACTIONS
+ AppProtoEnums.APP_OP_READ_MEDIA_AUDIO, // OP_READ_MEDIA_AUDIO
+ AppProtoEnums.APP_OP_WRITE_MEDIA_AUDIO, // OP_WRITE_MEDIA_AUDIO
+ AppProtoEnums.APP_OP_READ_MEDIA_VIDEO, // OP_READ_MEDIA_VIDEO
+ AppProtoEnums.APP_OP_WRITE_MEDIA_VIDEO, // OP_WRITE_MEDIA_VIDEO
+ AppProtoEnums.APP_OP_READ_MEDIA_IMAGES, // OP_READ_MEDIA_IMAGES
+ AppProtoEnums.APP_OP_WRITE_MEDIA_IMAGES, // OP_WRITE_MEDIA_IMAGES
+ AppProtoEnums.APP_OP_LEGACY_STORAGE, // OP_LEGACY_STORAGE
+ AppProtoEnums.APP_OP_ACCESS_ACCESSIBILITY, // OP_ACCESS_ACCESSIBILITY
+ AppProtoEnums.APP_OP_READ_DEVICE_IDENTIFIERS, // OP_READ_DEVICE_IDENTIFIERS
+ AppProtoEnums.APP_OP_ACCESS_MEDIA_LOCATION, // OP_ACCESS_MEDIA_LOCATION
+ AppProtoEnums.APP_OP_QUERY_ALL_PACKAGES, // OP_QUERY_ALL_PACKAGES
+ AppProtoEnums.APP_OP_MANAGE_EXTERNAL_STORAGE, // OP_MANAGE_EXTERNAL_STORAGE
+ AppProtoEnums.APP_OP_INTERACT_ACROSS_PROFILES, // OP_INTERACT_ACROSS_PROFILES
+ AppProtoEnums.APP_OP_ACTIVATE_PLATFORM_VPN, // OP_ACTIVATE_PLATFORM_VPN
+ AppProtoEnums.APP_OP_LOADER_USAGE_STATS, // OP_LOADER_USAGE_STATS
+ AppProtoEnums.APP_OP_ACCESS_CALL_AUDIO, // OP_ACCESS_CALL_AUDIO
+ AppProtoEnums.APP_OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
+ // OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED
+ AppProtoEnums.APP_OP_AUTO_REVOKE_MANAGED_BY_INSTALLER
+ //OP_AUTO_REVOKE_MANAGED_BY_INSTALLER
+ };
+
+
+ /**
* Mapping from an app op name to the app op code.
*/
private static HashMap<String, Integer> sOpStrToOp = new HashMap<>();
@@ -2353,6 +2486,10 @@ public class AppOpsManager {
throw new IllegalStateException("sOpToString length " + sOpToString.length
+ " should be " + _NUM_OP);
}
+ if (sOpToLoggingId.length != _NUM_OP) {
+ throw new IllegalStateException("sOpToLoggingId length " + sOpToLoggingId.length
+ + " should be " + _NUM_OP);
+ }
if (sOpNames.length != _NUM_OP) {
throw new IllegalStateException("sOpNames length " + sOpNames.length
+ " should be " + _NUM_OP);
@@ -2437,6 +2574,15 @@ public class AppOpsManager {
}
/**
+ * Retrieve a logging id for the operation.
+ *
+ * @hide
+ */
+ public static int opToLoggingId(int op) {
+ return sOpToLoggingId[op];
+ }
+
+ /**
* @hide
*/
public static int strDebugOpToOp(String op) {
@@ -5890,6 +6036,11 @@ public class AppOpsManager {
return mOp;
}
+ /** @hide */
+ public int getLoggingOpCode() {
+ return AppOpsManager.opToLoggingId(mOp);
+ }
+
/**
* Gets the number times the op was accessed (performed) in the foreground.
*
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index bfaa2be6da6a..18df401c9c8d 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -840,6 +840,27 @@ public class ApplicationPackageManager extends PackageManager {
}
@Override
+ public boolean setAutoRevokeWhitelisted(
+ @NonNull String packageName, boolean whitelisted) {
+ try {
+ final int userId = getUserId();
+ return mPermissionManager.setAutoRevokeWhitelisted(packageName, whitelisted, userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ public boolean isAutoRevokeWhitelisted(@NonNull String packageName) {
+ try {
+ final int userId = getUserId();
+ return mPermissionManager.isAutoRevokeWhitelisted(packageName, userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
public boolean removeWhitelistedRestrictedPermission(@NonNull String packageName,
@NonNull String permName, @PermissionWhitelistFlags int flags) {
try {
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 56e6aee83a44..9ccfe8df14c2 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1899,8 +1899,9 @@ class ContextImpl extends Context {
public Object getSystemService(String name) {
// Check incorrect Context usage.
if (isUiComponent(name) && !isUiContext() && vmIncorrectContextUseEnabled()) {
- final String errorMessage = "Tried to access visual service " + name
- + " from a non-visual Context.";
+ final String errorMessage = "Tried to access visual service "
+ + SystemServiceRegistry.getSystemServiceClassName(name)
+ + " from a non-visual Context. ";
final String message = "Visual services, such as WindowManager, WallpaperService or "
+ "LayoutInflater should be accessed from Activity or other visual Context. "
+ "Use an Activity or a Context created with "
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 061e5ff35f55..864af3d4ae4d 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -7526,9 +7526,11 @@ public class Notification implements Parcelable
p,
bindResult);
addExtras(mBuilder.mN.extras);
- // also update the end margin if there is an image
- contentView.setViewLayoutMarginEnd(R.id.notification_messaging,
- bindResult.getIconMarginEnd());
+ if (!CONVERSATION_LAYOUT_ENABLED) {
+ // also update the end margin if there is an image
+ contentView.setViewLayoutMarginEnd(R.id.notification_messaging,
+ bindResult.getIconMarginEnd());
+ }
contentView.setInt(R.id.status_bar_latest_event_content, "setLayoutColor",
mBuilder.isColorized(p) ? mBuilder.getPrimaryTextColor(p)
: mBuilder.resolveContrastColor(p));
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index cbbdf639d036..811b9c082be2 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1015,14 +1015,15 @@ public class NotificationManager {
}
/**
+ * Returns the currently applied notification policy.
+ *
* <p>
- * Gets the currently applied notification policy. If {@link #getCurrentInterruptionFilter}
- * is equal to {@link #INTERRUPTION_FILTER_ALL}, then the consolidated notification policy
- * will match the default notification policy returned by {@link #getNotificationPolicy}.
+ * If {@link #getCurrentInterruptionFilter} is equal to {@link #INTERRUPTION_FILTER_ALL},
+ * then the consolidated notification policy will match the default notification policy
+ * returned by {@link #getNotificationPolicy}.
* </p>
*/
- @Nullable
- public NotificationManager.Policy getConsolidatedNotificationPolicy() {
+ public @NonNull NotificationManager.Policy getConsolidatedNotificationPolicy() {
INotificationManager service = getService();
try {
return service.getConsolidatedNotificationPolicy();
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 42563b5a1561..1329fa45bb50 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -399,7 +399,6 @@ public class StatusBarManager {
* {@code false}, re-enables expansion of the status bar.
* @hide
*/
- @SystemApi
@TestApi
@RequiresPermission(android.Manifest.permission.STATUS_BAR)
public void setDisabledForSimNetworkLock(boolean disabled) {
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index a3bcc9cee39b..e8f30df614f3 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -19,6 +19,7 @@ package android.app;
import android.accounts.AccountManager;
import android.accounts.IAccountManager;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.app.ContextImpl.ServiceInitializationState;
import android.app.admin.DevicePolicyManager;
@@ -227,6 +228,8 @@ public final class SystemServiceRegistry {
new ArrayMap<Class<?>, String>();
private static final Map<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =
new ArrayMap<String, ServiceFetcher<?>>();
+ private static final Map<String, String> SYSTEM_SERVICE_CLASS_NAMES = new ArrayMap<>();
+
private static int sServiceCacheSize;
private static volatile boolean sInitializing;
@@ -1389,6 +1392,19 @@ public final class SystemServiceRegistry {
@NonNull Class<T> serviceClass, @NonNull ServiceFetcher<T> serviceFetcher) {
SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
+ SYSTEM_SERVICE_CLASS_NAMES.put(serviceName, serviceClass.getSimpleName());
+ }
+
+ /**
+ * Returns system service class name by system service name. This method is mostly an inverse of
+ * {@link #getSystemServiceName(Class)}
+ *
+ * @return system service class name. {@code null} if service name is invalid.
+ * @hide
+ */
+ @Nullable
+ public static String getSystemServiceClassName(@NonNull String name) {
+ return SYSTEM_SERVICE_CLASS_NAMES.get(name);
}
/**
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 1a845476e108..b2dd0ef012d8 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -23,6 +23,7 @@ import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.os.Parcel;
import android.os.RemoteException;
@@ -161,6 +162,13 @@ public class TaskInfo {
*/
public @WindowConfiguration.ActivityType int topActivityType;
+ /**
+ * The {@link ActivityInfo} of the top activity in this task.
+ * @hide
+ */
+ @Nullable
+ public ActivityInfo topActivityInfo;
+
TaskInfo() {
// Do nothing
}
@@ -217,8 +225,11 @@ public class TaskInfo {
token = IWindowContainer.Stub.asInterface(source.readStrongBinder());
topActivityType = source.readInt();
pictureInPictureParams = source.readInt() != 0
- ? PictureInPictureParams.CREATOR.createFromParcel(source)
- : null;
+ ? PictureInPictureParams.CREATOR.createFromParcel(source)
+ : null;
+ topActivityInfo = source.readInt() != 0
+ ? ActivityInfo.CREATOR.createFromParcel(source)
+ : null;
}
/**
@@ -262,6 +273,12 @@ public class TaskInfo {
dest.writeInt(1);
pictureInPictureParams.writeToParcel(dest, flags);
}
+ if (topActivityInfo == null) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(1);
+ topActivityInfo.writeToParcel(dest, flags);
+ }
}
@Override
@@ -278,6 +295,7 @@ public class TaskInfo {
+ " resizeMode=" + resizeMode
+ " token=" + token
+ " topActivityType=" + topActivityType
- + " pictureInPictureParams=" + pictureInPictureParams;
+ + " pictureInPictureParams=" + pictureInPictureParams
+ + " topActivityInfo=" + topActivityInfo;
}
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 347648a78886..5b8ee71c8ba3 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -5215,6 +5215,10 @@ public class DevicePolicyManager {
* <p>Because this method might take several seconds to complete, it should only be called from
* a worker thread. This method returns {@code null} when called from the main thread.
*
+ * <p>This method is not thread-safe, calling it from multiple threads at the same time will
+ * result in undefined behavior. If the calling thread is interrupted while the invocation is
+ * in-flight, it will eventually terminate and return {@code null}.
+ *
* <p>Note: If the provided {@code alias} is of an existing alias, all former grants that apps
* have been given to access the key and certificates associated with this alias will be
* revoked.
@@ -5732,6 +5736,10 @@ public class DevicePolicyManager {
throwIfParentInstance("isAlwaysOnVpnLockdownEnabled");
if (mService != null) {
try {
+ // Starting from Android R, the caller can pass the permission check in
+ // DevicePolicyManagerService if it holds android.permission.MAINLINE_NETWORK_STACK.
+ // Note that the android.permission.MAINLINE_NETWORK_STACK is a signature permission
+ // which is used by the NetworkStack mainline module.
return mService.isAlwaysOnVpnLockdownEnabled(admin);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -11848,18 +11856,19 @@ public class DevicePolicyManager {
}
/**
- * Called by Device owner to set packages as protected. User will not be able to clear app
- * data or force-stop protected packages.
+ * Called by Device owner to disable user control over apps. User will not be able to clear
+ * app data or force-stop packages.
*
* @param admin which {@link DeviceAdminReceiver} this request is associated with
- * @param packages The package names to protect.
+ * @param packages The package names for the apps.
* @throws SecurityException if {@code admin} is not a device owner.
*/
- public void setProtectedPackages(@NonNull ComponentName admin, @NonNull List<String> packages) {
- throwIfParentInstance("setProtectedPackages");
+ public void setUserControlDisabledPackages(@NonNull ComponentName admin,
+ @NonNull List<String> packages) {
+ throwIfParentInstance("setUserControlDisabledPackages");
if (mService != null) {
try {
- mService.setProtectedPackages(admin, packages);
+ mService.setUserControlDisabledPackages(admin, packages);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -11867,16 +11876,16 @@ public class DevicePolicyManager {
}
/**
- * Returns the list of packages protected by the device owner.
+ * Returns the list of packages over which user control is disabled by the device owner.
*
* @param admin which {@link DeviceAdminReceiver} this request is associated with
* @throws SecurityException if {@code admin} is not a device owner.
*/
- public @NonNull List<String> getProtectedPackages(@NonNull ComponentName admin) {
- throwIfParentInstance("getProtectedPackages");
+ public @NonNull List<String> getUserControlDisabledPackages(@NonNull ComponentName admin) {
+ throwIfParentInstance("getUserControlDisabledPackages");
if (mService != null) {
try {
- return mService.getProtectedPackages(admin);
+ return mService.getUserControlDisabledPackages(admin);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -11986,7 +11995,8 @@ public class DevicePolicyManager {
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with
* @param timeoutMillis Maximum time the profile is allowed to be off in milliseconds or 0 if
- * not limited.
+ * not limited. The minimum non-zero value corresponds to 72 hours. If an admin sets a
+ * smaller non-zero vaulue, 72 hours will be set instead.
* @throws IllegalStateException if the profile owner doesn't have an activity that handles
* {@link #ACTION_CHECK_POLICY_COMPLIANCE}
* @see #setPersonalAppsSuspended
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index f07123979529..514677e4867d 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -467,9 +467,9 @@ interface IDevicePolicyManager {
boolean setKeyGrantForApp(in ComponentName admin, String callerPackage, String alias, String packageName, boolean hasGrant);
- void setProtectedPackages(in ComponentName admin, in List<String> packages);
+ void setUserControlDisabledPackages(in ComponentName admin, in List<String> packages);
- List<String> getProtectedPackages(in ComponentName admin);
+ List<String> getUserControlDisabledPackages(in ComponentName admin);
void setCommonCriteriaModeEnabled(in ComponentName admin, boolean enabled);
boolean isCommonCriteriaModeEnabled(in ComponentName admin);
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 5b98188300c9..d6e77624a967 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -29,10 +29,10 @@ import android.net.ConnectivityManager;
import android.net.DataUsageRequest;
import android.net.INetworkStatsService;
import android.net.NetworkIdentity;
+import android.net.NetworkStack;
import android.net.NetworkTemplate;
-import android.net.netstats.provider.AbstractNetworkStatsProvider;
-import android.net.netstats.provider.NetworkStatsProviderCallback;
-import android.net.netstats.provider.NetworkStatsProviderWrapper;
+import android.net.netstats.provider.INetworkStatsProviderCallback;
+import android.net.netstats.provider.NetworkStatsProvider;
import android.os.Binder;
import android.os.Handler;
import android.os.Looper;
@@ -527,32 +527,53 @@ public class NetworkStatsManager {
/**
* Registers a custom provider of {@link android.net.NetworkStats} to provide network statistics
- * to the system. To unregister, invoke {@link NetworkStatsProviderCallback#unregister()}.
+ * to the system. To unregister, invoke {@link #unregisterNetworkStatsProvider}.
* Note that no de-duplication of statistics between providers is performed, so each provider
- * must only report network traffic that is not being reported by any other provider.
+ * must only report network traffic that is not being reported by any other provider. Also note
+ * that the provider cannot be re-registered after unregistering.
*
* @param tag a human readable identifier of the custom network stats provider. This is only
* used for debugging.
- * @param provider the subclass of {@link AbstractNetworkStatsProvider} that needs to be
+ * @param provider the subclass of {@link NetworkStatsProvider} that needs to be
* registered to the system.
- * @return a {@link NetworkStatsProviderCallback}, which can be used to report events to the
- * system or unregister the provider.
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
- @NonNull public NetworkStatsProviderCallback registerNetworkStatsProvider(
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_STATS_PROVIDER,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK})
+ @NonNull public void registerNetworkStatsProvider(
@NonNull String tag,
- @NonNull AbstractNetworkStatsProvider provider) {
+ @NonNull NetworkStatsProvider provider) {
try {
- final NetworkStatsProviderWrapper wrapper = new NetworkStatsProviderWrapper(provider);
- return new NetworkStatsProviderCallback(
- mService.registerNetworkStatsProvider(tag, wrapper));
+ if (provider.getProviderCallbackBinder() != null) {
+ throw new IllegalArgumentException("provider is already registered");
+ }
+ final INetworkStatsProviderCallback cbBinder =
+ mService.registerNetworkStatsProvider(tag, provider.getProviderBinder());
+ provider.setProviderCallbackBinder(cbBinder);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Unregisters an instance of {@link NetworkStatsProvider}.
+ *
+ * @param provider the subclass of {@link NetworkStatsProvider} that needs to be
+ * unregistered to the system.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_STATS_PROVIDER,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK})
+ @NonNull public void unregisterNetworkStatsProvider(@NonNull NetworkStatsProvider provider) {
+ try {
+ provider.getProviderCallbackBinderOrThrow().unregister();
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
- // Unreachable code, but compiler doesn't know about it.
- return null;
}
private static NetworkTemplate createTemplate(int networkType, String subscriberId) {
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 2c701b48455c..0d66198db038 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -288,25 +288,25 @@ public final class UsageStatsManager {
*/
public static final int REASON_SUB_PREDICTED_RESTORED = 0x0001;
/**
- * The reason for restricting the app is unknown or undefined.
+ * The reason the system forced the app into the bucket is unknown or undefined.
* @hide
*/
- public static final int REASON_SUB_RESTRICT_UNDEFINED = 0x0000;
+ public static final int REASON_SUB_FORCED_SYSTEM_FLAG_UNDEFINED = 0;
/**
* The app was unnecessarily using system resources (battery, memory, etc) in the background.
* @hide
*/
- public static final int REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE = 0x0001;
+ public static final int REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE = 1 << 0;
/**
* The app was deemed to be intentionally abusive.
* @hide
*/
- public static final int REASON_SUB_RESTRICT_ABUSE = 0x0002;
+ public static final int REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE = 1 << 1;
/**
* The app was displaying buggy behavior.
* @hide
*/
- public static final int REASON_SUB_RESTRICT_BUGGY = 0x0003;
+ public static final int REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY = 1 << 2;
/** @hide */
@@ -322,6 +322,17 @@ public final class UsageStatsManager {
@Retention(RetentionPolicy.SOURCE)
public @interface StandbyBuckets {}
+ /** @hide */
+ @IntDef(flag = true, prefix = {"REASON_SUB_FORCED_SYSTEM_FLAG_FLAG_"}, value = {
+ REASON_SUB_FORCED_SYSTEM_FLAG_UNDEFINED,
+ REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE,
+ REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE,
+ REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SystemForcedReasons {
+ }
+
/**
* Observer id of the registered observer for the group of packages that reached the usage
* time limit. Included as an extra in the PendingIntent that was registered.
@@ -1053,6 +1064,7 @@ public final class UsageStatsManager {
/** @hide */
public static String reasonToString(int standbyReason) {
+ final int subReason = standbyReason & REASON_SUB_MASK;
StringBuilder sb = new StringBuilder();
switch (standbyReason & REASON_MAIN_MASK) {
case REASON_MAIN_DEFAULT:
@@ -1060,19 +1072,8 @@ public final class UsageStatsManager {
break;
case REASON_MAIN_FORCED_BY_SYSTEM:
sb.append("s");
- switch (standbyReason & REASON_SUB_MASK) {
- case REASON_SUB_RESTRICT_ABUSE:
- sb.append("-ra");
- break;
- case REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE:
- sb.append("-rbru");
- break;
- case REASON_SUB_RESTRICT_BUGGY:
- sb.append("-rb");
- break;
- case REASON_SUB_RESTRICT_UNDEFINED:
- sb.append("-r");
- break;
+ if (subReason > 0) {
+ sb.append("-").append(Integer.toBinaryString(subReason));
}
break;
case REASON_MAIN_FORCED_BY_USER:
@@ -1080,7 +1081,7 @@ public final class UsageStatsManager {
break;
case REASON_MAIN_PREDICTED:
sb.append("p");
- switch (standbyReason & REASON_SUB_MASK) {
+ switch (subReason) {
case REASON_SUB_PREDICTED_RESTORED:
sb.append("-r");
break;
@@ -1091,7 +1092,7 @@ public final class UsageStatsManager {
break;
case REASON_MAIN_USAGE:
sb.append("u");
- switch (standbyReason & REASON_SUB_MASK) {
+ switch (subReason) {
case REASON_SUB_USAGE_SYSTEM_INTERACTION:
sb.append("-si");
break;
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index d4b5b1aab532..4bd7b059dfaa 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -257,11 +257,22 @@ public final class CompanionDeviceManager {
/**
* Check if a given package was {@link #associate associated} with a device with given
- * mac address by given user.
+ * Wi-Fi MAC address for a given user.
*
- * @param packageName the package to check for
- * @param macAddress the mac address or BSSID of the device to check for
- * @param user the user to check for
+ * <p>This is a system API protected by the
+ * {@link andrioid.Manifest.permission#MANAGE_COMPANION_DEVICES} permission, that’s currently
+ * called by the Android Wi-Fi stack to determine whether user consent is required to connect
+ * to a Wi-Fi network. Devices that have been pre-registered as companion devices will not
+ * require user consent to connect.</p>
+ *
+ * <p>Note if the caller has the
+ * {@link android.Manifest.permission#COMPANION_APPROVE_WIFI_CONNECTIONS} permission, this
+ * method will return true by default.</p>
+ *
+ * @param packageName the name of the package that has the association with the companion device
+ * @param macAddress the Wi-Fi MAC address or BSSID of the companion device to check for
+ * @param user the user handle that currently hosts the package being queried for a companion
+ * device association
* @return whether a corresponding association record exists
*
* @hide
diff --git a/core/java/android/content/PermissionChecker.java b/core/java/android/content/PermissionChecker.java
index 052c9209f6f7..b4340729a220 100644
--- a/core/java/android/content/PermissionChecker.java
+++ b/core/java/android/content/PermissionChecker.java
@@ -435,10 +435,11 @@ public final class PermissionChecker {
final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
final int opMode = (forDataDelivery)
? appOpsManager.noteProxyOpNoThrow(op, packageName, uid, attributionTag, message)
- : appOpsManager.unsafeCheckOpNoThrow(op, uid, packageName);
+ : appOpsManager.unsafeCheckOpRawNoThrow(op, uid, packageName);
switch (opMode) {
- case AppOpsManager.MODE_ALLOWED: {
+ case AppOpsManager.MODE_ALLOWED:
+ case AppOpsManager.MODE_FOREGROUND: {
return PERMISSION_GRANTED;
}
case AppOpsManager.MODE_DEFAULT: {
@@ -467,12 +468,14 @@ public final class PermissionChecker {
final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
final int opMode = (forDataDelivery)
? appOpsManager.noteProxyOpNoThrow(op, packageName, uid, attributionTag, message)
- : appOpsManager.unsafeCheckOpNoThrow(op, uid, packageName);
+ : appOpsManager.unsafeCheckOpRawNoThrow(op, uid, packageName);
- if (opMode == AppOpsManager.MODE_ALLOWED) {
- return PERMISSION_GRANTED;
- } else {
- return PERMISSION_SOFT_DENIED;
+ switch (opMode) {
+ case AppOpsManager.MODE_ALLOWED:
+ case AppOpsManager.MODE_FOREGROUND:
+ return PERMISSION_GRANTED;
+ default:
+ return PERMISSION_SOFT_DENIED;
}
}
}
diff --git a/core/java/android/content/integrity/AppIntegrityManager.java b/core/java/android/content/integrity/AppIntegrityManager.java
index 9f95d4d75f6f..2869abb53b37 100644
--- a/core/java/android/content/integrity/AppIntegrityManager.java
+++ b/core/java/android/content/integrity/AppIntegrityManager.java
@@ -25,6 +25,8 @@ import android.content.IntentSender;
import android.content.pm.ParceledListSlice;
import android.os.RemoteException;
+import java.util.List;
+
/**
* Class for pushing rules used to check the integrity of app installs.
*
@@ -121,4 +123,21 @@ public class AppIntegrityManager {
throw e.rethrowAsRuntimeException();
}
}
+
+ /**
+ * Get the package names of all whitelisted rule providers.
+ *
+ * <p>Warning: this method is only used for tests.
+ *
+ * @hide
+ */
+ @TestApi
+ @NonNull
+ public List<String> getWhitelistedRuleProviders() {
+ try {
+ return mManager.getWhitelistedRuleProviders();
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
}
diff --git a/core/java/android/content/integrity/IAppIntegrityManager.aidl b/core/java/android/content/integrity/IAppIntegrityManager.aidl
index 4714ad7c7989..94197bb9ec17 100644
--- a/core/java/android/content/integrity/IAppIntegrityManager.aidl
+++ b/core/java/android/content/integrity/IAppIntegrityManager.aidl
@@ -19,6 +19,7 @@ package android.content.integrity;
import android.content.integrity.Rule;
import android.content.IntentSender;
import android.content.pm.ParceledListSlice;
+import java.util.List;
/** @hide */
interface IAppIntegrityManager {
@@ -26,4 +27,5 @@ interface IAppIntegrityManager {
String getCurrentRuleSetVersion();
String getCurrentRuleSetProvider();
ParceledListSlice<Rule> getCurrentRules();
+ List<String> getWhitelistedRuleProviders();
}
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index 8a89840876d9..27c9cfcdd05b 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -103,4 +103,7 @@ interface ILauncherApps {
in UserHandle user);
void uncacheShortcuts(String callingPackage, String packageName, in List<String> shortcutIds,
in UserHandle user);
+
+ String getShortcutIconUri(String callingPackage, String packageName, String shortcutId,
+ int userId);
}
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 4e4897f59ad3..e73fd03e34b6 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -51,6 +51,7 @@ import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
+import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -67,8 +68,10 @@ import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Pair;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.function.pooled.PooledLambda;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -460,10 +463,7 @@ public class LauncherApps {
/**
* If non-null, return only the specified shortcuts by locus ID. When setting this field,
* a package name must also be set with {@link #setPackage}.
- *
- * @hide
*/
- @SystemApi
@NonNull
public ShortcutQuery setLocusIds(@Nullable List<LocusId> locusIds) {
mLocusIds = locusIds;
@@ -1201,6 +1201,35 @@ public class LauncherApps {
}
/**
+ * @hide internal/unit tests only
+ */
+ @VisibleForTesting
+ public ParcelFileDescriptor getUriShortcutIconFd(@NonNull ShortcutInfo shortcut) {
+ return getUriShortcutIconFd(shortcut.getPackage(), shortcut.getId(), shortcut.getUserId());
+ }
+
+ private ParcelFileDescriptor getUriShortcutIconFd(@NonNull String packageName,
+ @NonNull String shortcutId, int userId) {
+ String uri = null;
+ try {
+ uri = mService.getShortcutIconUri(mContext.getPackageName(), packageName, shortcutId,
+ userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ if (uri == null) {
+ return null;
+ }
+ try {
+ return mContext.getContentResolver().openFileDescriptor(Uri.parse(uri), "r");
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, "Icon file not found: " + uri);
+ return null;
+ }
+ }
+
+ /**
* Returns the icon for this shortcut, without any badging for the profile.
*
* <p>The calling launcher application must be allowed to access the shortcut information,
@@ -1220,26 +1249,10 @@ public class LauncherApps {
public Drawable getShortcutIconDrawable(@NonNull ShortcutInfo shortcut, int density) {
if (shortcut.hasIconFile()) {
final ParcelFileDescriptor pfd = getShortcutIconFd(shortcut);
- if (pfd == null) {
- return null;
- }
- try {
- final Bitmap bmp = BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor());
- if (bmp != null) {
- BitmapDrawable dr = new BitmapDrawable(mContext.getResources(), bmp);
- if (shortcut.hasAdaptiveBitmap()) {
- return new AdaptiveIconDrawable(null, dr);
- } else {
- return dr;
- }
- }
- return null;
- } finally {
- try {
- pfd.close();
- } catch (IOException ignore) {
- }
- }
+ return loadDrawableFromFileDescriptor(pfd, shortcut.hasAdaptiveBitmap());
+ } else if (shortcut.hasIconUri()) {
+ final ParcelFileDescriptor pfd = getUriShortcutIconFd(shortcut);
+ return loadDrawableFromFileDescriptor(pfd, shortcut.hasAdaptiveBitmap());
} else if (shortcut.hasIconResource()) {
return loadDrawableResourceFromPackage(shortcut.getPackage(),
shortcut.getIconResourceId(), shortcut.getUserHandle(), density);
@@ -1263,6 +1276,29 @@ public class LauncherApps {
}
}
+ private Drawable loadDrawableFromFileDescriptor(ParcelFileDescriptor pfd, boolean adaptive) {
+ if (pfd == null) {
+ return null;
+ }
+ try {
+ final Bitmap bmp = BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor());
+ if (bmp != null) {
+ BitmapDrawable dr = new BitmapDrawable(mContext.getResources(), bmp);
+ if (adaptive) {
+ return new AdaptiveIconDrawable(null, dr);
+ } else {
+ return dr;
+ }
+ }
+ return null;
+ } finally {
+ try {
+ pfd.close();
+ } catch (IOException ignore) {
+ }
+ }
+ }
+
private Drawable loadDrawableResourceFromPackage(String packageName, int resId,
UserHandle user, int density) {
try {
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index ec3590fd23cf..50bee854c027 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -16,6 +16,10 @@
package android.content.pm;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_DEFAULT;
+import static android.app.AppOpsManager.MODE_IGNORED;
+
import android.Manifest;
import android.annotation.CurrentTimeMillisLong;
import android.annotation.IntDef;
@@ -1456,6 +1460,8 @@ public class PackageInstaller {
/** {@hide} */
public List<String> whitelistedRestrictedPermissions;
/** {@hide} */
+ public int autoRevokePermissionsMode = MODE_DEFAULT;
+ /** {@hide} */
public String installerPackageName;
/** {@hide} */
public boolean isMultiPackage;
@@ -1498,6 +1504,7 @@ public class PackageInstaller {
volumeUuid = source.readString();
grantedRuntimePermissions = source.readStringArray();
whitelistedRestrictedPermissions = source.createStringArrayList();
+ autoRevokePermissionsMode = source.readInt();
installerPackageName = source.readString();
isMultiPackage = source.readBoolean();
isStaged = source.readBoolean();
@@ -1528,6 +1535,7 @@ public class PackageInstaller {
ret.volumeUuid = volumeUuid;
ret.grantedRuntimePermissions = grantedRuntimePermissions;
ret.whitelistedRestrictedPermissions = whitelistedRestrictedPermissions;
+ ret.autoRevokePermissionsMode = autoRevokePermissionsMode;
ret.installerPackageName = installerPackageName;
ret.isMultiPackage = isMultiPackage;
ret.isStaged = isStaged;
@@ -1691,6 +1699,22 @@ public class PackageInstaller {
}
/**
+ * Sets whether permissions should be auto-revoked if this package is unused for an
+ * extended periodd of time.
+ *
+ * It's disabled by default but generally the installer should enable it for most packages,
+ * excluding only those where doing so might cause breakage that cannot be easily addressed
+ * by simply re-requesting the permission(s).
+ *
+ * If user explicitly enabled or disabled it via settings, this call is ignored.
+ *
+ * @param shouldAutoRevoke whether permissions should be auto-revoked.
+ */
+ public void setAutoRevokePermissionsMode(boolean shouldAutoRevoke) {
+ autoRevokePermissionsMode = shouldAutoRevoke ? MODE_ALLOWED : MODE_IGNORED;
+ }
+
+ /**
* Request that rollbacks be enabled or disabled for the given upgrade with rollback data
* policy set to RESTORE.
*
@@ -1932,6 +1956,7 @@ public class PackageInstaller {
pw.printPair("volumeUuid", volumeUuid);
pw.printPair("grantedRuntimePermissions", grantedRuntimePermissions);
pw.printPair("whitelistedRestrictedPermissions", whitelistedRestrictedPermissions);
+ pw.printPair("autoRevokePermissions", autoRevokePermissionsMode);
pw.printPair("installerPackageName", installerPackageName);
pw.printPair("isMultiPackage", isMultiPackage);
pw.printPair("isStaged", isStaged);
@@ -1964,6 +1989,7 @@ public class PackageInstaller {
dest.writeString(volumeUuid);
dest.writeStringArray(grantedRuntimePermissions);
dest.writeStringList(whitelistedRestrictedPermissions);
+ dest.writeInt(autoRevokePermissionsMode);
dest.writeString(installerPackageName);
dest.writeBoolean(isMultiPackage);
dest.writeBoolean(isStaged);
@@ -2085,6 +2111,8 @@ public class PackageInstaller {
public String[] grantedRuntimePermissions;
/** {@hide}*/
public List<String> whitelistedRestrictedPermissions;
+ /** {@hide}*/
+ public int autoRevokePermissionsMode = MODE_DEFAULT;
/** {@hide} */
public int installFlags;
/** {@hide} */
@@ -2147,6 +2175,7 @@ public class PackageInstaller {
referrerUri = source.readParcelable(null);
grantedRuntimePermissions = source.readStringArray();
whitelistedRestrictedPermissions = source.createStringArrayList();
+ autoRevokePermissionsMode = source.readInt();
installFlags = source.readInt();
isMultiPackage = source.readBoolean();
@@ -2374,6 +2403,24 @@ public class PackageInstaller {
}
/**
+ * Get the status of whether permission auto-revocation should be allowed, ignored, or
+ * deferred to manifest data.
+ *
+ * @see android.app.AppOpsManager#MODE_ALLOWED
+ * @see android.app.AppOpsManager#MODE_IGNORED
+ * @see android.app.AppOpsManager#MODE_DEFAULT
+ *
+ * @return the status of auto-revoke for this package
+ *
+ * @hide
+ */
+ @TestApi
+ @SystemApi
+ public int getAutoRevokePermissionsMode() {
+ return autoRevokePermissionsMode;
+ }
+
+ /**
* Get the value set in {@link SessionParams#setAllowDowngrade(boolean)}.
*
* @deprecated use {@link #getRequestDowngrade()}.
@@ -2660,6 +2707,7 @@ public class PackageInstaller {
dest.writeParcelable(referrerUri, flags);
dest.writeStringArray(grantedRuntimePermissions);
dest.writeStringList(whitelistedRestrictedPermissions);
+ dest.writeInt(autoRevokePermissionsMode);
dest.writeInt(installFlags);
dest.writeBoolean(isMultiPackage);
dest.writeBoolean(isStaged);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 3764734d033c..05ff830aeed1 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -557,7 +557,6 @@ public abstract class PackageManager {
* Internal {@link PackageInfo} flag used to indicate that a package is a hidden system app.
* @hide
*/
- @SystemApi
public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS = 0x20000000;
/**
@@ -1978,7 +1977,7 @@ public abstract class PackageManager {
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device's main front and back cameras can stream
* concurrently as described in {@link
- * android.hardware.camera2.CameraManager#getConcurrentStreamingCameraIds()}
+ * android.hardware.camera2.CameraManager#getConcurrentCameraIds()}
*/
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_CAMERA_CONCURRENT = "android.hardware.camera.concurrent";
@@ -4576,6 +4575,53 @@ public abstract class PackageManager {
}
/**
+ * Marks an application exempt from having its permissions be automatically revoked when
+ * the app is unused for an extended period of time.
+ *
+ * Only the installer on record that installed the given package, or a holder of
+ * {@code WHITELIST_AUTO_REVOKE_PERMISSIONS} is allowed to call this.
+ *
+ * Packages start in whitelisted state, and it is the installer's responsibility to
+ * un-whitelist the packages it installs, unless auto-revoking permissions from that package
+ * would cause breakages beyond having to re-request the permission(s).
+ *
+ * @param packageName The app for which to set exemption.
+ * @param whitelisted Whether the app should be whitelisted.
+ *
+ * @return whether any change took effect.
+ *
+ * @see #isAutoRevokeWhitelisted
+ *
+ * @throws SecurityException if you you have no access to modify this.
+ */
+ @RequiresPermission(value = Manifest.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS,
+ conditional = true)
+ public boolean setAutoRevokeWhitelisted(@NonNull String packageName, boolean whitelisted) {
+ return false;
+ }
+
+ /**
+ * Checks whether an application is exempt from having its permissions be automatically revoked
+ * when the app is unused for an extended period of time.
+ *
+ * Only the installer on record that installed the given package, or a holder of
+ * {@code WHITELIST_AUTO_REVOKE_PERMISSIONS} is allowed to call this.
+ * @param packageName The app for which to set exemption.
+ *
+ * @return Whether the app is whitelisted.
+ *
+ * @see #setAutoRevokeWhitelisted
+ *
+ * @throws SecurityException if you you have no access to this.
+ */
+ @RequiresPermission(value = Manifest.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS,
+ conditional = true)
+ public boolean isAutoRevokeWhitelisted(@NonNull String packageName) {
+ return false;
+ }
+
+
+ /**
* Gets whether you should show UI with rationale for requesting a permission.
* You should do this only if you do not have the permission and the context in
* which the permission is requested does not clearly communicate to the user
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index af875787bba6..8d8776f1bc23 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -123,6 +123,9 @@ public final class ShortcutInfo implements Parcelable {
public static final int FLAG_CACHED = 1 << 14;
/** @hide */
+ public static final int FLAG_HAS_ICON_URI = 1 << 15;
+
+ /** @hide */
@IntDef(flag = true, prefix = { "FLAG_" }, value = {
FLAG_DYNAMIC,
FLAG_PINNED,
@@ -139,6 +142,7 @@ public final class ShortcutInfo implements Parcelable {
FLAG_SHADOW,
FLAG_LONG_LIVED,
FLAG_CACHED,
+ FLAG_HAS_ICON_URI,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ShortcutFlags {}
@@ -401,6 +405,9 @@ public final class ShortcutInfo implements Parcelable {
private String mIconResName;
// Internal use only.
+ private String mIconUri;
+
+ // Internal use only.
@Nullable
private String mBitmapPath;
@@ -554,6 +561,7 @@ public final class ShortcutInfo implements Parcelable {
if ((cloneFlags & CLONE_REMOVE_ICON) == 0) {
mIcon = source.mIcon;
mBitmapPath = source.mBitmapPath;
+ mIconUri = source.mIconUri;
}
mTitle = source.mTitle;
@@ -856,6 +864,7 @@ public final class ShortcutInfo implements Parcelable {
mIconResId = 0;
mIconResName = null;
mBitmapPath = null;
+ mIconUri = null;
}
if (source.mTitle != null) {
mTitle = source.mTitle;
@@ -916,6 +925,8 @@ public final class ShortcutInfo implements Parcelable {
case Icon.TYPE_RESOURCE:
case Icon.TYPE_BITMAP:
case Icon.TYPE_ADAPTIVE_BITMAP:
+ case Icon.TYPE_URI:
+ case Icon.TYPE_URI_ADAPTIVE_BITMAP:
break; // OK
default:
throw getInvalidIconException();
@@ -1792,6 +1803,15 @@ public final class ShortcutInfo implements Parcelable {
return hasFlags(FLAG_HAS_ICON_RES);
}
+ /**
+ * Return whether a shortcut's icon is provided via a URI.
+ *
+ * @hide internal/unit tests only
+ */
+ public boolean hasIconUri() {
+ return hasFlags(FLAG_HAS_ICON_URI);
+ }
+
/** @hide */
public boolean hasStringResources() {
return (mTitleResId != 0) || (mTextResId != 0) || (mDisabledMessageResId != 0);
@@ -1911,6 +1931,19 @@ public final class ShortcutInfo implements Parcelable {
return mIconResId;
}
+ /** @hide */
+ public void setIconUri(String iconUri) {
+ mIconUri = iconUri;
+ }
+
+ /**
+ * Get the Uri for the icon, valid only when {@link #hasIconUri()} } is true.
+ * @hide internal / tests only.
+ */
+ public String getIconUri() {
+ return mIconUri;
+ }
+
/**
* Bitmap path. Note this will be null even if {@link #hasIconFile()} is set when the save
* is pending. Use {@link #isIconPendingSave()} to check it.
@@ -2062,6 +2095,7 @@ public final class ShortcutInfo implements Parcelable {
mPersons = source.readParcelableArray(cl, Person.class);
mLocusId = source.readParcelable(cl);
+ mIconUri = source.readString();
}
@Override
@@ -2112,6 +2146,7 @@ public final class ShortcutInfo implements Parcelable {
dest.writeParcelableArray(mPersons, flags);
dest.writeParcelable(mLocusId, flags);
+ dest.writeString(mIconUri);
}
public static final @android.annotation.NonNull Creator<ShortcutInfo> CREATOR =
@@ -2203,6 +2238,12 @@ public final class ShortcutInfo implements Parcelable {
if (hasIconResource()) {
sb.append("Ic-r");
}
+ if (hasIconUri()) {
+ sb.append("Ic-u");
+ }
+ if (hasAdaptiveBitmap()) {
+ sb.append("Ic-a");
+ }
if (hasKeyFieldsOnly()) {
sb.append("Key");
}
@@ -2325,6 +2366,9 @@ public final class ShortcutInfo implements Parcelable {
sb.append(", bitmapPath=");
sb.append(mBitmapPath);
+
+ sb.append(", iconUri=");
+ sb.append(mIconUri);
}
if (mLocusId != null) {
@@ -2343,8 +2387,8 @@ public final class ShortcutInfo implements Parcelable {
CharSequence disabledMessage, int disabledMessageResId, String disabledMessageResName,
Set<String> categories, Intent[] intentsWithExtras, int rank, PersistableBundle extras,
long lastChangedTimestamp,
- int flags, int iconResId, String iconResName, String bitmapPath, int disabledReason,
- Person[] persons, LocusId locusId) {
+ int flags, int iconResId, String iconResName, String bitmapPath, String iconUri,
+ int disabledReason, Person[] persons, LocusId locusId) {
mUserId = userId;
mId = id;
mPackageName = packageName;
@@ -2369,6 +2413,7 @@ public final class ShortcutInfo implements Parcelable {
mIconResId = iconResId;
mIconResName = iconResName;
mBitmapPath = bitmapPath;
+ mIconUri = iconUri;
mDisabledReason = disabledReason;
mPersons = persons;
mLocusId = locusId;
diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java
index a50ce92a70bf..435c70ae999b 100644
--- a/core/java/android/content/pm/ShortcutServiceInternal.java
+++ b/core/java/android/content/pm/ShortcutServiceInternal.java
@@ -103,4 +103,10 @@ public abstract class ShortcutServiceInternal {
*/
public abstract List<ShortcutManager.ShareShortcutInfo> getShareTargets(
@NonNull String callingPackage, @NonNull IntentFilter intentFilter, int userId);
+
+ /**
+ * Returns the icon Uri of the shortcut, and grants Uri read permission to the caller.
+ */
+ public abstract String getShortcutIconUri(int launcherUserId, @NonNull String launcherPackage,
+ @NonNull String packageName, @NonNull String shortcutId, int userId);
}
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index 5fbf0da8b5a5..9906331be497 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -657,7 +657,9 @@ public class SensorEvent {
public int accuracy;
/**
- * The time in nanosecond at which the event happened
+ * The time in nanoseconds at which the event happened. For a given sensor,
+ * each new sensor event should be monotonically increasing using the same
+ * time base as {@link android.os.SystemClock#elapsedRealtimeNanos()}.
*/
public long timestamp;
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index b3a1ee2f9b69..7e72b73db358 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -2884,12 +2884,12 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* generated according to the documented
* {@link android.hardware.camera2.CameraDevice#createCaptureSession guideline} for each device
* which has its Id present in the set returned by
- * {@link android.hardware.camera2.CameraManager#getConcurrentStreamingCameraIds}.
+ * {@link android.hardware.camera2.CameraManager#getConcurrentCameraIds}.
* Clients can use the array as a quick reference to find an appropriate camera stream
* combination.
* The mandatory stream combination array will be {@code null} in case the device is not a part
* of at least one set of combinations returned by
- * {@link android.hardware.camera2.CameraManager#getConcurrentStreamingCameraIds}.</p>
+ * {@link android.hardware.camera2.CameraManager#getConcurrentCameraIds}.</p>
* <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
*/
@PublicKey
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index cc0c1a309038..30ee32604939 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -681,7 +681,7 @@ public abstract class CameraDevice implements AutoCloseable {
* </p>
*
*<p>Devices capable of streaming concurrently with other devices as described by
- * {@link android.hardware.camera2.CameraManager#getConcurrentStreamingCameraIds} have the
+ * {@link android.hardware.camera2.CameraManager#getConcurrentCameraIds} have the
* following guaranteed streams (when streaming concurrently with other devices)</p>
*
* <table>
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 972b0f55acef..e81c649796a0 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -160,8 +160,8 @@ public final class CameraManager {
* @throws CameraAccessException if the camera device has been disconnected.
*/
@NonNull
- public Set<Set<String>> getConcurrentStreamingCameraIds() throws CameraAccessException {
- return CameraManagerGlobal.get().getConcurrentStreamingCameraIds();
+ public Set<Set<String>> getConcurrentCameraIds() throws CameraAccessException {
+ return CameraManagerGlobal.get().getConcurrentCameraIds();
}
/**
@@ -189,11 +189,11 @@ public final class CameraManager {
*
* @return {@code true} if the given combination of session configurations and corresponding
* camera ids are concurrently supported by the camera sub-system,
- * {@code false} otherwise.
+ * {@code false} otherwise OR if the set of camera devices provided is not a subset of
+ * those returned by {@link #getConcurrentCameraIds}.
*
- * @throws IllegalArgumentException if the set of camera devices provided is not a subset of
- * those returned by getConcurrentStreamingCameraIds()
* @throws CameraAccessException if one of the camera devices queried is no longer connected.
+ *
*/
@RequiresPermission(android.Manifest.permission.CAMERA)
public boolean isConcurrentSessionConfigurationSupported(
@@ -1183,7 +1183,7 @@ public final class CameraManager {
try {
ConcurrentCameraIdCombination[] cameraIdCombinations =
- cameraService.getConcurrentStreamingCameraIds();
+ cameraService.getConcurrentCameraIds();
for (ConcurrentCameraIdCombination comb : cameraIdCombinations) {
mConcurrentCameraIdCombinations.add(comb.getConcurrentCameraIdCombination());
}
@@ -1372,7 +1372,7 @@ public final class CameraManager {
return cameraIds;
}
- public @NonNull Set<Set<String>> getConcurrentStreamingCameraIds() {
+ public @NonNull Set<Set<String>> getConcurrentCameraIds() {
Set<Set<String>> concurrentStreamingCameraIds = null;
synchronized (mLock) {
// Try to make sure we have an up-to-date list of concurrent camera devices.
@@ -1398,7 +1398,7 @@ public final class CameraManager {
synchronized (mLock) {
// Go through all the elements and check if the camera ids are valid at least /
- // belong to one of the combinations returned by getConcurrentStreamingCameraIds()
+ // belong to one of the combinations returned by getConcurrentCameraIds()
boolean subsetFound = false;
for (Set<String> combination : mConcurrentCameraIdCombinations) {
if (combination.containsAll(cameraIdsAndSessionConfigurations.keySet())) {
@@ -1406,9 +1406,9 @@ public final class CameraManager {
}
}
if (!subsetFound) {
- throw new IllegalArgumentException(
- "The set of camera ids provided is not a subset of"
- + "getConcurrentStreamingCameraIds");
+ Log.v(TAG, "isConcurrentSessionConfigurationSupported called with a subset of"
+ + "camera ids not returned by getConcurrentCameraIds");
+ return false;
}
CameraIdAndSessionConfiguration [] cameraIdsAndConfigs =
new CameraIdAndSessionConfiguration[size];
@@ -1436,10 +1436,10 @@ public final class CameraManager {
/**
* Helper function to find out if a camera id is in the set of combinations returned by
- * getConcurrentStreamingCameraIds()
+ * getConcurrentCameraIds()
* @param cameraId the unique identifier of the camera device to query
* @return Whether the camera device was found in the set of combinations returned by
- * getConcurrentStreamingCameraIds
+ * getConcurrentCameraIds
*/
public boolean cameraIdHasConcurrentStreamsLocked(String cameraId) {
if (!mDeviceStatus.containsKey(cameraId)) {
diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
index f0fab6a99d14..20d9c30bb4cc 100644
--- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
+++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
@@ -721,7 +721,7 @@ public final class MandatoryStreamCombination {
/**
* Retrieve a list of all available mandatory concurrent stream combinations.
* This method should only be called for devices which are listed in combinations returned
- * by CameraManager.getConcurrentStreamingCameraIds.
+ * by CameraManager.getConcurrentCameraIds.
*
* @return a non-modifiable list of supported mandatory concurrent stream combinations.
*/
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 1b6c1ee4f7e2..7e4d68d9925e 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -20,6 +20,7 @@ import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
import static android.view.WindowInsets.Type.navigationBars;
+import static android.view.WindowInsets.Type.statusBars;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -71,6 +72,7 @@ import android.view.ViewRootImpl;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.WindowInsets;
+import android.view.WindowInsets.Side;
import android.view.WindowManager;
import android.view.animation.AnimationUtils;
import android.view.autofill.AutofillId;
@@ -1246,7 +1248,8 @@ public class InputMethodService extends AbstractInputMethodService {
Context.LAYOUT_INFLATER_SERVICE);
mWindow = new SoftInputWindow(this, "InputMethod", mTheme, null, null, mDispatcherState,
WindowManager.LayoutParams.TYPE_INPUT_METHOD, Gravity.BOTTOM, false);
- mWindow.getWindow().getAttributes().setFitInsetsTypes(WindowInsets.Type.statusBars());
+ mWindow.getWindow().getAttributes().setFitInsetsTypes(statusBars() | navigationBars());
+ mWindow.getWindow().getAttributes().setFitInsetsSides(Side.all() & ~Side.BOTTOM);
// IME layout should always be inset by navigation bar, no matter its current visibility,
// unless automotive requests it, since automotive may hide the navigation bar.
diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/core/java/android/net/ConnectivityDiagnosticsManager.java
index d009144034f0..6f0a4f974442 100644
--- a/core/java/android/net/ConnectivityDiagnosticsManager.java
+++ b/core/java/android/net/ConnectivityDiagnosticsManager.java
@@ -136,7 +136,7 @@ public class ConnectivityDiagnosticsManager {
* {@link #NETWORK_VALIDATION_RESULT_PARTIALLY_VALID},
* {@link #NETWORK_VALIDATION_RESULT_SKIPPED}.
*
- * @see android.net.NetworkCapabilities#CAPABILITY_VALIDATED
+ * @see android.net.NetworkCapabilities#NET_CAPABILITY_VALIDATED
*/
@NetworkValidationResult
public static final String KEY_NETWORK_VALIDATION_RESULT = "networkValidationResult";
@@ -233,8 +233,8 @@ public class ConnectivityDiagnosticsManager {
* Constructor for ConnectivityReport.
*
* <p>Apps should obtain instances through {@link
- * ConnectivityDiagnosticsCallback#onConnectivityReport} instead of instantiating their own
- * instances (unless for testing purposes).
+ * ConnectivityDiagnosticsCallback#onConnectivityReportAvailable} instead of instantiating
+ * their own instances (unless for testing purposes).
*
* @param network The Network for which this ConnectivityReport applies
* @param reportTimestamp The timestamp for the report
@@ -368,7 +368,14 @@ public class ConnectivityDiagnosticsManager {
/** Class that includes information for a suspected data stall on a specific Network */
public static final class DataStallReport implements Parcelable {
+ /**
+ * Indicates that the Data Stall was detected using DNS events.
+ */
public static final int DETECTION_METHOD_DNS_EVENTS = 1;
+
+ /**
+ * Indicates that the Data Stall was detected using TCP metrics.
+ */
public static final int DETECTION_METHOD_TCP_METRICS = 2;
/** @hide */
@@ -615,10 +622,10 @@ public class ConnectivityDiagnosticsManager {
/** @hide */
@VisibleForTesting
- public void onConnectivityReport(@NonNull ConnectivityReport report) {
+ public void onConnectivityReportAvailable(@NonNull ConnectivityReport report) {
Binder.withCleanCallingIdentity(() -> {
mExecutor.execute(() -> {
- mCb.onConnectivityReport(report);
+ mCb.onConnectivityReportAvailable(report);
});
});
}
@@ -659,7 +666,7 @@ public class ConnectivityDiagnosticsManager {
*
* @param report The ConnectivityReport containing information about a connectivity check
*/
- public void onConnectivityReport(@NonNull ConnectivityReport report) {}
+ public void onConnectivityReportAvailable(@NonNull ConnectivityReport report) {}
/**
* Called when the platform suspects a data stall on some Network.
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index fc6954fb4808..81735ac8f693 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -3222,7 +3222,9 @@ public class ConnectivityManager {
/** {@hide} - returns the factory serial number */
@UnsupportedAppUsage
- @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_FACTORY})
public int registerNetworkFactory(Messenger messenger, String name) {
try {
return mService.registerNetworkFactory(messenger, name);
@@ -3233,7 +3235,9 @@ public class ConnectivityManager {
/** {@hide} */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_FACTORY})
public void unregisterNetworkFactory(Messenger messenger) {
try {
mService.unregisterNetworkFactory(messenger);
@@ -3253,7 +3257,9 @@ public class ConnectivityManager {
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_FACTORY})
public int registerNetworkProvider(@NonNull NetworkProvider provider) {
if (provider.getProviderId() != NetworkProvider.ID_NONE) {
throw new IllegalStateException("NetworkProviders can only be registered once");
@@ -3276,7 +3282,9 @@ public class ConnectivityManager {
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_FACTORY})
public void unregisterNetworkProvider(@NonNull NetworkProvider provider) {
try {
mService.unregisterNetworkProvider(provider.getMessenger());
@@ -3288,7 +3296,9 @@ public class ConnectivityManager {
/** @hide exposed via the NetworkProvider class. */
- @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_FACTORY})
public void declareNetworkRequestUnfulfillable(@NonNull NetworkRequest request) {
try {
mService.declareNetworkRequestUnfulfillable(request);
@@ -3306,7 +3316,9 @@ public class ConnectivityManager {
* Register a NetworkAgent with ConnectivityService.
* @return Network corresponding to NetworkAgent.
*/
- @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_FACTORY})
public Network registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
NetworkCapabilities nc, int score, NetworkAgentConfig config) {
return registerNetworkAgent(messenger, ni, lp, nc, score, config, NetworkProvider.ID_NONE);
@@ -3317,7 +3329,9 @@ public class ConnectivityManager {
* Register a NetworkAgent with ConnectivityService.
* @return Network corresponding to NetworkAgent.
*/
- @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_FACTORY})
public Network registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
NetworkCapabilities nc, int score, NetworkAgentConfig config, int providerId) {
try {
diff --git a/core/java/android/net/IConnectivityDiagnosticsCallback.aidl b/core/java/android/net/IConnectivityDiagnosticsCallback.aidl
index 3a161bfabfd2..82b64a928000 100644
--- a/core/java/android/net/IConnectivityDiagnosticsCallback.aidl
+++ b/core/java/android/net/IConnectivityDiagnosticsCallback.aidl
@@ -22,7 +22,7 @@ import android.net.Network;
/** @hide */
oneway interface IConnectivityDiagnosticsCallback {
- void onConnectivityReport(in ConnectivityDiagnosticsManager.ConnectivityReport report);
+ void onConnectivityReportAvailable(in ConnectivityDiagnosticsManager.ConnectivityReport report);
void onDataStallSuspected(in ConnectivityDiagnosticsManager.DataStallReport report);
void onNetworkConnectivityReported(in Network n, boolean hasConnectivity);
} \ No newline at end of file
diff --git a/core/java/android/net/ITestNetworkManager.aidl b/core/java/android/net/ITestNetworkManager.aidl
index d586038ebb89..2a863adde581 100644
--- a/core/java/android/net/ITestNetworkManager.aidl
+++ b/core/java/android/net/ITestNetworkManager.aidl
@@ -33,7 +33,7 @@ interface ITestNetworkManager
TestNetworkInterface createTapInterface();
void setupTestNetwork(in String iface, in LinkProperties lp, in boolean isMetered,
- in IBinder binder);
+ in int[] administratorUids, in IBinder binder);
void teardownTestNetwork(int netId);
}
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index c5681cb6d7e4..6f5471baa7ab 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -61,6 +61,7 @@ import javax.net.SocketFactory;
public class Network implements Parcelable {
/**
+ * The unique id of the network.
* @hide
*/
@SystemApi
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index fef353f604dc..5c754a1b9733 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -78,6 +78,7 @@ public abstract class NetworkAgent {
/**
* The ID of the {@link NetworkProvider} that created this object, or
* {@link NetworkProvider#ID_NONE} if unknown.
+ * @hide
*/
public final int providerId;
@@ -584,6 +585,7 @@ public abstract class NetworkAgent {
*
* @deprecated this is for backward compatibility only.
* @param legacySubtype the legacy subtype.
+ * @hide
*/
@Deprecated
public void setLegacySubtype(final int legacySubtype, @NonNull final String legacySubtypeName) {
@@ -608,6 +610,7 @@ public abstract class NetworkAgent {
*
* @deprecated this is for backward compatibility only.
* @param extraInfo the ExtraInfo.
+ * @hide
*/
@Deprecated
public void setLegacyExtraInfo(@Nullable final String extraInfo) {
@@ -711,6 +714,7 @@ public abstract class NetworkAgent {
/**
* Called when ConnectivityService request a bandwidth update. The parent factory
* shall try to overwrite this method and produce a bandwidth update if capable.
+ * @hide
*/
public void onBandwidthUpdateRequested() {
pollLceData();
diff --git a/core/java/android/net/NetworkAgentConfig.java b/core/java/android/net/NetworkAgentConfig.java
index 7e2db4a4fa95..ca9328a713f0 100644
--- a/core/java/android/net/NetworkAgentConfig.java
+++ b/core/java/android/net/NetworkAgentConfig.java
@@ -108,6 +108,7 @@ public final class NetworkAgentConfig implements Parcelable {
/**
*
* @return whether the sign in to network notification is enabled by this configuration.
+ * @hide
*/
public boolean isProvisioningNotificationEnabled() {
return !provisioningNotificationDisabled;
@@ -122,6 +123,7 @@ public final class NetworkAgentConfig implements Parcelable {
/**
* @return the subscriber ID, or null if none.
+ * @hide
*/
@Nullable
public String getSubscriberId() {
@@ -138,6 +140,7 @@ public final class NetworkAgentConfig implements Parcelable {
/**
* @return whether NAT64 prefix detection is enabled.
+ * @hide
*/
public boolean isNat64DetectionEnabled() {
return !skip464xlat;
@@ -247,6 +250,7 @@ public final class NetworkAgentConfig implements Parcelable {
* Sets the subscriber ID for this network.
*
* @return this builder, to facilitate chaining.
+ * @hide
*/
@NonNull
public Builder setSubscriberId(@Nullable String subscriberId) {
@@ -259,6 +263,7 @@ public final class NetworkAgentConfig implements Parcelable {
* and reduce idle traffic on networks that are known to be IPv6-only without a NAT64.
*
* @return this builder, to facilitate chaining.
+ * @hide
*/
@NonNull
public Builder disableNat64Detection() {
@@ -271,6 +276,7 @@ public final class NetworkAgentConfig implements Parcelable {
* perform its own carrier-specific provisioning procedure.
*
* @return this builder, to facilitate chaining.
+ * @hide
*/
@NonNull
public Builder disableProvisioningNotification() {
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 83f99802b85f..116e343ff250 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -613,7 +613,6 @@ public final class NetworkCapabilities implements Parcelable {
* @return {@code true} if the network should be restricted.
* @hide
*/
- @SystemApi
public boolean deduceRestrictedCapability() {
// Check if we have any capability that forces the network to be restricted.
final boolean forceRestrictedCapability =
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 14442a2088cd..1922b6df2e7f 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -21,7 +21,6 @@ import static android.content.pm.PackageManager.GET_SIGNATURES;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
-import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.app.ActivityManager;
import android.compat.annotation.UnsupportedAppUsage;
@@ -56,7 +55,6 @@ import java.util.concurrent.ConcurrentHashMap;
* @hide
*/
@SystemService(Context.NETWORK_POLICY_SERVICE)
-@SystemApi
public class NetworkPolicyManager {
/* POLICY_* are masks and can be ORed, although currently they are not.*/
@@ -162,11 +160,13 @@ public class NetworkPolicyManager {
/**
* Mask used to check if an override value is marked as unmetered.
+ * @hide
*/
public static final int SUBSCRIPTION_OVERRIDE_UNMETERED = 1 << 0;
/**
* Mask used to check if an override value is marked as congested.
+ * @hide
*/
public static final int SUBSCRIPTION_OVERRIDE_CONGESTED = 1 << 1;
@@ -294,7 +294,6 @@ public class NetworkPolicyManager {
/** @hide */
@RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY)
- @SystemApi
public void registerSubscriptionCallback(@NonNull SubscriptionCallback callback) {
if (callback == null) {
throw new NullPointerException("Callback cannot be null.");
@@ -309,7 +308,6 @@ public class NetworkPolicyManager {
/** @hide */
@RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY)
- @SystemApi
public void unregisterSubscriptionCallback(@NonNull SubscriptionCallback callback) {
if (callback == null) {
throw new NullPointerException("Callback cannot be null.");
@@ -373,6 +371,7 @@ public class NetworkPolicyManager {
* requested state until explicitly cleared, or the next reboot,
* whichever happens first
* @param callingPackage the name of the package making the call.
+ * @hide
*/
public void setSubscriptionOverride(int subId, @SubscriptionOverrideMask int overrideMask,
@SubscriptionOverrideMask int overrideValue, long timeoutMillis,
@@ -391,6 +390,7 @@ public class NetworkPolicyManager {
* @param subId the subscriber this relationship applies to.
* @param plans the list of plans.
* @param callingPackage the name of the package making the call
+ * @hide
*/
public void setSubscriptionPlans(int subId, @NonNull SubscriptionPlan[] plans,
@NonNull String callingPackage) {
@@ -406,6 +406,7 @@ public class NetworkPolicyManager {
*
* @param subId the subscriber to get the subscription plans for.
* @param callingPackage the name of the package making the call.
+ * @hide
*/
@NonNull
public SubscriptionPlan[] getSubscriptionPlans(int subId, @NonNull String callingPackage) {
@@ -549,7 +550,6 @@ public class NetworkPolicyManager {
}
/** @hide */
- @SystemApi
public static class SubscriptionCallback {
/**
* Notify clients of a new override about a given subscription.
diff --git a/core/java/android/net/NetworkProvider.java b/core/java/android/net/NetworkProvider.java
index 2c0e4aa700b1..418d6915d4b3 100644
--- a/core/java/android/net/NetworkProvider.java
+++ b/core/java/android/net/NetworkProvider.java
@@ -106,10 +106,12 @@ public class NetworkProvider {
}
// TODO: consider adding a register() method so ConnectivityManager does not need to call this.
+ /** @hide */
public @Nullable Messenger getMessenger() {
return mMessenger;
}
+ /** @hide */
public @NonNull String getName() {
return mName;
}
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 2f536ffd8ca6..9c1fb41ecc96 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -21,7 +21,6 @@ import static android.os.Process.CLAT_UID;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
@@ -58,9 +57,12 @@ import java.util.function.Predicate;
public final class NetworkStats implements Parcelable {
private static final String TAG = "NetworkStats";
- /** {@link #iface} value when interface details unavailable. */
- @SuppressLint("CompileTimeConstant")
+ /**
+ * {@link #iface} value when interface details unavailable.
+ * @hide
+ */
@Nullable public static final String IFACE_ALL = null;
+
/**
* Virtual network interface for video telephony. This is for VT data usage counting
* purpose.
@@ -248,7 +250,13 @@ public final class NetworkStats implements Parcelable {
@UnsupportedAppUsage
private long[] operations;
- /** @hide */
+ /**
+ * Basic element of network statistics. Contains the number of packets and number of bytes
+ * transferred on both directions in a given set of conditions. See
+ * {@link Entry#Entry(String, int, int, int, int, int, int, long, long, long, long, long)}.
+ *
+ * @hide
+ */
@SystemApi
public static class Entry {
/** @hide */
@@ -319,6 +327,35 @@ public final class NetworkStats implements Parcelable {
rxBytes, rxPackets, txBytes, txPackets, operations);
}
+ /**
+ * Construct a {@link Entry} object by giving statistics of packet and byte transferred on
+ * both direction, and associated with a set of given conditions.
+ *
+ * @param iface interface name of this {@link Entry}. Or null if not specified.
+ * @param uid uid of this {@link Entry}. {@link #UID_TETHERING} if this {@link Entry} is
+ * for tethering. Or {@link #UID_ALL} if this {@link NetworkStats} is only
+ * counting iface stats.
+ * @param set usage state of this {@link Entry}. Should be one of the following
+ * values: {@link #SET_DEFAULT}, {@link #SET_FOREGROUND}.
+ * @param tag tag of this {@link Entry}.
+ * @param metered metered state of this {@link Entry}. Should be one of the following
+ * values: {link #METERED_YES}, {link #METERED_NO}.
+ * @param roaming roaming state of this {@link Entry}. Should be one of the following
+ * values: {link #ROAMING_YES}, {link #ROAMING_NO}.
+ * @param defaultNetwork default network status of this {@link Entry}. Should be one
+ * of the following values: {link #DEFAULT_NETWORK_YES},
+ * {link #DEFAULT_NETWORK_NO}.
+ * @param rxBytes Number of bytes received for this {@link Entry}. Statistics should
+ * represent the contents of IP packets, including IP headers.
+ * @param rxPackets Number of packets received for this {@link Entry}. Statistics should
+ * represent the contents of IP packets, including IP headers.
+ * @param txBytes Number of bytes transmitted for this {@link Entry}. Statistics should
+ * represent the contents of IP packets, including IP headers.
+ * @param txPackets Number of bytes transmitted for this {@link Entry}. Statistics should
+ * represent the contents of IP packets, including IP headers.
+ * @param operations count of network operations performed for this {@link Entry}. This can
+ * be used to derive bytes-per-operation.
+ */
public Entry(@Nullable String iface, int uid, @State int set, int tag,
@Meteredness int metered, @Roaming int roaming, @DefaultNetwork int defaultNetwork,
long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
@@ -466,7 +503,7 @@ public final class NetworkStats implements Parcelable {
NetworkStats.Entry entry = null;
for (int i = 0; i < size; i++) {
entry = getValues(i, entry);
- clone.addEntry(entry);
+ clone.insertEntry(entry);
}
return clone;
}
@@ -493,26 +530,26 @@ public final class NetworkStats implements Parcelable {
/** @hide */
@VisibleForTesting
- public NetworkStats addIfaceValues(
+ public NetworkStats insertEntry(
String iface, long rxBytes, long rxPackets, long txBytes, long txPackets) {
- return addEntry(
+ return insertEntry(
iface, UID_ALL, SET_DEFAULT, TAG_NONE, rxBytes, rxPackets, txBytes, txPackets, 0L);
}
/** @hide */
@VisibleForTesting
- public NetworkStats addEntry(String iface, int uid, int set, int tag, long rxBytes,
+ public NetworkStats insertEntry(String iface, int uid, int set, int tag, long rxBytes,
long rxPackets, long txBytes, long txPackets, long operations) {
- return addEntry(new Entry(
+ return insertEntry(new Entry(
iface, uid, set, tag, rxBytes, rxPackets, txBytes, txPackets, operations));
}
/** @hide */
@VisibleForTesting
- public NetworkStats addEntry(String iface, int uid, int set, int tag, int metered, int roaming,
- int defaultNetwork, long rxBytes, long rxPackets, long txBytes, long txPackets,
- long operations) {
- return addEntry(new Entry(
+ public NetworkStats insertEntry(String iface, int uid, int set, int tag, int metered,
+ int roaming, int defaultNetwork, long rxBytes, long rxPackets, long txBytes,
+ long txPackets, long operations) {
+ return insertEntry(new Entry(
iface, uid, set, tag, metered, roaming, defaultNetwork, rxBytes, rxPackets,
txBytes, txPackets, operations));
}
@@ -522,7 +559,7 @@ public final class NetworkStats implements Parcelable {
* object can be recycled across multiple calls.
* @hide
*/
- public NetworkStats addEntry(Entry entry) {
+ public NetworkStats insertEntry(Entry entry) {
if (size >= capacity) {
final int newLength = Math.max(size, 10) * 3 / 2;
iface = Arrays.copyOf(iface, newLength);
@@ -665,7 +702,7 @@ public final class NetworkStats implements Parcelable {
entry.roaming, entry.defaultNetwork);
if (i == -1) {
// only create new entry when positive contribution
- addEntry(entry);
+ insertEntry(entry);
} else {
rxBytes[i] += entry.rxBytes;
rxPackets[i] += entry.rxPackets;
@@ -684,7 +721,7 @@ public final class NetworkStats implements Parcelable {
* @param entry the {@link Entry} to add.
* @return a new constructed {@link NetworkStats} object that contains the result.
*/
- public @NonNull NetworkStats addValues(@NonNull Entry entry) {
+ public @NonNull NetworkStats addEntry(@NonNull Entry entry) {
return this.clone().combineValues(entry);
}
@@ -1003,7 +1040,7 @@ public final class NetworkStats implements Parcelable {
entry.operations = Math.max(entry.operations, 0);
}
- result.addEntry(entry);
+ result.insertEntry(entry);
}
return result;
diff --git a/core/java/android/net/TestNetworkManager.java b/core/java/android/net/TestNetworkManager.java
index 4ac4a69e4b1b..c3284df39713 100644
--- a/core/java/android/net/TestNetworkManager.java
+++ b/core/java/android/net/TestNetworkManager.java
@@ -16,6 +16,7 @@
package android.net;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.TestApi;
import android.os.IBinder;
import android.os.RemoteException;
@@ -53,6 +54,19 @@ public class TestNetworkManager {
}
}
+ private void setupTestNetwork(
+ @NonNull String iface,
+ @Nullable LinkProperties lp,
+ boolean isMetered,
+ @NonNull int[] administratorUids,
+ @NonNull IBinder binder) {
+ try {
+ mService.setupTestNetwork(iface, lp, isMetered, administratorUids, binder);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/**
* Sets up a capability-limited, testing-only network for a given interface
*
@@ -66,11 +80,7 @@ public class TestNetworkManager {
public void setupTestNetwork(
@NonNull LinkProperties lp, boolean isMetered, @NonNull IBinder binder) {
Preconditions.checkNotNull(lp, "Invalid LinkProperties");
- try {
- mService.setupTestNetwork(lp.getInterfaceName(), lp, isMetered, binder);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ setupTestNetwork(lp.getInterfaceName(), lp, isMetered, new int[0], binder);
}
/**
@@ -82,11 +92,21 @@ public class TestNetworkManager {
*/
@TestApi
public void setupTestNetwork(@NonNull String iface, @NonNull IBinder binder) {
- try {
- mService.setupTestNetwork(iface, null, true, binder);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ setupTestNetwork(iface, null, true, new int[0], binder);
+ }
+
+ /**
+ * Sets up a capability-limited, testing-only network for a given interface with the given
+ * administrator UIDs.
+ *
+ * @param iface the name of the interface to be used for the Network LinkProperties.
+ * @param administratorUids The administrator UIDs to be used for the test-only network
+ * @param binder A binder object guarding the lifecycle of this test network.
+ * @hide
+ */
+ public void setupTestNetwork(
+ @NonNull String iface, @NonNull int[] administratorUids, @NonNull IBinder binder) {
+ setupTestNetwork(iface, null, true, administratorUids, binder);
}
/**
diff --git a/core/java/android/net/netstats/provider/AbstractNetworkStatsProvider.java b/core/java/android/net/netstats/provider/AbstractNetworkStatsProvider.java
deleted file mode 100644
index 740aa92ad484..000000000000
--- a/core/java/android/net/netstats/provider/AbstractNetworkStatsProvider.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.netstats.provider;
-
-import android.annotation.NonNull;
-import android.annotation.SystemApi;
-import android.net.NetworkStats;
-
-/**
- * A base class that allows external modules to implement a custom network statistics provider.
- * @hide
- */
-@SystemApi
-public abstract class AbstractNetworkStatsProvider {
- /**
- * A value used by {@link #setLimit} and {@link #setAlert} indicates there is no limit.
- */
- public static final int QUOTA_UNLIMITED = -1;
-
- /**
- * Called by {@code NetworkStatsService} when global polling is needed. Custom
- * implementation of providers MUST respond to it by calling
- * {@link NetworkStatsProviderCallback#onStatsUpdated} within one minute. Responding
- * later than this may cause the stats to be dropped.
- *
- * @param token a positive number identifying the new state of the system under which
- * {@link NetworkStats} have to be gathered from now on. When this is called,
- * custom implementations of providers MUST report the latest stats with the
- * previous token, under which stats were being gathered so far.
- */
- public abstract void requestStatsUpdate(int token);
-
- /**
- * Called by {@code NetworkStatsService} when setting the interface quota for the specified
- * upstream interface. When this is called, the custom implementation should block all egress
- * packets on the {@code iface} associated with the provider when {@code quotaBytes} bytes have
- * been reached, and MUST respond to it by calling
- * {@link NetworkStatsProviderCallback#onLimitReached()}.
- *
- * @param iface the interface requiring the operation.
- * @param quotaBytes the quota defined as the number of bytes, starting from zero and counting
- * from now. A value of {@link #QUOTA_UNLIMITED} indicates there is no limit.
- */
- public abstract void setLimit(@NonNull String iface, long quotaBytes);
-
- /**
- * Called by {@code NetworkStatsService} when setting the alert bytes. Custom implementations
- * MUST call {@link NetworkStatsProviderCallback#onAlertReached()} when {@code quotaBytes} bytes
- * have been reached. Unlike {@link #setLimit(String, long)}, the custom implementation should
- * not block all egress packets.
- *
- * @param quotaBytes the quota defined as the number of bytes, starting from zero and counting
- * from now. A value of {@link #QUOTA_UNLIMITED} indicates there is no alert.
- */
- public abstract void setAlert(long quotaBytes);
-}
diff --git a/core/java/android/net/netstats/provider/INetworkStatsProvider.aidl b/core/java/android/net/netstats/provider/INetworkStatsProvider.aidl
index 55b3d4edb157..4078b249218c 100644
--- a/core/java/android/net/netstats/provider/INetworkStatsProvider.aidl
+++ b/core/java/android/net/netstats/provider/INetworkStatsProvider.aidl
@@ -22,7 +22,7 @@ package android.net.netstats.provider;
* @hide
*/
oneway interface INetworkStatsProvider {
- void requestStatsUpdate(int token);
- void setLimit(String iface, long quotaBytes);
- void setAlert(long quotaBytes);
+ void onRequestStatsUpdate(int token);
+ void onSetLimit(String iface, long quotaBytes);
+ void onSetAlert(long quotaBytes);
}
diff --git a/core/java/android/net/netstats/provider/INetworkStatsProviderCallback.aidl b/core/java/android/net/netstats/provider/INetworkStatsProviderCallback.aidl
index 3ea9318f10d4..bd336dd348fe 100644
--- a/core/java/android/net/netstats/provider/INetworkStatsProviderCallback.aidl
+++ b/core/java/android/net/netstats/provider/INetworkStatsProviderCallback.aidl
@@ -24,8 +24,8 @@ import android.net.NetworkStats;
* @hide
*/
oneway interface INetworkStatsProviderCallback {
- void onStatsUpdated(int token, in NetworkStats ifaceStats, in NetworkStats uidStats);
- void onAlertReached();
- void onLimitReached();
+ void notifyStatsUpdated(int token, in NetworkStats ifaceStats, in NetworkStats uidStats);
+ void notifyAlertReached();
+ void notifyLimitReached();
void unregister();
}
diff --git a/core/java/android/net/netstats/provider/NetworkStatsProvider.java b/core/java/android/net/netstats/provider/NetworkStatsProvider.java
new file mode 100644
index 000000000000..7639d2244cfe
--- /dev/null
+++ b/core/java/android/net/netstats/provider/NetworkStatsProvider.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.netstats.provider;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.net.NetworkStats;
+import android.os.RemoteException;
+
+/**
+ * A base class that allows external modules to implement a custom network statistics provider.
+ * @hide
+ */
+@SystemApi
+public abstract class NetworkStatsProvider {
+ /**
+ * A value used by {@link #onSetLimit} and {@link #onSetAlert} indicates there is no limit.
+ */
+ public static final int QUOTA_UNLIMITED = -1;
+
+ @NonNull private final INetworkStatsProvider mProviderBinder =
+ new INetworkStatsProvider.Stub() {
+
+ @Override
+ public void onRequestStatsUpdate(int token) {
+ NetworkStatsProvider.this.onRequestStatsUpdate(token);
+ }
+
+ @Override
+ public void onSetLimit(String iface, long quotaBytes) {
+ NetworkStatsProvider.this.onSetLimit(iface, quotaBytes);
+ }
+
+ @Override
+ public void onSetAlert(long quotaBytes) {
+ NetworkStatsProvider.this.onSetAlert(quotaBytes);
+ }
+ };
+
+ // The binder given by the service when successfully registering. Only null before registering,
+ // never null once non-null.
+ @Nullable
+ private INetworkStatsProviderCallback mProviderCbBinder;
+
+ /**
+ * Return the binder invoked by the service and redirect function calls to the overridden
+ * methods.
+ * @hide
+ */
+ @NonNull
+ public INetworkStatsProvider getProviderBinder() {
+ return mProviderBinder;
+ }
+
+ /**
+ * Store the binder that was returned by the service when successfully registering. Note that
+ * the provider cannot be re-registered. Hence this method can only be called once per provider.
+ *
+ * @hide
+ */
+ public void setProviderCallbackBinder(@NonNull INetworkStatsProviderCallback binder) {
+ if (mProviderCbBinder != null) {
+ throw new IllegalArgumentException("provider is already registered");
+ }
+ mProviderCbBinder = binder;
+ }
+
+ /**
+ * Get the binder that was returned by the service when successfully registering. Or null if the
+ * provider was never registered.
+ *
+ * @hide
+ */
+ @Nullable
+ public INetworkStatsProviderCallback getProviderCallbackBinder() {
+ return mProviderCbBinder;
+ }
+
+ /**
+ * Get the binder that was returned by the service when successfully registering. Throw an
+ * {@link IllegalStateException} if the provider is not registered.
+ *
+ * @hide
+ */
+ @NonNull
+ public INetworkStatsProviderCallback getProviderCallbackBinderOrThrow() {
+ if (mProviderCbBinder == null) {
+ throw new IllegalStateException("the provider is not registered");
+ }
+ return mProviderCbBinder;
+ }
+
+ /**
+ * Notify the system of new network statistics.
+ *
+ * Send the network statistics recorded since the last call to {@link #notifyStatsUpdated}. Must
+ * be called as soon as possible after {@link NetworkStatsProvider#onRequestStatsUpdate(int)}
+ * being called. Responding later increases the probability stats will be dropped. The
+ * provider can also call this whenever it wants to reports new stats for any reason.
+ * Note that the system will not necessarily immediately propagate the statistics to
+ * reflect the update.
+ *
+ * @param token the token under which these stats were gathered. Providers can call this method
+ * with the current token as often as they want, until the token changes.
+ * {@see NetworkStatsProvider#onRequestStatsUpdate()}
+ * @param ifaceStats the {@link NetworkStats} per interface to be reported.
+ * The provider should not include any traffic that is already counted by
+ * kernel interface counters.
+ * @param uidStats the same stats as above, but counts {@link NetworkStats}
+ * per uid.
+ */
+ public void notifyStatsUpdated(int token, @NonNull NetworkStats ifaceStats,
+ @NonNull NetworkStats uidStats) {
+ try {
+ getProviderCallbackBinderOrThrow().notifyStatsUpdated(token, ifaceStats, uidStats);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Notify system that the quota set by {@code onSetAlert} has been reached.
+ */
+ public void notifyAlertReached() {
+ try {
+ getProviderCallbackBinderOrThrow().notifyAlertReached();
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Notify system that the quota set by {@code onSetLimit} has been reached.
+ */
+ public void notifyLimitReached() {
+ try {
+ getProviderCallbackBinderOrThrow().notifyLimitReached();
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Called by {@code NetworkStatsService} when it requires to know updated stats.
+ * The provider MUST respond by calling {@link #notifyStatsUpdated} as soon as possible.
+ * Responding later increases the probability stats will be dropped. Memory allowing, the
+ * system will try to take stats into account up to one minute after calling
+ * {@link #onRequestStatsUpdate}.
+ *
+ * @param token a positive number identifying the new state of the system under which
+ * {@link NetworkStats} have to be gathered from now on. When this is called,
+ * custom implementations of providers MUST tally and report the latest stats with
+ * the previous token, under which stats were being gathered so far.
+ */
+ public abstract void onRequestStatsUpdate(int token);
+
+ /**
+ * Called by {@code NetworkStatsService} when setting the interface quota for the specified
+ * upstream interface. When this is called, the custom implementation should block all egress
+ * packets on the {@code iface} associated with the provider when {@code quotaBytes} bytes have
+ * been reached, and MUST respond to it by calling
+ * {@link NetworkStatsProvider#notifyLimitReached()}.
+ *
+ * @param iface the interface requiring the operation.
+ * @param quotaBytes the quota defined as the number of bytes, starting from zero and counting
+ * from now. A value of {@link #QUOTA_UNLIMITED} indicates there is no limit.
+ */
+ public abstract void onSetLimit(@NonNull String iface, long quotaBytes);
+
+ /**
+ * Called by {@code NetworkStatsService} when setting the alert bytes. Custom implementations
+ * MUST call {@link NetworkStatsProvider#notifyAlertReached()} when {@code quotaBytes} bytes
+ * have been reached. Unlike {@link #onSetLimit(String, long)}, the custom implementation should
+ * not block all egress packets.
+ *
+ * @param quotaBytes the quota defined as the number of bytes, starting from zero and counting
+ * from now. A value of {@link #QUOTA_UNLIMITED} indicates there is no alert.
+ */
+ public abstract void onSetAlert(long quotaBytes);
+}
diff --git a/core/java/android/net/netstats/provider/NetworkStatsProviderCallback.java b/core/java/android/net/netstats/provider/NetworkStatsProviderCallback.java
deleted file mode 100644
index e17a8eee7ff0..000000000000
--- a/core/java/android/net/netstats/provider/NetworkStatsProviderCallback.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.netstats.provider;
-
-import android.annotation.NonNull;
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.net.NetworkStats;
-import android.os.RemoteException;
-
-/**
- * A callback class that allows callers to report events to the system.
- * @hide
- */
-@SystemApi
-@SuppressLint("CallbackMethodName")
-public class NetworkStatsProviderCallback {
- @NonNull private final INetworkStatsProviderCallback mBinder;
-
- /** @hide */
- public NetworkStatsProviderCallback(@NonNull INetworkStatsProviderCallback binder) {
- mBinder = binder;
- }
-
- /**
- * Notify the system of new network statistics.
- *
- * Send the network statistics recorded since the last call to {@link #onStatsUpdated}. Must be
- * called within one minute of {@link AbstractNetworkStatsProvider#requestStatsUpdate(int)}
- * being called. The provider can also call this whenever it wants to reports new stats for any
- * reason. Note that the system will not necessarily immediately propagate the statistics to
- * reflect the update.
- *
- * @param token the token under which these stats were gathered. Providers can call this method
- * with the current token as often as they want, until the token changes.
- * {@see AbstractNetworkStatsProvider#requestStatsUpdate()}
- * @param ifaceStats the {@link NetworkStats} per interface to be reported.
- * The provider should not include any traffic that is already counted by
- * kernel interface counters.
- * @param uidStats the same stats as above, but counts {@link NetworkStats}
- * per uid.
- */
- public void onStatsUpdated(int token, @NonNull NetworkStats ifaceStats,
- @NonNull NetworkStats uidStats) {
- try {
- mBinder.onStatsUpdated(token, ifaceStats, uidStats);
- } catch (RemoteException e) {
- e.rethrowAsRuntimeException();
- }
- }
-
- /**
- * Notify system that the quota set by {@code setAlert} has been reached.
- */
- public void onAlertReached() {
- try {
- mBinder.onAlertReached();
- } catch (RemoteException e) {
- e.rethrowAsRuntimeException();
- }
- }
-
- /**
- * Notify system that the quota set by {@code setLimit} has been reached.
- */
- public void onLimitReached() {
- try {
- mBinder.onLimitReached();
- } catch (RemoteException e) {
- e.rethrowAsRuntimeException();
- }
- }
-
- /**
- * Unregister the provider and the referencing callback.
- */
- public void unregister() {
- try {
- mBinder.unregister();
- } catch (RemoteException e) {
- // Ignore error.
- }
- }
-}
diff --git a/core/java/android/net/netstats/provider/NetworkStatsProviderWrapper.java b/core/java/android/net/netstats/provider/NetworkStatsProviderWrapper.java
deleted file mode 100644
index 4bf7c9bc086e..000000000000
--- a/core/java/android/net/netstats/provider/NetworkStatsProviderWrapper.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.netstats.provider;
-
-import android.annotation.NonNull;
-
-/**
- * A wrapper class of {@link INetworkStatsProvider} that hides the binder interface from exposing
- * to outer world.
- *
- * @hide
- */
-public class NetworkStatsProviderWrapper extends INetworkStatsProvider.Stub {
- @NonNull final AbstractNetworkStatsProvider mProvider;
-
- public NetworkStatsProviderWrapper(AbstractNetworkStatsProvider provider) {
- mProvider = provider;
- }
-
- @Override
- public void requestStatsUpdate(int token) {
- mProvider.requestStatsUpdate(token);
- }
-
- @Override
- public void setLimit(@NonNull String iface, long quotaBytes) {
- mProvider.setLimit(iface, quotaBytes);
- }
-
- @Override
- public void setAlert(long quotaBytes) {
- mProvider.setAlert(quotaBytes);
- }
-}
diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java
index 9ca1c3313670..fab906b9e122 100644
--- a/core/java/android/os/BugreportManager.java
+++ b/core/java/android/os/BugreportManager.java
@@ -157,20 +157,20 @@ public final class BugreportManager {
Preconditions.checkNotNull(executor);
Preconditions.checkNotNull(callback);
- boolean validScreenshotFd = screenshotFd != null;
+ boolean isScreenshotRequested = screenshotFd != null;
if (screenshotFd == null) {
// Binder needs a valid File Descriptor to be passed
screenshotFd = ParcelFileDescriptor.open(new File("/dev/null"),
ParcelFileDescriptor.MODE_READ_ONLY);
}
DumpstateListener dsListener = new DumpstateListener(executor, callback,
- validScreenshotFd);
+ isScreenshotRequested);
// Note: mBinder can get callingUid from the binder transaction.
mBinder.startBugreport(-1 /* callingUid */,
mContext.getOpPackageName(),
bugreportFd.getFileDescriptor(),
screenshotFd.getFileDescriptor(),
- params.getMode(), dsListener);
+ params.getMode(), dsListener, isScreenshotRequested);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (FileNotFoundException e) {
@@ -225,13 +225,13 @@ public final class BugreportManager {
private final class DumpstateListener extends IDumpstateListener.Stub {
private final Executor mExecutor;
private final BugreportCallback mCallback;
- private final boolean mValidScreenshotFd;
+ private final boolean mIsScreenshotRequested;
DumpstateListener(Executor executor, BugreportCallback callback,
- boolean validScreenshotFd) {
+ boolean isScreenshotRequested) {
mExecutor = executor;
mCallback = callback;
- mValidScreenshotFd = validScreenshotFd;
+ mIsScreenshotRequested = isScreenshotRequested;
}
@Override
@@ -272,7 +272,7 @@ public final class BugreportManager {
@Override
public void onScreenshotTaken(boolean success) throws RemoteException {
- if (!mValidScreenshotFd) {
+ if (!mIsScreenshotRequested) {
return;
}
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index f2fb5b246f39..ae65f1d0bd05 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -23,6 +23,9 @@ import android.annotation.TestApi;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.admin.DevicePolicyManager;
+import android.compat.Compatibility;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
@@ -88,6 +91,46 @@ public class Environment {
private static final File DIR_APEX_ROOT = getDirectory(ENV_APEX_ROOT,
"/apex");
+ /**
+ * Scoped Storage is on by default. However, it is not strictly enforced and there are multiple
+ * ways to opt out of scoped storage:
+ * <ul>
+ * <li>Target Sdk < Q</li>
+ * <li>Target Sdk = Q and has `requestLegacyExternalStorage` set in AndroidManifest.xml</li>
+ * <li>Target Sdk > Q: Upgrading from an app that was opted out of scoped storage and has
+ * `preserveLegacyExternalStorage` set in AndroidManifest.xml</li>
+ * </ul>
+ * This flag is enabled for all apps by default as Scoped Storage is enabled by default.
+ * Developers can disable this flag to opt out of Scoped Storage and have legacy storage
+ * workflow.
+ *
+ * Note: {@code FORCE_ENABLE_SCOPED_STORAGE} should also be disabled for apps to opt out of
+ * scoped storage.
+ * Note: This flag is also used in {@code com.android.providers.media.LocalCallingIdentity}.
+ * Any modifications to this flag should be reflected there as well.
+ * See https://developer.android.com/training/data-storage#scoped-storage for more information.
+ */
+ @ChangeId
+ private static final long DEFAULT_SCOPED_STORAGE = 149924527L;
+
+ /**
+ * Setting this flag strictly enforces Scoped Storage regardless of:
+ * <ul>
+ * <li>The value of Target Sdk</li>
+ * <li>The value of `requestLegacyExternalStorage` in AndroidManifest.xml</li>
+ * <li>The value of `preserveLegacyExternalStorage` in AndroidManifest.xml</li>
+ * </ul>
+ *
+ * Note: {@code DEFAULT_SCOPED_STORAGE} should also be enabled for apps to be enforced into
+ * scoped storage.
+ * Note: This flag is also used in {@code com.android.providers.media.LocalCallingIdentity}.
+ * Any modifications to this flag should be reflected there as well.
+ * See https://developer.android.com/training/data-storage#scoped-storage for more information.
+ */
+ @ChangeId
+ @Disabled
+ private static final long FORCE_ENABLE_SCOPED_STORAGE = 132649864L;
+
@UnsupportedAppUsage
private static UserEnvironment sCurrentUser;
private static boolean sUserRequired;
@@ -1191,12 +1234,13 @@ public class Environment {
}
/**
- * Returns whether the primary shared/external storage media is a legacy
- * view that includes files not owned by the app.
+ * Returns whether the shared/external storage media is a
+ * legacy view that includes files not owned by the app.
* <p>
* This value may be different from the value requested by
* {@code requestLegacyExternalStorage} in the app's manifest, since an app
- * may inherit its legacy state based on when it was first installed.
+ * may inherit its legacy state based on when it was first installed, target sdk and other
+ * factors.
* <p>
* Non-legacy apps can continue to discover and read media belonging to
* other apps via {@link android.provider.MediaStore}.
@@ -1207,18 +1251,19 @@ public class Environment {
}
/**
- * Returns whether the shared/external storage media at the given path is a
+ * Returns whether the shared/external storage media is a
* legacy view that includes files not owned by the app.
* <p>
* This value may be different from the value requested by
* {@code requestLegacyExternalStorage} in the app's manifest, since an app
- * may inherit its legacy state based on when it was first installed.
+ * may inherit its legacy state based on when it was first installed, target sdk and other
+ * factors.
* <p>
* Non-legacy apps can continue to discover and read media belonging to
* other apps via {@link android.provider.MediaStore}.
*
* @throws IllegalArgumentException if the path is not a valid storage
- * device.
+ * device.
*/
public static boolean isExternalStorageLegacy(@NonNull File path) {
final Context context = AppGlobals.getInitialApplication();
@@ -1232,24 +1277,23 @@ public class Environment {
return false;
}
- if (packageManager.checkPermission(Manifest.permission.WRITE_MEDIA_STORAGE,
- context.getPackageName()) == PackageManager.PERMISSION_GRANTED) {
- return true;
+ // TODO(b/150672994): Compat flags do not override instant app and isolated process's
+ // behavior.
+ boolean defaultScopedStorage = Compatibility.isChangeEnabled(DEFAULT_SCOPED_STORAGE);
+ boolean forceEnableScopedStorage = Compatibility.isChangeEnabled(
+ FORCE_ENABLE_SCOPED_STORAGE);
+ // if Scoped Storage is strictly enforced, the app does *not* have legacy storage access
+ // Note: does not require packagename/uid as this is directly called from an app process
+ if (isScopedStorageEnforced(defaultScopedStorage, forceEnableScopedStorage)) {
+ return false;
}
-
- if (packageManager.checkPermission(Manifest.permission.INSTALL_PACKAGES,
- context.getPackageName()) == PackageManager.PERMISSION_GRANTED) {
+ // if Scoped Storage is strictly disabled, the app has legacy storage access
+ // Note: does not require packagename/uid as this is directly called from an app process
+ if (isScopedStorageDisabled(defaultScopedStorage, forceEnableScopedStorage)) {
return true;
}
- final AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
- final String[] packagesForUid = packageManager.getPackagesForUid(uid);
- for (String packageName : packagesForUid) {
- if (appOps.checkOpNoThrow(AppOpsManager.OP_REQUEST_INSTALL_PACKAGES,
- uid, packageName) == AppOpsManager.MODE_ALLOWED) {
- return true;
- }
- }
+ final AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
return appOps.checkOpNoThrow(AppOpsManager.OP_LEGACY_STORAGE,
uid, context.getOpPackageName()) == AppOpsManager.MODE_ALLOWED;
}
@@ -1298,6 +1342,16 @@ public class Environment {
}
}
+ private static boolean isScopedStorageEnforced(boolean defaultScopedStorage,
+ boolean forceEnableScopedStorage) {
+ return defaultScopedStorage && forceEnableScopedStorage;
+ }
+
+ private static boolean isScopedStorageDisabled(boolean defaultScopedStorage,
+ boolean forceEnableScopedStorage) {
+ return !defaultScopedStorage && !forceEnableScopedStorage;
+ }
+
static File getDirectory(String variableName, String defaultPath) {
String path = System.getenv(variableName);
return path == null ? new File(defaultPath) : new File(path);
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index d7af1b9faa97..b7b3c4fc8add 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -607,6 +607,7 @@ public class Process {
* started.
* @param pkgDataInfoMap Map from related package names to private data directory
* volume UUID and inode number.
+ * @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data.
* @param zygoteArgs Additional arguments to supply to the zygote process.
* @return An object that describes the result of the attempt to start the process.
* @throws RuntimeException on fatal start failure
@@ -630,12 +631,13 @@ public class Process {
@Nullable long[] disabledCompatChanges,
@Nullable Map<String, Pair<String, Long>>
pkgDataInfoMap,
+ boolean bindMountAppStorageDirs,
@Nullable String[] zygoteArgs) {
return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, packageName,
zygotePolicyFlags, isTopApp, disabledCompatChanges,
- pkgDataInfoMap, zygoteArgs);
+ pkgDataInfoMap, bindMountAppStorageDirs, zygoteArgs);
}
/** @hide */
@@ -659,7 +661,7 @@ public class Process {
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, packageName,
/*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, /*isTopApp=*/ false,
- disabledCompatChanges, /* pkgDataInfoMap */ null, zygoteArgs);
+ disabledCompatChanges, /* pkgDataInfoMap */ null, false, zygoteArgs);
}
/**
@@ -929,6 +931,19 @@ public class Process {
public static final native void setProcessFrozen(int pid, int uid, boolean frozen);
/**
+ * Enable or disable the freezer. When enable == false all frozen processes are unfrozen,
+ * but aren't removed from the freezer. Processes can still be added or removed
+ * by using setProcessFrozen, but they won't actually be frozen until the freezer is enabled
+ * again. If enable == true the freezer is enabled again, and all processes
+ * in the freezer (including the ones added while the freezer was disabled) are frozen.
+ *
+ * @param enable Specify whether to enable (true) or disable (false) the freezer.
+ *
+ * @hide
+ */
+ public static final native void enableFreezer(boolean enable);
+
+ /**
* Return the scheduling group of requested process.
*
* @hide
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index 2d218f4f2541..1992f1dc911e 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -963,7 +963,7 @@ public abstract class VibrationEffect implements Parcelable {
PRIMITIVE_QUICK_RISE,
PRIMITIVE_SLOW_RISE,
PRIMITIVE_QUICK_FALL,
- PRIMITIVE_LIGHT_TICK,
+ PRIMITIVE_TICK,
})
@Retention(RetentionPolicy.SOURCE)
public @interface Primitive {}
@@ -981,10 +981,14 @@ public abstract class VibrationEffect implements Parcelable {
* A haptic effect that simulates downwards movement with gravity. Often
* followed by extra energy of hitting and reverberation to augment
* physicality.
+ *
+ * @hide Not confident enough to expose publicly yet
*/
public static final int PRIMITIVE_THUD = 2;
/**
* A haptic effect that simulates spinning momentum.
+ *
+ * @hide Not confident enough to expose publicly yet
*/
public static final int PRIMITIVE_SPIN = 3;
/**
@@ -1003,7 +1007,8 @@ public abstract class VibrationEffect implements Parcelable {
* This very short effect should produce a light crisp sensation intended
* to be used repetitively for dynamic feedback.
*/
- public static final int PRIMITIVE_LIGHT_TICK = 7;
+ // Internally this maps to the HAL constant CompositePrimitive::LIGHT_TICK
+ public static final int PRIMITIVE_TICK = 7;
private ArrayList<PrimitiveEffect> mEffects = new ArrayList<>();
@@ -1081,7 +1086,7 @@ public abstract class VibrationEffect implements Parcelable {
*
*/
static int checkPrimitive(int primitiveId) {
- Preconditions.checkArgumentInRange(primitiveId, PRIMITIVE_NOOP, PRIMITIVE_LIGHT_TICK,
+ Preconditions.checkArgumentInRange(primitiveId, PRIMITIVE_NOOP, PRIMITIVE_TICK,
"primitiveId");
return primitiveId;
}
@@ -1108,8 +1113,8 @@ public abstract class VibrationEffect implements Parcelable {
return "PRIMITIVE_SLOW_RISE";
case PRIMITIVE_QUICK_FALL:
return "PRIMITIVE_QUICK_FALL";
- case PRIMITIVE_LIGHT_TICK:
- return "PRIMITIVE_LIGHT_TICK";
+ case PRIMITIVE_TICK:
+ return "PRIMITIVE_TICK";
default:
return Integer.toString(id);
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 34cec061edcd..5f3f14facd75 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -333,6 +333,7 @@ public class ZygoteProcess {
* started.
* @param pkgDataInfoMap Map from related package names to private data directory
* volume UUID and inode number.
+ * @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data.
*
* @param zygoteArgs Additional arguments to supply to the Zygote process.
* @return An object that describes the result of the attempt to start the process.
@@ -354,6 +355,7 @@ public class ZygoteProcess {
@Nullable long[] disabledCompatChanges,
@Nullable Map<String, Pair<String, Long>>
pkgDataInfoMap,
+ boolean bindMountAppStorageDirs,
@Nullable String[] zygoteArgs) {
// TODO (chriswailes): Is there a better place to check this value?
if (fetchUsapPoolEnabledPropWithMinInterval()) {
@@ -365,7 +367,7 @@ public class ZygoteProcess {
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/ false,
packageName, zygotePolicyFlags, isTopApp, disabledCompatChanges,
- pkgDataInfoMap, zygoteArgs);
+ pkgDataInfoMap, bindMountAppStorageDirs, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
@@ -606,6 +608,7 @@ public class ZygoteProcess {
* @param disabledCompatChanges a list of disabled compat changes for the process being started.
* @param pkgDataInfoMap Map from related package names to private data directory volume UUID
* and inode number.
+ * @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data.
* @param extraArgs Additional arguments to supply to the zygote process.
* @return An object that describes the result of the attempt to start the process.
* @throws ZygoteStartFailedEx if process start failed for any reason
@@ -628,6 +631,7 @@ public class ZygoteProcess {
@Nullable long[] disabledCompatChanges,
@Nullable Map<String, Pair<String, Long>>
pkgDataInfoMap,
+ boolean bindMountAppStorageDirs,
@Nullable String[] extraArgs)
throws ZygoteStartFailedEx {
ArrayList<String> argsForZygote = new ArrayList<>();
@@ -725,6 +729,10 @@ public class ZygoteProcess {
argsForZygote.add(sb.toString());
}
+ if (bindMountAppStorageDirs) {
+ argsForZygote.add(Zygote.BIND_MOUNT_APP_STORAGE_DIRS);
+ }
+
if (disabledCompatChanges != null && disabledCompatChanges.length > 0) {
StringBuilder sb = new StringBuilder();
sb.append("--disabled-compat-changes=");
@@ -1282,7 +1290,9 @@ public class ZygoteProcess {
abi, instructionSet, null /* appDataDir */, null /* invokeWith */,
true /* startChildZygote */, null /* packageName */,
ZYGOTE_POLICY_FLAG_SYSTEM_PROCESS /* zygotePolicyFlags */, false /* isTopApp */,
- null /* disabledCompatChanges */, null /* pkgDataInfoMap */, extraArgs);
+ null /* disabledCompatChanges */, null /* pkgDataInfoMap */,
+ /* bindMountAppStorageDirs */ false, extraArgs);
+
} catch (ZygoteStartFailedEx ex) {
throw new RuntimeException("Starting child-zygote through Zygote failed", ex);
}
diff --git a/core/java/android/os/incremental/IncrementalNewFileParams.aidl b/core/java/android/os/incremental/IncrementalNewFileParams.aidl
index 182732cebdf1..8faf158b72a0 100644
--- a/core/java/android/os/incremental/IncrementalNewFileParams.aidl
+++ b/core/java/android/os/incremental/IncrementalNewFileParams.aidl
@@ -16,8 +16,6 @@
package android.os.incremental;
-import android.os.incremental.IncrementalSignature;
-
/**
* All the parameters to create a new file on IncFS
* FileId is a 16 byte-long identifier.
@@ -27,5 +25,5 @@ parcelable IncrementalNewFileParams {
long size;
byte[] fileId;
byte[] metadata;
- @nullable IncrementalSignature signature;
+ @nullable byte[] signature;
}
diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java
index bf31bc206278..7092751c0d7e 100644
--- a/core/java/android/os/incremental/IncrementalStorage.java
+++ b/core/java/android/os/incremental/IncrementalStorage.java
@@ -20,8 +20,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.RemoteException;
-import java.io.ByteArrayInputStream;
-import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -180,11 +178,12 @@ public final class IncrementalStorage {
if (id == null && metadata == null) {
throw new IOException("File ID and metadata cannot both be null");
}
+ validateV4Signature(v4signatureBytes);
final IncrementalNewFileParams params = new IncrementalNewFileParams();
params.size = size;
params.metadata = (metadata == null ? new byte[0] : metadata);
params.fileId = idToBytes(id);
- params.signature = parseV4Signature(v4signatureBytes);
+ params.signature = v4signatureBytes;
int res = mService.makeFile(mId, path, params);
if (res != 0) {
throw new IOException("makeFile() failed with errno " + -res);
@@ -415,27 +414,23 @@ public final class IncrementalStorage {
return new UUID(msb, lsb);
}
- private static final int INCFS_HASH_SHA256 = 1;
private static final int INCFS_MAX_HASH_SIZE = 32; // SHA256
private static final int INCFS_MAX_ADD_DATA_SIZE = 128;
/**
* Deserialize and validate v4 signature bytes.
*/
- private static IncrementalSignature parseV4Signature(@Nullable byte[] v4signatureBytes)
+ private static void validateV4Signature(@Nullable byte[] v4signatureBytes)
throws IOException {
if (v4signatureBytes == null || v4signatureBytes.length == 0) {
- return null;
+ return;
}
final V4Signature signature;
- try (DataInputStream input = new DataInputStream(
- new ByteArrayInputStream(v4signatureBytes))) {
- try {
- signature = V4Signature.readFrom(input);
- } catch (IOException e) {
- throw new IOException("Failed to read v4 signature:", e);
- }
+ try {
+ signature = V4Signature.readFrom(v4signatureBytes);
+ } catch (IOException e) {
+ throw new IOException("Failed to read v4 signature:", e);
}
if (!signature.isVersionSupported()) {
@@ -443,25 +438,27 @@ public final class IncrementalStorage {
+ " is not supported");
}
- final byte[] rootHash = signature.verityRootHash;
- final byte[] additionalData = signature.v3Digest;
- final byte[] pkcs7Signature = signature.pkcs7SignatureBlock;
+ final V4Signature.HashingInfo hashingInfo = V4Signature.HashingInfo.fromByteArray(
+ signature.hashingInfo);
+ final V4Signature.SigningInfo signingInfo = V4Signature.SigningInfo.fromByteArray(
+ signature.signingInfo);
- if (rootHash.length != INCFS_MAX_HASH_SIZE) {
- throw new IOException("rootHash has to be " + INCFS_MAX_HASH_SIZE + " bytes");
+ if (hashingInfo.hashAlgorithm != V4Signature.HASHING_ALGORITHM_SHA256) {
+ throw new IOException("Unsupported hashAlgorithm: " + hashingInfo.hashAlgorithm);
+ }
+ if (hashingInfo.log2BlockSize != V4Signature.LOG2_BLOCK_SIZE_4096_BYTES) {
+ throw new IOException("Unsupported log2BlockSize: " + hashingInfo.log2BlockSize);
}
- if (additionalData.length > INCFS_MAX_ADD_DATA_SIZE) {
+ if (hashingInfo.salt != null && hashingInfo.salt.length > 0) {
+ throw new IOException("Unsupported salt: " + hashingInfo.salt);
+ }
+ if (hashingInfo.rawRootHash.length != INCFS_MAX_HASH_SIZE) {
+ throw new IOException("rawRootHash has to be " + INCFS_MAX_HASH_SIZE + " bytes");
+ }
+ if (signingInfo.additionalData.length > INCFS_MAX_ADD_DATA_SIZE) {
throw new IOException(
"additionalData has to be at most " + INCFS_MAX_ADD_DATA_SIZE + " bytes");
}
-
- IncrementalSignature result = new IncrementalSignature();
- result.hashAlgorithm = INCFS_HASH_SHA256;
- result.rootHash = rootHash;
- result.additionalData = additionalData;
- result.signature = pkcs7Signature;
-
- return result;
}
/**
diff --git a/core/java/android/os/incremental/V4Signature.java b/core/java/android/os/incremental/V4Signature.java
index 6d334f539fc9..71f931da1a92 100644
--- a/core/java/android/os/incremental/V4Signature.java
+++ b/core/java/android/os/incremental/V4Signature.java
@@ -20,9 +20,12 @@ import android.os.ParcelFileDescriptor;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
+import java.io.EOFException;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
/**
* V4 signature fields.
@@ -31,30 +34,95 @@ import java.io.IOException;
*/
public class V4Signature {
public static final String EXT = ".idsig";
- public static final int SUPPORTED_VERSION = 1;
+ public static final int SUPPORTED_VERSION = 2;
- public final int version;
- public final byte[] verityRootHash;
- public final byte[] v3Digest;
- public final byte[] pkcs7SignatureBlock;
+ public static final int HASHING_ALGORITHM_SHA256 = 1;
+ public static final byte LOG2_BLOCK_SIZE_4096_BYTES = 12;
+
+ /**
+ * IncFS hashing data.
+ */
+ public static class HashingInfo {
+ public final int hashAlgorithm; // only 1 == SHA256 supported
+ public final byte log2BlockSize; // only 12 (block size 4096) supported now
+ public final byte[] salt; // used exactly as in fs-verity, 32 bytes max
+ public final byte[] rawRootHash; // salted digest of the first Merkle tree page
+
+ HashingInfo(int hashAlgorithm, byte log2BlockSize, byte[] salt, byte[] rawRootHash) {
+ this.hashAlgorithm = hashAlgorithm;
+ this.log2BlockSize = log2BlockSize;
+ this.salt = salt;
+ this.rawRootHash = rawRootHash;
+ }
+
+ /**
+ * Constructs HashingInfo from byte array.
+ */
+ public static HashingInfo fromByteArray(byte[] bytes) throws IOException {
+ ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
+ final int hashAlgorithm = buffer.getInt();
+ final byte log2BlockSize = buffer.get();
+ byte[] salt = readBytes(buffer);
+ byte[] rawRootHash = readBytes(buffer);
+ return new HashingInfo(hashAlgorithm, log2BlockSize, salt, rawRootHash);
+ }
+ }
+
+ /**
+ * V4 signature data.
+ */
+ public static class SigningInfo {
+ public final byte[] v3Digest; // used to match with the corresponding APK
+ public final byte[] certificate; // ASN.1 DER form
+ public final byte[] additionalData; // a free-form binary data blob
+ public final byte[] publicKey; // ASN.1 DER, must match the certificate
+ public final int signatureAlgorithmId; // see the APK v2 doc for the list
+ public final byte[] signature;
+
+ SigningInfo(byte[] v3Digest, byte[] certificate, byte[] additionalData,
+ byte[] publicKey, int signatureAlgorithmId, byte[] signature) {
+ this.v3Digest = v3Digest;
+ this.certificate = certificate;
+ this.additionalData = additionalData;
+ this.publicKey = publicKey;
+ this.signatureAlgorithmId = signatureAlgorithmId;
+ this.signature = signature;
+ }
+
+ /**
+ * Constructs SigningInfo from byte array.
+ */
+ public static SigningInfo fromByteArray(byte[] bytes) throws IOException {
+ ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
+ byte[] v3Digest = readBytes(buffer);
+ byte[] certificate = readBytes(buffer);
+ byte[] additionalData = readBytes(buffer);
+ byte[] publicKey = readBytes(buffer);
+ int signatureAlgorithmId = buffer.getInt();
+ byte[] signature = readBytes(buffer);
+ return new SigningInfo(v3Digest, certificate, additionalData, publicKey,
+ signatureAlgorithmId, signature);
+ }
+ }
+
+ public final int version; // Always 2 for now.
+ public final byte[] hashingInfo;
+ public final byte[] signingInfo; // Passed as-is to the kernel. Can be retrieved later.
/**
* Construct a V4Signature from .idsig file.
*/
public static V4Signature readFrom(ParcelFileDescriptor pfd) throws IOException {
- final ParcelFileDescriptor dupedFd = pfd.dup();
- final ParcelFileDescriptor.AutoCloseInputStream fdInputStream =
- new ParcelFileDescriptor.AutoCloseInputStream(dupedFd);
- try (DataInputStream stream = new DataInputStream(fdInputStream)) {
+ try (InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(pfd.dup())) {
return readFrom(stream);
}
}
/**
- * Construct a V4Signature from .idsig file.
+ * Construct a V4Signature from a byte array.
*/
public static V4Signature readFrom(byte[] bytes) throws IOException {
- try (DataInputStream stream = new DataInputStream(new ByteArrayInputStream(bytes))) {
+ try (InputStream stream = new ByteArrayInputStream(bytes)) {
return readFrom(stream);
}
}
@@ -63,51 +131,131 @@ public class V4Signature {
* Store the V4Signature to a byte-array.
*/
public byte[] toByteArray() {
- try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
- try (DataOutputStream steam = new DataOutputStream(byteArrayOutputStream)) {
- this.writeTo(steam);
- steam.flush();
- }
- return byteArrayOutputStream.toByteArray();
+ try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
+ this.writeTo(stream);
+ return stream.toByteArray();
} catch (IOException e) {
return null;
}
}
- boolean isVersionSupported() {
- return this.version == SUPPORTED_VERSION;
+ /**
+ * Combines necessary data to a signed data blob.
+ * The blob can be validated against signingInfo.signature.
+ *
+ * @param fileSize - size of the signed file (APK)
+ */
+ public static byte[] getSigningData(long fileSize, HashingInfo hashingInfo,
+ SigningInfo signingInfo) {
+ final int size =
+ 4/*size*/ + 8/*fileSize*/ + 4/*hash_algorithm*/ + 1/*log2_blocksize*/ + bytesSize(
+ hashingInfo.salt) + bytesSize(hashingInfo.rawRootHash) + bytesSize(
+ signingInfo.v3Digest) + bytesSize(signingInfo.certificate) + bytesSize(
+ signingInfo.additionalData);
+ ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN);
+ buffer.putInt(size);
+ buffer.putLong(fileSize);
+ buffer.putInt(hashingInfo.hashAlgorithm);
+ buffer.put(hashingInfo.log2BlockSize);
+ writeBytes(buffer, hashingInfo.salt);
+ writeBytes(buffer, hashingInfo.rawRootHash);
+ writeBytes(buffer, signingInfo.v3Digest);
+ writeBytes(buffer, signingInfo.certificate);
+ writeBytes(buffer, signingInfo.additionalData);
+ return buffer.array();
}
- static V4Signature readFrom(DataInputStream stream) throws IOException {
- final int version = stream.readInt();
- byte[] verityRootHash = readBytes(stream);
- byte[] v3Digest = readBytes(stream);
- byte[] pkcs7SignatureBlock = readBytes(stream);
- return new V4Signature(version, verityRootHash, v3Digest, pkcs7SignatureBlock);
+ public boolean isVersionSupported() {
+ return this.version == SUPPORTED_VERSION;
}
- V4Signature(int version, byte[] verityRootHash, byte[] v3Digest, byte[] pkcs7SignatureBlock) {
+ private V4Signature(int version, byte[] hashingInfo, byte[] signingInfo) {
this.version = version;
- this.verityRootHash = verityRootHash;
- this.v3Digest = v3Digest;
- this.pkcs7SignatureBlock = pkcs7SignatureBlock;
+ this.hashingInfo = hashingInfo;
+ this.signingInfo = signingInfo;
}
- void writeTo(DataOutputStream stream) throws IOException {
- stream.writeInt(this.version);
- writeBytes(stream, this.verityRootHash);
- writeBytes(stream, this.v3Digest);
- writeBytes(stream, this.pkcs7SignatureBlock);
+ private static V4Signature readFrom(InputStream stream) throws IOException {
+ final int version = readIntLE(stream);
+ final byte[] hashingInfo = readBytes(stream);
+ final byte[] signingInfo = readBytes(stream);
+ return new V4Signature(version, hashingInfo, signingInfo);
}
- private static byte[] readBytes(DataInputStream stream) throws IOException {
- byte[] result = new byte[stream.readInt()];
- stream.read(result);
- return result;
+ private void writeTo(OutputStream stream) throws IOException {
+ writeIntLE(stream, this.version);
+ writeBytes(stream, this.hashingInfo);
+ writeBytes(stream, this.signingInfo);
}
- private static void writeBytes(DataOutputStream stream, byte[] bytes) throws IOException {
- stream.writeInt(bytes.length);
+ // Utility methods.
+ private static int bytesSize(byte[] bytes) {
+ return 4/*length*/ + (bytes == null ? 0 : bytes.length);
+ }
+
+ private static void readFully(InputStream stream, byte[] buffer) throws IOException {
+ int len = buffer.length;
+ int n = 0;
+ while (n < len) {
+ int count = stream.read(buffer, n, len - n);
+ if (count < 0) {
+ throw new EOFException();
+ }
+ n += count;
+ }
+ }
+
+ private static int readIntLE(InputStream stream) throws IOException {
+ final byte[] buffer = new byte[4];
+ readFully(stream, buffer);
+ return ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN).getInt();
+ }
+
+ private static void writeIntLE(OutputStream stream, int v) throws IOException {
+ final byte[] buffer = ByteBuffer.wrap(new byte[4]).order(ByteOrder.LITTLE_ENDIAN).putInt(
+ v).array();
+ stream.write(buffer);
+ }
+
+ private static byte[] readBytes(InputStream stream) throws IOException {
+ try {
+ final int size = readIntLE(stream);
+ final byte[] bytes = new byte[size];
+ readFully(stream, bytes);
+ return bytes;
+ } catch (EOFException ignored) {
+ return null;
+ }
+ }
+
+ private static byte[] readBytes(ByteBuffer buffer) throws IOException {
+ if (buffer.remaining() < 4) {
+ throw new EOFException();
+ }
+ final int size = buffer.getInt();
+ if (buffer.remaining() < size) {
+ throw new EOFException();
+ }
+ final byte[] bytes = new byte[size];
+ buffer.get(bytes);
+ return bytes;
+ }
+
+ private static void writeBytes(OutputStream stream, byte[] bytes) throws IOException {
+ if (bytes == null) {
+ writeIntLE(stream, 0);
+ return;
+ }
+ writeIntLE(stream, bytes.length);
stream.write(bytes);
}
+
+ private static void writeBytes(ByteBuffer buffer, byte[] bytes) {
+ if (bytes == null) {
+ buffer.putInt(0);
+ return;
+ }
+ buffer.putInt(bytes.length);
+ buffer.put(bytes);
+ }
}
diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java
index a2def7fff2d2..f43a2523b5b3 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -100,10 +100,11 @@ public abstract class StorageManagerInternal {
}
/**
- * Check if fuse is running in target user, if it's running then setup its obb directories.
- * TODO: System server should store a list of active pids that obb is not mounted and use it.
+ * Create storage directories if it does not exist.
+ * Return true if the directories were setup correctly, otherwise false.
*/
- public abstract void prepareObbDirs(int userId, Set<String> packageList, String processName);
+ public abstract boolean prepareStorageDirs(int userId, Set<String> packageList,
+ String processName);
/**
* Add a listener to listen to reset event in StorageManagerService.
diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl
index 40acb7be797b..09df72c286d0 100644
--- a/core/java/android/permission/IPermissionManager.aidl
+++ b/core/java/android/permission/IPermissionManager.aidl
@@ -110,4 +110,8 @@ interface IPermissionManager {
List<String> getAutoRevokeExemptionRequestedPackages(int userId);
List<String> getAutoRevokeExemptionGrantedPackages(int userId);
+
+ boolean setAutoRevokeWhitelisted(String packageName, boolean whitelisted, int userId);
+
+ boolean isAutoRevokeWhitelisted(String packageName, int userId);
}
diff --git a/core/java/android/permission/Permissions.md b/core/java/android/permission/Permissions.md
index e62bd0a88bf6..e116dc6f37a1 100644
--- a/core/java/android/permission/Permissions.md
+++ b/core/java/android/permission/Permissions.md
@@ -49,6 +49,10 @@ The purpose of install time permissions is to control access to APIs where it do
to involve the user. This can be either because the API is not sensitive, or because additional
checks exist.
+Another benefit of install time permissions is that is becomes very easy to monitor which apps can
+access certain APIs. E.g. by checking which apps have the `android.permission.INTERNET` permission
+you can list all apps that are allowed to use APIs that can send data to the internet.
+
#### Defining a permission
Any package can define a permission. For that it simply adds an entry in the manifest file
@@ -591,8 +595,9 @@ User 0:
### Permission restricted components
As [publicly documented](https://developer.android.com/guide/topics/permissions/overview#permission_enforcement)
-it is possible to restrict starting an activity/binding to a service by using permission. It is
-a common pattern to
+it is possible to restrict starting an activity/binding to a service by using permission.
+
+It is a common pattern to
- define a permission in the platform as `signature`
- protect a service in an app by this permission using the `android:permission` attribute of the
@@ -605,6 +610,75 @@ accessibility services.
This does not work for app-op or runtime permissions as the way to check these permissions is
more complex than install time permissions.
+#### End-to-end A service only the system can bind to
+
+Make sure to set the `android:permission` flag for this service. As developers can forget this it is
+a good habit to check this before binding to the service. This makes sure that the services are
+implemented correctly and no random app can bind to the service.
+
+The result is that the permission is granted only to the system. It is not granted to the service's
+package, but this has no negative side-effects.
+
+##### Permission definition
+
+frameworks/base/core/res/AndroidManifest.xml:
+
+```xml
+<manifest>
+[...]
+ <permission android:name="android.permission.BIND_MY_SERVICE"
+ android:label="@string/permlab_bindMyService"
+ android:description="@string/permdesc_bindMyService"
+ android:protectionLevel="signature" />
+[...]
+</manifest>
+```
+
+##### Service definition
+
+Manifest of the service providing the functionality:
+
+```xml
+<manifest>
+ <service android:name="com.my.ServiceImpl"
+ android:permission="android.permission.BIND_MY_SERVICE">
+ <!-- add an intent filter so that the system can find this package -->
+ <intent-filter>
+ <action android:name="android.my.Service" />
+ </intent-filter>
+ </service>
+</manifest>
+```
+
+##### System server code binding to service
+
+```kotlin
+val serviceConnections = mutableListOf<ServiceConnection>()
+
+val potentialServices = context.packageManager.queryIntentServicesAsUser(
+ Intent("android.my.Service"), GET_SERVICES or GET_META_DATA, userId)
+
+for (val ri in potentialServices) {
+ val serviceComponent = ComponentName(ri.serviceInfo.packageName, ri.serviceInfo.name)
+
+ if (android.Manifest.permission.BIND_MY_SERVICE != ri.serviceInfo.permission) {
+ Slog.w(TAG, "$serviceComponent is not protected by " +
+ "${android.Manifest.permission.BIND_MY_SERVICE}")
+ continue
+ }
+
+ val newConnection = object : ServiceConnection {
+ ...
+ }
+
+ val wasBound = context.bindServiceAsUser(Intent().setComponent(serviceComponent),
+ serviceConnection, BIND_AUTO_CREATE, UserHandle.of(userId))
+ if (wasBound) {
+ serviceConnections.add(newConnection)
+ }
+}
+```
+
## Permissions for system apps
System apps need to integrate deeper with the system than regular apps. Hence they need to be
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ce9b4496f275..d8679b20e6dc 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8497,7 +8497,6 @@ public final class Settings {
*
* @hide
*/
- @SystemApi
public static final String CARRIER_APPS_HANDLED = "carrier_apps_handled";
/**
diff --git a/core/java/android/service/autofill/InlineSuggestionRenderService.java b/core/java/android/service/autofill/InlineSuggestionRenderService.java
index b6cc62dc213e..7fbc309d308c 100644
--- a/core/java/android/service/autofill/InlineSuggestionRenderService.java
+++ b/core/java/android/service/autofill/InlineSuggestionRenderService.java
@@ -94,9 +94,12 @@ public abstract class InlineSuggestionRenderService extends Service {
final SurfaceControlViewHost host = new SurfaceControlViewHost(this, getDisplay(),
hostInputToken);
- host.addView(suggestionRoot, lp);
+ host.setView(suggestionRoot, lp);
suggestionRoot.setOnClickListener((v) -> {
try {
+ if (suggestionView.hasOnClickListeners()) {
+ suggestionView.callOnClick();
+ }
callback.onClick();
} catch (RemoteException e) {
Log.w(TAG, "RemoteException calling onClick()");
@@ -105,6 +108,9 @@ public abstract class InlineSuggestionRenderService extends Service {
suggestionRoot.setOnLongClickListener((v) -> {
try {
+ if (suggestionView.hasOnLongClickListeners()) {
+ suggestionView.performLongClick();
+ }
callback.onLongClick();
} catch (RemoteException e) {
Log.w(TAG, "RemoteException calling onLongClick()");
diff --git a/core/java/android/service/autofill/InlineSuggestionRoot.java b/core/java/android/service/autofill/InlineSuggestionRoot.java
index bdcc2531e485..6c9d36b329ff 100644
--- a/core/java/android/service/autofill/InlineSuggestionRoot.java
+++ b/core/java/android/service/autofill/InlineSuggestionRoot.java
@@ -52,6 +52,11 @@ public class InlineSuggestionRoot extends FrameLayout {
}
@Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ return true;
+ }
+
+ @Override
@SuppressLint("ClickableViewAccessibility")
public boolean onTouchEvent(@NonNull MotionEvent event) {
switch (event.getActionMasked()) {
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 0cd96b8ea688..c52b02bb6a3c 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -55,7 +55,6 @@ import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Log;
-import android.util.Slog;
import android.widget.RemoteViews;
import com.android.internal.annotations.GuardedBy;
@@ -1570,6 +1569,7 @@ public abstract class NotificationListenerService extends Service {
private boolean mVisuallyInterruptive;
private boolean mIsConversation;
private ShortcutInfo mShortcutInfo;
+ private boolean mIsBubble;
private static final int PARCEL_VERSION = 2;
@@ -1604,6 +1604,7 @@ public abstract class NotificationListenerService extends Service {
out.writeBoolean(mVisuallyInterruptive);
out.writeBoolean(mIsConversation);
out.writeParcelable(mShortcutInfo, flags);
+ out.writeBoolean(mIsBubble);
}
/** @hide */
@@ -1639,6 +1640,7 @@ public abstract class NotificationListenerService extends Service {
mVisuallyInterruptive = in.readBoolean();
mIsConversation = in.readBoolean();
mShortcutInfo = in.readParcelable(cl);
+ mIsBubble = in.readBoolean();
}
@@ -1844,6 +1846,14 @@ public abstract class NotificationListenerService extends Service {
}
/**
+ * Returns whether this notification is actively a bubble.
+ * @hide
+ */
+ public boolean isBubble() {
+ return mIsBubble;
+ }
+
+ /**
* @hide
*/
public @Nullable ShortcutInfo getShortcutInfo() {
@@ -1862,7 +1872,8 @@ public abstract class NotificationListenerService extends Service {
int userSentiment, boolean hidden, long lastAudiblyAlertedMs,
boolean noisy, ArrayList<Notification.Action> smartActions,
ArrayList<CharSequence> smartReplies, boolean canBubble,
- boolean visuallyInterruptive, boolean isConversation, ShortcutInfo shortcutInfo) {
+ boolean visuallyInterruptive, boolean isConversation, ShortcutInfo shortcutInfo,
+ boolean isBubble) {
mKey = key;
mRank = rank;
mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW;
@@ -1886,6 +1897,7 @@ public abstract class NotificationListenerService extends Service {
mVisuallyInterruptive = visuallyInterruptive;
mIsConversation = isConversation;
mShortcutInfo = shortcutInfo;
+ mIsBubble = isBubble;
}
/**
@@ -1913,7 +1925,8 @@ public abstract class NotificationListenerService extends Service {
other.mCanBubble,
other.mVisuallyInterruptive,
other.mIsConversation,
- other.mShortcutInfo);
+ other.mShortcutInfo,
+ other.mIsBubble);
}
/**
@@ -1970,7 +1983,8 @@ public abstract class NotificationListenerService extends Service {
&& Objects.equals(mIsConversation, other.mIsConversation)
// Shortcutinfo doesn't have equals either; use id
&& Objects.equals((mShortcutInfo == null ? 0 : mShortcutInfo.getId()),
- (other.mShortcutInfo == null ? 0 : other.mShortcutInfo.getId()));
+ (other.mShortcutInfo == null ? 0 : other.mShortcutInfo.getId()))
+ && Objects.equals(mIsBubble, other.mIsBubble);
}
}
diff --git a/core/java/android/service/quickaccesswallet/GetWalletCardsCallbackImpl.java b/core/java/android/service/quickaccesswallet/GetWalletCardsCallbackImpl.java
index d2494a544d7a..ae67068d9f32 100644
--- a/core/java/android/service/quickaccesswallet/GetWalletCardsCallbackImpl.java
+++ b/core/java/android/service/quickaccesswallet/GetWalletCardsCallbackImpl.java
@@ -24,8 +24,6 @@ import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;
-import java.util.List;
-
/**
* Handles response from the {@link QuickAccessWalletService} for {@link GetWalletCardsRequest}
*
@@ -56,7 +54,6 @@ final class GetWalletCardsCallbackImpl implements GetWalletCardsCallback {
* presented as the selected card.
*/
public void onSuccess(@NonNull GetWalletCardsResponse response) {
- Log.i(TAG, "onSuccess");
if (isValidResponse(response)) {
mHandler.post(() -> onSuccessInternal(response));
} else {
@@ -78,7 +75,6 @@ final class GetWalletCardsCallbackImpl implements GetWalletCardsCallback {
}
private void onSuccessInternal(GetWalletCardsResponse response) {
- Log.i(TAG, "onSuccessInternal");
if (mCalled) {
Log.w(TAG, "already called");
return;
@@ -86,7 +82,6 @@ final class GetWalletCardsCallbackImpl implements GetWalletCardsCallback {
mCalled = true;
try {
mCallback.onGetWalletCardsSuccess(response);
- Log.i(TAG, "onSuccessInternal: returned response");
} catch (RemoteException e) {
Log.w(TAG, "Error returning wallet cards", e);
}
@@ -106,29 +101,53 @@ final class GetWalletCardsCallbackImpl implements GetWalletCardsCallback {
}
private boolean isValidResponse(@NonNull GetWalletCardsResponse response) {
- return response != null
- && response.getWalletCards() != null
- && response.getSelectedIndex() >= 0
- && (response.getWalletCards().isEmpty() // selectedIndex may be 0 when list is empty
- || response.getSelectedIndex() < response.getWalletCards().size())
- && response.getWalletCards().size() < mRequest.getMaxCards()
- && areValidCards(response.getWalletCards());
- }
-
- private boolean areValidCards(List<WalletCard> walletCards) {
- for (WalletCard walletCard : walletCards) {
- if (walletCard == null
- || walletCard.getCardId() == null
- || walletCard.getCardImage() == null
- || TextUtils.isEmpty(walletCard.getContentDescription())
- || walletCard.getPendingIntent() == null) {
+ if (response == null) {
+ Log.w(TAG, "Invalid response: response is null");
+ return false;
+ }
+ if (response.getWalletCards() == null) {
+ Log.w(TAG, "Invalid response: walletCards is null");
+ return false;
+ }
+ if (response.getSelectedIndex() < 0) {
+ Log.w(TAG, "Invalid response: selectedIndex is negative");
+ return false;
+ }
+ if (!response.getWalletCards().isEmpty()
+ && response.getSelectedIndex() >= response.getWalletCards().size()) {
+ Log.w(TAG, "Invalid response: selectedIndex out of bounds");
+ return false;
+ }
+ if (response.getWalletCards().size() > mRequest.getMaxCards()) {
+ Log.w(TAG, "Invalid response: too many cards");
+ return false;
+ }
+ for (WalletCard walletCard : response.getWalletCards()) {
+ if (walletCard == null) {
+ Log.w(TAG, "Invalid response: card is null");
+ return false;
+ }
+ if (walletCard.getCardId() == null) {
+ Log.w(TAG, "Invalid response: cardId is null");
return false;
}
Icon cardImage = walletCard.getCardImage();
+ if (cardImage == null) {
+ Log.w(TAG, "Invalid response: cardImage is null");
+ return false;
+ }
if (cardImage.getType() == Icon.TYPE_BITMAP
- && walletCard.getCardImage().getBitmap().getConfig()
- != Bitmap.Config.HARDWARE) {
- Log.w(TAG, "WalletCard bitmaps should be hardware bitmaps");
+ && cardImage.getBitmap().getConfig() != Bitmap.Config.HARDWARE) {
+ Log.w(TAG, "Invalid response: cardImage bitmaps must be hardware bitmaps");
+ return false;
+ }
+ if (TextUtils.isEmpty(walletCard.getContentDescription())) {
+ Log.w(TAG, "Invalid response: contentDescription is null");
+ return false;
+ }
+ if (walletCard.getPendingIntent() == null) {
+ Log.w(TAG, "Invalid response: pendingIntent is null");
+ return false;
}
}
return true;
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java
index be9ab11fb3a2..4f5b13981d64 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java
@@ -16,19 +16,23 @@
package android.service.quickaccesswallet;
+import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.content.Context;
import android.content.Intent;
+import java.io.Closeable;
+import java.util.concurrent.Executor;
+
/**
* Facilitates accessing cards from the {@link QuickAccessWalletService}.
*
* @hide
*/
@TestApi
-public interface QuickAccessWalletClient {
+public interface QuickAccessWalletClient extends Closeable {
/**
* Create a client for accessing wallet cards from the {@link QuickAccessWalletService}. If the
@@ -92,6 +96,14 @@ public interface QuickAccessWalletClient {
@NonNull OnWalletCardsRetrievedCallback callback);
/**
+ * Get wallet cards from the {@link QuickAccessWalletService}.
+ */
+ void getWalletCards(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull GetWalletCardsRequest request,
+ @NonNull OnWalletCardsRetrievedCallback callback);
+
+ /**
* Callback for getWalletCards
*/
interface OnWalletCardsRetrievedCallback {
@@ -111,12 +123,19 @@ public interface QuickAccessWalletClient {
void notifyWalletDismissed();
/**
- * Unregister event listener.
+ * Register an event listener.
*/
void addWalletServiceEventListener(@NonNull WalletServiceEventListener listener);
/**
- * Unregister event listener
+ * Register an event listener.
+ */
+ void addWalletServiceEventListener(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull WalletServiceEventListener listener);
+
+ /**
+ * Unregister an event listener
*/
void removeWalletServiceEventListener(@NonNull WalletServiceEventListener listener);
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
index 37a8703ddebe..e4dd082edaa5 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
@@ -18,6 +18,7 @@ package android.service.quickaccesswallet;
import static android.service.quickaccesswallet.QuickAccessWalletService.SERVICE_INTERFACE;
+import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -36,16 +37,19 @@ import android.util.Log;
import com.android.internal.widget.LockPatternUtils;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.UUID;
+import java.util.concurrent.Executor;
/**
* Implements {@link QuickAccessWalletClient}. The client connects, performs requests, waits for
- * responses, and disconnects automatically after a short period of time. The client may
+ * responses, and disconnects automatically one minute after the last call is performed.
+ *
* @hide
*/
public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, ServiceConnection {
@@ -78,15 +82,14 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
@Override
public boolean isWalletServiceAvailable() {
- boolean available = mServiceInfo != null;
- Log.i(TAG, "isWalletServiceAvailable: " + available);
- return available;
+ return mServiceInfo != null;
}
@Override
public boolean isWalletFeatureAvailable() {
int currentUser = ActivityManager.getCurrentUser();
- return checkUserSetupComplete()
+ return currentUser == UserHandle.USER_SYSTEM
+ && checkUserSetupComplete()
&& checkSecureSetting(Settings.Secure.GLOBAL_ACTIONS_PANEL_ENABLED)
&& !new LockPatternUtils(mContext).isUserInLockdown(currentUser);
}
@@ -101,23 +104,29 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
public void getWalletCards(
@NonNull GetWalletCardsRequest request,
@NonNull OnWalletCardsRetrievedCallback callback) {
+ getWalletCards(mContext.getMainExecutor(), request, callback);
+ }
- Log.i(TAG, "getWalletCards");
-
+ @Override
+ public void getWalletCards(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull GetWalletCardsRequest request,
+ @NonNull OnWalletCardsRetrievedCallback callback) {
if (!isWalletServiceAvailable()) {
- callback.onWalletCardRetrievalError(new GetWalletCardsError(null, null));
+ executor.execute(
+ () -> callback.onWalletCardRetrievalError(new GetWalletCardsError(null, null)));
return;
}
BaseCallbacks serviceCallback = new BaseCallbacks() {
@Override
public void onGetWalletCardsSuccess(GetWalletCardsResponse response) {
- mHandler.post(() -> callback.onWalletCardsRetrieved(response));
+ executor.execute(() -> callback.onWalletCardsRetrieved(response));
}
@Override
public void onGetWalletCardsFailure(GetWalletCardsError error) {
- mHandler.post(() -> callback.onWalletCardRetrievalError(error));
+ executor.execute(() -> callback.onWalletCardRetrievalError(error));
}
};
@@ -132,11 +141,11 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
serviceCallback.onGetWalletCardsFailure(new GetWalletCardsError(null, null));
}
});
+
}
@Override
public void selectWalletCard(@NonNull SelectWalletCardRequest request) {
- Log.i(TAG, "selectWalletCard");
if (!isWalletServiceAvailable()) {
return;
}
@@ -153,7 +162,6 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
if (!isWalletServiceAvailable()) {
return;
}
- Log.i(TAG, "notifyWalletDismissed");
executeApiCall(new ApiCaller("onWalletDismissed") {
@Override
public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
@@ -164,15 +172,20 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
@Override
public void addWalletServiceEventListener(WalletServiceEventListener listener) {
+ addWalletServiceEventListener(mContext.getMainExecutor(), listener);
+ }
+
+ @Override
+ public void addWalletServiceEventListener(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull WalletServiceEventListener listener) {
if (!isWalletServiceAvailable()) {
return;
}
- Log.i(TAG, "registerWalletServiceEventListener");
BaseCallbacks callback = new BaseCallbacks() {
@Override
public void onWalletServiceEvent(WalletServiceEvent event) {
- Log.i(TAG, "onWalletServiceEvent");
- mHandler.post(() -> listener.onWalletServiceEvent(event));
+ executor.execute(() -> listener.onWalletServiceEvent(event));
}
};
@@ -193,7 +206,6 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
if (!isWalletServiceAvailable()) {
return;
}
- Log.i(TAG, "unregisterWalletServiceEventListener");
executeApiCall(new ApiCaller("unregisterListener") {
@Override
public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
@@ -209,8 +221,12 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
}
@Override
+ public void close() throws IOException {
+ disconnect();
+ }
+
+ @Override
public void disconnect() {
- Log.i(TAG, "disconnect");
mHandler.post(() -> disconnectInternal(true));
}
@@ -241,18 +257,15 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
}
private void connect() {
- Log.i(TAG, "connect");
mHandler.post(this::connectInternal);
}
private void connectInternal() {
- Log.i(TAG, "connectInternal");
if (mServiceInfo == null) {
Log.w(TAG, "Wallet service unavailable");
return;
}
if (mIsConnected) {
- Log.w(TAG, "already connected");
return;
}
mIsConnected = true;
@@ -264,23 +277,14 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
}
private void onConnectedInternal(IQuickAccessWalletService service) {
- Log.i(TAG, "onConnectedInternal");
if (!mIsConnected) {
Log.w(TAG, "onConnectInternal but connection closed");
mService = null;
return;
}
mService = service;
- Log.i(TAG, "onConnectedInternal success: request queue size " + mRequestQueue.size());
for (ApiCaller apiCaller : new ArrayList<>(mRequestQueue)) {
- try {
- apiCaller.performApiCall(mService);
- } catch (RemoteException e) {
- Log.e(TAG, "onConnectedInternal error", e);
- apiCaller.onApiError();
- disconnect();
- break;
- }
+ performApiCallInternal(apiCaller, mService);
mRequestQueue.remove(apiCaller);
}
}
@@ -290,7 +294,6 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
* posting a new delayed message.
*/
private void resetServiceConnectionTimeout() {
- Log.i(TAG, "resetServiceConnectionTimeout");
mHandler.removeMessages(MSG_TIMEOUT_SERVICE);
mHandler.postDelayed(
() -> disconnectInternal(true),
@@ -299,13 +302,11 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
}
private void disconnectInternal(boolean clearEventListeners) {
- Log.i(TAG, "disconnectInternal: " + clearEventListeners);
if (!mIsConnected) {
Log.w(TAG, "already disconnected");
return;
}
if (clearEventListeners && !mEventListeners.isEmpty()) {
- Log.i(TAG, "disconnectInternal: clear event listeners");
for (WalletServiceEventListener listener : mEventListeners.keySet()) {
removeWalletServiceEventListener(listener);
}
@@ -320,29 +321,33 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
}
private void executeApiCall(ApiCaller apiCaller) {
- Log.i(TAG, "execute: " + apiCaller.mDesc);
mHandler.post(() -> executeInternal(apiCaller));
}
- private void executeInternal(ApiCaller apiCall) {
- Log.i(TAG, "executeInternal: " + apiCall.mDesc);
+ private void executeInternal(ApiCaller apiCaller) {
if (mIsConnected && mService != null) {
- try {
- apiCall.performApiCall(mService);
- Log.i(TAG, "executeInternal success: " + apiCall.mDesc);
- resetServiceConnectionTimeout();
- } catch (RemoteException e) {
- Log.w(TAG, "executeInternal error: " + apiCall.mDesc, e);
- apiCall.onApiError();
- disconnect();
- }
+ performApiCallInternal(apiCaller, mService);
} else {
- Log.i(TAG, "executeInternal: queued" + apiCall.mDesc);
- mRequestQueue.add(apiCall);
+ mRequestQueue.add(apiCaller);
connect();
}
}
+ private void performApiCallInternal(ApiCaller apiCaller, IQuickAccessWalletService service) {
+ if (service == null) {
+ apiCaller.onApiError();
+ return;
+ }
+ try {
+ apiCaller.performApiCall(service);
+ resetServiceConnectionTimeout();
+ } catch (RemoteException e) {
+ Log.w(TAG, "executeInternal error: " + apiCaller.mDesc, e);
+ apiCaller.onApiError();
+ disconnect();
+ }
+ }
+
private abstract static class ApiCaller {
private final String mDesc;
@@ -350,7 +355,8 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
this.mDesc = desc;
}
- abstract void performApiCall(IQuickAccessWalletService service) throws RemoteException;
+ abstract void performApiCall(IQuickAccessWalletService service)
+ throws RemoteException;
void onApiError() {
Log.w(TAG, "api error: " + mDesc);
@@ -359,7 +365,6 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
@Override // ServiceConnection
public void onServiceConnected(ComponentName name, IBinder binder) {
- Log.i(TAG, "onServiceConnected: " + name);
IQuickAccessWalletService service = IQuickAccessWalletService.Stub.asInterface(binder);
mHandler.post(() -> onConnectedInternal(service));
}
@@ -367,19 +372,16 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
@Override // ServiceConnection
public void onServiceDisconnected(ComponentName name) {
// Do not disconnect, as we may later be re-connected
- Log.w(TAG, "onServiceDisconnected");
}
@Override // ServiceConnection
public void onBindingDied(ComponentName name) {
// This is a recoverable error but the client will need to reconnect.
- Log.w(TAG, "onBindingDied");
disconnect();
}
@Override // ServiceConnection
public void onNullBinding(ComponentName name) {
- Log.w(TAG, "onNullBinding");
disconnect();
}
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java
index 23173a820a2e..31e51bb945d6 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java
@@ -66,13 +66,11 @@ class QuickAccessWalletServiceInfo {
static QuickAccessWalletServiceInfo tryCreate(@NonNull Context context) {
ComponentName defaultPaymentApp = getDefaultPaymentApp(context);
if (defaultPaymentApp == null) {
- Log.d(TAG, "create: default payment app not set");
return null;
}
ServiceInfo serviceInfo = getWalletServiceInfo(context, defaultPaymentApp.getPackageName());
if (serviceInfo == null) {
- Log.d(TAG, "create: unable to resolve service intent");
return null;
}
diff --git a/core/java/android/service/quickaccesswallet/WalletCard.java b/core/java/android/service/quickaccesswallet/WalletCard.java
index e6ae0abef918..b09d2e9e7f13 100644
--- a/core/java/android/service/quickaccesswallet/WalletCard.java
+++ b/core/java/android/service/quickaccesswallet/WalletCard.java
@@ -180,7 +180,7 @@ public final class WalletCard implements Parcelable {
* GetWalletCardsRequest#getCardWidthPx()} and a height of {@link
* GetWalletCardsRequest#getCardHeightPx()}. If the card image
* does not have these dimensions, it may appear distorted when it
- * is scaled to fit these dimensions on screen. Bitmaps should be
+ * is scaled to fit these dimensions on screen. Bitmaps must be
* of type {@link android.graphics.Bitmap.Config#HARDWARE} for
* performance reasons.
* @param contentDescription The content description of the card image. This field is
diff --git a/core/java/android/service/textclassifier/TextClassifierService.java b/core/java/android/service/textclassifier/TextClassifierService.java
index 3ff6f549e337..9dfbc285c381 100644
--- a/core/java/android/service/textclassifier/TextClassifierService.java
+++ b/core/java/android/service/textclassifier/TextClassifierService.java
@@ -41,7 +41,6 @@ import android.util.Slog;
import android.view.textclassifier.ConversationActions;
import android.view.textclassifier.SelectionEvent;
import android.view.textclassifier.TextClassification;
-import android.view.textclassifier.TextClassificationConstants;
import android.view.textclassifier.TextClassificationContext;
import android.view.textclassifier.TextClassificationManager;
import android.view.textclassifier.TextClassificationSessionId;
@@ -405,27 +404,19 @@ public abstract class TextClassifierService extends Service {
*/
@NonNull
public static TextClassifier getDefaultTextClassifierImplementation(@NonNull Context context) {
- final TextClassificationManager tcm =
- context.getSystemService(TextClassificationManager.class);
- if (tcm == null) {
+ final String defaultTextClassifierPackageName =
+ context.getPackageManager().getDefaultTextClassifierPackageName();
+ if (TextUtils.isEmpty(defaultTextClassifierPackageName)) {
return TextClassifier.NO_OP;
}
- TextClassificationConstants settings = new TextClassificationConstants();
- if (settings.getUseDefaultTextClassifierAsDefaultImplementation()) {
- final String defaultTextClassifierPackageName =
- context.getPackageManager().getDefaultTextClassifierPackageName();
- if (TextUtils.isEmpty(defaultTextClassifierPackageName)) {
- return TextClassifier.NO_OP;
- }
- if (defaultTextClassifierPackageName.equals(context.getPackageName())) {
- throw new RuntimeException(
- "The default text classifier itself should not call the"
- + "getDefaultTextClassifierImplementation() method.");
- }
- return tcm.getTextClassifier(TextClassifier.DEFAULT_SERVICE);
- } else {
- return tcm.getTextClassifier(TextClassifier.LOCAL);
+ if (defaultTextClassifierPackageName.equals(context.getPackageName())) {
+ throw new RuntimeException(
+ "The default text classifier itself should not call the"
+ + "getDefaultTextClassifierImplementation() method.");
}
+ final TextClassificationManager tcm =
+ context.getSystemService(TextClassificationManager.class);
+ return tcm.getTextClassifier(TextClassifier.DEFAULT_SYSTEM);
}
/** @hide **/
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index d2735008611c..28db2708988e 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -176,7 +176,6 @@ public class PhoneStateListener {
* @hide
*/
@RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
- @SystemApi
public static final int LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH = 0x00000200;
/**
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 6b87a10643fd..cfbe393bfcc8 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -71,7 +71,7 @@ public class FeatureFlagUtils {
// introduced in R and will be removed in the next release (b/148367230).
DEFAULT_FLAGS.put(SETTINGS_DO_NOT_RESTORE_PRESERVED, "false");
- DEFAULT_FLAGS.put("settings_tether_all_in_one", "true");
+ DEFAULT_FLAGS.put("settings_tether_all_in_one", "false");
DEFAULT_FLAGS.put(SETTINGS_SCHEDULES_FLAG, "false");
DEFAULT_FLAGS.put("settings_contextual_home2", "false");
}
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
index abd04cc2b0e7..2437af26770b 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
@@ -16,6 +16,8 @@
package android.util.apk;
+import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256;
+import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512;
import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256;
import static android.util.apk.ApkSigningBlockUtils.compareSignatureAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.getContentDigestAlgorithmJcaDigestAlgorithm;
@@ -211,9 +213,24 @@ public class ApkSignatureSchemeV3Verifier {
verityDigest, apk.length(), signatureInfo);
}
+ result.digest = pickBestV3DigestForV4(contentDigests);
+
return result;
}
+ // Keep in sync with pickBestV3DigestForV4 in apksigner.V3SchemeVerifier.
+ private static byte[] pickBestV3DigestForV4(Map<Integer, byte[]> contentDigests) {
+ final int[] orderedContentDigestTypes =
+ {CONTENT_DIGEST_CHUNKED_SHA512, CONTENT_DIGEST_VERITY_CHUNKED_SHA256,
+ CONTENT_DIGEST_CHUNKED_SHA256};
+ for (int contentDigestType : orderedContentDigestTypes) {
+ if (contentDigests.containsKey(contentDigestType)) {
+ return contentDigests.get(contentDigestType);
+ }
+ }
+ return null;
+ }
+
private static VerifiedSigner verifySigner(
ByteBuffer signerBlock,
Map<Integer, byte[]> contentDigests,
@@ -568,6 +585,7 @@ public class ApkSignatureSchemeV3Verifier {
public final VerifiedProofOfRotation por;
public byte[] verityRootHash;
+ public byte[] digest;
public VerifiedSigner(X509Certificate[] certs, VerifiedProofOfRotation por) {
this.certs = certs;
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
index b6b8089b1743..8c240d99f590 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
@@ -16,13 +16,32 @@
package android.util.apk;
+import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm;
+import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm;
+import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm;
+
import android.os.incremental.IncrementalManager;
+import android.os.incremental.V4Signature;
+import android.util.Pair;
+import java.io.ByteArrayInputStream;
import java.io.File;
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
import java.security.cert.Certificate;
-
-import sun.security.pkcs.PKCS7;
-import sun.security.pkcs.ParsingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Arrays;
/**
* APK Signature Scheme v4 verifier.
@@ -30,24 +49,118 @@ import sun.security.pkcs.ParsingException;
* @hide for internal use only.
*/
public class ApkSignatureSchemeV4Verifier {
-
/**
- * Extracts APK Signature Scheme v4 signatures of the provided APK and returns the certificates
- * associated with each signer.
+ * Extracts and verifies APK Signature Scheme v4 signatures of the provided APK and returns the
+ * certificates associated with each signer.
*/
- public static Certificate[] extractCertificates(String apkFile)
+ public static VerifiedSigner extractCertificates(String apkFile)
throws SignatureNotFoundException, SecurityException {
- final byte[] rawSignature = IncrementalManager.unsafeGetFileSignature(
- new File(apkFile).getAbsolutePath());
- if (rawSignature == null || rawSignature.length == 0) {
- throw new SignatureNotFoundException("Failed to obtain raw signature from IncFS.");
+ final File apk = new File(apkFile);
+ final byte[] signatureBytes = IncrementalManager.unsafeGetFileSignature(
+ apk.getAbsolutePath());
+ if (signatureBytes == null || signatureBytes.length == 0) {
+ throw new SignatureNotFoundException("Failed to obtain signature bytes from IncFS.");
+ }
+
+ final V4Signature signature;
+ final V4Signature.HashingInfo hashingInfo;
+ final V4Signature.SigningInfo signingInfo;
+ try {
+ signature = V4Signature.readFrom(signatureBytes);
+
+ if (!signature.isVersionSupported()) {
+ throw new SecurityException(
+ "v4 signature version " + signature.version + " is not supported");
+ }
+
+ hashingInfo = V4Signature.HashingInfo.fromByteArray(signature.hashingInfo);
+ signingInfo = V4Signature.SigningInfo.fromByteArray(signature.signingInfo);
+ } catch (IOException e) {
+ throw new SignatureNotFoundException("Failed to read V4 signature.", e);
}
+ final byte[] signedData = V4Signature.getSigningData(apk.length(), hashingInfo,
+ signingInfo);
+
+ return verifySigner(signingInfo, signedData);
+ }
+
+ private static VerifiedSigner verifySigner(V4Signature.SigningInfo signingInfo,
+ final byte[] signedData) throws SecurityException {
+ if (!isSupportedSignatureAlgorithm(signingInfo.signatureAlgorithmId)) {
+ throw new SecurityException("No supported signatures found");
+ }
+
+ final int signatureAlgorithmId = signingInfo.signatureAlgorithmId;
+ final byte[] signatureBytes = signingInfo.signature;
+ final byte[] publicKeyBytes = signingInfo.publicKey;
+ final byte[] encodedCert = signingInfo.certificate;
+
+ String keyAlgorithm = getSignatureAlgorithmJcaKeyAlgorithm(signatureAlgorithmId);
+ Pair<String, ? extends AlgorithmParameterSpec> signatureAlgorithmParams =
+ getSignatureAlgorithmJcaSignatureAlgorithm(signatureAlgorithmId);
+ String jcaSignatureAlgorithm = signatureAlgorithmParams.first;
+ AlgorithmParameterSpec jcaSignatureAlgorithmParams = signatureAlgorithmParams.second;
+ boolean sigVerified;
try {
- PKCS7 pkcs7 = new PKCS7(rawSignature);
- return pkcs7.getCertificates();
- } catch (ParsingException e) {
- throw new SecurityException("Failed to parse signature and extract certificates", e);
+ PublicKey publicKey =
+ KeyFactory.getInstance(keyAlgorithm)
+ .generatePublic(new X509EncodedKeySpec(publicKeyBytes));
+ Signature sig = Signature.getInstance(jcaSignatureAlgorithm);
+ sig.initVerify(publicKey);
+ if (jcaSignatureAlgorithmParams != null) {
+ sig.setParameter(jcaSignatureAlgorithmParams);
+ }
+ sig.update(signedData);
+ sigVerified = sig.verify(signatureBytes);
+ } catch (NoSuchAlgorithmException | InvalidKeySpecException | InvalidKeyException
+ | InvalidAlgorithmParameterException | SignatureException e) {
+ throw new SecurityException(
+ "Failed to verify " + jcaSignatureAlgorithm + " signature", e);
}
+ if (!sigVerified) {
+ throw new SecurityException(jcaSignatureAlgorithm + " signature did not verify");
+ }
+
+ // Signature over signedData has verified.
+ CertificateFactory certFactory;
+ try {
+ certFactory = CertificateFactory.getInstance("X.509");
+ } catch (CertificateException e) {
+ throw new RuntimeException("Failed to obtain X.509 CertificateFactory", e);
+ }
+
+ X509Certificate certificate;
+ try {
+ certificate = (X509Certificate)
+ certFactory.generateCertificate(new ByteArrayInputStream(encodedCert));
+ } catch (CertificateException e) {
+ throw new SecurityException("Failed to decode certificate", e);
+ }
+ certificate = new VerbatimX509Certificate(certificate, encodedCert);
+
+ byte[] certificatePublicKeyBytes = certificate.getPublicKey().getEncoded();
+ if (!Arrays.equals(publicKeyBytes, certificatePublicKeyBytes)) {
+ throw new SecurityException(
+ "Public key mismatch between certificate and signature record");
+ }
+
+ return new VerifiedSigner(new Certificate[]{certificate}, signingInfo.v3Digest);
+ }
+
+ /**
+ * Verified APK Signature Scheme v4 signer, including V3 digest.
+ *
+ * @hide for internal use only.
+ */
+ public static class VerifiedSigner {
+ public final Certificate[] certs;
+ public byte[] v3Digest;
+
+ public VerifiedSigner(Certificate[] certs, byte[] v3Digest) {
+ this.certs = certs;
+ this.v3Digest = v3Digest;
+ }
+
}
}
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index f325c2171c23..c1cee48cc663 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -168,7 +168,7 @@ public class ApkSignatureVerifier {
/**
* Verifies the provided APK using V4 schema.
*
- * @param verifyFull whether to verify all contents of this APK or just collect certificates.
+ * @param verifyFull whether to verify (V4 vs V3) or just collect certificates.
* @return the certificates associated with each signer.
* @throws SignatureNotFoundException if there are no V4 signatures in the APK
* @throws PackageParserException if there was a problem collecting certificates
@@ -178,30 +178,34 @@ public class ApkSignatureVerifier {
throws SignatureNotFoundException, PackageParserException {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, verifyFull ? "verifyV4" : "certsOnlyV4");
try {
- Certificate[] certs = ApkSignatureSchemeV4Verifier.extractCertificates(apkPath);
- Certificate[][] signerCerts = new Certificate[][]{certs};
+ ApkSignatureSchemeV4Verifier.VerifiedSigner vSigner =
+ ApkSignatureSchemeV4Verifier.extractCertificates(apkPath);
+ Certificate[][] signerCerts = new Certificate[][]{vSigner.certs};
Signature[] signerSigs = convertToSignatures(signerCerts);
if (verifyFull) {
- // v4 is an add-on and requires v2/v3 signature to validate against its certificates
- final PackageParser.SigningDetails nonstreaming = verifyV3AndBelowSignatures(
- apkPath, minSignatureSchemeVersion, false);
- if (nonstreaming.signatureSchemeVersion <= SignatureSchemeVersion.JAR) {
+ // v4 is an add-on and requires v3 signature to validate against its certificates
+ ApkSignatureSchemeV3Verifier.VerifiedSigner nonstreaming =
+ ApkSignatureSchemeV3Verifier.unsafeGetCertsWithoutVerification(apkPath);
+ Certificate[][] nonstreamingCerts = new Certificate[][]{nonstreaming.certs};
+ Signature[] nonstreamingSigs = convertToSignatures(nonstreamingCerts);
+
+ if (nonstreamingSigs.length != signerSigs.length) {
throw new SecurityException(
- "V4 signing block can only be verified along with V2 and above.");
- }
- if (nonstreaming.signatures.length == 0
- || nonstreaming.signatures.length != signerSigs.length) {
- throw new SecurityException("Invalid number of signatures in "
- + nonstreaming.signatureSchemeVersion);
+ "Invalid number of certificates: " + nonstreaming.certs.length);
}
for (int i = 0, size = signerSigs.length; i < size; ++i) {
- if (!nonstreaming.signatures[i].equals(signerSigs[i])) {
- throw new SecurityException("V4 signature certificate does not match "
- + nonstreaming.signatureSchemeVersion);
+ if (!nonstreamingSigs[i].equals(signerSigs[i])) {
+ throw new SecurityException("V4 signature certificate does not match V3");
}
}
+
+ // TODO(b/151240006): add support for v2 digest and make it mandatory.
+ if (!ArrayUtils.isEmpty(vSigner.v3Digest) && !ArrayUtils.equals(vSigner.v3Digest,
+ nonstreaming.digest, vSigner.v3Digest.length)) {
+ throw new SecurityException("V3 digest in V4 signature does not match V3");
+ }
}
return new PackageParser.SigningDetails(signerSigs,
diff --git a/core/java/android/util/apk/SourceStampVerifier.java b/core/java/android/util/apk/SourceStampVerifier.java
index 70e4a51bc07a..a7ae32d1baa2 100644
--- a/core/java/android/util/apk/SourceStampVerifier.java
+++ b/core/java/android/util/apk/SourceStampVerifier.java
@@ -24,6 +24,7 @@ import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorith
import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray;
import android.util.Pair;
+import android.util.Slog;
import android.util.jar.StrictJarFile;
import libcore.io.IoUtils;
@@ -43,6 +44,7 @@ import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
+import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
@@ -68,6 +70,8 @@ import java.util.zip.ZipEntry;
*/
public abstract class SourceStampVerifier {
+ private static final String TAG = "SourceStampVerifier";
+
private static final int APK_SIGNATURE_SCHEME_V2_BLOCK_ID = 0x7109871a;
private static final int APK_SIGNATURE_SCHEME_V3_BLOCK_ID = 0xf05368c0;
private static final int SOURCE_STAMP_BLOCK_ID = 0x2b09189e;
@@ -78,30 +82,51 @@ public abstract class SourceStampVerifier {
/** Hidden constructor to prevent instantiation. */
private SourceStampVerifier() {}
- /** Verifies SourceStamp present in the provided APK. */
- public static SourceStampVerificationResult verify(String apkFile) {
- try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) {
- return verify(apk);
- } catch (IOException e) {
- // Any exception in reading the APK returns a non-present SourceStamp outcome
- // without affecting the outcome of any of the other signature schemes.
- return SourceStampVerificationResult.notPresent();
+ /** Verifies SourceStamp present in a list of APKs. */
+ public static SourceStampVerificationResult verify(List<String> apkFiles) {
+ Certificate stampCertificate = null;
+ for (String apkFile : apkFiles) {
+ SourceStampVerificationResult sourceStampVerificationResult = verify(apkFile);
+ if (!sourceStampVerificationResult.isPresent()
+ || !sourceStampVerificationResult.isVerified()) {
+ return sourceStampVerificationResult;
+ }
+ if (stampCertificate != null
+ && !stampCertificate.equals(sourceStampVerificationResult.getCertificate())) {
+ return SourceStampVerificationResult.notVerified();
+ }
+ stampCertificate = sourceStampVerificationResult.getCertificate();
}
+ return SourceStampVerificationResult.verified(stampCertificate);
}
- private static SourceStampVerificationResult verify(RandomAccessFile apk) {
- byte[] sourceStampCertificateDigest;
- try {
- sourceStampCertificateDigest = getSourceStampCertificateDigest(apk);
+ /** Verifies SourceStamp present in the provided APK. */
+ public static SourceStampVerificationResult verify(String apkFile) {
+ StrictJarFile apkJar = null;
+ try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) {
+ apkJar =
+ new StrictJarFile(
+ apkFile,
+ /* verify= */ false,
+ /* signatureSchemeRollbackProtectionsEnforced= */ false);
+ byte[] sourceStampCertificateDigest = getSourceStampCertificateDigest(apkJar);
if (sourceStampCertificateDigest == null) {
// SourceStamp certificate hash file not found, which means that there is not
// SourceStamp present.
return SourceStampVerificationResult.notPresent();
}
+ return verify(apk, sourceStampCertificateDigest);
} catch (IOException e) {
+ // Any exception in reading the APK returns a non-present SourceStamp outcome
+ // without affecting the outcome of any of the other signature schemes.
return SourceStampVerificationResult.notPresent();
+ } finally {
+ closeApkJar(apkJar);
}
+ }
+ private static SourceStampVerificationResult verify(
+ RandomAccessFile apk, byte[] sourceStampCertificateDigest) {
try {
SignatureInfo signatureInfo =
ApkSigningBlockUtils.findSignature(apk, SOURCE_STAMP_BLOCK_ID);
@@ -264,22 +289,17 @@ public abstract class SourceStampVerifier {
return apkContentDigests;
}
- private static byte[] getSourceStampCertificateDigest(RandomAccessFile apk) throws IOException {
- StrictJarFile apkJar =
- new StrictJarFile(
- apk.getFD(),
- /* verify= */ false,
- /* signatureSchemeRollbackProtectionsEnforced= */ false);
- ZipEntry zipEntry = apkJar.findEntry(SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME);
- if (zipEntry == null) {
- // SourceStamp certificate hash file not found, which means that there is not
- // SourceStamp present.
- return null;
- }
+ private static byte[] getSourceStampCertificateDigest(StrictJarFile apkJar) throws IOException {
InputStream inputStream = null;
- ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try {
+ ZipEntry zipEntry = apkJar.findEntry(SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME);
+ if (zipEntry == null) {
+ // SourceStamp certificate hash file not found, which means that there is not
+ // SourceStamp present.
+ return null;
+ }
inputStream = apkJar.getInputStream(zipEntry);
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
// Trying to read the certificate digest, which should be less than 1024 bytes.
byte[] buffer = new byte[1024];
@@ -308,4 +328,15 @@ public abstract class SourceStampVerifier {
}
return result.array();
}
+
+ private static void closeApkJar(StrictJarFile apkJar) {
+ try {
+ if (apkJar == null) {
+ return;
+ }
+ apkJar.close();
+ } catch (IOException e) {
+ Slog.e(TAG, "Could not close APK jar", e);
+ }
+ }
}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 0dcb9cc5256e..dffcafe1de0e 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -1307,6 +1307,15 @@ public final class Display {
}
/**
+ * Returns true if the display is in active state such as {@link #STATE_ON}
+ * or {@link #STATE_VR}.
+ * @hide
+ */
+ public static boolean isActiveState(int state) {
+ return state == STATE_ON || state == STATE_VR;
+ }
+
+ /**
* A mode supported by a given display.
*
* @see Display#getSupportedModes()
diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java
index 74fac2b40472..6784cf7407fa 100644
--- a/core/java/android/view/ImeFocusController.java
+++ b/core/java/android/view/ImeFocusController.java
@@ -165,10 +165,16 @@ public final class ImeFocusController {
if (!getImmDelegate().isCurrentRootView(view.getViewRootImpl())) {
return;
}
- if (mServedView == view || !view.hasImeFocus() || !view.hasWindowFocus()) {
+ if (!view.hasImeFocus() || !view.hasWindowFocus()) {
return;
}
- mNextServedView = hasFocus ? view : null;
+ if (DEBUG) Log.d(TAG, "onViewFocusChanged, view=" + view + ", mServedView=" + mServedView);
+
+ if (hasFocus) {
+ mNextServedView = view;
+ } else if (view == mServedView) {
+ mNextServedView = null;
+ }
mViewRootImpl.dispatchCheckFocus();
}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 88e7f2ed9cf1..123b9db75804 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -53,7 +53,6 @@ import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import java.io.PrintWriter;
@@ -518,14 +517,13 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
PendingControlRequest pendingRequest = mPendingImeControlRequest;
mPendingImeControlRequest = null;
mHandler.removeCallbacks(mPendingControlTimeout);
- CancellationSignal cancellationSignal = controlAnimationUnchecked(
- pendingRequest.types,
+ controlAnimationUnchecked(
+ pendingRequest.types, pendingRequest.cancellationSignal,
pendingRequest.listener, mFrame,
true /* fromIme */, pendingRequest.durationMs, pendingRequest.interpolator,
false /* fade */, pendingRequest.animationType,
pendingRequest.layoutInsetsDuringAnimation,
pendingRequest.useInsetsAnimationThread);
- pendingRequest.cancellationSignal.setOnCancelListener(cancellationSignal::cancel);
return;
}
@@ -571,24 +569,26 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
}
@Override
- public CancellationSignal controlWindowInsetsAnimation(@InsetsType int types, long durationMs,
+ public void controlWindowInsetsAnimation(@InsetsType int types, long durationMillis,
@Nullable Interpolator interpolator,
+ @Nullable CancellationSignal cancellationSignal,
@NonNull WindowInsetsAnimationControlListener listener) {
- return controlWindowInsetsAnimation(types, listener, false /* fromIme */, durationMs,
- interpolator, ANIMATION_TYPE_USER);
+ controlWindowInsetsAnimation(types, cancellationSignal, listener,
+ false /* fromIme */, durationMillis, interpolator, ANIMATION_TYPE_USER);
}
- private CancellationSignal controlWindowInsetsAnimation(@InsetsType int types,
- WindowInsetsAnimationControlListener listener, boolean fromIme, long durationMs,
- @Nullable Interpolator interpolator, @AnimationType int animationType) {
+ private void controlWindowInsetsAnimation(@InsetsType int types,
+ @Nullable CancellationSignal cancellationSignal,
+ WindowInsetsAnimationControlListener listener,
+ boolean fromIme, long durationMs, @Nullable Interpolator interpolator,
+ @AnimationType int animationType) {
if (!checkDisplayFramesForControlling()) {
listener.onCancelled();
- CancellationSignal cancellationSignal = new CancellationSignal();
- cancellationSignal.cancel();
- return cancellationSignal;
+ return;
}
- return controlAnimationUnchecked(types, listener, mFrame, fromIme, durationMs, interpolator,
- false /* fade */, animationType, getLayoutInsetsDuringAnimationMode(types),
+ controlAnimationUnchecked(types, cancellationSignal, listener, mFrame, fromIme, durationMs,
+ interpolator, false /* fade */, animationType,
+ getLayoutInsetsDuringAnimationMode(types),
false /* useInsetsAnimationThread */);
}
@@ -599,18 +599,17 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
return mState.getDisplayFrame().equals(mFrame);
}
- private CancellationSignal controlAnimationUnchecked(@InsetsType int types,
+ private void controlAnimationUnchecked(@InsetsType int types,
+ @Nullable CancellationSignal cancellationSignal,
WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme,
long durationMs, Interpolator interpolator, boolean fade,
@AnimationType int animationType,
@LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
boolean useInsetsAnimationThread) {
- CancellationSignal cancellationSignal = new CancellationSignal();
if (types == 0) {
// nothing to animate.
listener.onCancelled();
- cancellationSignal.cancel();
- return cancellationSignal;
+ return;
}
cancelExistingControllers(types);
mLastStartedAnimTypes |= types;
@@ -631,18 +630,19 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
useInsetsAnimationThread);
mPendingImeControlRequest = request;
mHandler.postDelayed(mPendingControlTimeout, PENDING_CONTROL_TIMEOUT_MS);
- cancellationSignal.setOnCancelListener(() -> {
- if (mPendingImeControlRequest == request) {
- abortPendingImeControlRequest();
- }
- });
- return cancellationSignal;
+ if (cancellationSignal != null) {
+ cancellationSignal.setOnCancelListener(() -> {
+ if (mPendingImeControlRequest == request) {
+ abortPendingImeControlRequest();
+ }
+ });
+ }
+ return;
}
if (typesReady == 0) {
listener.onCancelled();
- cancellationSignal.cancel();
- return cancellationSignal;
+ return;
}
@@ -654,13 +654,14 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
frame, mState, listener, typesReady, this, durationMs, interpolator, fade,
animationType);
mRunningAnimations.add(new RunningAnimation(runner, animationType));
- cancellationSignal.setOnCancelListener(runner::cancel);
+ if (cancellationSignal != null) {
+ cancellationSignal.setOnCancelListener(runner::cancel);
+ }
if (layoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN) {
showDirectly(types);
} else {
hideDirectly(types, false /* animationFinished */, animationType);
}
- return cancellationSignal;
}
/**
@@ -922,7 +923,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
// Show/hide animations always need to be relative to the display frame, in order that shown
// and hidden state insets are correct.
controlAnimationUnchecked(
- types, listener, mState.getDisplayFrame(), fromIme, listener.getDurationMs(),
+ types, null /* cancellationSignal */, listener, mState.getDisplayFrame(), fromIme,
+ listener.getDurationMs(),
INTERPOLATOR, true /* fade */, show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE,
show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN
: LAYOUT_INSETS_DURING_ANIMATION_HIDDEN,
diff --git a/core/java/android/view/PendingInsetsController.java b/core/java/android/view/PendingInsetsController.java
index 7f3641803770..e8d9bb50c290 100644
--- a/core/java/android/view/PendingInsetsController.java
+++ b/core/java/android/view/PendingInsetsController.java
@@ -16,6 +16,8 @@
package android.view;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.CancellationSignal;
import android.view.WindowInsets.Type.InsetsType;
import android.view.animation.Interpolator;
@@ -60,21 +62,6 @@ public class PendingInsetsController implements WindowInsetsController {
}
@Override
- public CancellationSignal controlWindowInsetsAnimation(int types, long durationMillis,
- Interpolator interpolator,
- WindowInsetsAnimationControlListener listener) {
- if (mReplayedInsetsController != null) {
- return mReplayedInsetsController.controlWindowInsetsAnimation(types, durationMillis,
- interpolator, listener);
- } else {
- listener.onCancelled();
- CancellationSignal cancellationSignal = new CancellationSignal();
- cancellationSignal.cancel();
- return cancellationSignal;
- }
- }
-
- @Override
public void setSystemBarsAppearance(int appearance, int mask) {
if (mReplayedInsetsController != null) {
mReplayedInsetsController.setSystemBarsAppearance(appearance, mask);
@@ -176,6 +163,19 @@ public class PendingInsetsController implements WindowInsetsController {
mReplayedInsetsController = null;
}
+ @Override
+ public void controlWindowInsetsAnimation(@InsetsType int types, long durationMillis,
+ @Nullable Interpolator interpolator,
+ CancellationSignal cancellationSignal,
+ @NonNull WindowInsetsAnimationControlListener listener) {
+ if (mReplayedInsetsController != null) {
+ mReplayedInsetsController.controlWindowInsetsAnimation(types, durationMillis,
+ interpolator, cancellationSignal, listener);
+ } else {
+ listener.onCancelled();
+ }
+ }
+
private interface PendingRequest {
void replay(InsetsController controller);
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 25f5609d9922..c87808b113f6 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -1960,7 +1960,7 @@ public final class SurfaceControl implements Parcelable {
* @hide
*/
public static ScreenshotGraphicBuffer captureLayersExcluding(SurfaceControl layer,
- Rect sourceCrop, float frameScale, SurfaceControl[] exclude) {
+ Rect sourceCrop, float frameScale, int format, SurfaceControl[] exclude) {
final IBinder displayToken = SurfaceControl.getInternalDisplayToken();
long[] nativeExcludeObjects = new long[exclude.length];
for (int i = 0; i < exclude.length; i++) {
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index a3b3f1f72310..41a384797521 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -26,6 +26,8 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.view.accessibility.IAccessibilityEmbeddedConnection;
+import java.util.Objects;
+
/**
* Utility class for adding a View hierarchy to a {@link SurfaceControl}. The View hierarchy
* will render in to a root SurfaceControl, and receive input based on the SurfaceControl's
@@ -159,7 +161,8 @@ public class SurfaceControlViewHost {
* @hide
*/
@TestApi
- public void addView(@NonNull View view, WindowManager.LayoutParams attrs) {
+ public void setView(@NonNull View view, @NonNull WindowManager.LayoutParams attrs) {
+ Objects.requireNonNull(view);
mViewRoot.setView(view, attrs, null);
}
@@ -172,11 +175,18 @@ public class SurfaceControlViewHost {
* @param width The width to layout the View within, in pixels.
* @param height The height to layout the View within, in pixels.
*/
- public void addView(@NonNull View view, int width, int height) {
+ public void setView(@NonNull View view, int width, int height) {
final WindowManager.LayoutParams lp =
new WindowManager.LayoutParams(width, height,
WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSPARENT);
- addView(view, lp);
+ setView(view, lp);
+ }
+
+ /**
+ * @return The view passed to setView, or null if none has been passed.
+ */
+ public @Nullable View getView() {
+ return mViewRoot.getView();
}
/**
diff --git a/core/java/android/view/WindowContainerTransaction.java b/core/java/android/view/WindowContainerTransaction.java
index 9c16e1334e00..56b49515f419 100644
--- a/core/java/android/view/WindowContainerTransaction.java
+++ b/core/java/android/view/WindowContainerTransaction.java
@@ -168,6 +168,18 @@ public class WindowContainerTransaction implements Parcelable {
}
/**
+ * Sets whether a container or its children should be hidden. When {@code false}, the existing
+ * visibility of the container applies, but when {@code true} the container will be forced
+ * to be hidden.
+ */
+ public WindowContainerTransaction setHidden(IWindowContainer container, boolean hidden) {
+ Change chg = getOrCreateChange(container.asBinder());
+ chg.mHidden = hidden;
+ chg.mChangeMask |= Change.CHANGE_HIDDEN;
+ return this;
+ }
+
+ /**
* Set the smallestScreenWidth of a container.
*/
public WindowContainerTransaction setSmallestScreenWidthDp(IWindowContainer container,
@@ -250,9 +262,11 @@ public class WindowContainerTransaction implements Parcelable {
public static final int CHANGE_FOCUSABLE = 1;
public static final int CHANGE_BOUNDS_TRANSACTION = 1 << 1;
public static final int CHANGE_PIP_CALLBACK = 1 << 2;
+ public static final int CHANGE_HIDDEN = 1 << 3;
private final Configuration mConfiguration = new Configuration();
private boolean mFocusable = true;
+ private boolean mHidden = false;
private int mChangeMask = 0;
private @ActivityInfo.Config int mConfigSetMask = 0;
private @WindowConfiguration.WindowConfig int mWindowSetMask = 0;
@@ -268,6 +282,7 @@ public class WindowContainerTransaction implements Parcelable {
protected Change(Parcel in) {
mConfiguration.readFromParcel(in);
mFocusable = in.readBoolean();
+ mHidden = in.readBoolean();
mChangeMask = in.readInt();
mConfigSetMask = in.readInt();
mWindowSetMask = in.readInt();
@@ -296,7 +311,7 @@ public class WindowContainerTransaction implements Parcelable {
return mConfiguration;
}
- /** Gets the requested focusable value */
+ /** Gets the requested focusable state */
public boolean getFocusable() {
if ((mChangeMask & CHANGE_FOCUSABLE) == 0) {
throw new RuntimeException("Focusable not set. check CHANGE_FOCUSABLE first");
@@ -304,6 +319,14 @@ public class WindowContainerTransaction implements Parcelable {
return mFocusable;
}
+ /** Gets the requested hidden state */
+ public boolean getHidden() {
+ if ((mChangeMask & CHANGE_HIDDEN) == 0) {
+ throw new RuntimeException("Hidden not set. check CHANGE_HIDDEN first");
+ }
+ return mHidden;
+ }
+
public int getChangeMask() {
return mChangeMask;
}
@@ -369,6 +392,7 @@ public class WindowContainerTransaction implements Parcelable {
public void writeToParcel(Parcel dest, int flags) {
mConfiguration.writeToParcel(dest, flags);
dest.writeBoolean(mFocusable);
+ dest.writeBoolean(mHidden);
dest.writeInt(mChangeMask);
dest.writeInt(mConfigSetMask);
dest.writeInt(mWindowSetMask);
diff --git a/core/java/android/view/WindowInsetsAnimationControlListener.java b/core/java/android/view/WindowInsetsAnimationControlListener.java
index 701bd3158b1e..faaf9203af32 100644
--- a/core/java/android/view/WindowInsetsAnimationControlListener.java
+++ b/core/java/android/view/WindowInsetsAnimationControlListener.java
@@ -16,7 +16,6 @@
package android.view;
-import android.annotation.Hide;
import android.annotation.NonNull;
import android.view.WindowInsets.Type.InsetsType;
import android.view.inputmethod.EditorInfo;
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
index 2ad557e6d9f6..0282ecac8920 100644
--- a/core/java/android/view/WindowInsetsController.java
+++ b/core/java/android/view/WindowInsetsController.java
@@ -156,16 +156,17 @@ public interface WindowInsetsController {
* calculate {@link WindowInsetsAnimation#getInterpolatedFraction()}.
* @param listener The {@link WindowInsetsAnimationControlListener} that gets called when the
* windows are ready to be controlled, among other callbacks.
- * @return A cancellation signal that the caller can use to cancel the request to obtain
- * control, or once they have control, to cancel the control.
+ * @param cancellationSignal A cancellation signal that the caller can use to cancel the
+ * request to obtain control, or once they have control, to cancel the
+ * control.
* @see WindowInsetsAnimation#getFraction()
* @see WindowInsetsAnimation#getInterpolatedFraction()
* @see WindowInsetsAnimation#getInterpolator()
* @see WindowInsetsAnimation#getDurationMillis()
*/
- @NonNull
- CancellationSignal controlWindowInsetsAnimation(@InsetsType int types, long durationMillis,
+ void controlWindowInsetsAnimation(@InsetsType int types, long durationMillis,
@Nullable Interpolator interpolator,
+ @Nullable CancellationSignal cancellationSignal,
@NonNull WindowInsetsAnimationControlListener listener);
/**
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index c5fa3c83a0e5..77ce5c1b1ddc 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -89,6 +89,7 @@ import android.view.accessibility.AccessibilityNodeInfo;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@@ -2896,6 +2897,18 @@ public interface WindowManager extends ViewManager {
private boolean mFitInsetsIgnoringVisibility = false;
/**
+ * {@link InsetsState.InternalInsetsType}s to be applied to the window
+ * If {@link #type} has the predefined insets (like {@link #TYPE_STATUS_BAR} or
+ * {@link #TYPE_NAVIGATION_BAR}), this field will be ignored.
+ *
+ * <p>Note: provide only one inset corresponding to the window type (like
+ * {@link InsetsState.InternalInsetsType#ITYPE_STATUS_BAR} or
+ * {@link InsetsState.InternalInsetsType#ITYPE_NAVIGATION_BAR})</p>
+ * @hide
+ */
+ public @InsetsState.InternalInsetsType int[] providesInsetsTypes;
+
+ /**
* Specifies types of insets that this window should avoid overlapping during layout.
*
* @param types which types of insets that this window should avoid. The initial value of
@@ -3116,6 +3129,12 @@ public interface WindowManager extends ViewManager {
out.writeInt(mFitInsetsSides);
out.writeBoolean(mFitInsetsIgnoringVisibility);
out.writeBoolean(preferMinimalPostProcessing);
+ if (providesInsetsTypes != null) {
+ out.writeInt(providesInsetsTypes.length);
+ out.writeIntArray(providesInsetsTypes);
+ } else {
+ out.writeInt(0);
+ }
}
public static final @android.annotation.NonNull Parcelable.Creator<LayoutParams> CREATOR
@@ -3177,6 +3196,11 @@ public interface WindowManager extends ViewManager {
mFitInsetsSides = in.readInt();
mFitInsetsIgnoringVisibility = in.readBoolean();
preferMinimalPostProcessing = in.readBoolean();
+ int insetsTypesLength = in.readInt();
+ if (insetsTypesLength > 0) {
+ providesInsetsTypes = new int[insetsTypesLength];
+ in.readIntArray(providesInsetsTypes);
+ }
}
@SuppressWarnings({"PointlessBitwiseExpression"})
@@ -3437,6 +3461,11 @@ public interface WindowManager extends ViewManager {
changes |= LAYOUT_CHANGED;
}
+ if (!Arrays.equals(providesInsetsTypes, o.providesInsetsTypes)) {
+ providesInsetsTypes = o.providesInsetsTypes;
+ changes |= LAYOUT_CHANGED;
+ }
+
return changes;
}
@@ -3609,6 +3638,14 @@ public interface WindowManager extends ViewManager {
sb.append(System.lineSeparator());
sb.append(prefix).append(" fitIgnoreVis");
}
+ if (providesInsetsTypes != null) {
+ sb.append(System.lineSeparator());
+ sb.append(prefix).append(" insetsTypes=");
+ for (int i = 0; i < providesInsetsTypes.length; ++i) {
+ if (i > 0) sb.append(' ');
+ sb.append(InsetsState.typeToString(providesInsetsTypes[i]));
+ }
+ }
sb.append('}');
return sb.toString();
diff --git a/core/java/android/view/inline/InlinePresentationSpec.java b/core/java/android/view/inline/InlinePresentationSpec.java
index 3cc04b8c5b86..3788e2b2a883 100644
--- a/core/java/android/view/inline/InlinePresentationSpec.java
+++ b/core/java/android/view/inline/InlinePresentationSpec.java
@@ -58,7 +58,7 @@ public final class InlinePresentationSpec implements Parcelable {
- // Code below generated by codegen v1.0.14.
+ // Code below generated by codegen v1.0.15.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -104,8 +104,8 @@ public final class InlinePresentationSpec implements Parcelable {
}
/**
- * The extras encoding the UI style information. Defaults to null in which case the default
- * system UI style will be used.
+ * The extras encoding the UI style information. Defaults to {@code null} in which case the
+ * default system UI style will be used.
*/
@DataClass.Generated.Member
public @Nullable Bundle getStyle() {
@@ -244,11 +244,11 @@ public final class InlinePresentationSpec implements Parcelable {
}
/**
- * The extras encoding the UI style information. Defaults to null in which case the default
- * system UI style will be used.
+ * The extras encoding the UI style information. Defaults to {@code null} in which case the
+ * default system UI style will be used.
*/
@DataClass.Generated.Member
- public @NonNull Builder setStyle(@Nullable Bundle value) {
+ public @NonNull Builder setStyle(@NonNull Bundle value) {
checkNotUsed();
mBuilderFieldsSet |= 0x4;
mStyle = value;
@@ -279,8 +279,8 @@ public final class InlinePresentationSpec implements Parcelable {
}
@DataClass.Generated(
- time = 1582078731418L,
- codegenVersion = "1.0.14",
+ time = 1584067238741L,
+ codegenVersion = "1.0.15",
sourceFile = "frameworks/base/core/java/android/view/inline/InlinePresentationSpec.java",
inputSignatures = "private final @android.annotation.NonNull android.util.Size mMinSize\nprivate final @android.annotation.NonNull android.util.Size mMaxSize\nprivate final @android.annotation.Nullable android.os.Bundle mStyle\nprivate static android.os.Bundle defaultStyle()\nclass InlinePresentationSpec extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
index 8e8c7dffd199..2945a8613bb7 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
@@ -72,7 +72,6 @@ public final class InlineSuggestionsRequest implements Parcelable {
/**
* The extras state propagated from the IME to pass extra data.
*/
- @DataClass.MaySetToNull
private @Nullable Bundle mExtras;
/**
@@ -81,7 +80,6 @@ public final class InlineSuggestionsRequest implements Parcelable {
*
* @hide
*/
- @DataClass.MaySetToNull
private @Nullable IBinder mHostInputToken;
/**
@@ -498,7 +496,7 @@ public final class InlineSuggestionsRequest implements Parcelable {
* The extras state propagated from the IME to pass extra data.
*/
@DataClass.Generated.Member
- public @NonNull Builder setExtras(@Nullable Bundle value) {
+ public @NonNull Builder setExtras(@NonNull Bundle value) {
checkNotUsed();
mBuilderFieldsSet |= 0x10;
mExtras = value;
@@ -513,7 +511,7 @@ public final class InlineSuggestionsRequest implements Parcelable {
*/
@DataClass.Generated.Member
@Override
- @NonNull Builder setHostInputToken(@Nullable IBinder value) {
+ @NonNull Builder setHostInputToken(@NonNull IBinder value) {
checkNotUsed();
mBuilderFieldsSet |= 0x20;
mHostInputToken = value;
@@ -578,10 +576,10 @@ public final class InlineSuggestionsRequest implements Parcelable {
}
@DataClass.Generated(
- time = 1583975428858L,
+ time = 1584067152935L,
codegenVersion = "1.0.15",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java",
- inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> mPresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @com.android.internal.util.DataClass.MaySetToNull @android.annotation.Nullable android.os.Bundle mExtras\nprivate @com.android.internal.util.DataClass.MaySetToNull @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\npublic void setHostInputToken(android.os.IBinder)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.Nullable android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setPresentationSpecs(java.util.List<android.view.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
+ inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> mPresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.Nullable android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\npublic void setHostInputToken(android.os.IBinder)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.Nullable android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setPresentationSpecs(java.util.List<android.view.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/textclassifier/ActionsModelParamsSupplier.java b/core/java/android/view/textclassifier/ActionsModelParamsSupplier.java
deleted file mode 100644
index 31645672f049..000000000000
--- a/core/java/android/view/textclassifier/ActionsModelParamsSupplier.java
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view.textclassifier;
-
-import android.annotation.Nullable;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.database.ContentObserver;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.Base64;
-import android.util.KeyValueListParser;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
-
-import java.lang.ref.WeakReference;
-import java.util.Objects;
-import java.util.function.Supplier;
-
-/**
- * Parses the {@link Settings.Global#TEXT_CLASSIFIER_ACTION_MODEL_PARAMS} flag.
- *
- * @hide
- */
-public final class ActionsModelParamsSupplier implements
- Supplier<ActionsModelParamsSupplier.ActionsModelParams> {
- private static final String TAG = TextClassifier.DEFAULT_LOG_TAG;
-
- @VisibleForTesting
- static final String KEY_REQUIRED_MODEL_VERSION = "required_model_version";
- @VisibleForTesting
- static final String KEY_REQUIRED_LOCALES = "required_locales";
- @VisibleForTesting
- static final String KEY_SERIALIZED_PRECONDITIONS = "serialized_preconditions";
-
- private final Context mAppContext;
- private final SettingsObserver mSettingsObserver;
-
- private final Object mLock = new Object();
- private final Runnable mOnChangedListener;
- @Nullable
- @GuardedBy("mLock")
- private ActionsModelParams mActionsModelParams;
- @GuardedBy("mLock")
- private boolean mParsed = true;
-
- public ActionsModelParamsSupplier(Context context, @Nullable Runnable onChangedListener) {
- final Context appContext = Preconditions.checkNotNull(context).getApplicationContext();
- // Some contexts don't have an app context.
- mAppContext = appContext != null ? appContext : context;
- mOnChangedListener = onChangedListener == null ? () -> {} : onChangedListener;
- mSettingsObserver = new SettingsObserver(mAppContext, () -> {
- synchronized (mLock) {
- Log.v(TAG, "Settings.Global.TEXT_CLASSIFIER_ACTION_MODEL_PARAMS is updated");
- mParsed = true;
- mOnChangedListener.run();
- }
- });
- }
-
- /**
- * Returns the parsed actions params or {@link ActionsModelParams#INVALID} if the value is
- * invalid.
- */
- @Override
- public ActionsModelParams get() {
- synchronized (mLock) {
- if (mParsed) {
- mActionsModelParams = parse(mAppContext.getContentResolver());
- mParsed = false;
- }
- }
- return mActionsModelParams;
- }
-
- private ActionsModelParams parse(ContentResolver contentResolver) {
- String settingStr = Settings.Global.getString(contentResolver,
- Settings.Global.TEXT_CLASSIFIER_ACTION_MODEL_PARAMS);
- if (TextUtils.isEmpty(settingStr)) {
- return ActionsModelParams.INVALID;
- }
- try {
- KeyValueListParser keyValueListParser = new KeyValueListParser(',');
- keyValueListParser.setString(settingStr);
- int version = keyValueListParser.getInt(KEY_REQUIRED_MODEL_VERSION, -1);
- if (version == -1) {
- Log.w(TAG, "ActionsModelParams.Parse, invalid model version");
- return ActionsModelParams.INVALID;
- }
- String locales = keyValueListParser.getString(KEY_REQUIRED_LOCALES, null);
- if (locales == null) {
- Log.w(TAG, "ActionsModelParams.Parse, invalid locales");
- return ActionsModelParams.INVALID;
- }
- String serializedPreconditionsStr =
- keyValueListParser.getString(KEY_SERIALIZED_PRECONDITIONS, null);
- if (serializedPreconditionsStr == null) {
- Log.w(TAG, "ActionsModelParams.Parse, invalid preconditions");
- return ActionsModelParams.INVALID;
- }
- byte[] serializedPreconditions =
- Base64.decode(serializedPreconditionsStr, Base64.NO_WRAP);
- return new ActionsModelParams(version, locales, serializedPreconditions);
- } catch (Throwable t) {
- Log.e(TAG, "Invalid TEXT_CLASSIFIER_ACTION_MODEL_PARAMS, ignore", t);
- }
- return ActionsModelParams.INVALID;
- }
-
- @Override
- protected void finalize() throws Throwable {
- try {
- mAppContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
- } finally {
- super.finalize();
- }
- }
-
- /**
- * Represents the parsed result.
- */
- public static final class ActionsModelParams {
-
- public static final ActionsModelParams INVALID =
- new ActionsModelParams(-1, "", new byte[0]);
-
- /**
- * The required model version to apply {@code mSerializedPreconditions}.
- */
- private final int mRequiredModelVersion;
-
- /**
- * The required model locales to apply {@code mSerializedPreconditions}.
- */
- private final String mRequiredModelLocales;
-
- /**
- * The serialized params that will be applied to the model file, if all requirements are
- * met. Do not modify.
- */
- private final byte[] mSerializedPreconditions;
-
- public ActionsModelParams(int requiredModelVersion, String requiredModelLocales,
- byte[] serializedPreconditions) {
- mRequiredModelVersion = requiredModelVersion;
- mRequiredModelLocales = Preconditions.checkNotNull(requiredModelLocales);
- mSerializedPreconditions = Preconditions.checkNotNull(serializedPreconditions);
- }
-
- /**
- * Returns the serialized preconditions. Returns {@code null} if the the model in use does
- * not meet all the requirements listed in the {@code ActionsModelParams} or the params
- * are invalid.
- */
- @Nullable
- public byte[] getSerializedPreconditions(ModelFileManager.ModelFile modelInUse) {
- if (this == INVALID) {
- return null;
- }
- if (modelInUse.getVersion() != mRequiredModelVersion) {
- Log.w(TAG, String.format(
- "Not applying mSerializedPreconditions, required version=%d, actual=%d",
- mRequiredModelVersion, modelInUse.getVersion()));
- return null;
- }
- if (!Objects.equals(modelInUse.getSupportedLocalesStr(), mRequiredModelLocales)) {
- Log.w(TAG, String.format(
- "Not applying mSerializedPreconditions, required locales=%s, actual=%s",
- mRequiredModelLocales, modelInUse.getSupportedLocalesStr()));
- return null;
- }
- return mSerializedPreconditions;
- }
- }
-
- private static final class SettingsObserver extends ContentObserver {
-
- private final WeakReference<Runnable> mOnChangedListener;
-
- SettingsObserver(Context appContext, Runnable listener) {
- super(null);
- mOnChangedListener = new WeakReference<>(listener);
- appContext.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.TEXT_CLASSIFIER_ACTION_MODEL_PARAMS),
- false /* notifyForDescendants */,
- this);
- }
-
- public void onChange(boolean selfChange) {
- if (mOnChangedListener.get() != null) {
- mOnChangedListener.get().run();
- }
- }
- }
-}
diff --git a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
deleted file mode 100644
index 3ed48f655d47..000000000000
--- a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier;
-
-import android.annotation.Nullable;
-import android.app.Person;
-import android.app.RemoteAction;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.Pair;
-import android.view.textclassifier.intent.LabeledIntent;
-import android.view.textclassifier.intent.TemplateIntentFactory;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import com.google.android.textclassifier.ActionsSuggestionsModel;
-import com.google.android.textclassifier.RemoteActionTemplate;
-
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Deque;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Objects;
-import java.util.StringJoiner;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
-/**
- * Helper class for action suggestions.
- *
- * @hide
- */
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public final class ActionsSuggestionsHelper {
- private static final String TAG = "ActionsSuggestions";
- private static final int USER_LOCAL = 0;
- private static final int FIRST_NON_LOCAL_USER = 1;
-
- private ActionsSuggestionsHelper() {}
-
- /**
- * Converts the messages to a list of native messages object that the model can understand.
- * <p>
- * User id encoding - local user is represented as 0, Other users are numbered according to
- * how far before they spoke last time in the conversation. For example, considering this
- * conversation:
- * <ul>
- * <li> User A: xxx
- * <li> Local user: yyy
- * <li> User B: zzz
- * </ul>
- * User A will be encoded as 2, user B will be encoded as 1 and local user will be encoded as 0.
- */
- public static ActionsSuggestionsModel.ConversationMessage[] toNativeMessages(
- List<ConversationActions.Message> messages,
- Function<CharSequence, String> languageDetector) {
- List<ConversationActions.Message> messagesWithText =
- messages.stream()
- .filter(message -> !TextUtils.isEmpty(message.getText()))
- .collect(Collectors.toCollection(ArrayList::new));
- if (messagesWithText.isEmpty()) {
- return new ActionsSuggestionsModel.ConversationMessage[0];
- }
- Deque<ActionsSuggestionsModel.ConversationMessage> nativeMessages = new ArrayDeque<>();
- PersonEncoder personEncoder = new PersonEncoder();
- int size = messagesWithText.size();
- for (int i = size - 1; i >= 0; i--) {
- ConversationActions.Message message = messagesWithText.get(i);
- long referenceTime = message.getReferenceTime() == null
- ? 0
- : message.getReferenceTime().toInstant().toEpochMilli();
- String timeZone = message.getReferenceTime() == null
- ? null
- : message.getReferenceTime().getZone().getId();
- nativeMessages.push(new ActionsSuggestionsModel.ConversationMessage(
- personEncoder.encode(message.getAuthor()),
- message.getText().toString(), referenceTime, timeZone,
- languageDetector.apply(message.getText())));
- }
- return nativeMessages.toArray(
- new ActionsSuggestionsModel.ConversationMessage[nativeMessages.size()]);
- }
-
- /**
- * Returns the result id for logging.
- */
- public static String createResultId(
- Context context,
- List<ConversationActions.Message> messages,
- int modelVersion,
- List<Locale> modelLocales) {
- final StringJoiner localesJoiner = new StringJoiner(",");
- for (Locale locale : modelLocales) {
- localesJoiner.add(locale.toLanguageTag());
- }
- final String modelName = String.format(
- Locale.US, "%s_v%d", localesJoiner.toString(), modelVersion);
- final int hash = Objects.hash(
- messages.stream().mapToInt(ActionsSuggestionsHelper::hashMessage),
- context.getPackageName(),
- System.currentTimeMillis());
- return SelectionSessionLogger.SignatureParser.createSignature(
- SelectionSessionLogger.CLASSIFIER_ID, modelName, hash);
- }
-
- /**
- * Generated labeled intent from an action suggestion and return the resolved result.
- */
- @Nullable
- public static LabeledIntent.Result createLabeledIntentResult(
- Context context,
- TemplateIntentFactory templateIntentFactory,
- ActionsSuggestionsModel.ActionSuggestion nativeSuggestion) {
- RemoteActionTemplate[] remoteActionTemplates =
- nativeSuggestion.getRemoteActionTemplates();
- if (remoteActionTemplates == null) {
- Log.w(TAG, "createRemoteAction: Missing template for type "
- + nativeSuggestion.getActionType());
- return null;
- }
- List<LabeledIntent> labeledIntents = templateIntentFactory.create(remoteActionTemplates);
- if (labeledIntents.isEmpty()) {
- return null;
- }
- // Given that we only support implicit intent here, we should expect there is just one
- // intent for each action type.
- LabeledIntent.TitleChooser titleChooser =
- ActionsSuggestionsHelper.createTitleChooser(nativeSuggestion.getActionType());
- return labeledIntents.get(0).resolve(context, titleChooser, null);
- }
-
- /**
- * Returns a {@link LabeledIntent.TitleChooser} for conversation actions use case.
- */
- @Nullable
- public static LabeledIntent.TitleChooser createTitleChooser(String actionType) {
- if (ConversationAction.TYPE_OPEN_URL.equals(actionType)) {
- return (labeledIntent, resolveInfo) -> {
- if (resolveInfo.handleAllWebDataURI) {
- return labeledIntent.titleWithEntity;
- }
- if ("android".equals(resolveInfo.activityInfo.packageName)) {
- return labeledIntent.titleWithEntity;
- }
- return labeledIntent.titleWithoutEntity;
- };
- }
- return null;
- }
-
- /**
- * Returns a list of {@link ConversationAction}s that have 0 duplicates. Two actions are
- * duplicates if they may look the same to users. This function assumes every
- * ConversationActions with a non-null RemoteAction also have a non-null intent in the extras.
- */
- public static List<ConversationAction> removeActionsWithDuplicates(
- List<ConversationAction> conversationActions) {
- // Ideally, we should compare title and icon here, but comparing icon is expensive and thus
- // we use the component name of the target handler as the heuristic.
- Map<Pair<String, String>, Integer> counter = new ArrayMap<>();
- for (ConversationAction conversationAction : conversationActions) {
- Pair<String, String> representation = getRepresentation(conversationAction);
- if (representation == null) {
- continue;
- }
- Integer existingCount = counter.getOrDefault(representation, 0);
- counter.put(representation, existingCount + 1);
- }
- List<ConversationAction> result = new ArrayList<>();
- for (ConversationAction conversationAction : conversationActions) {
- Pair<String, String> representation = getRepresentation(conversationAction);
- if (representation == null || counter.getOrDefault(representation, 0) == 1) {
- result.add(conversationAction);
- }
- }
- return result;
- }
-
- @Nullable
- private static Pair<String, String> getRepresentation(
- ConversationAction conversationAction) {
- RemoteAction remoteAction = conversationAction.getAction();
- if (remoteAction == null) {
- return null;
- }
- Intent actionIntent = ExtrasUtils.getActionIntent(conversationAction.getExtras());
- ComponentName componentName = actionIntent.getComponent();
- // Action without a component name will be considered as from the same app.
- String packageName = componentName == null ? null : componentName.getPackageName();
- return new Pair<>(
- conversationAction.getAction().getTitle().toString(), packageName);
- }
-
- private static final class PersonEncoder {
- private final Map<Person, Integer> mMapping = new ArrayMap<>();
- private int mNextUserId = FIRST_NON_LOCAL_USER;
-
- private int encode(Person person) {
- if (ConversationActions.Message.PERSON_USER_SELF.equals(person)) {
- return USER_LOCAL;
- }
- Integer result = mMapping.get(person);
- if (result == null) {
- mMapping.put(person, mNextUserId);
- result = mNextUserId;
- mNextUserId++;
- }
- return result;
- }
- }
-
- private static int hashMessage(ConversationActions.Message message) {
- return Objects.hash(message.getAuthor(), message.getText(), message.getReferenceTime());
- }
-}
diff --git a/core/java/android/view/textclassifier/ExtrasUtils.java b/core/java/android/view/textclassifier/ExtrasUtils.java
index 11e0e2ca072c..9e2b6427eaea 100644
--- a/core/java/android/view/textclassifier/ExtrasUtils.java
+++ b/core/java/android/view/textclassifier/ExtrasUtils.java
@@ -19,15 +19,9 @@ package android.view.textclassifier;
import android.annotation.Nullable;
import android.app.RemoteAction;
import android.content.Intent;
-import android.icu.util.ULocale;
import android.os.Bundle;
-import com.android.internal.util.ArrayUtils;
-
-import com.google.android.textclassifier.AnnotatorModel;
-
import java.util.ArrayList;
-import java.util.List;
/**
* Utility class for inserting and retrieving data in TextClassifier request/response extras.
@@ -37,52 +31,19 @@ import java.util.List;
public final class ExtrasUtils {
// Keys for response objects.
- private static final String SERIALIZED_ENTITIES_DATA = "serialized-entities-data";
- private static final String ENTITIES_EXTRAS = "entities-extras";
private static final String ACTION_INTENT = "action-intent";
private static final String ACTIONS_INTENTS = "actions-intents";
private static final String FOREIGN_LANGUAGE = "foreign-language";
private static final String ENTITY_TYPE = "entity-type";
private static final String SCORE = "score";
- private static final String MODEL_VERSION = "model-version";
private static final String MODEL_NAME = "model-name";
- private static final String TEXT_LANGUAGES = "text-languages";
- private static final String ENTITIES = "entities";
-
- // Keys for request objects.
- private static final String IS_SERIALIZED_ENTITY_DATA_ENABLED =
- "is-serialized-entity-data-enabled";
- private ExtrasUtils() {}
-
- /**
- * Bundles and returns foreign language detection information for TextClassifier responses.
- */
- static Bundle createForeignLanguageExtra(
- String language, float score, int modelVersion) {
- final Bundle bundle = new Bundle();
- bundle.putString(ENTITY_TYPE, language);
- bundle.putFloat(SCORE, score);
- bundle.putInt(MODEL_VERSION, modelVersion);
- bundle.putString(MODEL_NAME, "langId_v" + modelVersion);
- return bundle;
- }
-
- /**
- * Stores {@code extra} as foreign language information in TextClassifier response object's
- * extras {@code container}.
- *
- * @see #getForeignLanguageExtra(TextClassification)
- */
- static void putForeignLanguageExtra(Bundle container, Bundle extra) {
- container.putParcelable(FOREIGN_LANGUAGE, extra);
+ private ExtrasUtils() {
}
/**
* Returns foreign language detection information contained in the TextClassification object.
* responses.
- *
- * @see #putForeignLanguageExtra(Bundle, Bundle)
*/
@Nullable
public static Bundle getForeignLanguageExtra(@Nullable TextClassification classification) {
@@ -93,72 +54,6 @@ public final class ExtrasUtils {
}
/**
- * @see #getTopLanguage(Intent)
- */
- static void putTopLanguageScores(Bundle container, EntityConfidence languageScores) {
- final int maxSize = Math.min(3, languageScores.getEntities().size());
- final String[] languages = languageScores.getEntities().subList(0, maxSize)
- .toArray(new String[0]);
- final float[] scores = new float[languages.length];
- for (int i = 0; i < languages.length; i++) {
- scores[i] = languageScores.getConfidenceScore(languages[i]);
- }
- container.putStringArray(ENTITY_TYPE, languages);
- container.putFloatArray(SCORE, scores);
- }
-
- /**
- * @see #putTopLanguageScores(Bundle, EntityConfidence)
- */
- @Nullable
- public static ULocale getTopLanguage(@Nullable Intent intent) {
- if (intent == null) {
- return null;
- }
- final Bundle tcBundle = intent.getBundleExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER);
- if (tcBundle == null) {
- return null;
- }
- final Bundle textLanguagesExtra = tcBundle.getBundle(TEXT_LANGUAGES);
- if (textLanguagesExtra == null) {
- return null;
- }
- final String[] languages = textLanguagesExtra.getStringArray(ENTITY_TYPE);
- final float[] scores = textLanguagesExtra.getFloatArray(SCORE);
- if (languages == null || scores == null
- || languages.length == 0 || languages.length != scores.length) {
- return null;
- }
- int highestScoringIndex = 0;
- for (int i = 1; i < languages.length; i++) {
- if (scores[highestScoringIndex] < scores[i]) {
- highestScoringIndex = i;
- }
- }
- return ULocale.forLanguageTag(languages[highestScoringIndex]);
- }
-
- public static void putTextLanguagesExtra(Bundle container, Bundle extra) {
- container.putBundle(TEXT_LANGUAGES, extra);
- }
-
- /**
- * Stores {@code actionIntents} information in TextClassifier response object's extras
- * {@code container}.
- */
- static void putActionsIntents(Bundle container, ArrayList<Intent> actionsIntents) {
- container.putParcelableArrayList(ACTIONS_INTENTS, actionsIntents);
- }
-
- /**
- * Stores {@code actionIntents} information in TextClassifier response object's extras
- * {@code container}.
- */
- public static void putActionIntent(Bundle container, @Nullable Intent actionIntent) {
- container.putParcelable(ACTION_INTENT, actionIntent);
- }
-
- /**
* Returns {@code actionIntent} information contained in a TextClassifier response object.
*/
@Nullable
@@ -167,48 +62,6 @@ public final class ExtrasUtils {
}
/**
- * Stores serialized entity data information in TextClassifier response object's extras
- * {@code container}.
- */
- public static void putSerializedEntityData(
- Bundle container, @Nullable byte[] serializedEntityData) {
- container.putByteArray(SERIALIZED_ENTITIES_DATA, serializedEntityData);
- }
-
- /**
- * Returns serialized entity data information contained in a TextClassifier response
- * object.
- */
- @Nullable
- public static byte[] getSerializedEntityData(Bundle container) {
- return container.getByteArray(SERIALIZED_ENTITIES_DATA);
- }
-
- /**
- * Stores {@code entities} information in TextClassifier response object's extras
- * {@code container}.
- *
- * @see {@link #getCopyText(Bundle)}
- */
- public static void putEntitiesExtras(Bundle container, @Nullable Bundle entitiesExtras) {
- container.putParcelable(ENTITIES_EXTRAS, entitiesExtras);
- }
-
- /**
- * Returns {@code entities} information contained in a TextClassifier response object.
- *
- * @see {@link #putEntitiesExtras(Bundle, Bundle)}
- */
- @Nullable
- public static String getCopyText(Bundle container) {
- Bundle entitiesExtras = container.getParcelable(ENTITIES_EXTRAS);
- if (entitiesExtras == null) {
- return null;
- }
- return entitiesExtras.getString("text");
- }
-
- /**
* Returns {@code actionIntents} information contained in the TextClassification object.
*/
@Nullable
@@ -224,7 +77,7 @@ public final class ExtrasUtils {
* action string, {@code intentAction}.
*/
@Nullable
- public static RemoteAction findAction(
+ private static RemoteAction findAction(
@Nullable TextClassification classification, @Nullable String intentAction) {
if (classification == null || intentAction == null) {
return null;
@@ -283,53 +136,4 @@ public final class ExtrasUtils {
}
return extra.getString(MODEL_NAME);
}
-
- /**
- * Stores the entities from {@link AnnotatorModel.ClassificationResult} in {@code container}.
- */
- public static void putEntities(
- Bundle container,
- @Nullable AnnotatorModel.ClassificationResult[] classifications) {
- if (ArrayUtils.isEmpty(classifications)) {
- return;
- }
- ArrayList<Bundle> entitiesBundle = new ArrayList<>();
- for (AnnotatorModel.ClassificationResult classification : classifications) {
- if (classification == null) {
- continue;
- }
- Bundle entityBundle = new Bundle();
- entityBundle.putString(ENTITY_TYPE, classification.getCollection());
- entityBundle.putByteArray(
- SERIALIZED_ENTITIES_DATA,
- classification.getSerializedEntityData());
- entitiesBundle.add(entityBundle);
- }
- if (!entitiesBundle.isEmpty()) {
- container.putParcelableArrayList(ENTITIES, entitiesBundle);
- }
- }
-
- /**
- * Returns a list of entities contained in the {@code extra}.
- */
- @Nullable
- public static List<Bundle> getEntities(Bundle container) {
- return container.getParcelableArrayList(ENTITIES);
- }
-
- /**
- * Whether the annotator should populate serialized entity data into the result object.
- */
- public static boolean isSerializedEntityDataEnabled(TextLinks.Request request) {
- return request.getExtras().getBoolean(IS_SERIALIZED_ENTITY_DATA_ENABLED);
- }
-
- /**
- * To indicate whether the annotator should populate serialized entity data in the result
- * object.
- */
- public static void putIsSerializedEntityDataEnabled(Bundle bundle, boolean isEnabled) {
- bundle.putBoolean(IS_SERIALIZED_ENTITY_DATA_ENABLED, isEnabled);
- }
-}
+} \ No newline at end of file
diff --git a/core/java/android/view/textclassifier/GenerateLinksLogger.java b/core/java/android/view/textclassifier/GenerateLinksLogger.java
deleted file mode 100644
index 17ec73ad395f..000000000000
--- a/core/java/android/view/textclassifier/GenerateLinksLogger.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier;
-
-import android.annotation.Nullable;
-import android.metrics.LogMaker;
-import android.util.ArrayMap;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
-import java.util.Locale;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Random;
-import java.util.UUID;
-
-/**
- * A helper for logging calls to generateLinks.
- * @hide
- */
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public final class GenerateLinksLogger {
-
- private static final String LOG_TAG = "GenerateLinksLogger";
- private static final String ZERO = "0";
-
- private final MetricsLogger mMetricsLogger;
- private final Random mRng;
- private final int mSampleRate;
-
- /**
- * @param sampleRate the rate at which log events are written. (e.g. 100 means there is a 0.01
- * chance that a call to logGenerateLinks results in an event being written).
- * To write all events, pass 1.
- */
- public GenerateLinksLogger(int sampleRate) {
- mSampleRate = sampleRate;
- mRng = new Random(System.nanoTime());
- mMetricsLogger = new MetricsLogger();
- }
-
- @VisibleForTesting
- public GenerateLinksLogger(int sampleRate, MetricsLogger metricsLogger) {
- mSampleRate = sampleRate;
- mRng = new Random(System.nanoTime());
- mMetricsLogger = metricsLogger;
- }
-
- /** Logs statistics about a call to generateLinks. */
- public void logGenerateLinks(CharSequence text, TextLinks links, String callingPackageName,
- long latencyMs) {
- Objects.requireNonNull(text);
- Objects.requireNonNull(links);
- Objects.requireNonNull(callingPackageName);
- if (!shouldLog()) {
- return;
- }
-
- // Always populate the total stats, and per-entity stats for each entity type detected.
- final LinkifyStats totalStats = new LinkifyStats();
- final Map<String, LinkifyStats> perEntityTypeStats = new ArrayMap<>();
- for (TextLinks.TextLink link : links.getLinks()) {
- if (link.getEntityCount() == 0) continue;
- final String entityType = link.getEntity(0);
- if (entityType == null
- || TextClassifier.TYPE_OTHER.equals(entityType)
- || TextClassifier.TYPE_UNKNOWN.equals(entityType)) {
- continue;
- }
- totalStats.countLink(link);
- perEntityTypeStats.computeIfAbsent(entityType, k -> new LinkifyStats()).countLink(link);
- }
-
- final String callId = UUID.randomUUID().toString();
- writeStats(callId, callingPackageName, null, totalStats, text, latencyMs);
- for (Map.Entry<String, LinkifyStats> entry : perEntityTypeStats.entrySet()) {
- writeStats(callId, callingPackageName, entry.getKey(), entry.getValue(), text,
- latencyMs);
- }
- }
-
- /**
- * Returns whether this particular event should be logged.
- *
- * Sampling is used to reduce the amount of logging data generated.
- **/
- private boolean shouldLog() {
- if (mSampleRate <= 1) {
- return true;
- } else {
- return mRng.nextInt(mSampleRate) == 0;
- }
- }
-
- /** Writes a log event for the given stats. */
- private void writeStats(String callId, String callingPackageName, @Nullable String entityType,
- LinkifyStats stats, CharSequence text, long latencyMs) {
- final LogMaker log = new LogMaker(MetricsEvent.TEXT_CLASSIFIER_GENERATE_LINKS)
- .setPackageName(callingPackageName)
- .addTaggedData(MetricsEvent.FIELD_LINKIFY_CALL_ID, callId)
- .addTaggedData(MetricsEvent.FIELD_LINKIFY_NUM_LINKS, stats.mNumLinks)
- .addTaggedData(MetricsEvent.FIELD_LINKIFY_LINK_LENGTH, stats.mNumLinksTextLength)
- .addTaggedData(MetricsEvent.FIELD_LINKIFY_TEXT_LENGTH, text.length())
- .addTaggedData(MetricsEvent.FIELD_LINKIFY_LATENCY, latencyMs);
- if (entityType != null) {
- log.addTaggedData(MetricsEvent.FIELD_LINKIFY_ENTITY_TYPE, entityType);
- }
- mMetricsLogger.write(log);
- debugLog(log);
- }
-
- private static void debugLog(LogMaker log) {
- if (!Log.ENABLE_FULL_LOGGING) {
- return;
- }
- final String callId = Objects.toString(
- log.getTaggedData(MetricsEvent.FIELD_LINKIFY_CALL_ID), "");
- final String entityType = Objects.toString(
- log.getTaggedData(MetricsEvent.FIELD_LINKIFY_ENTITY_TYPE), "ANY_ENTITY");
- final int numLinks = Integer.parseInt(
- Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_NUM_LINKS), ZERO));
- final int linkLength = Integer.parseInt(
- Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_LINK_LENGTH), ZERO));
- final int textLength = Integer.parseInt(
- Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_TEXT_LENGTH), ZERO));
- final int latencyMs = Integer.parseInt(
- Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_LATENCY), ZERO));
-
- Log.v(LOG_TAG,
- String.format(Locale.US, "%s:%s %d links (%d/%d chars) %dms %s", callId, entityType,
- numLinks, linkLength, textLength, latencyMs, log.getPackageName()));
- }
-
- /** Helper class for storing per-entity type statistics. */
- private static final class LinkifyStats {
- int mNumLinks;
- int mNumLinksTextLength;
-
- void countLink(TextLinks.TextLink link) {
- mNumLinks += 1;
- mNumLinksTextLength += link.getEnd() - link.getStart();
- }
- }
-}
diff --git a/core/java/android/view/textclassifier/Log.java b/core/java/android/view/textclassifier/Log.java
index 03ed4967e7b6..98ee09ce29c4 100644
--- a/core/java/android/view/textclassifier/Log.java
+++ b/core/java/android/view/textclassifier/Log.java
@@ -32,7 +32,7 @@ public final class Log {
* false: Limits logging to debug level.
*/
static final boolean ENABLE_FULL_LOGGING =
- android.util.Log.isLoggable(TextClassifier.DEFAULT_LOG_TAG, android.util.Log.VERBOSE);
+ android.util.Log.isLoggable(TextClassifier.LOG_TAG, android.util.Log.VERBOSE);
private Log() {
}
diff --git a/core/java/android/view/textclassifier/ModelFileManager.java b/core/java/android/view/textclassifier/ModelFileManager.java
deleted file mode 100644
index 0a4ff5d559ab..000000000000
--- a/core/java/android/view/textclassifier/ModelFileManager.java
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view.textclassifier;
-
-import static android.view.textclassifier.TextClassifier.DEFAULT_LOG_TAG;
-
-import android.annotation.Nullable;
-import android.os.LocaleList;
-import android.os.ParcelFileDescriptor;
-import android.text.TextUtils;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-import java.util.Objects;
-import java.util.StringJoiner;
-import java.util.function.Function;
-import java.util.function.Supplier;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Manages model files that are listed by the model files supplier.
- * @hide
- */
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public final class ModelFileManager {
- private final Object mLock = new Object();
- private final Supplier<List<ModelFile>> mModelFileSupplier;
-
- private List<ModelFile> mModelFiles;
-
- public ModelFileManager(Supplier<List<ModelFile>> modelFileSupplier) {
- mModelFileSupplier = Objects.requireNonNull(modelFileSupplier);
- }
-
- /**
- * Returns an unmodifiable list of model files listed by the given model files supplier.
- * <p>
- * The result is cached.
- */
- public List<ModelFile> listModelFiles() {
- synchronized (mLock) {
- if (mModelFiles == null) {
- mModelFiles = Collections.unmodifiableList(mModelFileSupplier.get());
- }
- return mModelFiles;
- }
- }
-
- /**
- * Returns the best model file for the given localelist, {@code null} if nothing is found.
- *
- * @param localeList the required locales, use {@code null} if there is no preference.
- */
- public ModelFile findBestModelFile(@Nullable LocaleList localeList) {
- final String languages = localeList == null || localeList.isEmpty()
- ? LocaleList.getDefault().toLanguageTags()
- : localeList.toLanguageTags();
- final List<Locale.LanguageRange> languageRangeList = Locale.LanguageRange.parse(languages);
-
- ModelFile bestModel = null;
- for (ModelFile model : listModelFiles()) {
- if (model.isAnyLanguageSupported(languageRangeList)) {
- if (model.isPreferredTo(bestModel)) {
- bestModel = model;
- }
- }
- }
- return bestModel;
- }
-
- /**
- * Default implementation of the model file supplier.
- */
- public static final class ModelFileSupplierImpl implements Supplier<List<ModelFile>> {
- private final File mUpdatedModelFile;
- private final File mFactoryModelDir;
- private final Pattern mModelFilenamePattern;
- private final Function<Integer, Integer> mVersionSupplier;
- private final Function<Integer, String> mSupportedLocalesSupplier;
-
- public ModelFileSupplierImpl(
- File factoryModelDir,
- String factoryModelFileNameRegex,
- File updatedModelFile,
- Function<Integer, Integer> versionSupplier,
- Function<Integer, String> supportedLocalesSupplier) {
- mUpdatedModelFile = Objects.requireNonNull(updatedModelFile);
- mFactoryModelDir = Objects.requireNonNull(factoryModelDir);
- mModelFilenamePattern = Pattern.compile(
- Objects.requireNonNull(factoryModelFileNameRegex));
- mVersionSupplier = Objects.requireNonNull(versionSupplier);
- mSupportedLocalesSupplier = Objects.requireNonNull(supportedLocalesSupplier);
- }
-
- @Override
- public List<ModelFile> get() {
- final List<ModelFile> modelFiles = new ArrayList<>();
- // The update model has the highest precedence.
- if (mUpdatedModelFile.exists()) {
- final ModelFile updatedModel = createModelFile(mUpdatedModelFile);
- if (updatedModel != null) {
- modelFiles.add(updatedModel);
- }
- }
- // Factory models should never have overlapping locales, so the order doesn't matter.
- if (mFactoryModelDir.exists() && mFactoryModelDir.isDirectory()) {
- final File[] files = mFactoryModelDir.listFiles();
- for (File file : files) {
- final Matcher matcher = mModelFilenamePattern.matcher(file.getName());
- if (matcher.matches() && file.isFile()) {
- final ModelFile model = createModelFile(file);
- if (model != null) {
- modelFiles.add(model);
- }
- }
- }
- }
- return modelFiles;
- }
-
- /** Returns null if the path did not point to a compatible model. */
- @Nullable
- private ModelFile createModelFile(File file) {
- if (!file.exists()) {
- return null;
- }
- ParcelFileDescriptor modelFd = null;
- try {
- modelFd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
- if (modelFd == null) {
- return null;
- }
- final int modelFdInt = modelFd.getFd();
- final int version = mVersionSupplier.apply(modelFdInt);
- final String supportedLocalesStr = mSupportedLocalesSupplier.apply(modelFdInt);
- if (supportedLocalesStr.isEmpty()) {
- Log.d(DEFAULT_LOG_TAG, "Ignoring " + file.getAbsolutePath());
- return null;
- }
- final List<Locale> supportedLocales = new ArrayList<>();
- for (String langTag : supportedLocalesStr.split(",")) {
- supportedLocales.add(Locale.forLanguageTag(langTag));
- }
- return new ModelFile(
- file,
- version,
- supportedLocales,
- supportedLocalesStr,
- ModelFile.LANGUAGE_INDEPENDENT.equals(supportedLocalesStr));
- } catch (FileNotFoundException e) {
- Log.e(DEFAULT_LOG_TAG, "Failed to find " + file.getAbsolutePath(), e);
- return null;
- } finally {
- maybeCloseAndLogError(modelFd);
- }
- }
-
- /**
- * Closes the ParcelFileDescriptor, if non-null, and logs any errors that occur.
- */
- private static void maybeCloseAndLogError(@Nullable ParcelFileDescriptor fd) {
- if (fd == null) {
- return;
- }
- try {
- fd.close();
- } catch (IOException e) {
- Log.e(DEFAULT_LOG_TAG, "Error closing file.", e);
- }
- }
-
- }
-
- /**
- * Describes TextClassifier model files on disk.
- */
- public static final class ModelFile {
- public static final String LANGUAGE_INDEPENDENT = "*";
-
- private final File mFile;
- private final int mVersion;
- private final List<Locale> mSupportedLocales;
- private final String mSupportedLocalesStr;
- private final boolean mLanguageIndependent;
-
- public ModelFile(File file, int version, List<Locale> supportedLocales,
- String supportedLocalesStr,
- boolean languageIndependent) {
- mFile = Objects.requireNonNull(file);
- mVersion = version;
- mSupportedLocales = Objects.requireNonNull(supportedLocales);
- mSupportedLocalesStr = Objects.requireNonNull(supportedLocalesStr);
- mLanguageIndependent = languageIndependent;
- }
-
- /** Returns the absolute path to the model file. */
- public String getPath() {
- return mFile.getAbsolutePath();
- }
-
- /** Returns a name to use for id generation, effectively the name of the model file. */
- public String getName() {
- return mFile.getName();
- }
-
- /** Returns the version tag in the model's metadata. */
- public int getVersion() {
- return mVersion;
- }
-
- /** Returns whether the language supports any language in the given ranges. */
- public boolean isAnyLanguageSupported(List<Locale.LanguageRange> languageRanges) {
- Objects.requireNonNull(languageRanges);
- return mLanguageIndependent || Locale.lookup(languageRanges, mSupportedLocales) != null;
- }
-
- /** Returns an immutable lists of supported locales. */
- public List<Locale> getSupportedLocales() {
- return Collections.unmodifiableList(mSupportedLocales);
- }
-
- /** Returns the original supported locals string read from the model file. */
- public String getSupportedLocalesStr() {
- return mSupportedLocalesStr;
- }
-
- /**
- * Returns if this model file is preferred to the given one.
- */
- public boolean isPreferredTo(@Nullable ModelFile model) {
- // A model is preferred to no model.
- if (model == null) {
- return true;
- }
-
- // A language-specific model is preferred to a language independent
- // model.
- if (!mLanguageIndependent && model.mLanguageIndependent) {
- return true;
- }
- if (mLanguageIndependent && !model.mLanguageIndependent) {
- return false;
- }
-
- // A higher-version model is preferred.
- if (mVersion > model.getVersion()) {
- return true;
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(getPath());
- }
-
- @Override
- public boolean equals(Object other) {
- if (this == other) {
- return true;
- }
- if (other instanceof ModelFile) {
- final ModelFile otherModel = (ModelFile) other;
- return TextUtils.equals(getPath(), otherModel.getPath());
- }
- return false;
- }
-
- @Override
- public String toString() {
- final StringJoiner localesJoiner = new StringJoiner(",");
- for (Locale locale : mSupportedLocales) {
- localesJoiner.add(locale.toLanguageTag());
- }
- return String.format(Locale.US,
- "ModelFile { path=%s name=%s version=%d locales=%s }",
- getPath(), getName(), mVersion, localesJoiner.toString());
- }
- }
-}
diff --git a/core/java/android/view/textclassifier/SelectionEvent.java b/core/java/android/view/textclassifier/SelectionEvent.java
index 9a5454472076..6f9556b6a96e 100644
--- a/core/java/android/view/textclassifier/SelectionEvent.java
+++ b/core/java/android/view/textclassifier/SelectionEvent.java
@@ -158,7 +158,6 @@ public final class SelectionEvent implements Parcelable {
mEventType = in.readInt();
mEntityType = in.readString();
mWidgetVersion = in.readInt() > 0 ? in.readString() : null;
- // TODO: remove mPackageName once aiai does not need it
mPackageName = in.readString();
mWidgetType = in.readString();
mInvocationMethod = in.readInt();
@@ -186,7 +185,6 @@ public final class SelectionEvent implements Parcelable {
if (mWidgetVersion != null) {
dest.writeString(mWidgetVersion);
}
- // TODO: remove mPackageName once aiai does not need it
dest.writeString(mPackageName);
dest.writeString(mWidgetType);
dest.writeInt(mInvocationMethod);
@@ -406,7 +404,7 @@ public final class SelectionEvent implements Parcelable {
*/
@NonNull
public String getPackageName() {
- return mSystemTcMetadata != null ? mSystemTcMetadata.getCallingPackageName() : "";
+ return mPackageName;
}
/**
diff --git a/core/java/android/view/textclassifier/SelectionSessionLogger.java b/core/java/android/view/textclassifier/SelectionSessionLogger.java
index ae9f65ba6129..e7d896eb3f23 100644
--- a/core/java/android/view/textclassifier/SelectionSessionLogger.java
+++ b/core/java/android/view/textclassifier/SelectionSessionLogger.java
@@ -16,251 +16,24 @@
package android.view.textclassifier;
-import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.Context;
-import android.metrics.LogMaker;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
-import java.text.BreakIterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Objects;
-import java.util.StringJoiner;
/**
* A helper for logging selection session events.
+ *
* @hide
*/
public final class SelectionSessionLogger {
-
- private static final String LOG_TAG = "SelectionSessionLogger";
- static final String CLASSIFIER_ID = "androidtc";
-
- private static final int START_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_START;
- private static final int PREV_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_PREVIOUS;
- private static final int INDEX = MetricsEvent.FIELD_SELECTION_SESSION_INDEX;
- private static final int WIDGET_TYPE = MetricsEvent.FIELD_SELECTION_WIDGET_TYPE;
- private static final int WIDGET_VERSION = MetricsEvent.FIELD_SELECTION_WIDGET_VERSION;
- private static final int MODEL_NAME = MetricsEvent.FIELD_TEXTCLASSIFIER_MODEL;
- private static final int ENTITY_TYPE = MetricsEvent.FIELD_SELECTION_ENTITY_TYPE;
- private static final int SMART_START = MetricsEvent.FIELD_SELECTION_SMART_RANGE_START;
- private static final int SMART_END = MetricsEvent.FIELD_SELECTION_SMART_RANGE_END;
- private static final int EVENT_START = MetricsEvent.FIELD_SELECTION_RANGE_START;
- private static final int EVENT_END = MetricsEvent.FIELD_SELECTION_RANGE_END;
- private static final int SESSION_ID = MetricsEvent.FIELD_SELECTION_SESSION_ID;
-
- private static final String ZERO = "0";
- private static final String UNKNOWN = "unknown";
-
- private final MetricsLogger mMetricsLogger;
-
- public SelectionSessionLogger() {
- mMetricsLogger = new MetricsLogger();
- }
-
- @VisibleForTesting
- public SelectionSessionLogger(@NonNull MetricsLogger metricsLogger) {
- mMetricsLogger = Objects.requireNonNull(metricsLogger);
- }
-
- /** Emits a selection event to the logs. */
- public void writeEvent(@NonNull SelectionEvent event) {
- Objects.requireNonNull(event);
- final LogMaker log = new LogMaker(MetricsEvent.TEXT_SELECTION_SESSION)
- .setType(getLogType(event))
- .setSubtype(getLogSubType(event))
- .setPackageName(event.getPackageName())
- .addTaggedData(START_EVENT_DELTA, event.getDurationSinceSessionStart())
- .addTaggedData(PREV_EVENT_DELTA, event.getDurationSincePreviousEvent())
- .addTaggedData(INDEX, event.getEventIndex())
- .addTaggedData(WIDGET_TYPE, event.getWidgetType())
- .addTaggedData(WIDGET_VERSION, event.getWidgetVersion())
- .addTaggedData(ENTITY_TYPE, event.getEntityType())
- .addTaggedData(EVENT_START, event.getStart())
- .addTaggedData(EVENT_END, event.getEnd());
- if (isPlatformLocalTextClassifierSmartSelection(event.getResultId())) {
- // Ensure result id and smart indices are only set for events with smart selection from
- // the platform's textclassifier.
- log.addTaggedData(MODEL_NAME, SignatureParser.getModelName(event.getResultId()))
- .addTaggedData(SMART_START, event.getSmartStart())
- .addTaggedData(SMART_END, event.getSmartEnd());
- }
- if (event.getSessionId() != null) {
- log.addTaggedData(SESSION_ID, event.getSessionId().getValue());
- }
- mMetricsLogger.write(log);
- debugLog(log);
- }
-
- private static int getLogType(SelectionEvent event) {
- switch (event.getEventType()) {
- case SelectionEvent.ACTION_OVERTYPE:
- return MetricsEvent.ACTION_TEXT_SELECTION_OVERTYPE;
- case SelectionEvent.ACTION_COPY:
- return MetricsEvent.ACTION_TEXT_SELECTION_COPY;
- case SelectionEvent.ACTION_PASTE:
- return MetricsEvent.ACTION_TEXT_SELECTION_PASTE;
- case SelectionEvent.ACTION_CUT:
- return MetricsEvent.ACTION_TEXT_SELECTION_CUT;
- case SelectionEvent.ACTION_SHARE:
- return MetricsEvent.ACTION_TEXT_SELECTION_SHARE;
- case SelectionEvent.ACTION_SMART_SHARE:
- return MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE;
- case SelectionEvent.ACTION_DRAG:
- return MetricsEvent.ACTION_TEXT_SELECTION_DRAG;
- case SelectionEvent.ACTION_ABANDON:
- return MetricsEvent.ACTION_TEXT_SELECTION_ABANDON;
- case SelectionEvent.ACTION_OTHER:
- return MetricsEvent.ACTION_TEXT_SELECTION_OTHER;
- case SelectionEvent.ACTION_SELECT_ALL:
- return MetricsEvent.ACTION_TEXT_SELECTION_SELECT_ALL;
- case SelectionEvent.ACTION_RESET:
- return MetricsEvent.ACTION_TEXT_SELECTION_RESET;
- case SelectionEvent.EVENT_SELECTION_STARTED:
- return MetricsEvent.ACTION_TEXT_SELECTION_START;
- case SelectionEvent.EVENT_SELECTION_MODIFIED:
- return MetricsEvent.ACTION_TEXT_SELECTION_MODIFY;
- case SelectionEvent.EVENT_SMART_SELECTION_SINGLE:
- return MetricsEvent.ACTION_TEXT_SELECTION_SMART_SINGLE;
- case SelectionEvent.EVENT_SMART_SELECTION_MULTI:
- return MetricsEvent.ACTION_TEXT_SELECTION_SMART_MULTI;
- case SelectionEvent.EVENT_AUTO_SELECTION:
- return MetricsEvent.ACTION_TEXT_SELECTION_AUTO;
- default:
- return MetricsEvent.VIEW_UNKNOWN;
- }
- }
-
- private static int getLogSubType(SelectionEvent event) {
- switch (event.getInvocationMethod()) {
- case SelectionEvent.INVOCATION_MANUAL:
- return MetricsEvent.TEXT_SELECTION_INVOCATION_MANUAL;
- case SelectionEvent.INVOCATION_LINK:
- return MetricsEvent.TEXT_SELECTION_INVOCATION_LINK;
- default:
- return MetricsEvent.TEXT_SELECTION_INVOCATION_UNKNOWN;
- }
- }
-
- private static String getLogTypeString(int logType) {
- switch (logType) {
- case MetricsEvent.ACTION_TEXT_SELECTION_OVERTYPE:
- return "OVERTYPE";
- case MetricsEvent.ACTION_TEXT_SELECTION_COPY:
- return "COPY";
- case MetricsEvent.ACTION_TEXT_SELECTION_PASTE:
- return "PASTE";
- case MetricsEvent.ACTION_TEXT_SELECTION_CUT:
- return "CUT";
- case MetricsEvent.ACTION_TEXT_SELECTION_SHARE:
- return "SHARE";
- case MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE:
- return "SMART_SHARE";
- case MetricsEvent.ACTION_TEXT_SELECTION_DRAG:
- return "DRAG";
- case MetricsEvent.ACTION_TEXT_SELECTION_ABANDON:
- return "ABANDON";
- case MetricsEvent.ACTION_TEXT_SELECTION_OTHER:
- return "OTHER";
- case MetricsEvent.ACTION_TEXT_SELECTION_SELECT_ALL:
- return "SELECT_ALL";
- case MetricsEvent.ACTION_TEXT_SELECTION_RESET:
- return "RESET";
- case MetricsEvent.ACTION_TEXT_SELECTION_START:
- return "SELECTION_STARTED";
- case MetricsEvent.ACTION_TEXT_SELECTION_MODIFY:
- return "SELECTION_MODIFIED";
- case MetricsEvent.ACTION_TEXT_SELECTION_SMART_SINGLE:
- return "SMART_SELECTION_SINGLE";
- case MetricsEvent.ACTION_TEXT_SELECTION_SMART_MULTI:
- return "SMART_SELECTION_MULTI";
- case MetricsEvent.ACTION_TEXT_SELECTION_AUTO:
- return "AUTO_SELECTION";
- default:
- return UNKNOWN;
- }
- }
-
- private static String getLogSubTypeString(int logSubType) {
- switch (logSubType) {
- case MetricsEvent.TEXT_SELECTION_INVOCATION_MANUAL:
- return "MANUAL";
- case MetricsEvent.TEXT_SELECTION_INVOCATION_LINK:
- return "LINK";
- default:
- return UNKNOWN;
- }
- }
+ // Keep this in sync with the ResultIdUtils in libtextclassifier.
+ private static final String CLASSIFIER_ID = "androidtc";
static boolean isPlatformLocalTextClassifierSmartSelection(String signature) {
return SelectionSessionLogger.CLASSIFIER_ID.equals(
SelectionSessionLogger.SignatureParser.getClassifierId(signature));
}
- private static void debugLog(LogMaker log) {
- if (!Log.ENABLE_FULL_LOGGING) {
- return;
- }
- final String widgetType = Objects.toString(log.getTaggedData(WIDGET_TYPE), UNKNOWN);
- final String widgetVersion = Objects.toString(log.getTaggedData(WIDGET_VERSION), "");
- final String widget = widgetVersion.isEmpty()
- ? widgetType : widgetType + "-" + widgetVersion;
- final int index = Integer.parseInt(Objects.toString(log.getTaggedData(INDEX), ZERO));
- if (log.getType() == MetricsEvent.ACTION_TEXT_SELECTION_START) {
- String sessionId = Objects.toString(log.getTaggedData(SESSION_ID), "");
- sessionId = sessionId.substring(sessionId.lastIndexOf("-") + 1);
- Log.d(LOG_TAG, String.format("New selection session: %s (%s)", widget, sessionId));
- }
-
- final String model = Objects.toString(log.getTaggedData(MODEL_NAME), UNKNOWN);
- final String entity = Objects.toString(log.getTaggedData(ENTITY_TYPE), UNKNOWN);
- final String type = getLogTypeString(log.getType());
- final String subType = getLogSubTypeString(log.getSubtype());
- final int smartStart = Integer.parseInt(
- Objects.toString(log.getTaggedData(SMART_START), ZERO));
- final int smartEnd = Integer.parseInt(
- Objects.toString(log.getTaggedData(SMART_END), ZERO));
- final int eventStart = Integer.parseInt(
- Objects.toString(log.getTaggedData(EVENT_START), ZERO));
- final int eventEnd = Integer.parseInt(
- Objects.toString(log.getTaggedData(EVENT_END), ZERO));
-
- Log.v(LOG_TAG,
- String.format(Locale.US, "%2d: %s/%s/%s, range=%d,%d - smart_range=%d,%d (%s/%s)",
- index, type, subType, entity, eventStart, eventEnd, smartStart, smartEnd,
- widget, model));
- }
-
- /**
- * Returns a token iterator for tokenizing text for logging purposes.
- */
- public static BreakIterator getTokenIterator(@NonNull Locale locale) {
- return BreakIterator.getWordInstance(Objects.requireNonNull(locale));
- }
-
- /**
- * Creates a string id that may be used to identify a TextClassifier result.
- */
- public static String createId(
- String text, int start, int end, Context context, int modelVersion,
- List<Locale> locales) {
- Objects.requireNonNull(text);
- Objects.requireNonNull(context);
- Objects.requireNonNull(locales);
- final StringJoiner localesJoiner = new StringJoiner(",");
- for (Locale locale : locales) {
- localesJoiner.add(locale.toLanguageTag());
- }
- final String modelName = String.format(Locale.US, "%s_v%d", localesJoiner.toString(),
- modelVersion);
- final int hash = Objects.hash(text, start, end, context.getPackageName());
- return SignatureParser.createSignature(CLASSIFIER_ID, modelName, hash);
- }
-
/**
* Helper for creating and parsing string ids for
* {@link android.view.textclassifier.TextClassifierImpl}.
@@ -268,10 +41,6 @@ public final class SelectionSessionLogger {
@VisibleForTesting
public static final class SignatureParser {
- static String createSignature(String classifierId, String modelName, int hash) {
- return String.format(Locale.US, "%s|%s|%d", classifierId, modelName, hash);
- }
-
static String getClassifierId(@Nullable String signature) {
if (signature == null) {
return "";
@@ -282,29 +51,5 @@ public final class SelectionSessionLogger {
}
return "";
}
-
- static String getModelName(@Nullable String signature) {
- if (signature == null) {
- return "";
- }
- final int start = signature.indexOf("|") + 1;
- final int end = signature.indexOf("|", start);
- if (start >= 1 && end >= start) {
- return signature.substring(start, end);
- }
- return "";
- }
-
- static int getHash(@Nullable String signature) {
- if (signature == null) {
- return 0;
- }
- final int index1 = signature.indexOf("|");
- final int index2 = signature.indexOf("|", index1);
- if (index2 > 0) {
- return Integer.parseInt(signature.substring(index2));
- }
- return 0;
- }
}
}
diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java
index 86ef4e103990..8eac1c1520c0 100644
--- a/core/java/android/view/textclassifier/SystemTextClassifier.java
+++ b/core/java/android/view/textclassifier/SystemTextClassifier.java
@@ -45,7 +45,7 @@ import java.util.concurrent.TimeUnit;
@VisibleForTesting(visibility = Visibility.PACKAGE)
public final class SystemTextClassifier implements TextClassifier {
- private static final String LOG_TAG = "SystemTextClassifier";
+ private static final String LOG_TAG = TextClassifier.LOG_TAG;
private final ITextClassifierService mManagerService;
private final TextClassificationConstants mSettings;
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index 8b9d12916595..3aed32adea1c 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -44,8 +44,6 @@ import android.view.textclassifier.TextClassifier.Utils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
-import com.google.android.textclassifier.AnnotatorModel;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.time.ZonedDateTime;
@@ -327,9 +325,6 @@ public final class TextClassification implements Parcelable {
@NonNull private List<RemoteAction> mActions = new ArrayList<>();
@NonNull private final Map<String, Float> mTypeScoreMap = new ArrayMap<>();
- @NonNull
- private final Map<String, AnnotatorModel.ClassificationResult> mClassificationResults =
- new ArrayMap<>();
@Nullable private String mText;
@Nullable private Drawable mLegacyIcon;
@Nullable private String mLegacyLabel;
@@ -362,36 +357,7 @@ public final class TextClassification implements Parcelable {
public Builder setEntityType(
@NonNull @EntityType String type,
@FloatRange(from = 0.0, to = 1.0) float confidenceScore) {
- setEntityType(type, confidenceScore, null);
- return this;
- }
-
- /**
- * @see #setEntityType(String, float)
- *
- * @hide
- */
- @NonNull
- public Builder setEntityType(AnnotatorModel.ClassificationResult classificationResult) {
- setEntityType(
- classificationResult.getCollection(),
- classificationResult.getScore(),
- classificationResult);
- return this;
- }
-
- /**
- * @see #setEntityType(String, float)
- *
- * @hide
- */
- @NonNull
- private Builder setEntityType(
- @NonNull @EntityType String type,
- @FloatRange(from = 0.0, to = 1.0) float confidenceScore,
- @Nullable AnnotatorModel.ClassificationResult classificationResult) {
mTypeScoreMap.put(type, confidenceScore);
- mClassificationResults.put(type, classificationResult);
return this;
}
@@ -517,25 +483,7 @@ public final class TextClassification implements Parcelable {
EntityConfidence entityConfidence = new EntityConfidence(mTypeScoreMap);
return new TextClassification(mText, mLegacyIcon, mLegacyLabel, mLegacyIntent,
mLegacyOnClickListener, mActions, entityConfidence, mId,
- buildExtras(entityConfidence));
- }
-
- private Bundle buildExtras(EntityConfidence entityConfidence) {
- final Bundle extras = mExtras == null ? new Bundle() : mExtras;
- if (mActionIntents.stream().anyMatch(Objects::nonNull)) {
- ExtrasUtils.putActionsIntents(extras, mActionIntents);
- }
- if (mForeignLanguageExtra != null) {
- ExtrasUtils.putForeignLanguageExtra(extras, mForeignLanguageExtra);
- }
- List<String> sortedTypes = entityConfidence.getEntities();
- ArrayList<AnnotatorModel.ClassificationResult> sortedEntities = new ArrayList<>();
- for (String type : sortedTypes) {
- sortedEntities.add(mClassificationResults.get(type));
- }
- ExtrasUtils.putEntities(
- extras, sortedEntities.toArray(new AnnotatorModel.ClassificationResult[0]));
- return extras.isEmpty() ? Bundle.EMPTY : extras;
+ mExtras == null ? Bundle.EMPTY : mExtras);
}
}
diff --git a/core/java/android/view/textclassifier/TextClassificationConstants.java b/core/java/android/view/textclassifier/TextClassificationConstants.java
index 3d5ac58e7704..adb6fea91799 100644
--- a/core/java/android/view/textclassifier/TextClassificationConstants.java
+++ b/core/java/android/view/textclassifier/TextClassificationConstants.java
@@ -17,16 +17,11 @@
package android.view.textclassifier;
import android.annotation.Nullable;
-import android.content.Context;
import android.provider.DeviceConfig;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
/**
* TextClassifier specific settings.
*
@@ -41,8 +36,6 @@ import java.util.List;
*/
// TODO: Rename to TextClassifierSettings.
public final class TextClassificationConstants {
- private static final String DELIMITER = ":";
-
/**
* Whether the smart linkify feature is enabled.
*/
@@ -60,7 +53,6 @@ public final class TextClassificationConstants {
* Enable smart selection without a visible UI changes.
*/
private static final String MODEL_DARK_LAUNCH_ENABLED = "model_dark_launch_enabled";
-
/**
* Whether the smart selection feature is enabled.
*/
@@ -75,88 +67,10 @@ public final class TextClassificationConstants {
private static final String SMART_SELECT_ANIMATION_ENABLED =
"smart_select_animation_enabled";
/**
- * Max length of text that suggestSelection can accept.
- */
- @VisibleForTesting
- static final String SUGGEST_SELECTION_MAX_RANGE_LENGTH =
- "suggest_selection_max_range_length";
- /**
- * Max length of text that classifyText can accept.
- */
- private static final String CLASSIFY_TEXT_MAX_RANGE_LENGTH = "classify_text_max_range_length";
- /**
* Max length of text that generateLinks can accept.
*/
- private static final String GENERATE_LINKS_MAX_TEXT_LENGTH = "generate_links_max_text_length";
- /**
- * Sampling rate for generateLinks logging.
- */
- private static final String GENERATE_LINKS_LOG_SAMPLE_RATE =
- "generate_links_log_sample_rate";
- /**
- * A colon(:) separated string that specifies the default entities types for
- * generateLinks when hint is not given.
- */
- @VisibleForTesting
- static final String ENTITY_LIST_DEFAULT = "entity_list_default";
- /**
- * A colon(:) separated string that specifies the default entities types for
- * generateLinks when the text is in a not editable UI widget.
- */
- private static final String ENTITY_LIST_NOT_EDITABLE = "entity_list_not_editable";
- /**
- * A colon(:) separated string that specifies the default entities types for
- * generateLinks when the text is in an editable UI widget.
- */
- private static final String ENTITY_LIST_EDITABLE = "entity_list_editable";
- /**
- * A colon(:) separated string that specifies the default action types for
- * suggestConversationActions when the suggestions are used in an app.
- */
- private static final String IN_APP_CONVERSATION_ACTION_TYPES_DEFAULT =
- "in_app_conversation_action_types_default";
- /**
- * A colon(:) separated string that specifies the default action types for
- * suggestConversationActions when the suggestions are used in a notification.
- */
- private static final String NOTIFICATION_CONVERSATION_ACTION_TYPES_DEFAULT =
- "notification_conversation_action_types_default";
- /**
- * Threshold to accept a suggested language from LangID model.
- */
@VisibleForTesting
- static final String LANG_ID_THRESHOLD_OVERRIDE = "lang_id_threshold_override";
- /**
- * Whether to enable {@link android.view.textclassifier.TemplateIntentFactory}.
- */
- private static final String TEMPLATE_INTENT_FACTORY_ENABLED = "template_intent_factory_enabled";
- /**
- * Whether to enable "translate" action in classifyText.
- */
- private static final String TRANSLATE_IN_CLASSIFICATION_ENABLED =
- "translate_in_classification_enabled";
- /**
- * Whether to detect the languages of the text in request by using langId for the native
- * model.
- */
- private static final String DETECT_LANGUAGES_FROM_TEXT_ENABLED =
- "detect_languages_from_text_enabled";
- /**
- * A colon(:) separated string that specifies the configuration to use when including
- * surrounding context text in language detection queries.
- * <p>
- * Format= minimumTextSize<int>:penalizeRatio<float>:textScoreRatio<float>
- * <p>
- * e.g. 20:1.0:0.4
- * <p>
- * Accept all text lengths with minimumTextSize=0
- * <p>
- * Reject all text less than minimumTextSize with penalizeRatio=0
- * @see {@code TextClassifierImpl#detectLanguages(String, int, int)} for reference.
- */
- @VisibleForTesting
- static final String LANG_ID_CONTEXT_SETTINGS = "lang_id_context_settings";
-
+ static final String GENERATE_LINKS_MAX_TEXT_LENGTH = "generate_links_max_text_length";
/**
* The TextClassifierService which would like to use. Example of setting the package:
* <pre>
@@ -168,16 +82,6 @@ public final class TextClassificationConstants {
static final String TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE =
"textclassifier_service_package_override";
- /**
- * Whether to use the default system text classifier as the default text classifier
- * implementation. The local text classifier is used if it is {@code false}.
- *
- * @see android.service.textclassifier.TextClassifierService#getDefaultTextClassifierImplementation(Context)
- */
- // TODO: Once the system health experiment is done, remove this together with local TC.
- private static final String USE_DEFAULT_SYSTEM_TEXT_CLASSIFIER_AS_DEFAULT_IMPL =
- "use_default_system_text_classifier_as_default_impl";
-
private static final String DEFAULT_TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE = null;
private static final boolean LOCAL_TEXT_CLASSIFIER_ENABLED_DEFAULT = true;
private static final boolean SYSTEM_TEXT_CLASSIFIER_ENABLED_DEFAULT = true;
@@ -186,42 +90,7 @@ public final class TextClassificationConstants {
private static final boolean SMART_TEXT_SHARE_ENABLED_DEFAULT = true;
private static final boolean SMART_LINKIFY_ENABLED_DEFAULT = true;
private static final boolean SMART_SELECT_ANIMATION_ENABLED_DEFAULT = true;
- private static final int SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT = 10 * 1000;
- private static final int CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT = 10 * 1000;
private static final int GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT = 100 * 1000;
- private static final int GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT = 100;
- private static final List<String> ENTITY_LIST_DEFAULT_VALUE = Arrays.asList(
- TextClassifier.TYPE_ADDRESS,
- TextClassifier.TYPE_EMAIL,
- TextClassifier.TYPE_PHONE,
- TextClassifier.TYPE_URL,
- TextClassifier.TYPE_DATE,
- TextClassifier.TYPE_DATE_TIME,
- TextClassifier.TYPE_FLIGHT_NUMBER);
- private static final List<String> CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES = Arrays.asList(
- ConversationAction.TYPE_TEXT_REPLY,
- ConversationAction.TYPE_CREATE_REMINDER,
- ConversationAction.TYPE_CALL_PHONE,
- ConversationAction.TYPE_OPEN_URL,
- ConversationAction.TYPE_SEND_EMAIL,
- ConversationAction.TYPE_SEND_SMS,
- ConversationAction.TYPE_TRACK_FLIGHT,
- ConversationAction.TYPE_VIEW_CALENDAR,
- ConversationAction.TYPE_VIEW_MAP,
- ConversationAction.TYPE_ADD_CONTACT,
- ConversationAction.TYPE_COPY);
- /**
- * < 0 : Not set. Use value from LangId model.
- * 0 - 1: Override value in LangId model.
- *
- * @see EntityConfidence
- */
- private static final float LANG_ID_THRESHOLD_OVERRIDE_DEFAULT = -1f;
- private static final boolean TEMPLATE_INTENT_FACTORY_ENABLED_DEFAULT = true;
- private static final boolean TRANSLATE_IN_CLASSIFICATION_ENABLED_DEFAULT = true;
- private static final boolean DETECT_LANGUAGES_FROM_TEXT_ENABLED_DEFAULT = true;
- private static final float[] LANG_ID_CONTEXT_SETTINGS_DEFAULT = new float[]{20f, 1.0f, 0.4f};
- private static final boolean USE_DEFAULT_SYSTEM_TEXT_CLASSIFIER_AS_DEFAULT_IMPL_DEFAULT = true;
@Nullable
public String getTextClassifierServicePackageOverride() {
@@ -266,119 +135,20 @@ public final class TextClassificationConstants {
SMART_SELECT_ANIMATION_ENABLED, SMART_SELECT_ANIMATION_ENABLED_DEFAULT);
}
- public int getSuggestSelectionMaxRangeLength() {
- return DeviceConfig.getInt(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- SUGGEST_SELECTION_MAX_RANGE_LENGTH, SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT);
- }
-
- public int getClassifyTextMaxRangeLength() {
- return DeviceConfig.getInt(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- CLASSIFY_TEXT_MAX_RANGE_LENGTH, CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT);
- }
-
public int getGenerateLinksMaxTextLength() {
return DeviceConfig.getInt(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
GENERATE_LINKS_MAX_TEXT_LENGTH, GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT);
}
- public int getGenerateLinksLogSampleRate() {
- return DeviceConfig.getInt(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- GENERATE_LINKS_LOG_SAMPLE_RATE, GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT);
- }
-
- public List<String> getEntityListDefault() {
- return getDeviceConfigStringList(ENTITY_LIST_DEFAULT, ENTITY_LIST_DEFAULT_VALUE);
- }
-
- public List<String> getEntityListNotEditable() {
- return getDeviceConfigStringList(ENTITY_LIST_NOT_EDITABLE, ENTITY_LIST_DEFAULT_VALUE);
- }
-
- public List<String> getEntityListEditable() {
- return getDeviceConfigStringList(ENTITY_LIST_EDITABLE, ENTITY_LIST_DEFAULT_VALUE);
- }
-
- public List<String> getInAppConversationActionTypes() {
- return getDeviceConfigStringList(
- IN_APP_CONVERSATION_ACTION_TYPES_DEFAULT,
- CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES);
- }
-
- public List<String> getNotificationConversationActionTypes() {
- return getDeviceConfigStringList(
- NOTIFICATION_CONVERSATION_ACTION_TYPES_DEFAULT,
- CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES);
- }
-
- public float getLangIdThresholdOverride() {
- return DeviceConfig.getFloat(
- DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- LANG_ID_THRESHOLD_OVERRIDE,
- LANG_ID_THRESHOLD_OVERRIDE_DEFAULT);
- }
-
- public boolean isTemplateIntentFactoryEnabled() {
- return DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- TEMPLATE_INTENT_FACTORY_ENABLED,
- TEMPLATE_INTENT_FACTORY_ENABLED_DEFAULT);
- }
-
- public boolean isTranslateInClassificationEnabled() {
- return DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- TRANSLATE_IN_CLASSIFICATION_ENABLED,
- TRANSLATE_IN_CLASSIFICATION_ENABLED_DEFAULT);
- }
-
- public boolean isDetectLanguagesFromTextEnabled() {
- return DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- DETECT_LANGUAGES_FROM_TEXT_ENABLED,
- DETECT_LANGUAGES_FROM_TEXT_ENABLED_DEFAULT);
- }
-
- public float[] getLangIdContextSettings() {
- return getDeviceConfigFloatArray(
- LANG_ID_CONTEXT_SETTINGS, LANG_ID_CONTEXT_SETTINGS_DEFAULT);
- }
-
- public boolean getUseDefaultTextClassifierAsDefaultImplementation() {
- return DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- USE_DEFAULT_SYSTEM_TEXT_CLASSIFIER_AS_DEFAULT_IMPL,
- USE_DEFAULT_SYSTEM_TEXT_CLASSIFIER_AS_DEFAULT_IMPL_DEFAULT);
- }
-
void dump(IndentingPrintWriter pw) {
pw.println("TextClassificationConstants:");
pw.increaseIndent();
- pw.printPair("classify_text_max_range_length", getClassifyTextMaxRangeLength())
- .println();
- pw.printPair("detect_languages_from_text_enabled", isDetectLanguagesFromTextEnabled())
- .println();
- pw.printPair("entity_list_default", getEntityListDefault())
- .println();
- pw.printPair("entity_list_editable", getEntityListEditable())
- .println();
- pw.printPair("entity_list_not_editable", getEntityListNotEditable())
- .println();
- pw.printPair("generate_links_log_sample_rate", getGenerateLinksLogSampleRate())
- .println();
pw.printPair("generate_links_max_text_length", getGenerateLinksMaxTextLength())
.println();
- pw.printPair("in_app_conversation_action_types_default", getInAppConversationActionTypes())
- .println();
- pw.printPair("lang_id_context_settings", Arrays.toString(getLangIdContextSettings()))
- .println();
- pw.printPair("lang_id_threshold_override", getLangIdThresholdOverride())
- .println();
pw.printPair("local_textclassifier_enabled", isLocalTextClassifierEnabled())
.println();
pw.printPair("model_dark_launch_enabled", isModelDarkLaunchEnabled())
.println();
- pw.printPair("notification_conversation_action_types_default",
- getNotificationConversationActionTypes()).println();
pw.printPair("smart_linkify_enabled", isSmartLinkifyEnabled())
.println();
pw.printPair("smart_select_animation_enabled", isSmartSelectionAnimationEnabled())
@@ -387,57 +157,10 @@ public final class TextClassificationConstants {
.println();
pw.printPair("smart_text_share_enabled", isSmartTextShareEnabled())
.println();
- pw.printPair("suggest_selection_max_range_length", getSuggestSelectionMaxRangeLength())
- .println();
pw.printPair("system_textclassifier_enabled", isSystemTextClassifierEnabled())
.println();
- pw.printPair("template_intent_factory_enabled", isTemplateIntentFactoryEnabled())
- .println();
- pw.printPair("translate_in_classification_enabled", isTranslateInClassificationEnabled())
- .println();
pw.printPair("textclassifier_service_package_override",
getTextClassifierServicePackageOverride()).println();
- pw.printPair("use_default_system_text_classifier_as_default_impl",
- getUseDefaultTextClassifierAsDefaultImplementation()).println();
pw.decreaseIndent();
}
-
- private static List<String> getDeviceConfigStringList(String key, List<String> defaultValue) {
- return parse(
- DeviceConfig.getString(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key, null),
- defaultValue);
- }
-
- private static float[] getDeviceConfigFloatArray(String key, float[] defaultValue) {
- return parse(
- DeviceConfig.getString(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key, null),
- defaultValue);
- }
-
- private static List<String> parse(@Nullable String listStr, List<String> defaultValue) {
- if (listStr != null) {
- return Collections.unmodifiableList(Arrays.asList(listStr.split(DELIMITER)));
- }
- return defaultValue;
- }
-
- private static float[] parse(@Nullable String arrayStr, float[] defaultValue) {
- if (arrayStr != null) {
- final String[] split = arrayStr.split(DELIMITER);
- if (split.length != defaultValue.length) {
- return defaultValue;
- }
- final float[] result = new float[split.length];
- for (int i = 0; i < split.length; i++) {
- try {
- result[i] = Float.parseFloat(split[i]);
- } catch (NumberFormatException e) {
- return defaultValue;
- }
- }
- return result;
- } else {
- return defaultValue;
- }
- }
} \ No newline at end of file
diff --git a/core/java/android/view/textclassifier/TextClassificationContext.java b/core/java/android/view/textclassifier/TextClassificationContext.java
index f2323c625d15..5d5683f7110e 100644
--- a/core/java/android/view/textclassifier/TextClassificationContext.java
+++ b/core/java/android/view/textclassifier/TextClassificationContext.java
@@ -31,7 +31,6 @@ import java.util.Objects;
*/
public final class TextClassificationContext implements Parcelable {
- // NOTE: Modify packageName only in the constructor or in setSystemTextClassifierMetadata()
private String mPackageName;
private final String mWidgetType;
@Nullable private final String mWidgetVersion;
@@ -47,7 +46,7 @@ public final class TextClassificationContext implements Parcelable {
}
/**
- * Returns the package name for the calling package.
+ * Returns the package name of the app that this context originated in.
*/
@NonNull
public String getPackageName() {
@@ -57,14 +56,10 @@ public final class TextClassificationContext implements Parcelable {
/**
* Sets the information about the {@link SystemTextClassifier} that sent this request.
*
- * <p><b>NOTE: </b>This will override the value returned in {@link getPackageName()}.
* @hide
*/
void setSystemTextClassifierMetadata(@Nullable SystemTextClassifierMetadata systemTcMetadata) {
mSystemTcMetadata = systemTcMetadata;
- if (mSystemTcMetadata != null) {
- mPackageName = mSystemTcMetadata.getCallingPackageName();
- }
}
/**
diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java
index dfbec9b67027..fa4f7d6d32da 100644
--- a/core/java/android/view/textclassifier/TextClassificationManager.java
+++ b/core/java/android/view/textclassifier/TextClassificationManager.java
@@ -19,21 +19,15 @@ package android.view.textclassifier;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemService;
-import android.app.ActivityThread;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.ServiceManager;
-import android.provider.DeviceConfig;
-import android.provider.DeviceConfig.Properties;
import android.view.textclassifier.TextClassifier.TextClassifierType;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
-import java.lang.ref.WeakReference;
import java.util.Objects;
-import java.util.Set;
/**
* Interface to the text classification service.
@@ -41,7 +35,7 @@ import java.util.Set;
@SystemService(Context.TEXT_CLASSIFICATION_SERVICE)
public final class TextClassificationManager {
- private static final String LOG_TAG = "TextClassificationManager";
+ private static final String LOG_TAG = TextClassifier.LOG_TAG;
private static final TextClassificationConstants sDefaultSettings =
new TextClassificationConstants();
@@ -52,15 +46,11 @@ public final class TextClassificationManager {
classificationContext, getTextClassifier());
private final Context mContext;
- private final SettingsObserver mSettingsObserver;
@GuardedBy("mLock")
@Nullable
private TextClassifier mCustomTextClassifier;
@GuardedBy("mLock")
- @Nullable
- private TextClassifier mLocalTextClassifier;
- @GuardedBy("mLock")
private TextClassificationSessionFactory mSessionFactory;
@GuardedBy("mLock")
private TextClassificationConstants mSettings;
@@ -69,7 +59,6 @@ public final class TextClassificationManager {
public TextClassificationManager(Context context) {
mContext = Objects.requireNonNull(context);
mSessionFactory = mDefaultSessionFactory;
- mSettingsObserver = new SettingsObserver(this);
}
/**
@@ -112,7 +101,7 @@ public final class TextClassificationManager {
*
* @see TextClassifier#LOCAL
* @see TextClassifier#SYSTEM
- * @see TextClassifier#DEFAULT_SERVICE
+ * @see TextClassifier#DEFAULT_SYSTEM
* @hide
*/
@UnsupportedAppUsage
@@ -189,28 +178,17 @@ public final class TextClassificationManager {
}
}
- @Override
- protected void finalize() throws Throwable {
- try {
- // Note that fields could be null if the constructor threw.
- if (mSettingsObserver != null) {
- DeviceConfig.removeOnPropertiesChangedListener(mSettingsObserver);
- }
- } finally {
- super.finalize();
- }
- }
-
/** @hide */
private TextClassifier getSystemTextClassifier(@TextClassifierType int type) {
synchronized (mLock) {
if (getSettings().isSystemTextClassifierEnabled()) {
try {
- Log.d(LOG_TAG, "Initializing SystemTextClassifier, type = " + type);
+ Log.d(LOG_TAG, "Initializing SystemTextClassifier, type = "
+ + TextClassifier.typeToString(type));
return new SystemTextClassifier(
mContext,
getSettings(),
- /* useDefault= */ type == TextClassifier.DEFAULT_SERVICE);
+ /* useDefault= */ type == TextClassifier.DEFAULT_SYSTEM);
} catch (ServiceManager.ServiceNotFoundException e) {
Log.e(LOG_TAG, "Could not initialize SystemTextClassifier", e);
}
@@ -224,49 +202,13 @@ public final class TextClassificationManager {
*/
@NonNull
private TextClassifier getLocalTextClassifier() {
- synchronized (mLock) {
- if (mLocalTextClassifier == null) {
- if (getSettings().isLocalTextClassifierEnabled()) {
- mLocalTextClassifier =
- new TextClassifierImpl(mContext, getSettings(), TextClassifier.NO_OP);
- } else {
- Log.d(LOG_TAG, "Local TextClassifier disabled");
- mLocalTextClassifier = TextClassifier.NO_OP;
- }
- }
- return mLocalTextClassifier;
- }
- }
-
- /** @hide */
- @VisibleForTesting
- public void invalidateForTesting() {
- invalidate();
- }
-
- private void invalidate() {
- synchronized (mLock) {
- mSettings = null;
- invalidateTextClassifiers();
- }
- }
-
- private void invalidateTextClassifiers() {
- synchronized (mLock) {
- mLocalTextClassifier = null;
- }
- }
-
- Context getApplicationContext() {
- return mContext.getApplicationContext() != null
- ? mContext.getApplicationContext()
- : mContext;
+ Log.d(LOG_TAG, "Local text-classifier not supported. Returning a no-op text-classifier.");
+ return TextClassifier.NO_OP;
}
/** @hide **/
public void dump(IndentingPrintWriter pw) {
- getLocalTextClassifier().dump(pw);
- getSystemTextClassifier(TextClassifier.DEFAULT_SERVICE).dump(pw);
+ getSystemTextClassifier(TextClassifier.DEFAULT_SYSTEM).dump(pw);
getSystemTextClassifier(TextClassifier.SYSTEM).dump(pw);
getSettings().dump(pw);
}
@@ -283,31 +225,4 @@ public final class TextClassificationManager {
return sDefaultSettings;
}
}
-
- private static final class SettingsObserver implements
- DeviceConfig.OnPropertiesChangedListener {
-
- private final WeakReference<TextClassificationManager> mTcm;
-
- SettingsObserver(TextClassificationManager tcm) {
- mTcm = new WeakReference<>(tcm);
- DeviceConfig.addOnPropertiesChangedListener(
- DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- ActivityThread.currentApplication().getMainExecutor(),
- this);
- }
-
- @Override
- public void onPropertiesChanged(Properties properties) {
- final TextClassificationManager tcm = mTcm.get();
- if (tcm != null) {
- final Set<String> keys = properties.getKeyset();
- if (keys.contains(TextClassificationConstants.SYSTEM_TEXT_CLASSIFIER_ENABLED)
- || keys.contains(
- TextClassificationConstants.LOCAL_TEXT_CLASSIFIER_ENABLED)) {
- tcm.invalidateTextClassifiers();
- }
- }
- }
- }
}
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 2cc226d5a601..6d5077a99c62 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -61,19 +61,32 @@ import java.util.Set;
public interface TextClassifier {
/** @hide */
- String DEFAULT_LOG_TAG = "androidtc";
+ String LOG_TAG = "androidtc";
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(value = {LOCAL, SYSTEM, DEFAULT_SERVICE})
+ @IntDef(value = {LOCAL, SYSTEM, DEFAULT_SYSTEM})
@interface TextClassifierType {} // TODO: Expose as system APIs.
/** Specifies a TextClassifier that runs locally in the app's process. @hide */
int LOCAL = 0;
/** Specifies a TextClassifier that runs in the system process and serves all apps. @hide */
int SYSTEM = 1;
/** Specifies the default TextClassifier that runs in the system process. @hide */
- int DEFAULT_SERVICE = 2;
+ int DEFAULT_SYSTEM = 2;
+
+ /** @hide */
+ static String typeToString(@TextClassifierType int type) {
+ switch (type) {
+ case LOCAL:
+ return "Local";
+ case SYSTEM:
+ return "System";
+ case DEFAULT_SYSTEM:
+ return "Default system";
+ }
+ return "Unknown";
+ }
/** The TextClassifier failed to run. */
String TYPE_UNKNOWN = "";
@@ -776,7 +789,7 @@ public interface TextClassifier {
static void checkMainThread() {
if (Looper.myLooper() == Looper.getMainLooper()) {
- Log.w(DEFAULT_LOG_TAG, "TextClassifier called on main thread");
+ Log.w(LOG_TAG, "TextClassifier called on main thread");
}
}
}
diff --git a/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java b/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java
deleted file mode 100644
index 8162699a74c6..000000000000
--- a/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view.textclassifier;
-
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXTCLASSIFIER_MODEL;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_SCORE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_SECOND_ENTITY_TYPE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_SESSION_ID;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_THIRD_ENTITY_TYPE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_WIDGET_TYPE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_WIDGET_VERSION;
-
-import android.metrics.LogMaker;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import java.util.Objects;
-
-
-/**
- * Log {@link TextClassifierEvent} by using Tron, only support language detection and
- * conversation actions.
- *
- * @hide
- */
-public final class TextClassifierEventTronLogger {
-
- private static final String TAG = "TCEventTronLogger";
-
- private final MetricsLogger mMetricsLogger;
-
- public TextClassifierEventTronLogger() {
- this(new MetricsLogger());
- }
-
- @VisibleForTesting
- public TextClassifierEventTronLogger(MetricsLogger metricsLogger) {
- mMetricsLogger = Objects.requireNonNull(metricsLogger);
- }
-
- /** Emits a text classifier event to the logs. */
- public void writeEvent(TextClassifierEvent event) {
- Objects.requireNonNull(event);
-
- int category = getCategory(event);
- if (category == -1) {
- Log.w(TAG, "Unknown category: " + event.getEventCategory());
- return;
- }
- final LogMaker log = new LogMaker(category)
- .setSubtype(getLogType(event))
- .addTaggedData(FIELD_TEXT_CLASSIFIER_SESSION_ID, event.getResultId())
- .addTaggedData(FIELD_TEXTCLASSIFIER_MODEL, getModelName(event));
- if (event.getScores().length >= 1) {
- log.addTaggedData(FIELD_TEXT_CLASSIFIER_SCORE, event.getScores()[0]);
- }
- String[] entityTypes = event.getEntityTypes();
- // The old logger does not support a field of list type, and thus workaround by store them
- // in three separate fields. This is not an issue with the new logger.
- if (entityTypes.length >= 1) {
- log.addTaggedData(FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE, entityTypes[0]);
- }
- if (entityTypes.length >= 2) {
- log.addTaggedData(FIELD_TEXT_CLASSIFIER_SECOND_ENTITY_TYPE, entityTypes[1]);
- }
- if (entityTypes.length >= 3) {
- log.addTaggedData(FIELD_TEXT_CLASSIFIER_THIRD_ENTITY_TYPE, entityTypes[2]);
- }
- TextClassificationContext eventContext = event.getEventContext();
- if (eventContext != null) {
- log.addTaggedData(FIELD_TEXT_CLASSIFIER_WIDGET_TYPE, eventContext.getWidgetType());
- log.addTaggedData(FIELD_TEXT_CLASSIFIER_WIDGET_VERSION,
- eventContext.getWidgetVersion());
- log.setPackageName(eventContext.getPackageName());
- }
- mMetricsLogger.write(log);
- debugLog(log);
- }
-
- private static String getModelName(TextClassifierEvent event) {
- if (event.getModelName() != null) {
- return event.getModelName();
- }
- return SelectionSessionLogger.SignatureParser.getModelName(event.getResultId());
- }
-
- private static int getCategory(TextClassifierEvent event) {
- switch (event.getEventCategory()) {
- case TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS:
- return MetricsEvent.CONVERSATION_ACTIONS;
- case TextClassifierEvent.CATEGORY_LANGUAGE_DETECTION:
- return MetricsEvent.LANGUAGE_DETECTION;
- }
- return -1;
- }
-
- private static int getLogType(TextClassifierEvent event) {
- switch (event.getEventType()) {
- case TextClassifierEvent.TYPE_SMART_ACTION:
- return MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE;
- case TextClassifierEvent.TYPE_ACTIONS_SHOWN:
- return MetricsEvent.ACTION_TEXT_CLASSIFIER_ACTIONS_SHOWN;
- case TextClassifierEvent.TYPE_MANUAL_REPLY:
- return MetricsEvent.ACTION_TEXT_CLASSIFIER_MANUAL_REPLY;
- case TextClassifierEvent.TYPE_ACTIONS_GENERATED:
- return MetricsEvent.ACTION_TEXT_CLASSIFIER_ACTIONS_GENERATED;
- default:
- return MetricsEvent.VIEW_UNKNOWN;
- }
- }
-
- private String toCategoryName(int category) {
- switch (category) {
- case MetricsEvent.CONVERSATION_ACTIONS:
- return "conversation_actions";
- case MetricsEvent.LANGUAGE_DETECTION:
- return "language_detection";
- }
- return "unknown";
- }
-
- private String toEventName(int logType) {
- switch (logType) {
- case MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE:
- return "smart_share";
- case MetricsEvent.ACTION_TEXT_CLASSIFIER_ACTIONS_SHOWN:
- return "actions_shown";
- case MetricsEvent.ACTION_TEXT_CLASSIFIER_MANUAL_REPLY:
- return "manual_reply";
- case MetricsEvent.ACTION_TEXT_CLASSIFIER_ACTIONS_GENERATED:
- return "actions_generated";
- }
- return "unknown";
- }
-
- private void debugLog(LogMaker log) {
- if (!Log.ENABLE_FULL_LOGGING) {
- return;
- }
- final String id = String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_SESSION_ID));
- final String categoryName = toCategoryName(log.getCategory());
- final String eventName = toEventName(log.getSubtype());
- final String widgetType =
- String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_WIDGET_TYPE));
- final String widgetVersion =
- String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_WIDGET_VERSION));
- final String model = String.valueOf(log.getTaggedData(FIELD_TEXTCLASSIFIER_MODEL));
- final String firstEntityType =
- String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE));
- final String secondEntityType =
- String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_SECOND_ENTITY_TYPE));
- final String thirdEntityType =
- String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_THIRD_ENTITY_TYPE));
- final String score =
- String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_SCORE));
-
- StringBuilder builder = new StringBuilder();
- builder.append("writeEvent: ");
- builder.append("id=").append(id);
- builder.append(", category=").append(categoryName);
- builder.append(", eventName=").append(eventName);
- builder.append(", widgetType=").append(widgetType);
- builder.append(", widgetVersion=").append(widgetVersion);
- builder.append(", model=").append(model);
- builder.append(", firstEntityType=").append(firstEntityType);
- builder.append(", secondEntityType=").append(secondEntityType);
- builder.append(", thirdEntityType=").append(thirdEntityType);
- builder.append(", score=").append(score);
-
- Log.v(TAG, builder.toString());
- }
-}
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
deleted file mode 100644
index d7149ee05b57..000000000000
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ /dev/null
@@ -1,911 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.WorkerThread;
-import android.app.RemoteAction;
-import android.content.Context;
-import android.content.Intent;
-import android.icu.util.ULocale;
-import android.os.Bundle;
-import android.os.LocaleList;
-import android.os.ParcelFileDescriptor;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Pair;
-import android.view.textclassifier.ActionsModelParamsSupplier.ActionsModelParams;
-import android.view.textclassifier.intent.ClassificationIntentFactory;
-import android.view.textclassifier.intent.LabeledIntent;
-import android.view.textclassifier.intent.LegacyClassificationIntentFactory;
-import android.view.textclassifier.intent.TemplateClassificationIntentFactory;
-import android.view.textclassifier.intent.TemplateIntentFactory;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.IndentingPrintWriter;
-import com.android.internal.util.Preconditions;
-
-import com.google.android.textclassifier.ActionsSuggestionsModel;
-import com.google.android.textclassifier.AnnotatorModel;
-import com.google.android.textclassifier.LangIdModel;
-import com.google.android.textclassifier.LangIdModel.LanguageResult;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.time.Instant;
-import java.time.ZonedDateTime;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.function.Supplier;
-
-/**
- * Default implementation of the {@link TextClassifier} interface.
- *
- * <p>This class uses machine learning to recognize entities in text.
- * Unless otherwise stated, methods of this class are blocking operations and should most
- * likely not be called on the UI thread.
- *
- * @hide
- */
-public final class TextClassifierImpl implements TextClassifier {
-
- private static final String LOG_TAG = DEFAULT_LOG_TAG;
-
- private static final boolean DEBUG = false;
-
- private static final File FACTORY_MODEL_DIR = new File("/etc/textclassifier/");
- // Annotator
- private static final String ANNOTATOR_FACTORY_MODEL_FILENAME_REGEX =
- "textclassifier\\.(.*)\\.model";
- private static final File ANNOTATOR_UPDATED_MODEL_FILE =
- new File("/data/misc/textclassifier/textclassifier.model");
-
- // LangID
- private static final String LANG_ID_FACTORY_MODEL_FILENAME_REGEX = "lang_id.model";
- private static final File UPDATED_LANG_ID_MODEL_FILE =
- new File("/data/misc/textclassifier/lang_id.model");
-
- // Actions
- private static final String ACTIONS_FACTORY_MODEL_FILENAME_REGEX =
- "actions_suggestions\\.(.*)\\.model";
- private static final File UPDATED_ACTIONS_MODEL =
- new File("/data/misc/textclassifier/actions_suggestions.model");
-
- private final Context mContext;
- private final TextClassifier mFallback;
- private final GenerateLinksLogger mGenerateLinksLogger;
-
- private final Object mLock = new Object();
-
- @GuardedBy("mLock")
- private ModelFileManager.ModelFile mAnnotatorModelInUse;
- @GuardedBy("mLock")
- private AnnotatorModel mAnnotatorImpl;
-
- @GuardedBy("mLock")
- private ModelFileManager.ModelFile mLangIdModelInUse;
- @GuardedBy("mLock")
- private LangIdModel mLangIdImpl;
-
- @GuardedBy("mLock")
- private ModelFileManager.ModelFile mActionModelInUse;
- @GuardedBy("mLock")
- private ActionsSuggestionsModel mActionsImpl;
-
- private final SelectionSessionLogger mSessionLogger = new SelectionSessionLogger();
- private final TextClassifierEventTronLogger mTextClassifierEventTronLogger =
- new TextClassifierEventTronLogger();
-
- private final TextClassificationConstants mSettings;
-
- private final ModelFileManager mAnnotatorModelFileManager;
- private final ModelFileManager mLangIdModelFileManager;
- private final ModelFileManager mActionsModelFileManager;
-
- private final ClassificationIntentFactory mClassificationIntentFactory;
- private final TemplateIntentFactory mTemplateIntentFactory;
- private final Supplier<ActionsModelParams> mActionsModelParamsSupplier;
-
- public TextClassifierImpl(
- Context context, TextClassificationConstants settings, TextClassifier fallback) {
- mContext = Objects.requireNonNull(context);
- mFallback = Objects.requireNonNull(fallback);
- mSettings = Objects.requireNonNull(settings);
- mGenerateLinksLogger = new GenerateLinksLogger(mSettings.getGenerateLinksLogSampleRate());
- mAnnotatorModelFileManager = new ModelFileManager(
- new ModelFileManager.ModelFileSupplierImpl(
- FACTORY_MODEL_DIR,
- ANNOTATOR_FACTORY_MODEL_FILENAME_REGEX,
- ANNOTATOR_UPDATED_MODEL_FILE,
- AnnotatorModel::getVersion,
- AnnotatorModel::getLocales));
- mLangIdModelFileManager = new ModelFileManager(
- new ModelFileManager.ModelFileSupplierImpl(
- FACTORY_MODEL_DIR,
- LANG_ID_FACTORY_MODEL_FILENAME_REGEX,
- UPDATED_LANG_ID_MODEL_FILE,
- LangIdModel::getVersion,
- fd -> ModelFileManager.ModelFile.LANGUAGE_INDEPENDENT));
- mActionsModelFileManager = new ModelFileManager(
- new ModelFileManager.ModelFileSupplierImpl(
- FACTORY_MODEL_DIR,
- ACTIONS_FACTORY_MODEL_FILENAME_REGEX,
- UPDATED_ACTIONS_MODEL,
- ActionsSuggestionsModel::getVersion,
- ActionsSuggestionsModel::getLocales));
-
- mTemplateIntentFactory = new TemplateIntentFactory();
- mClassificationIntentFactory = mSettings.isTemplateIntentFactoryEnabled()
- ? new TemplateClassificationIntentFactory(
- mTemplateIntentFactory, new LegacyClassificationIntentFactory())
- : new LegacyClassificationIntentFactory();
- mActionsModelParamsSupplier = new ActionsModelParamsSupplier(mContext,
- () -> {
- synchronized (mLock) {
- // Clear mActionsImpl here, so that we will create a new
- // ActionsSuggestionsModel object with the new flag in the next request.
- mActionsImpl = null;
- mActionModelInUse = null;
- }
- });
- }
-
- public TextClassifierImpl(Context context, TextClassificationConstants settings) {
- this(context, settings, TextClassifier.NO_OP);
- }
-
- /** @inheritDoc */
- @Override
- @WorkerThread
- public TextSelection suggestSelection(TextSelection.Request request) {
- Objects.requireNonNull(request);
- Utils.checkMainThread();
- try {
- final int rangeLength = request.getEndIndex() - request.getStartIndex();
- final String string = request.getText().toString();
- if (string.length() > 0
- && rangeLength <= mSettings.getSuggestSelectionMaxRangeLength()) {
- final String localesString = concatenateLocales(request.getDefaultLocales());
- final String detectLanguageTags = detectLanguageTagsFromText(request.getText());
- final ZonedDateTime refTime = ZonedDateTime.now();
- final AnnotatorModel annotatorImpl = getAnnotatorImpl(request.getDefaultLocales());
- final int start;
- final int end;
- if (mSettings.isModelDarkLaunchEnabled() && !request.isDarkLaunchAllowed()) {
- start = request.getStartIndex();
- end = request.getEndIndex();
- } else {
- final int[] startEnd = annotatorImpl.suggestSelection(
- string, request.getStartIndex(), request.getEndIndex(),
- new AnnotatorModel.SelectionOptions(localesString, detectLanguageTags));
- start = startEnd[0];
- end = startEnd[1];
- }
- if (start < end
- && start >= 0 && end <= string.length()
- && start <= request.getStartIndex() && end >= request.getEndIndex()) {
- final TextSelection.Builder tsBuilder = new TextSelection.Builder(start, end);
- final AnnotatorModel.ClassificationResult[] results =
- annotatorImpl.classifyText(
- string, start, end,
- new AnnotatorModel.ClassificationOptions(
- refTime.toInstant().toEpochMilli(),
- refTime.getZone().getId(),
- localesString,
- detectLanguageTags),
- // Passing null here to suppress intent generation
- // TODO: Use an explicit flag to suppress it.
- /* appContext */ null,
- /* deviceLocales */null);
- final int size = results.length;
- for (int i = 0; i < size; i++) {
- tsBuilder.setEntityType(results[i].getCollection(), results[i].getScore());
- }
- return tsBuilder.setId(createId(
- string, request.getStartIndex(), request.getEndIndex()))
- .build();
- } else {
- // We can not trust the result. Log the issue and ignore the result.
- Log.d(LOG_TAG, "Got bad indices for input text. Ignoring result.");
- }
- }
- } catch (Throwable t) {
- // Avoid throwing from this method. Log the error.
- Log.e(LOG_TAG,
- "Error suggesting selection for text. No changes to selection suggested.",
- t);
- }
- // Getting here means something went wrong, return a NO_OP result.
- return mFallback.suggestSelection(request);
- }
-
- /** @inheritDoc */
- @Override
- @WorkerThread
- public TextClassification classifyText(TextClassification.Request request) {
- Objects.requireNonNull(request);
- Utils.checkMainThread();
- try {
- final int rangeLength = request.getEndIndex() - request.getStartIndex();
- final String string = request.getText().toString();
- if (string.length() > 0 && rangeLength <= mSettings.getClassifyTextMaxRangeLength()) {
- final String localesString = concatenateLocales(request.getDefaultLocales());
- final String detectLanguageTags = detectLanguageTagsFromText(request.getText());
- final ZonedDateTime refTime = request.getReferenceTime() != null
- ? request.getReferenceTime() : ZonedDateTime.now();
- final AnnotatorModel.ClassificationResult[] results =
- getAnnotatorImpl(request.getDefaultLocales())
- .classifyText(
- string, request.getStartIndex(), request.getEndIndex(),
- new AnnotatorModel.ClassificationOptions(
- refTime.toInstant().toEpochMilli(),
- refTime.getZone().getId(),
- localesString,
- detectLanguageTags),
- mContext,
- getResourceLocalesString()
- );
- if (results.length > 0) {
- return createClassificationResult(
- results, string,
- request.getStartIndex(), request.getEndIndex(), refTime.toInstant());
- }
- }
- } catch (Throwable t) {
- // Avoid throwing from this method. Log the error.
- Log.e(LOG_TAG, "Error getting text classification info.", t);
- }
- // Getting here means something went wrong, return a NO_OP result.
- return mFallback.classifyText(request);
- }
-
- /** @inheritDoc */
- @Override
- @WorkerThread
- public TextLinks generateLinks(@NonNull TextLinks.Request request) {
- Objects.requireNonNull(request);
- Utils.checkMainThread();
- if (!Utils.checkTextLength(request.getText(), getMaxGenerateLinksTextLength())) {
- return mFallback.generateLinks(request);
- }
-
- if (!mSettings.isSmartLinkifyEnabled() && request.isLegacyFallback()) {
- return Utils.generateLegacyLinks(request);
- }
-
- final String textString = request.getText().toString();
- final TextLinks.Builder builder = new TextLinks.Builder(textString);
-
- try {
- final long startTimeMs = System.currentTimeMillis();
- final ZonedDateTime refTime = ZonedDateTime.now();
- final Collection<String> entitiesToIdentify = request.getEntityConfig() != null
- ? request.getEntityConfig().resolveEntityListModifications(
- getEntitiesForHints(request.getEntityConfig().getHints()))
- : mSettings.getEntityListDefault();
- final String localesString = concatenateLocales(request.getDefaultLocales());
- final String detectLanguageTags = detectLanguageTagsFromText(request.getText());
- final AnnotatorModel annotatorImpl =
- getAnnotatorImpl(request.getDefaultLocales());
- final boolean isSerializedEntityDataEnabled =
- ExtrasUtils.isSerializedEntityDataEnabled(request);
- final AnnotatorModel.AnnotatedSpan[] annotations =
- annotatorImpl.annotate(
- textString,
- new AnnotatorModel.AnnotationOptions(
- refTime.toInstant().toEpochMilli(),
- refTime.getZone().getId(),
- localesString,
- detectLanguageTags,
- entitiesToIdentify,
- AnnotatorModel.AnnotationUsecase.SMART.getValue(),
- isSerializedEntityDataEnabled));
- for (AnnotatorModel.AnnotatedSpan span : annotations) {
- final AnnotatorModel.ClassificationResult[] results =
- span.getClassification();
- if (results.length == 0
- || !entitiesToIdentify.contains(results[0].getCollection())) {
- continue;
- }
- final Map<String, Float> entityScores = new ArrayMap<>();
- for (int i = 0; i < results.length; i++) {
- entityScores.put(results[i].getCollection(), results[i].getScore());
- }
- Bundle extras = new Bundle();
- if (isSerializedEntityDataEnabled) {
- ExtrasUtils.putEntities(extras, results);
- }
- builder.addLink(span.getStartIndex(), span.getEndIndex(), entityScores, extras);
- }
- final TextLinks links = builder.build();
- final long endTimeMs = System.currentTimeMillis();
- final String callingPackageName = request.getCallingPackageName() == null
- ? mContext.getPackageName() // local (in process) TC.
- : request.getCallingPackageName();
- mGenerateLinksLogger.logGenerateLinks(
- request.getText(), links, callingPackageName, endTimeMs - startTimeMs);
- return links;
- } catch (Throwable t) {
- // Avoid throwing from this method. Log the error.
- Log.e(LOG_TAG, "Error getting links info.", t);
- }
- return mFallback.generateLinks(request);
- }
-
- /** @inheritDoc */
- @Override
- public int getMaxGenerateLinksTextLength() {
- return mSettings.getGenerateLinksMaxTextLength();
- }
-
- private Collection<String> getEntitiesForHints(Collection<String> hints) {
- final boolean editable = hints.contains(HINT_TEXT_IS_EDITABLE);
- final boolean notEditable = hints.contains(HINT_TEXT_IS_NOT_EDITABLE);
-
- // Use the default if there is no hint, or conflicting ones.
- final boolean useDefault = editable == notEditable;
- if (useDefault) {
- return mSettings.getEntityListDefault();
- } else if (editable) {
- return mSettings.getEntityListEditable();
- } else { // notEditable
- return mSettings.getEntityListNotEditable();
- }
- }
-
- /** @inheritDoc */
- @Override
- public void onSelectionEvent(SelectionEvent event) {
- mSessionLogger.writeEvent(event);
- }
-
- @Override
- public void onTextClassifierEvent(TextClassifierEvent event) {
- if (DEBUG) {
- Log.d(DEFAULT_LOG_TAG, "onTextClassifierEvent() called with: event = [" + event + "]");
- }
- try {
- final SelectionEvent selEvent = event.toSelectionEvent();
- if (selEvent != null) {
- mSessionLogger.writeEvent(selEvent);
- } else {
- mTextClassifierEventTronLogger.writeEvent(event);
- }
- } catch (Exception e) {
- Log.e(LOG_TAG, "Error writing event", e);
- }
- }
-
- /** @inheritDoc */
- @Override
- public TextLanguage detectLanguage(@NonNull TextLanguage.Request request) {
- Objects.requireNonNull(request);
- Utils.checkMainThread();
- try {
- final TextLanguage.Builder builder = new TextLanguage.Builder();
- final LangIdModel.LanguageResult[] langResults =
- getLangIdImpl().detectLanguages(request.getText().toString());
- for (int i = 0; i < langResults.length; i++) {
- builder.putLocale(
- ULocale.forLanguageTag(langResults[i].getLanguage()),
- langResults[i].getScore());
- }
- return builder.build();
- } catch (Throwable t) {
- // Avoid throwing from this method. Log the error.
- Log.e(LOG_TAG, "Error detecting text language.", t);
- }
- return mFallback.detectLanguage(request);
- }
-
- @Override
- public ConversationActions suggestConversationActions(ConversationActions.Request request) {
- Objects.requireNonNull(request);
- Utils.checkMainThread();
- try {
- ActionsSuggestionsModel actionsImpl = getActionsImpl();
- if (actionsImpl == null) {
- // Actions model is optional, fallback if it is not available.
- return mFallback.suggestConversationActions(request);
- }
- ActionsSuggestionsModel.ConversationMessage[] nativeMessages =
- ActionsSuggestionsHelper.toNativeMessages(
- request.getConversation(), this::detectLanguageTagsFromText);
- if (nativeMessages.length == 0) {
- return mFallback.suggestConversationActions(request);
- }
- ActionsSuggestionsModel.Conversation nativeConversation =
- new ActionsSuggestionsModel.Conversation(nativeMessages);
-
- ActionsSuggestionsModel.ActionSuggestion[] nativeSuggestions =
- actionsImpl.suggestActionsWithIntents(
- nativeConversation,
- null,
- mContext,
- getResourceLocalesString(),
- getAnnotatorImpl(LocaleList.getDefault()));
- return createConversationActionResult(request, nativeSuggestions);
- } catch (Throwable t) {
- // Avoid throwing from this method. Log the error.
- Log.e(LOG_TAG, "Error suggesting conversation actions.", t);
- }
- return mFallback.suggestConversationActions(request);
- }
-
- /**
- * Returns the {@link ConversationAction} result, with a non-null extras.
- * <p>
- * Whenever the RemoteAction is non-null, you can expect its corresponding intent
- * with a non-null component name is in the extras.
- */
- private ConversationActions createConversationActionResult(
- ConversationActions.Request request,
- ActionsSuggestionsModel.ActionSuggestion[] nativeSuggestions) {
- Collection<String> expectedTypes = resolveActionTypesFromRequest(request);
- List<ConversationAction> conversationActions = new ArrayList<>();
- for (ActionsSuggestionsModel.ActionSuggestion nativeSuggestion : nativeSuggestions) {
- String actionType = nativeSuggestion.getActionType();
- if (!expectedTypes.contains(actionType)) {
- continue;
- }
- LabeledIntent.Result labeledIntentResult =
- ActionsSuggestionsHelper.createLabeledIntentResult(
- mContext,
- mTemplateIntentFactory,
- nativeSuggestion);
- RemoteAction remoteAction = null;
- Bundle extras = new Bundle();
- if (labeledIntentResult != null) {
- remoteAction = labeledIntentResult.remoteAction;
- ExtrasUtils.putActionIntent(extras, labeledIntentResult.resolvedIntent);
- }
- ExtrasUtils.putSerializedEntityData(extras, nativeSuggestion.getSerializedEntityData());
- ExtrasUtils.putEntitiesExtras(
- extras,
- TemplateIntentFactory.nameVariantsToBundle(nativeSuggestion.getEntityData()));
- conversationActions.add(
- new ConversationAction.Builder(actionType)
- .setConfidenceScore(nativeSuggestion.getScore())
- .setTextReply(nativeSuggestion.getResponseText())
- .setAction(remoteAction)
- .setExtras(extras)
- .build());
- }
- conversationActions =
- ActionsSuggestionsHelper.removeActionsWithDuplicates(conversationActions);
- if (request.getMaxSuggestions() >= 0
- && conversationActions.size() > request.getMaxSuggestions()) {
- conversationActions = conversationActions.subList(0, request.getMaxSuggestions());
- }
- String resultId = ActionsSuggestionsHelper.createResultId(
- mContext,
- request.getConversation(),
- mActionModelInUse.getVersion(),
- mActionModelInUse.getSupportedLocales());
- return new ConversationActions(conversationActions, resultId);
- }
-
- @Nullable
- private String detectLanguageTagsFromText(CharSequence text) {
- if (!mSettings.isDetectLanguagesFromTextEnabled()) {
- return null;
- }
- final float threshold = getLangIdThreshold();
- if (threshold < 0 || threshold > 1) {
- Log.w(LOG_TAG,
- "[detectLanguageTagsFromText] unexpected threshold is found: " + threshold);
- return null;
- }
- TextLanguage.Request request = new TextLanguage.Request.Builder(text).build();
- TextLanguage textLanguage = detectLanguage(request);
- int localeHypothesisCount = textLanguage.getLocaleHypothesisCount();
- List<String> languageTags = new ArrayList<>();
- for (int i = 0; i < localeHypothesisCount; i++) {
- ULocale locale = textLanguage.getLocale(i);
- if (textLanguage.getConfidenceScore(locale) < threshold) {
- break;
- }
- languageTags.add(locale.toLanguageTag());
- }
- if (languageTags.isEmpty()) {
- return null;
- }
- return String.join(",", languageTags);
- }
-
- private Collection<String> resolveActionTypesFromRequest(ConversationActions.Request request) {
- List<String> defaultActionTypes =
- request.getHints().contains(ConversationActions.Request.HINT_FOR_NOTIFICATION)
- ? mSettings.getNotificationConversationActionTypes()
- : mSettings.getInAppConversationActionTypes();
- return request.getTypeConfig().resolveEntityListModifications(defaultActionTypes);
- }
-
- private AnnotatorModel getAnnotatorImpl(LocaleList localeList)
- throws FileNotFoundException {
- synchronized (mLock) {
- localeList = localeList == null ? LocaleList.getDefault() : localeList;
- final ModelFileManager.ModelFile bestModel =
- mAnnotatorModelFileManager.findBestModelFile(localeList);
- if (bestModel == null) {
- throw new FileNotFoundException(
- "No annotator model for " + localeList.toLanguageTags());
- }
- if (mAnnotatorImpl == null || !Objects.equals(mAnnotatorModelInUse, bestModel)) {
- Log.d(DEFAULT_LOG_TAG, "Loading " + bestModel);
- final ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
- new File(bestModel.getPath()), ParcelFileDescriptor.MODE_READ_ONLY);
- try {
- if (pfd != null) {
- // The current annotator model may be still used by another thread / model.
- // Do not call close() here, and let the GC to clean it up when no one else
- // is using it.
- mAnnotatorImpl = new AnnotatorModel(pfd.getFd());
- mAnnotatorModelInUse = bestModel;
- }
- } finally {
- maybeCloseAndLogError(pfd);
- }
- }
- return mAnnotatorImpl;
- }
- }
-
- private LangIdModel getLangIdImpl() throws FileNotFoundException {
- synchronized (mLock) {
- final ModelFileManager.ModelFile bestModel =
- mLangIdModelFileManager.findBestModelFile(null);
- if (bestModel == null) {
- throw new FileNotFoundException("No LangID model is found");
- }
- if (mLangIdImpl == null || !Objects.equals(mLangIdModelInUse, bestModel)) {
- Log.d(DEFAULT_LOG_TAG, "Loading " + bestModel);
- final ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
- new File(bestModel.getPath()), ParcelFileDescriptor.MODE_READ_ONLY);
- try {
- if (pfd != null) {
- mLangIdImpl = new LangIdModel(pfd.getFd());
- mLangIdModelInUse = bestModel;
- }
- } finally {
- maybeCloseAndLogError(pfd);
- }
- }
- return mLangIdImpl;
- }
- }
-
- @Nullable
- private ActionsSuggestionsModel getActionsImpl() throws FileNotFoundException {
- synchronized (mLock) {
- // TODO: Use LangID to determine the locale we should use here?
- final ModelFileManager.ModelFile bestModel =
- mActionsModelFileManager.findBestModelFile(LocaleList.getDefault());
- if (bestModel == null) {
- return null;
- }
- if (mActionsImpl == null || !Objects.equals(mActionModelInUse, bestModel)) {
- Log.d(DEFAULT_LOG_TAG, "Loading " + bestModel);
- final ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
- new File(bestModel.getPath()), ParcelFileDescriptor.MODE_READ_ONLY);
- try {
- if (pfd == null) {
- Log.d(LOG_TAG, "Failed to read the model file: " + bestModel.getPath());
- return null;
- }
- ActionsModelParams params = mActionsModelParamsSupplier.get();
- mActionsImpl = new ActionsSuggestionsModel(
- pfd.getFd(), params.getSerializedPreconditions(bestModel));
- mActionModelInUse = bestModel;
- } finally {
- maybeCloseAndLogError(pfd);
- }
- }
- return mActionsImpl;
- }
- }
-
- private String createId(String text, int start, int end) {
- synchronized (mLock) {
- return SelectionSessionLogger.createId(text, start, end, mContext,
- mAnnotatorModelInUse.getVersion(),
- mAnnotatorModelInUse.getSupportedLocales());
- }
- }
-
- private static String concatenateLocales(@Nullable LocaleList locales) {
- return (locales == null) ? "" : locales.toLanguageTags();
- }
-
- private TextClassification createClassificationResult(
- AnnotatorModel.ClassificationResult[] classifications,
- String text, int start, int end, @Nullable Instant referenceTime) {
- final String classifiedText = text.substring(start, end);
- final TextClassification.Builder builder = new TextClassification.Builder()
- .setText(classifiedText);
-
- final int typeCount = classifications.length;
- AnnotatorModel.ClassificationResult highestScoringResult =
- typeCount > 0 ? classifications[0] : null;
- for (int i = 0; i < typeCount; i++) {
- builder.setEntityType(classifications[i]);
- if (classifications[i].getScore() > highestScoringResult.getScore()) {
- highestScoringResult = classifications[i];
- }
- }
-
- final Pair<Bundle, Bundle> languagesBundles = generateLanguageBundles(text, start, end);
- final Bundle textLanguagesBundle = languagesBundles.first;
- final Bundle foreignLanguageBundle = languagesBundles.second;
- builder.setForeignLanguageExtra(foreignLanguageBundle);
-
- boolean isPrimaryAction = true;
- final List<LabeledIntent> labeledIntents = mClassificationIntentFactory.create(
- mContext,
- classifiedText,
- foreignLanguageBundle != null,
- referenceTime,
- highestScoringResult);
- final LabeledIntent.TitleChooser titleChooser =
- (labeledIntent, resolveInfo) -> labeledIntent.titleWithoutEntity;
-
- for (LabeledIntent labeledIntent : labeledIntents) {
- final LabeledIntent.Result result =
- labeledIntent.resolve(mContext, titleChooser, textLanguagesBundle);
- if (result == null) {
- continue;
- }
-
- final Intent intent = result.resolvedIntent;
- final RemoteAction action = result.remoteAction;
- if (isPrimaryAction) {
- // For O backwards compatibility, the first RemoteAction is also written to the
- // legacy API fields.
- builder.setIcon(action.getIcon().loadDrawable(mContext));
- builder.setLabel(action.getTitle().toString());
- builder.setIntent(intent);
- builder.setOnClickListener(TextClassification.createIntentOnClickListener(
- TextClassification.createPendingIntent(
- mContext, intent, labeledIntent.requestCode)));
- isPrimaryAction = false;
- }
- builder.addAction(action, intent);
- }
- return builder.setId(createId(text, start, end)).build();
- }
-
- /**
- * Returns a bundle pair with language detection information for extras.
- * <p>
- * Pair.first = textLanguagesBundle - A bundle containing information about all detected
- * languages in the text. May be null if language detection fails or is disabled. This is
- * typically expected to be added to a textClassifier generated remote action intent.
- * See {@link ExtrasUtils#putTextLanguagesExtra(Bundle, Bundle)}.
- * See {@link ExtrasUtils#getTopLanguage(Intent)}.
- * <p>
- * Pair.second = foreignLanguageBundle - A bundle with the language and confidence score if the
- * system finds the text to be in a foreign language. Otherwise is null.
- * See {@link TextClassification.Builder#setForeignLanguageExtra(Bundle)}.
- *
- * @param context the context of the text to detect languages for
- * @param start the start index of the text
- * @param end the end index of the text
- */
- // TODO: Revisit this algorithm.
- // TODO: Consider making this public API.
- private Pair<Bundle, Bundle> generateLanguageBundles(String context, int start, int end) {
- if (!mSettings.isTranslateInClassificationEnabled()) {
- return null;
- }
- try {
- final float threshold = getLangIdThreshold();
- if (threshold < 0 || threshold > 1) {
- Log.w(LOG_TAG,
- "[detectForeignLanguage] unexpected threshold is found: " + threshold);
- return Pair.create(null, null);
- }
-
- final EntityConfidence languageScores = detectLanguages(context, start, end);
- if (languageScores.getEntities().isEmpty()) {
- return Pair.create(null, null);
- }
-
- final Bundle textLanguagesBundle = new Bundle();
- ExtrasUtils.putTopLanguageScores(textLanguagesBundle, languageScores);
-
- final String language = languageScores.getEntities().get(0);
- final float score = languageScores.getConfidenceScore(language);
- if (score < threshold) {
- return Pair.create(textLanguagesBundle, null);
- }
-
- Log.v(LOG_TAG, String.format(
- Locale.US, "Language detected: <%s:%.2f>", language, score));
-
- final Locale detected = new Locale(language);
- final LocaleList deviceLocales = LocaleList.getDefault();
- final int size = deviceLocales.size();
- for (int i = 0; i < size; i++) {
- if (deviceLocales.get(i).getLanguage().equals(detected.getLanguage())) {
- return Pair.create(textLanguagesBundle, null);
- }
- }
- final Bundle foreignLanguageBundle = ExtrasUtils.createForeignLanguageExtra(
- detected.getLanguage(), score, getLangIdImpl().getVersion());
- return Pair.create(textLanguagesBundle, foreignLanguageBundle);
- } catch (Throwable t) {
- Log.e(LOG_TAG, "Error generating language bundles.", t);
- }
- return Pair.create(null, null);
- }
-
- /**
- * Detect the language of a piece of text by taking surrounding text into consideration.
- *
- * @param text text providing context for the text for which its language is to be detected
- * @param start the start index of the text to detect its language
- * @param end the end index of the text to detect its language
- */
- // TODO: Revisit this algorithm.
- private EntityConfidence detectLanguages(String text, int start, int end)
- throws FileNotFoundException {
- Preconditions.checkArgument(start >= 0);
- Preconditions.checkArgument(end <= text.length());
- Preconditions.checkArgument(start <= end);
-
- final float[] langIdContextSettings = mSettings.getLangIdContextSettings();
- // The minimum size of text to prefer for detection.
- final int minimumTextSize = (int) langIdContextSettings[0];
- // For reducing the score when text is less than the preferred size.
- final float penalizeRatio = langIdContextSettings[1];
- // Original detection score to surrounding text detection score ratios.
- final float subjectTextScoreRatio = langIdContextSettings[2];
- final float moreTextScoreRatio = 1f - subjectTextScoreRatio;
- Log.v(LOG_TAG,
- String.format(Locale.US, "LangIdContextSettings: "
- + "minimumTextSize=%d, penalizeRatio=%.2f, "
- + "subjectTextScoreRatio=%.2f, moreTextScoreRatio=%.2f",
- minimumTextSize, penalizeRatio, subjectTextScoreRatio, moreTextScoreRatio));
-
- if (end - start < minimumTextSize && penalizeRatio <= 0) {
- return new EntityConfidence(Collections.emptyMap());
- }
-
- final String subject = text.substring(start, end);
- final EntityConfidence scores = detectLanguages(subject);
-
- if (subject.length() >= minimumTextSize
- || subject.length() == text.length()
- || subjectTextScoreRatio * penalizeRatio >= 1) {
- return scores;
- }
-
- final EntityConfidence moreTextScores;
- if (moreTextScoreRatio >= 0) {
- // Attempt to grow the detection text to be at least minimumTextSize long.
- final String moreText = Utils.getSubString(text, start, end, minimumTextSize);
- moreTextScores = detectLanguages(moreText);
- } else {
- moreTextScores = new EntityConfidence(Collections.emptyMap());
- }
-
- // Combine the original detection scores with the those returned after including more text.
- final Map<String, Float> newScores = new ArrayMap<>();
- final Set<String> languages = new ArraySet<>();
- languages.addAll(scores.getEntities());
- languages.addAll(moreTextScores.getEntities());
- for (String language : languages) {
- final float score = (subjectTextScoreRatio * scores.getConfidenceScore(language)
- + moreTextScoreRatio * moreTextScores.getConfidenceScore(language))
- * penalizeRatio;
- newScores.put(language, score);
- }
- return new EntityConfidence(newScores);
- }
-
- /**
- * Detect languages for the specified text.
- */
- private EntityConfidence detectLanguages(String text) throws FileNotFoundException {
- final LangIdModel langId = getLangIdImpl();
- final LangIdModel.LanguageResult[] langResults = langId.detectLanguages(text);
- final Map<String, Float> languagesMap = new ArrayMap<>();
- for (LanguageResult langResult : langResults) {
- languagesMap.put(langResult.getLanguage(), langResult.getScore());
- }
- return new EntityConfidence(languagesMap);
- }
-
- private float getLangIdThreshold() {
- try {
- return mSettings.getLangIdThresholdOverride() >= 0
- ? mSettings.getLangIdThresholdOverride()
- : getLangIdImpl().getLangIdThreshold();
- } catch (FileNotFoundException e) {
- final float defaultThreshold = 0.5f;
- Log.v(LOG_TAG, "Using default foreign language threshold: " + defaultThreshold);
- return defaultThreshold;
- }
- }
-
- @Override
- public void dump(@NonNull IndentingPrintWriter printWriter) {
- synchronized (mLock) {
- printWriter.println("TextClassifierImpl:");
- printWriter.increaseIndent();
- printWriter.println("Annotator model file(s):");
- printWriter.increaseIndent();
- for (ModelFileManager.ModelFile modelFile :
- mAnnotatorModelFileManager.listModelFiles()) {
- printWriter.println(modelFile.toString());
- }
- printWriter.decreaseIndent();
- printWriter.println("LangID model file(s):");
- printWriter.increaseIndent();
- for (ModelFileManager.ModelFile modelFile :
- mLangIdModelFileManager.listModelFiles()) {
- printWriter.println(modelFile.toString());
- }
- printWriter.decreaseIndent();
- printWriter.println("Actions model file(s):");
- printWriter.increaseIndent();
- for (ModelFileManager.ModelFile modelFile :
- mActionsModelFileManager.listModelFiles()) {
- printWriter.println(modelFile.toString());
- }
- printWriter.decreaseIndent();
- printWriter.printPair("mFallback", mFallback);
- printWriter.decreaseIndent();
- printWriter.println();
- }
- }
-
- /**
- * Closes the ParcelFileDescriptor, if non-null, and logs any errors that occur.
- */
- private static void maybeCloseAndLogError(@Nullable ParcelFileDescriptor fd) {
- if (fd == null) {
- return;
- }
-
- try {
- fd.close();
- } catch (IOException e) {
- Log.e(LOG_TAG, "Error closing file.", e);
- }
- }
-
- /**
- * Returns the locales string for the current resources configuration.
- */
- private String getResourceLocalesString() {
- try {
- return mContext.getResources().getConfiguration().getLocales().toLanguageTags();
- } catch (NullPointerException e) {
- // NPE is unexpected. Erring on the side of caution.
- return LocaleList.getDefault().toLanguageTags();
- }
- }
-}
diff --git a/core/java/android/view/textclassifier/intent/ClassificationIntentFactory.java b/core/java/android/view/textclassifier/intent/ClassificationIntentFactory.java
deleted file mode 100644
index 22e374f2b38f..000000000000
--- a/core/java/android/view/textclassifier/intent/ClassificationIntentFactory.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view.textclassifier.intent;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.Intent;
-
-import com.google.android.textclassifier.AnnotatorModel;
-
-import java.time.Instant;
-import java.util.List;
-
-/**
- * @hide
- */
-public interface ClassificationIntentFactory {
-
- /**
- * Return a list of LabeledIntent from the classification result.
- */
- List<LabeledIntent> create(
- Context context,
- String text,
- boolean foreignText,
- @Nullable Instant referenceTime,
- @Nullable AnnotatorModel.ClassificationResult classification);
-
- /**
- * Inserts translate action to the list if it is a foreign text.
- */
- static void insertTranslateAction(
- List<LabeledIntent> actions, Context context, String text) {
- actions.add(new LabeledIntent(
- context.getString(com.android.internal.R.string.translate),
- /* titleWithEntity */ null,
- context.getString(com.android.internal.R.string.translate_desc),
- /* descriptionWithAppName */ null,
- new Intent(Intent.ACTION_TRANSLATE)
- // TODO: Probably better to introduce a "translate" scheme instead of
- // using EXTRA_TEXT.
- .putExtra(Intent.EXTRA_TEXT, text),
- text.hashCode()));
- }
-}
diff --git a/core/java/android/view/textclassifier/intent/LabeledIntent.java b/core/java/android/view/textclassifier/intent/LabeledIntent.java
deleted file mode 100644
index cbd9d1a522f6..000000000000
--- a/core/java/android/view/textclassifier/intent/LabeledIntent.java
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view.textclassifier.intent;
-
-import android.annotation.Nullable;
-import android.app.PendingIntent;
-import android.app.RemoteAction;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.graphics.drawable.Icon;
-import android.os.Bundle;
-import android.text.TextUtils;
-import android.view.textclassifier.ExtrasUtils;
-import android.view.textclassifier.Log;
-import android.view.textclassifier.TextClassification;
-import android.view.textclassifier.TextClassifier;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.util.Objects;
-
-/**
- * Helper class to store the information from which RemoteActions are built.
- *
- * @hide
- */
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public final class LabeledIntent {
- private static final String TAG = "LabeledIntent";
- public static final int DEFAULT_REQUEST_CODE = 0;
- private static final TitleChooser DEFAULT_TITLE_CHOOSER =
- (labeledIntent, resolveInfo) -> {
- if (!TextUtils.isEmpty(labeledIntent.titleWithEntity)) {
- return labeledIntent.titleWithEntity;
- }
- return labeledIntent.titleWithoutEntity;
- };
-
- @Nullable
- public final String titleWithoutEntity;
- @Nullable
- public final String titleWithEntity;
- public final String description;
- @Nullable
- public final String descriptionWithAppName;
- // Do not update this intent.
- public final Intent intent;
- public final int requestCode;
-
- /**
- * Initializes a LabeledIntent.
- *
- * <p>NOTE: {@code requestCode} is required to not be {@link #DEFAULT_REQUEST_CODE}
- * if distinguishing info (e.g. the classified text) is represented in intent extras only.
- * In such circumstances, the request code should represent the distinguishing info
- * (e.g. by generating a hashcode) so that the generated PendingIntent is (somewhat)
- * unique. To be correct, the PendingIntent should be definitely unique but we try a
- * best effort approach that avoids spamming the system with PendingIntents.
- */
- // TODO: Fix the issue mentioned above so the behaviour is correct.
- public LabeledIntent(
- @Nullable String titleWithoutEntity,
- @Nullable String titleWithEntity,
- String description,
- @Nullable String descriptionWithAppName,
- Intent intent,
- int requestCode) {
- if (TextUtils.isEmpty(titleWithEntity) && TextUtils.isEmpty(titleWithoutEntity)) {
- throw new IllegalArgumentException(
- "titleWithEntity and titleWithoutEntity should not be both null");
- }
- this.titleWithoutEntity = titleWithoutEntity;
- this.titleWithEntity = titleWithEntity;
- this.description = Objects.requireNonNull(description);
- this.descriptionWithAppName = descriptionWithAppName;
- this.intent = Objects.requireNonNull(intent);
- this.requestCode = requestCode;
- }
-
- /**
- * Return the resolved result.
- *
- * @param context the context to resolve the result's intent and action
- * @param titleChooser for choosing an action title
- * @param textLanguagesBundle containing language detection information
- */
- @Nullable
- public Result resolve(
- Context context,
- @Nullable TitleChooser titleChooser,
- @Nullable Bundle textLanguagesBundle) {
- final PackageManager pm = context.getPackageManager();
- final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0);
-
- if (resolveInfo == null || resolveInfo.activityInfo == null) {
- Log.w(TAG, "resolveInfo or activityInfo is null");
- return null;
- }
- final String packageName = resolveInfo.activityInfo.packageName;
- final String className = resolveInfo.activityInfo.name;
- if (packageName == null || className == null) {
- Log.w(TAG, "packageName or className is null");
- return null;
- }
- Intent resolvedIntent = new Intent(intent);
- resolvedIntent.putExtra(
- TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER,
- getFromTextClassifierExtra(textLanguagesBundle));
- boolean shouldShowIcon = false;
- Icon icon = null;
- if (!"android".equals(packageName)) {
- // We only set the component name when the package name is not resolved to "android"
- // to workaround a bug that explicit intent with component name == ResolverActivity
- // can't be launched on keyguard.
- resolvedIntent.setComponent(new ComponentName(packageName, className));
- if (resolveInfo.activityInfo.getIconResource() != 0) {
- icon = Icon.createWithResource(
- packageName, resolveInfo.activityInfo.getIconResource());
- shouldShowIcon = true;
- }
- }
- if (icon == null) {
- // RemoteAction requires that there be an icon.
- icon = Icon.createWithResource(
- "android", com.android.internal.R.drawable.ic_more_items);
- }
- final PendingIntent pendingIntent =
- TextClassification.createPendingIntent(context, resolvedIntent, requestCode);
- titleChooser = titleChooser == null ? DEFAULT_TITLE_CHOOSER : titleChooser;
- CharSequence title = titleChooser.chooseTitle(this, resolveInfo);
- if (TextUtils.isEmpty(title)) {
- Log.w(TAG, "Custom titleChooser return null, fallback to the default titleChooser");
- title = DEFAULT_TITLE_CHOOSER.chooseTitle(this, resolveInfo);
- }
- final RemoteAction action =
- new RemoteAction(icon, title, resolveDescription(resolveInfo, pm), pendingIntent);
- action.setShouldShowIcon(shouldShowIcon);
- return new Result(resolvedIntent, action);
- }
-
- private String resolveDescription(ResolveInfo resolveInfo, PackageManager packageManager) {
- if (!TextUtils.isEmpty(descriptionWithAppName)) {
- // Example string format of descriptionWithAppName: "Use %1$s to open map".
- String applicationName = getApplicationName(resolveInfo, packageManager);
- if (!TextUtils.isEmpty(applicationName)) {
- return String.format(descriptionWithAppName, applicationName);
- }
- }
- return description;
- }
-
- @Nullable
- private String getApplicationName(
- ResolveInfo resolveInfo, PackageManager packageManager) {
- if (resolveInfo.activityInfo == null) {
- return null;
- }
- if ("android".equals(resolveInfo.activityInfo.packageName)) {
- return null;
- }
- if (resolveInfo.activityInfo.applicationInfo == null) {
- return null;
- }
- return (String) packageManager.getApplicationLabel(
- resolveInfo.activityInfo.applicationInfo);
- }
-
- private Bundle getFromTextClassifierExtra(@Nullable Bundle textLanguagesBundle) {
- if (textLanguagesBundle != null) {
- final Bundle bundle = new Bundle();
- ExtrasUtils.putTextLanguagesExtra(bundle, textLanguagesBundle);
- return bundle;
- } else {
- return Bundle.EMPTY;
- }
- }
-
- /**
- * Data class that holds the result.
- */
- public static final class Result {
- public final Intent resolvedIntent;
- public final RemoteAction remoteAction;
-
- public Result(Intent resolvedIntent, RemoteAction remoteAction) {
- this.resolvedIntent = Objects.requireNonNull(resolvedIntent);
- this.remoteAction = Objects.requireNonNull(remoteAction);
- }
- }
-
- /**
- * An object to choose a title from resolved info. If {@code null} is returned,
- * {@link #titleWithEntity} will be used if it exists, {@link #titleWithoutEntity} otherwise.
- */
- public interface TitleChooser {
- /**
- * Picks a title from a {@link LabeledIntent} by looking into resolved info.
- * {@code resolveInfo} is guaranteed to have a non-null {@code activityInfo}.
- */
- @Nullable
- CharSequence chooseTitle(LabeledIntent labeledIntent, ResolveInfo resolveInfo);
- }
-}
diff --git a/core/java/android/view/textclassifier/intent/LegacyClassificationIntentFactory.java b/core/java/android/view/textclassifier/intent/LegacyClassificationIntentFactory.java
deleted file mode 100644
index 8d60ad850afa..000000000000
--- a/core/java/android/view/textclassifier/intent/LegacyClassificationIntentFactory.java
+++ /dev/null
@@ -1,280 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view.textclassifier.intent;
-
-import static java.time.temporal.ChronoUnit.MILLIS;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.SearchManager;
-import android.content.ContentUris;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.UserManager;
-import android.provider.Browser;
-import android.provider.CalendarContract;
-import android.provider.ContactsContract;
-import android.view.textclassifier.Log;
-import android.view.textclassifier.TextClassifier;
-
-import com.google.android.textclassifier.AnnotatorModel;
-
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
-import java.time.Instant;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Creates intents based on the classification type.
- * @hide
- */
-// TODO: Consider to support {@code descriptionWithAppName}.
-public final class LegacyClassificationIntentFactory implements ClassificationIntentFactory {
-
- private static final String TAG = "LegacyClassificationIntentFactory";
- private static final long MIN_EVENT_FUTURE_MILLIS = TimeUnit.MINUTES.toMillis(5);
- private static final long DEFAULT_EVENT_DURATION = TimeUnit.HOURS.toMillis(1);
-
- @NonNull
- @Override
- public List<LabeledIntent> create(Context context, String text, boolean foreignText,
- @Nullable Instant referenceTime,
- AnnotatorModel.ClassificationResult classification) {
- final String type = classification != null
- ? classification.getCollection().trim().toLowerCase(Locale.ENGLISH)
- : "";
- text = text.trim();
- final List<LabeledIntent> actions;
- switch (type) {
- case TextClassifier.TYPE_EMAIL:
- actions = createForEmail(context, text);
- break;
- case TextClassifier.TYPE_PHONE:
- actions = createForPhone(context, text);
- break;
- case TextClassifier.TYPE_ADDRESS:
- actions = createForAddress(context, text);
- break;
- case TextClassifier.TYPE_URL:
- actions = createForUrl(context, text);
- break;
- case TextClassifier.TYPE_DATE: // fall through
- case TextClassifier.TYPE_DATE_TIME:
- if (classification.getDatetimeResult() != null) {
- final Instant parsedTime = Instant.ofEpochMilli(
- classification.getDatetimeResult().getTimeMsUtc());
- actions = createForDatetime(context, type, referenceTime, parsedTime);
- } else {
- actions = new ArrayList<>();
- }
- break;
- case TextClassifier.TYPE_FLIGHT_NUMBER:
- actions = createForFlight(context, text);
- break;
- case TextClassifier.TYPE_DICTIONARY:
- actions = createForDictionary(context, text);
- break;
- default:
- actions = new ArrayList<>();
- break;
- }
- if (foreignText) {
- ClassificationIntentFactory.insertTranslateAction(actions, context, text);
- }
- return actions;
- }
-
- @NonNull
- private static List<LabeledIntent> createForEmail(Context context, String text) {
- final List<LabeledIntent> actions = new ArrayList<>();
- actions.add(new LabeledIntent(
- context.getString(com.android.internal.R.string.email),
- /* titleWithEntity */ null,
- context.getString(com.android.internal.R.string.email_desc),
- /* descriptionWithAppName */ null,
- new Intent(Intent.ACTION_SENDTO)
- .setData(Uri.parse(String.format("mailto:%s", text))),
- LabeledIntent.DEFAULT_REQUEST_CODE));
- actions.add(new LabeledIntent(
- context.getString(com.android.internal.R.string.add_contact),
- /* titleWithEntity */ null,
- context.getString(com.android.internal.R.string.add_contact_desc),
- /* descriptionWithAppName */ null,
- new Intent(Intent.ACTION_INSERT_OR_EDIT)
- .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
- .putExtra(ContactsContract.Intents.Insert.EMAIL, text),
- text.hashCode()));
- return actions;
- }
-
- @NonNull
- private static List<LabeledIntent> createForPhone(Context context, String text) {
- final List<LabeledIntent> actions = new ArrayList<>();
- final UserManager userManager = context.getSystemService(UserManager.class);
- final Bundle userRestrictions = userManager != null
- ? userManager.getUserRestrictions() : new Bundle();
- if (!userRestrictions.getBoolean(UserManager.DISALLOW_OUTGOING_CALLS, false)) {
- actions.add(new LabeledIntent(
- context.getString(com.android.internal.R.string.dial),
- /* titleWithEntity */ null,
- context.getString(com.android.internal.R.string.dial_desc),
- /* descriptionWithAppName */ null,
- new Intent(Intent.ACTION_DIAL).setData(
- Uri.parse(String.format("tel:%s", text))),
- LabeledIntent.DEFAULT_REQUEST_CODE));
- }
- actions.add(new LabeledIntent(
- context.getString(com.android.internal.R.string.add_contact),
- /* titleWithEntity */ null,
- context.getString(com.android.internal.R.string.add_contact_desc),
- /* descriptionWithAppName */ null,
- new Intent(Intent.ACTION_INSERT_OR_EDIT)
- .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
- .putExtra(ContactsContract.Intents.Insert.PHONE, text),
- text.hashCode()));
- if (!userRestrictions.getBoolean(UserManager.DISALLOW_SMS, false)) {
- actions.add(new LabeledIntent(
- context.getString(com.android.internal.R.string.sms),
- /* titleWithEntity */ null,
- context.getString(com.android.internal.R.string.sms_desc),
- /* descriptionWithAppName */ null,
- new Intent(Intent.ACTION_SENDTO)
- .setData(Uri.parse(String.format("smsto:%s", text))),
- LabeledIntent.DEFAULT_REQUEST_CODE));
- }
- return actions;
- }
-
- @NonNull
- private static List<LabeledIntent> createForAddress(Context context, String text) {
- final List<LabeledIntent> actions = new ArrayList<>();
- try {
- final String encText = URLEncoder.encode(text, "UTF-8");
- actions.add(new LabeledIntent(
- context.getString(com.android.internal.R.string.map),
- /* titleWithEntity */ null,
- context.getString(com.android.internal.R.string.map_desc),
- /* descriptionWithAppName */ null,
- new Intent(Intent.ACTION_VIEW)
- .setData(Uri.parse(String.format("geo:0,0?q=%s", encText))),
- LabeledIntent.DEFAULT_REQUEST_CODE));
- } catch (UnsupportedEncodingException e) {
- Log.e(TAG, "Could not encode address", e);
- }
- return actions;
- }
-
- @NonNull
- private static List<LabeledIntent> createForUrl(Context context, String text) {
- if (Uri.parse(text).getScheme() == null) {
- text = "http://" + text;
- }
- final List<LabeledIntent> actions = new ArrayList<>();
- actions.add(new LabeledIntent(
- context.getString(com.android.internal.R.string.browse),
- /* titleWithEntity */ null,
- context.getString(com.android.internal.R.string.browse_desc),
- /* descriptionWithAppName */ null,
- new Intent(Intent.ACTION_VIEW)
- .setDataAndNormalize(Uri.parse(text))
- .putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName()),
- LabeledIntent.DEFAULT_REQUEST_CODE));
- return actions;
- }
-
- @NonNull
- private static List<LabeledIntent> createForDatetime(
- Context context, String type, @Nullable Instant referenceTime,
- Instant parsedTime) {
- if (referenceTime == null) {
- // If no reference time was given, use now.
- referenceTime = Instant.now();
- }
- List<LabeledIntent> actions = new ArrayList<>();
- actions.add(createCalendarViewIntent(context, parsedTime));
- final long millisUntilEvent = referenceTime.until(parsedTime, MILLIS);
- if (millisUntilEvent > MIN_EVENT_FUTURE_MILLIS) {
- actions.add(createCalendarCreateEventIntent(context, parsedTime, type));
- }
- return actions;
- }
-
- @NonNull
- private static List<LabeledIntent> createForFlight(Context context, String text) {
- final List<LabeledIntent> actions = new ArrayList<>();
- actions.add(new LabeledIntent(
- context.getString(com.android.internal.R.string.view_flight),
- /* titleWithEntity */ null,
- context.getString(com.android.internal.R.string.view_flight_desc),
- /* descriptionWithAppName */ null,
- new Intent(Intent.ACTION_WEB_SEARCH)
- .putExtra(SearchManager.QUERY, text),
- text.hashCode()));
- return actions;
- }
-
- @NonNull
- private static LabeledIntent createCalendarViewIntent(Context context, Instant parsedTime) {
- Uri.Builder builder = CalendarContract.CONTENT_URI.buildUpon();
- builder.appendPath("time");
- ContentUris.appendId(builder, parsedTime.toEpochMilli());
- return new LabeledIntent(
- context.getString(com.android.internal.R.string.view_calendar),
- /* titleWithEntity */ null,
- context.getString(com.android.internal.R.string.view_calendar_desc),
- /* descriptionWithAppName */ null,
- new Intent(Intent.ACTION_VIEW).setData(builder.build()),
- LabeledIntent.DEFAULT_REQUEST_CODE);
- }
-
- @NonNull
- private static LabeledIntent createCalendarCreateEventIntent(
- Context context, Instant parsedTime, @TextClassifier.EntityType String type) {
- final boolean isAllDay = TextClassifier.TYPE_DATE.equals(type);
- return new LabeledIntent(
- context.getString(com.android.internal.R.string.add_calendar_event),
- /* titleWithEntity */ null,
- context.getString(com.android.internal.R.string.add_calendar_event_desc),
- /* descriptionWithAppName */ null,
- new Intent(Intent.ACTION_INSERT)
- .setData(CalendarContract.Events.CONTENT_URI)
- .putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, isAllDay)
- .putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME,
- parsedTime.toEpochMilli())
- .putExtra(CalendarContract.EXTRA_EVENT_END_TIME,
- parsedTime.toEpochMilli() + DEFAULT_EVENT_DURATION),
- parsedTime.hashCode());
- }
-
- @NonNull
- private static List<LabeledIntent> createForDictionary(Context context, String text) {
- final List<LabeledIntent> actions = new ArrayList<>();
- actions.add(new LabeledIntent(
- context.getString(com.android.internal.R.string.define),
- /* titleWithEntity */ null,
- context.getString(com.android.internal.R.string.define_desc),
- /* descriptionWithAppName */ null,
- new Intent(Intent.ACTION_DEFINE)
- .putExtra(Intent.EXTRA_TEXT, text),
- text.hashCode()));
- return actions;
- }
-}
diff --git a/core/java/android/view/textclassifier/intent/TemplateClassificationIntentFactory.java b/core/java/android/view/textclassifier/intent/TemplateClassificationIntentFactory.java
deleted file mode 100644
index aef4bd6c7351..000000000000
--- a/core/java/android/view/textclassifier/intent/TemplateClassificationIntentFactory.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view.textclassifier.intent;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.view.textclassifier.Log;
-import android.view.textclassifier.TextClassifier;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
-
-import com.google.android.textclassifier.AnnotatorModel;
-import com.google.android.textclassifier.RemoteActionTemplate;
-
-import java.time.Instant;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * Creates intents based on {@link RemoteActionTemplate} objects for a ClassificationResult.
- *
- * @hide
- */
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public final class TemplateClassificationIntentFactory implements ClassificationIntentFactory {
- private static final String TAG = TextClassifier.DEFAULT_LOG_TAG;
- private final TemplateIntentFactory mTemplateIntentFactory;
- private final ClassificationIntentFactory mFallback;
-
- public TemplateClassificationIntentFactory(TemplateIntentFactory templateIntentFactory,
- ClassificationIntentFactory fallback) {
- mTemplateIntentFactory = Objects.requireNonNull(templateIntentFactory);
- mFallback = Objects.requireNonNull(fallback);
- }
-
- /**
- * Returns a list of {@link LabeledIntent}
- * that are constructed from the classification result.
- */
- @NonNull
- @Override
- public List<LabeledIntent> create(
- Context context,
- String text,
- boolean foreignText,
- @Nullable Instant referenceTime,
- @Nullable AnnotatorModel.ClassificationResult classification) {
- if (classification == null) {
- return Collections.emptyList();
- }
- RemoteActionTemplate[] remoteActionTemplates = classification.getRemoteActionTemplates();
- if (remoteActionTemplates == null) {
- // RemoteActionTemplate is missing, fallback.
- Log.w(TAG, "RemoteActionTemplate is missing, fallback to"
- + " LegacyClassificationIntentFactory.");
- return mFallback.create(context, text, foreignText, referenceTime, classification);
- }
- final List<LabeledIntent> labeledIntents =
- mTemplateIntentFactory.create(remoteActionTemplates);
- if (foreignText) {
- ClassificationIntentFactory.insertTranslateAction(labeledIntents, context, text.trim());
- }
- return labeledIntents;
- }
-}
diff --git a/core/java/android/view/textclassifier/intent/TemplateIntentFactory.java b/core/java/android/view/textclassifier/intent/TemplateIntentFactory.java
deleted file mode 100644
index 7a3956996972..000000000000
--- a/core/java/android/view/textclassifier/intent/TemplateIntentFactory.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view.textclassifier.intent;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.text.TextUtils;
-import android.view.textclassifier.Log;
-import android.view.textclassifier.TextClassifier;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import com.google.android.textclassifier.NamedVariant;
-import com.google.android.textclassifier.RemoteActionTemplate;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Creates intents based on {@link RemoteActionTemplate} objects.
- *
- * @hide
- */
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public final class TemplateIntentFactory {
- private static final String TAG = TextClassifier.DEFAULT_LOG_TAG;
-
- /**
- * Constructs and returns a list of {@link LabeledIntent} based on the given templates.
- */
- @Nullable
- public List<LabeledIntent> create(
- @NonNull RemoteActionTemplate[] remoteActionTemplates) {
- if (remoteActionTemplates.length == 0) {
- return new ArrayList<>();
- }
- final List<LabeledIntent> labeledIntents = new ArrayList<>();
- for (RemoteActionTemplate remoteActionTemplate : remoteActionTemplates) {
- if (!isValidTemplate(remoteActionTemplate)) {
- Log.w(TAG, "Invalid RemoteActionTemplate skipped.");
- continue;
- }
- labeledIntents.add(
- new LabeledIntent(
- remoteActionTemplate.titleWithoutEntity,
- remoteActionTemplate.titleWithEntity,
- remoteActionTemplate.description,
- remoteActionTemplate.descriptionWithAppName,
- createIntent(remoteActionTemplate),
- remoteActionTemplate.requestCode == null
- ? LabeledIntent.DEFAULT_REQUEST_CODE
- : remoteActionTemplate.requestCode));
- }
- return labeledIntents;
- }
-
- private static boolean isValidTemplate(@Nullable RemoteActionTemplate remoteActionTemplate) {
- if (remoteActionTemplate == null) {
- Log.w(TAG, "Invalid RemoteActionTemplate: is null");
- return false;
- }
- if (TextUtils.isEmpty(remoteActionTemplate.titleWithEntity)
- && TextUtils.isEmpty(remoteActionTemplate.titleWithoutEntity)) {
- Log.w(TAG, "Invalid RemoteActionTemplate: title is null");
- return false;
- }
- if (TextUtils.isEmpty(remoteActionTemplate.description)) {
- Log.w(TAG, "Invalid RemoteActionTemplate: description is null");
- return false;
- }
- if (!TextUtils.isEmpty(remoteActionTemplate.packageName)) {
- Log.w(TAG, "Invalid RemoteActionTemplate: package name is set");
- return false;
- }
- if (TextUtils.isEmpty(remoteActionTemplate.action)) {
- Log.w(TAG, "Invalid RemoteActionTemplate: intent action not set");
- return false;
- }
- return true;
- }
-
- private static Intent createIntent(RemoteActionTemplate remoteActionTemplate) {
- final Intent intent = new Intent(remoteActionTemplate.action);
- final Uri uri = TextUtils.isEmpty(remoteActionTemplate.data)
- ? null : Uri.parse(remoteActionTemplate.data).normalizeScheme();
- final String type = TextUtils.isEmpty(remoteActionTemplate.type)
- ? null : Intent.normalizeMimeType(remoteActionTemplate.type);
- intent.setDataAndType(uri, type);
- intent.setFlags(remoteActionTemplate.flags == null ? 0 : remoteActionTemplate.flags);
- if (remoteActionTemplate.category != null) {
- for (String category : remoteActionTemplate.category) {
- if (category != null) {
- intent.addCategory(category);
- }
- }
- }
- intent.putExtras(nameVariantsToBundle(remoteActionTemplate.extras));
- return intent;
- }
-
- /**
- * Converts an array of {@link NamedVariant} to a Bundle and returns it.
- */
- public static Bundle nameVariantsToBundle(@Nullable NamedVariant[] namedVariants) {
- if (namedVariants == null) {
- return Bundle.EMPTY;
- }
- Bundle bundle = new Bundle();
- for (NamedVariant namedVariant : namedVariants) {
- if (namedVariant == null) {
- continue;
- }
- switch (namedVariant.getType()) {
- case NamedVariant.TYPE_INT:
- bundle.putInt(namedVariant.getName(), namedVariant.getInt());
- break;
- case NamedVariant.TYPE_LONG:
- bundle.putLong(namedVariant.getName(), namedVariant.getLong());
- break;
- case NamedVariant.TYPE_FLOAT:
- bundle.putFloat(namedVariant.getName(), namedVariant.getFloat());
- break;
- case NamedVariant.TYPE_DOUBLE:
- bundle.putDouble(namedVariant.getName(), namedVariant.getDouble());
- break;
- case NamedVariant.TYPE_BOOL:
- bundle.putBoolean(namedVariant.getName(), namedVariant.getBool());
- break;
- case NamedVariant.TYPE_STRING:
- bundle.putString(namedVariant.getName(), namedVariant.getString());
- break;
- default:
- Log.w(TAG,
- "Unsupported type found in nameVariantsToBundle : "
- + namedVariant.getType());
- }
- }
- return bundle;
- }
-}
diff --git a/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java b/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java
deleted file mode 100644
index 28cb80d0c74c..000000000000
--- a/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java
+++ /dev/null
@@ -1,599 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier.logging;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.Context;
-import android.metrics.LogMaker;
-import android.os.Build;
-import android.util.Log;
-import android.view.textclassifier.TextClassification;
-import android.view.textclassifier.TextClassifier;
-import android.view.textclassifier.TextSelection;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.util.Preconditions;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Objects;
-import java.util.UUID;
-
-/**
- * A selection event tracker.
- * @hide
- */
-//TODO: Do not allow any crashes from this class.
-public final class SmartSelectionEventTracker {
-
- private static final String LOG_TAG = "SmartSelectEventTracker";
- private static final boolean DEBUG_LOG_ENABLED = true;
-
- private static final int START_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_START;
- private static final int PREV_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_PREVIOUS;
- private static final int INDEX = MetricsEvent.FIELD_SELECTION_SESSION_INDEX;
- private static final int WIDGET_TYPE = MetricsEvent.FIELD_SELECTION_WIDGET_TYPE;
- private static final int WIDGET_VERSION = MetricsEvent.FIELD_SELECTION_WIDGET_VERSION;
- private static final int MODEL_NAME = MetricsEvent.FIELD_TEXTCLASSIFIER_MODEL;
- private static final int ENTITY_TYPE = MetricsEvent.FIELD_SELECTION_ENTITY_TYPE;
- private static final int SMART_START = MetricsEvent.FIELD_SELECTION_SMART_RANGE_START;
- private static final int SMART_END = MetricsEvent.FIELD_SELECTION_SMART_RANGE_END;
- private static final int EVENT_START = MetricsEvent.FIELD_SELECTION_RANGE_START;
- private static final int EVENT_END = MetricsEvent.FIELD_SELECTION_RANGE_END;
- private static final int SESSION_ID = MetricsEvent.FIELD_SELECTION_SESSION_ID;
-
- private static final String ZERO = "0";
- private static final String TEXTVIEW = "textview";
- private static final String EDITTEXT = "edittext";
- private static final String UNSELECTABLE_TEXTVIEW = "nosel-textview";
- private static final String WEBVIEW = "webview";
- private static final String EDIT_WEBVIEW = "edit-webview";
- private static final String CUSTOM_TEXTVIEW = "customview";
- private static final String CUSTOM_EDITTEXT = "customedit";
- private static final String CUSTOM_UNSELECTABLE_TEXTVIEW = "nosel-customview";
- private static final String UNKNOWN = "unknown";
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({WidgetType.UNSPECIFIED, WidgetType.TEXTVIEW, WidgetType.WEBVIEW,
- WidgetType.EDITTEXT, WidgetType.EDIT_WEBVIEW})
- public @interface WidgetType {
- int UNSPECIFIED = 0;
- int TEXTVIEW = 1;
- int WEBVIEW = 2;
- int EDITTEXT = 3;
- int EDIT_WEBVIEW = 4;
- int UNSELECTABLE_TEXTVIEW = 5;
- int CUSTOM_TEXTVIEW = 6;
- int CUSTOM_EDITTEXT = 7;
- int CUSTOM_UNSELECTABLE_TEXTVIEW = 8;
- }
-
- private final MetricsLogger mMetricsLogger = new MetricsLogger();
- private final int mWidgetType;
- @Nullable private final String mWidgetVersion;
- private final Context mContext;
-
- @Nullable private String mSessionId;
- private final int[] mSmartIndices = new int[2];
- private final int[] mPrevIndices = new int[2];
- private int mOrigStart;
- private int mIndex;
- private long mSessionStartTime;
- private long mLastEventTime;
- private boolean mSmartSelectionTriggered;
- private String mModelName;
-
- @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
- publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
- public SmartSelectionEventTracker(@NonNull Context context, @WidgetType int widgetType) {
- mWidgetType = widgetType;
- mWidgetVersion = null;
- mContext = Objects.requireNonNull(context);
- }
-
- public SmartSelectionEventTracker(
- @NonNull Context context, @WidgetType int widgetType, @Nullable String widgetVersion) {
- mWidgetType = widgetType;
- mWidgetVersion = widgetVersion;
- mContext = Objects.requireNonNull(context);
- }
-
- /**
- * Logs a selection event.
- *
- * @param event the selection event
- */
- @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
- publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
- public void logEvent(@NonNull SelectionEvent event) {
- Objects.requireNonNull(event);
-
- if (event.mEventType != SelectionEvent.EventType.SELECTION_STARTED && mSessionId == null
- && DEBUG_LOG_ENABLED) {
- Log.d(LOG_TAG, "Selection session not yet started. Ignoring event");
- return;
- }
-
- final long now = System.currentTimeMillis();
- switch (event.mEventType) {
- case SelectionEvent.EventType.SELECTION_STARTED:
- mSessionId = startNewSession();
- Preconditions.checkArgument(event.mEnd == event.mStart + 1);
- mOrigStart = event.mStart;
- mSessionStartTime = now;
- break;
- case SelectionEvent.EventType.SMART_SELECTION_SINGLE: // fall through
- case SelectionEvent.EventType.SMART_SELECTION_MULTI:
- mSmartSelectionTriggered = true;
- mModelName = getModelName(event);
- mSmartIndices[0] = event.mStart;
- mSmartIndices[1] = event.mEnd;
- break;
- case SelectionEvent.EventType.SELECTION_MODIFIED: // fall through
- case SelectionEvent.EventType.AUTO_SELECTION:
- if (mPrevIndices[0] == event.mStart && mPrevIndices[1] == event.mEnd) {
- // Selection did not change. Ignore event.
- return;
- }
- }
- writeEvent(event, now);
-
- if (event.isTerminal()) {
- endSession();
- }
- }
-
- private void writeEvent(SelectionEvent event, long now) {
- final long prevEventDelta = mLastEventTime == 0 ? 0 : now - mLastEventTime;
- final LogMaker log = new LogMaker(MetricsEvent.TEXT_SELECTION_SESSION)
- .setType(getLogType(event))
- .setSubtype(MetricsEvent.TEXT_SELECTION_INVOCATION_MANUAL)
- .setPackageName(mContext.getPackageName())
- .addTaggedData(START_EVENT_DELTA, now - mSessionStartTime)
- .addTaggedData(PREV_EVENT_DELTA, prevEventDelta)
- .addTaggedData(INDEX, mIndex)
- .addTaggedData(WIDGET_TYPE, getWidgetTypeName())
- .addTaggedData(WIDGET_VERSION, mWidgetVersion)
- .addTaggedData(MODEL_NAME, mModelName)
- .addTaggedData(ENTITY_TYPE, event.mEntityType)
- .addTaggedData(SMART_START, getSmartRangeDelta(mSmartIndices[0]))
- .addTaggedData(SMART_END, getSmartRangeDelta(mSmartIndices[1]))
- .addTaggedData(EVENT_START, getRangeDelta(event.mStart))
- .addTaggedData(EVENT_END, getRangeDelta(event.mEnd))
- .addTaggedData(SESSION_ID, mSessionId);
- mMetricsLogger.write(log);
- debugLog(log);
- mLastEventTime = now;
- mPrevIndices[0] = event.mStart;
- mPrevIndices[1] = event.mEnd;
- mIndex++;
- }
-
- private String startNewSession() {
- endSession();
- mSessionId = createSessionId();
- return mSessionId;
- }
-
- private void endSession() {
- // Reset fields.
- mOrigStart = 0;
- mSmartIndices[0] = mSmartIndices[1] = 0;
- mPrevIndices[0] = mPrevIndices[1] = 0;
- mIndex = 0;
- mSessionStartTime = 0;
- mLastEventTime = 0;
- mSmartSelectionTriggered = false;
- mModelName = getModelName(null);
- mSessionId = null;
- }
-
- private static int getLogType(SelectionEvent event) {
- switch (event.mEventType) {
- case SelectionEvent.ActionType.OVERTYPE:
- return MetricsEvent.ACTION_TEXT_SELECTION_OVERTYPE;
- case SelectionEvent.ActionType.COPY:
- return MetricsEvent.ACTION_TEXT_SELECTION_COPY;
- case SelectionEvent.ActionType.PASTE:
- return MetricsEvent.ACTION_TEXT_SELECTION_PASTE;
- case SelectionEvent.ActionType.CUT:
- return MetricsEvent.ACTION_TEXT_SELECTION_CUT;
- case SelectionEvent.ActionType.SHARE:
- return MetricsEvent.ACTION_TEXT_SELECTION_SHARE;
- case SelectionEvent.ActionType.SMART_SHARE:
- return MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE;
- case SelectionEvent.ActionType.DRAG:
- return MetricsEvent.ACTION_TEXT_SELECTION_DRAG;
- case SelectionEvent.ActionType.ABANDON:
- return MetricsEvent.ACTION_TEXT_SELECTION_ABANDON;
- case SelectionEvent.ActionType.OTHER:
- return MetricsEvent.ACTION_TEXT_SELECTION_OTHER;
- case SelectionEvent.ActionType.SELECT_ALL:
- return MetricsEvent.ACTION_TEXT_SELECTION_SELECT_ALL;
- case SelectionEvent.ActionType.RESET:
- return MetricsEvent.ACTION_TEXT_SELECTION_RESET;
- case SelectionEvent.EventType.SELECTION_STARTED:
- return MetricsEvent.ACTION_TEXT_SELECTION_START;
- case SelectionEvent.EventType.SELECTION_MODIFIED:
- return MetricsEvent.ACTION_TEXT_SELECTION_MODIFY;
- case SelectionEvent.EventType.SMART_SELECTION_SINGLE:
- return MetricsEvent.ACTION_TEXT_SELECTION_SMART_SINGLE;
- case SelectionEvent.EventType.SMART_SELECTION_MULTI:
- return MetricsEvent.ACTION_TEXT_SELECTION_SMART_MULTI;
- case SelectionEvent.EventType.AUTO_SELECTION:
- return MetricsEvent.ACTION_TEXT_SELECTION_AUTO;
- default:
- return MetricsEvent.VIEW_UNKNOWN;
- }
- }
-
- private static String getLogTypeString(int logType) {
- switch (logType) {
- case MetricsEvent.ACTION_TEXT_SELECTION_OVERTYPE:
- return "OVERTYPE";
- case MetricsEvent.ACTION_TEXT_SELECTION_COPY:
- return "COPY";
- case MetricsEvent.ACTION_TEXT_SELECTION_PASTE:
- return "PASTE";
- case MetricsEvent.ACTION_TEXT_SELECTION_CUT:
- return "CUT";
- case MetricsEvent.ACTION_TEXT_SELECTION_SHARE:
- return "SHARE";
- case MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE:
- return "SMART_SHARE";
- case MetricsEvent.ACTION_TEXT_SELECTION_DRAG:
- return "DRAG";
- case MetricsEvent.ACTION_TEXT_SELECTION_ABANDON:
- return "ABANDON";
- case MetricsEvent.ACTION_TEXT_SELECTION_OTHER:
- return "OTHER";
- case MetricsEvent.ACTION_TEXT_SELECTION_SELECT_ALL:
- return "SELECT_ALL";
- case MetricsEvent.ACTION_TEXT_SELECTION_RESET:
- return "RESET";
- case MetricsEvent.ACTION_TEXT_SELECTION_START:
- return "SELECTION_STARTED";
- case MetricsEvent.ACTION_TEXT_SELECTION_MODIFY:
- return "SELECTION_MODIFIED";
- case MetricsEvent.ACTION_TEXT_SELECTION_SMART_SINGLE:
- return "SMART_SELECTION_SINGLE";
- case MetricsEvent.ACTION_TEXT_SELECTION_SMART_MULTI:
- return "SMART_SELECTION_MULTI";
- case MetricsEvent.ACTION_TEXT_SELECTION_AUTO:
- return "AUTO_SELECTION";
- default:
- return UNKNOWN;
- }
- }
-
- private int getRangeDelta(int offset) {
- return offset - mOrigStart;
- }
-
- private int getSmartRangeDelta(int offset) {
- return mSmartSelectionTriggered ? getRangeDelta(offset) : 0;
- }
-
- private String getWidgetTypeName() {
- switch (mWidgetType) {
- case WidgetType.TEXTVIEW:
- return TEXTVIEW;
- case WidgetType.WEBVIEW:
- return WEBVIEW;
- case WidgetType.EDITTEXT:
- return EDITTEXT;
- case WidgetType.EDIT_WEBVIEW:
- return EDIT_WEBVIEW;
- case WidgetType.UNSELECTABLE_TEXTVIEW:
- return UNSELECTABLE_TEXTVIEW;
- case WidgetType.CUSTOM_TEXTVIEW:
- return CUSTOM_TEXTVIEW;
- case WidgetType.CUSTOM_EDITTEXT:
- return CUSTOM_EDITTEXT;
- case WidgetType.CUSTOM_UNSELECTABLE_TEXTVIEW:
- return CUSTOM_UNSELECTABLE_TEXTVIEW;
- default:
- return UNKNOWN;
- }
- }
-
- private String getModelName(@Nullable SelectionEvent event) {
- return event == null
- ? SelectionEvent.NO_VERSION_TAG
- : Objects.toString(event.mVersionTag, SelectionEvent.NO_VERSION_TAG);
- }
-
- private static String createSessionId() {
- return UUID.randomUUID().toString();
- }
-
- private static void debugLog(LogMaker log) {
- if (!DEBUG_LOG_ENABLED) return;
-
- final String widgetType = Objects.toString(log.getTaggedData(WIDGET_TYPE), UNKNOWN);
- final String widgetVersion = Objects.toString(log.getTaggedData(WIDGET_VERSION), "");
- final String widget = widgetVersion.isEmpty()
- ? widgetType : widgetType + "-" + widgetVersion;
- final int index = Integer.parseInt(Objects.toString(log.getTaggedData(INDEX), ZERO));
- if (log.getType() == MetricsEvent.ACTION_TEXT_SELECTION_START) {
- String sessionId = Objects.toString(log.getTaggedData(SESSION_ID), "");
- sessionId = sessionId.substring(sessionId.lastIndexOf("-") + 1);
- Log.d(LOG_TAG, String.format("New selection session: %s (%s)", widget, sessionId));
- }
-
- final String model = Objects.toString(log.getTaggedData(MODEL_NAME), UNKNOWN);
- final String entity = Objects.toString(log.getTaggedData(ENTITY_TYPE), UNKNOWN);
- final String type = getLogTypeString(log.getType());
- final int smartStart = Integer.parseInt(
- Objects.toString(log.getTaggedData(SMART_START), ZERO));
- final int smartEnd = Integer.parseInt(
- Objects.toString(log.getTaggedData(SMART_END), ZERO));
- final int eventStart = Integer.parseInt(
- Objects.toString(log.getTaggedData(EVENT_START), ZERO));
- final int eventEnd = Integer.parseInt(
- Objects.toString(log.getTaggedData(EVENT_END), ZERO));
-
- Log.d(LOG_TAG, String.format("%2d: %s/%s, range=%d,%d - smart_range=%d,%d (%s/%s)",
- index, type, entity, eventStart, eventEnd, smartStart, smartEnd, widget, model));
- }
-
- /**
- * A selection event.
- * Specify index parameters as word token indices.
- */
- public static final class SelectionEvent {
-
- /**
- * Use this to specify an indeterminate positive index.
- */
- public static final int OUT_OF_BOUNDS = Integer.MAX_VALUE;
-
- /**
- * Use this to specify an indeterminate negative index.
- */
- public static final int OUT_OF_BOUNDS_NEGATIVE = Integer.MIN_VALUE;
-
- private static final String NO_VERSION_TAG = "";
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({ActionType.OVERTYPE, ActionType.COPY, ActionType.PASTE, ActionType.CUT,
- ActionType.SHARE, ActionType.SMART_SHARE, ActionType.DRAG, ActionType.ABANDON,
- ActionType.OTHER, ActionType.SELECT_ALL, ActionType.RESET})
- public @interface ActionType {
- /** User typed over the selection. */
- int OVERTYPE = 100;
- /** User copied the selection. */
- int COPY = 101;
- /** User pasted over the selection. */
- int PASTE = 102;
- /** User cut the selection. */
- int CUT = 103;
- /** User shared the selection. */
- int SHARE = 104;
- /** User clicked the textAssist menu item. */
- int SMART_SHARE = 105;
- /** User dragged+dropped the selection. */
- int DRAG = 106;
- /** User abandoned the selection. */
- int ABANDON = 107;
- /** User performed an action on the selection. */
- int OTHER = 108;
-
- /* Non-terminal actions. */
- /** User activated Select All */
- int SELECT_ALL = 200;
- /** User reset the smart selection. */
- int RESET = 201;
- }
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({ActionType.OVERTYPE, ActionType.COPY, ActionType.PASTE, ActionType.CUT,
- ActionType.SHARE, ActionType.SMART_SHARE, ActionType.DRAG, ActionType.ABANDON,
- ActionType.OTHER, ActionType.SELECT_ALL, ActionType.RESET,
- EventType.SELECTION_STARTED, EventType.SELECTION_MODIFIED,
- EventType.SMART_SELECTION_SINGLE, EventType.SMART_SELECTION_MULTI,
- EventType.AUTO_SELECTION})
- private @interface EventType {
- /** User started a new selection. */
- int SELECTION_STARTED = 1;
- /** User modified an existing selection. */
- int SELECTION_MODIFIED = 2;
- /** Smart selection triggered for a single token (word). */
- int SMART_SELECTION_SINGLE = 3;
- /** Smart selection triggered spanning multiple tokens (words). */
- int SMART_SELECTION_MULTI = 4;
- /** Something else other than User or the default TextClassifier triggered a selection. */
- int AUTO_SELECTION = 5;
- }
-
- private final int mStart;
- private final int mEnd;
- private @EventType int mEventType;
- private final @TextClassifier.EntityType String mEntityType;
- private final String mVersionTag;
-
- private SelectionEvent(
- int start, int end, int eventType,
- @TextClassifier.EntityType String entityType, String versionTag) {
- Preconditions.checkArgument(end >= start, "end cannot be less than start");
- mStart = start;
- mEnd = end;
- mEventType = eventType;
- mEntityType = Objects.requireNonNull(entityType);
- mVersionTag = Objects.requireNonNull(versionTag);
- }
-
- /**
- * Creates a "selection started" event.
- *
- * @param start the word index of the selected word
- */
- @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
- publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
- public static SelectionEvent selectionStarted(int start) {
- return new SelectionEvent(
- start, start + 1, EventType.SELECTION_STARTED,
- TextClassifier.TYPE_UNKNOWN, NO_VERSION_TAG);
- }
-
- /**
- * Creates a "selection modified" event.
- * Use when the user modifies the selection.
- *
- * @param start the start word (inclusive) index of the selection
- * @param end the end word (exclusive) index of the selection
- */
- @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
- publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
- public static SelectionEvent selectionModified(int start, int end) {
- return new SelectionEvent(
- start, end, EventType.SELECTION_MODIFIED,
- TextClassifier.TYPE_UNKNOWN, NO_VERSION_TAG);
- }
-
- /**
- * Creates a "selection modified" event.
- * Use when the user modifies the selection and the selection's entity type is known.
- *
- * @param start the start word (inclusive) index of the selection
- * @param end the end word (exclusive) index of the selection
- * @param classification the TextClassification object returned by the TextClassifier that
- * classified the selected text
- */
- @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
- publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
- public static SelectionEvent selectionModified(
- int start, int end, @NonNull TextClassification classification) {
- final String entityType = classification.getEntityCount() > 0
- ? classification.getEntity(0)
- : TextClassifier.TYPE_UNKNOWN;
- final String versionTag = getVersionInfo(classification.getId());
- return new SelectionEvent(
- start, end, EventType.SELECTION_MODIFIED, entityType, versionTag);
- }
-
- /**
- * Creates a "selection modified" event.
- * Use when a TextClassifier modifies the selection.
- *
- * @param start the start word (inclusive) index of the selection
- * @param end the end word (exclusive) index of the selection
- * @param selection the TextSelection object returned by the TextClassifier for the
- * specified selection
- */
- @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
- publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
- public static SelectionEvent selectionModified(
- int start, int end, @NonNull TextSelection selection) {
- final boolean smartSelection = getSourceClassifier(selection.getId())
- .equals(TextClassifier.DEFAULT_LOG_TAG);
- final int eventType;
- if (smartSelection) {
- eventType = end - start > 1
- ? EventType.SMART_SELECTION_MULTI
- : EventType.SMART_SELECTION_SINGLE;
-
- } else {
- eventType = EventType.AUTO_SELECTION;
- }
- final String entityType = selection.getEntityCount() > 0
- ? selection.getEntity(0)
- : TextClassifier.TYPE_UNKNOWN;
- final String versionTag = getVersionInfo(selection.getId());
- return new SelectionEvent(start, end, eventType, entityType, versionTag);
- }
-
- /**
- * Creates an event specifying an action taken on a selection.
- * Use when the user clicks on an action to act on the selected text.
- *
- * @param start the start word (inclusive) index of the selection
- * @param end the end word (exclusive) index of the selection
- * @param actionType the action that was performed on the selection
- */
- @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
- publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
- public static SelectionEvent selectionAction(
- int start, int end, @ActionType int actionType) {
- return new SelectionEvent(
- start, end, actionType, TextClassifier.TYPE_UNKNOWN, NO_VERSION_TAG);
- }
-
- /**
- * Creates an event specifying an action taken on a selection.
- * Use when the user clicks on an action to act on the selected text and the selection's
- * entity type is known.
- *
- * @param start the start word (inclusive) index of the selection
- * @param end the end word (exclusive) index of the selection
- * @param actionType the action that was performed on the selection
- * @param classification the TextClassification object returned by the TextClassifier that
- * classified the selected text
- */
- @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
- publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
- public static SelectionEvent selectionAction(
- int start, int end, @ActionType int actionType,
- @NonNull TextClassification classification) {
- final String entityType = classification.getEntityCount() > 0
- ? classification.getEntity(0)
- : TextClassifier.TYPE_UNKNOWN;
- final String versionTag = getVersionInfo(classification.getId());
- return new SelectionEvent(start, end, actionType, entityType, versionTag);
- }
-
- @VisibleForTesting
- public static String getVersionInfo(String signature) {
- final int start = signature.indexOf("|") + 1;
- final int end = signature.indexOf("|", start);
- if (start >= 1 && end >= start) {
- return signature.substring(start, end);
- }
- return "";
- }
-
- private static String getSourceClassifier(String signature) {
- final int end = signature.indexOf("|");
- if (end >= 0) {
- return signature.substring(0, end);
- }
- return "";
- }
-
- private boolean isTerminal() {
- switch (mEventType) {
- case ActionType.OVERTYPE: // fall through
- case ActionType.COPY: // fall through
- case ActionType.PASTE: // fall through
- case ActionType.CUT: // fall through
- case ActionType.SHARE: // fall through
- case ActionType.SMART_SHARE: // fall through
- case ActionType.DRAG: // fall through
- case ActionType.ABANDON: // fall through
- case ActionType.OTHER: // fall through
- return true;
- default:
- return false;
- }
- }
- }
-}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 816612f1dcc7..16d9ed369f54 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -4589,7 +4589,7 @@ public class Editor {
private float mTouchOffsetY;
// Where the touch position should be on the handle to ensure a maximum cursor visibility.
// This is the distance in pixels from the top of the handle view.
- private float mIdealVerticalOffset;
+ private final float mIdealVerticalOffset;
// Parent's (TextView) previous position in window
private int mLastParentX, mLastParentY;
// Parent's (TextView) previous position on screen
@@ -4638,8 +4638,18 @@ public class Editor {
final int handleHeight = getPreferredHeight();
mTouchOffsetY = -0.3f * handleHeight;
- mIdealVerticalOffset = 0.7f * handleHeight;
- mIdealFingerToCursorOffset = (int)(mIdealVerticalOffset - mTouchOffsetY);
+ final int distance = AppGlobals.getIntCoreSetting(
+ WidgetFlags.KEY_FINGER_TO_CURSOR_DISTANCE,
+ WidgetFlags.FINGER_TO_CURSOR_DISTANCE_DEFAULT);
+ if (distance < 0 || distance > 100) {
+ mIdealVerticalOffset = 0.7f * handleHeight;
+ mIdealFingerToCursorOffset = (int)(mIdealVerticalOffset - mTouchOffsetY);
+ } else {
+ mIdealFingerToCursorOffset = (int) TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP, distance,
+ mTextView.getContext().getResources().getDisplayMetrics());
+ mIdealVerticalOffset = mIdealFingerToCursorOffset + mTouchOffsetY;
+ }
}
public float getIdealVerticalOffset() {
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 01a0e9b15463..7f6c0d2077f1 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -2818,7 +2818,7 @@ public class RemoteViews implements Parcelable, Filter {
/**
* When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very
- * costly to set PendingIntents on the individual items, and is hence not permitted. Instead
+ * costly to set PendingIntents on the individual items, and is hence not recommended. Instead
* this method should be used to set a single PendingIntent template on the collection, and
* individual items can differentiate their on-click behavior using
* {@link RemoteViews#setOnClickFillInIntent(int, Intent)}.
@@ -2834,7 +2834,7 @@ public class RemoteViews implements Parcelable, Filter {
/**
* When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very
- * costly to set PendingIntents on the individual items, and is hence not permitted. Instead
+ * costly to set PendingIntents on the individual items, and is hence not recommended. Instead
* a single PendingIntent template can be set on the collection, see {@link
* RemoteViews#setPendingIntentTemplate(int, PendingIntent)}, and the individual on-click
* action of a given item can be distinguished by setting a fillInIntent on that item. The
@@ -3960,7 +3960,7 @@ public class RemoteViews implements Parcelable, Filter {
/**
* When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is
- * very costly to set PendingIntents on the individual items, and is hence not permitted.
+ * very costly to set PendingIntents on the individual items, and is hence not recommended.
* Instead a single PendingIntent template can be set on the collection, see {@link
* RemoteViews#setPendingIntentTemplate(int, PendingIntent)}, and the individual on-click
* action of a given item can be distinguished by setting a fillInIntent on that item. The
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 4ef3f61b5280..45943f512c22 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -39,7 +39,6 @@ import android.view.ActionMode;
import android.view.textclassifier.ExtrasUtils;
import android.view.textclassifier.SelectionEvent;
import android.view.textclassifier.SelectionEvent.InvocationMethod;
-import android.view.textclassifier.SelectionSessionLogger;
import android.view.textclassifier.TextClassification;
import android.view.textclassifier.TextClassificationConstants;
import android.view.textclassifier.TextClassificationContext;
@@ -705,7 +704,7 @@ public final class SelectionActionModeHelper {
SelectionMetricsLogger(TextView textView) {
Objects.requireNonNull(textView);
mEditTextLogger = textView.isTextEditable();
- mTokenIterator = SelectionSessionLogger.getTokenIterator(textView.getTextLocale());
+ mTokenIterator = BreakIterator.getWordInstance(textView.getTextLocale());
}
public void logSelectionStarted(
diff --git a/core/java/android/widget/WidgetFlags.java b/core/java/android/widget/WidgetFlags.java
index bce5497a7c2d..09ab5aa17782 100644
--- a/core/java/android/widget/WidgetFlags.java
+++ b/core/java/android/widget/WidgetFlags.java
@@ -41,6 +41,25 @@ public final class WidgetFlags {
public static final boolean ENABLE_CURSOR_DRAG_FROM_ANYWHERE_DEFAULT = true;
/**
+ * The flag of finger-to-cursor distance in DP for cursor dragging.
+ * The value unit is DP and the range is {0..100}. If the value is out of range, the legacy
+ * value, which is based on handle size, will be used.
+ */
+ public static final String FINGER_TO_CURSOR_DISTANCE =
+ "CursorControlFeature__finger_to_cursor_distance";
+
+ /**
+ * The key used in app core settings for the flag {@link #FINGER_TO_CURSOR_DISTANCE}.
+ */
+ public static final String KEY_FINGER_TO_CURSOR_DISTANCE =
+ "widget__finger_to_cursor_distance";
+
+ /**
+ * Default value for the flag {@link #FINGER_TO_CURSOR_DISTANCE}.
+ */
+ public static final int FINGER_TO_CURSOR_DISTANCE_DEFAULT = -1;
+
+ /**
* Whether additional gestures should be enabled for the insertion cursor handle (e.g.
* long-press or double-tap on the handle to trigger selection).
*/
diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
index fa567f235c94..ec2653fe67b2 100644
--- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
@@ -26,6 +26,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.IPackageManager;
import android.content.pm.ResolveInfo;
+import android.os.AsyncTask;
import android.os.UserHandle;
import android.os.UserManager;
import android.stats.devicepolicy.DevicePolicyEnums;
@@ -90,7 +91,9 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
@Override
public void requestQuietModeEnabled(boolean enabled, UserHandle workProfileUserHandle) {
- userManager.requestQuietModeEnabled(enabled, workProfileUserHandle);
+ AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
+ userManager.requestQuietModeEnabled(enabled, workProfileUserHandle);
+ });
mIsWaitingToEnableWorkProfile = true;
}
};
@@ -284,7 +287,7 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
}
private int userHandleToPageIndex(UserHandle userHandle) {
- if (userHandle == getPersonalListAdapter().mResolverListController.getUserHandle()) {
+ if (userHandle.equals(getPersonalListAdapter().mResolverListController.getUserHandle())) {
return PROFILE_PERSONAL;
} else {
return PROFILE_WORK;
@@ -293,7 +296,7 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
private boolean rebuildTab(ResolverListAdapter activeListAdapter, boolean doPostProcessing) {
UserHandle listUserHandle = activeListAdapter.getUserHandle();
- if (listUserHandle == mWorkProfileUserHandle
+ if (listUserHandle.equals(mWorkProfileUserHandle)
&& mInjector.isQuietModeEnabled(mWorkProfileUserHandle)) {
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.RESOLVER_EMPTY_STATE_WORK_APPS_DISABLED)
@@ -311,7 +314,7 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
if (UserHandle.myUserId() != listUserHandle.getIdentifier()) {
if (!mInjector.hasCrossProfileIntents(activeListAdapter.getIntents(),
UserHandle.myUserId(), listUserHandle.getIdentifier())) {
- if (listUserHandle == mPersonalProfileUserHandle) {
+ if (listUserHandle.equals(mPersonalProfileUserHandle)) {
DevicePolicyEventLogger.createEvent(
DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL)
.setStrings(getMetricsCategory())
diff --git a/core/java/com/android/internal/app/AbstractResolverComparator.java b/core/java/com/android/internal/app/AbstractResolverComparator.java
index eb746127a351..e0bbc04515e0 100644
--- a/core/java/com/android/internal/app/AbstractResolverComparator.java
+++ b/core/java/com/android/internal/app/AbstractResolverComparator.java
@@ -221,6 +221,12 @@ public abstract class AbstractResolverComparator implements Comparator<ResolvedC
*/
abstract float getScore(ComponentName name);
+ /**
+ * Returns the list of top K component names which have highest
+ * {@link #getScore(ComponentName)}
+ */
+ abstract List<ComponentName> getTopComponentNames(int topK);
+
/** Handles result message sent to mHandler. */
abstract void handleResultMessage(Message message);
diff --git a/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java b/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java
index 04ad7e9ee8a7..ba8c7b32ad02 100644
--- a/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java
+++ b/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java
@@ -36,7 +36,9 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.concurrent.Executors;
+import java.util.stream.Collectors;
/**
* Uses an {@link AppPredictor} to sort Resolver targets. If the AppPredictionService appears to be
@@ -160,6 +162,15 @@ class AppPredictionServiceResolverComparator extends AbstractResolverComparator
}
@Override
+ List<ComponentName> getTopComponentNames(int topK) {
+ return mTargetRanks.entrySet().stream()
+ .sorted(Entry.comparingByValue())
+ .limit(topK)
+ .map(Entry::getKey)
+ .collect(Collectors.toList());
+ }
+
+ @Override
void updateModel(ComponentName componentName) {
if (mResolverRankerService != null) {
mResolverRankerService.updateModel(componentName);
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 5620bff5142b..78a0ae0907ff 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -223,6 +223,11 @@ public class ChooserActivity extends ResolverActivity implements
SystemUiDeviceConfigFlags.HASH_SALT_MAX_DAYS,
DEFAULT_SALT_EXPIRATION_DAYS);
+ private boolean mAppendDirectShareEnabled = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.APPEND_DIRECT_SHARE_ENABLED,
+ false);
+
private Bundle mReplacementExtras;
private IntentSender mChosenComponentSender;
private IntentSender mRefinementIntentSender;
@@ -409,6 +414,11 @@ public class ChooserActivity extends ResolverActivity implements
private static final int WATCHDOG_TIMEOUT_MAX_MILLIS = 10000;
private static final int WATCHDOG_TIMEOUT_MIN_MILLIS = 3000;
+ private static final int DEFAULT_DIRECT_SHARE_TIMEOUT_MILLIS = 1500;
+ private int mDirectShareTimeout = DeviceConfig.getInt(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.SHARE_SHEET_DIRECT_SHARE_TIMEOUT,
+ DEFAULT_DIRECT_SHARE_TIMEOUT_MILLIS);
+
private boolean mMinTimeoutPassed = false;
private void removeAllMessages() {
@@ -427,15 +437,14 @@ public class ChooserActivity extends ResolverActivity implements
if (DEBUG) {
Log.d(TAG, "queryTargets setting watchdog timer for "
- + WATCHDOG_TIMEOUT_MIN_MILLIS + "-"
+ + mDirectShareTimeout + "-"
+ WATCHDOG_TIMEOUT_MAX_MILLIS + "ms");
}
sendEmptyMessageDelayed(CHOOSER_TARGET_SERVICE_WATCHDOG_MIN_TIMEOUT,
WATCHDOG_TIMEOUT_MIN_MILLIS);
sendEmptyMessageDelayed(CHOOSER_TARGET_SERVICE_WATCHDOG_MAX_TIMEOUT,
- WATCHDOG_TIMEOUT_MAX_MILLIS);
-
+ mAppendDirectShareEnabled ? mDirectShareTimeout : WATCHDOG_TIMEOUT_MAX_MILLIS);
}
private void maybeStopServiceRequestTimer() {
@@ -463,6 +472,7 @@ public class ChooserActivity extends ResolverActivity implements
final ServiceResultInfo sri = (ServiceResultInfo) msg.obj;
if (!mServiceConnections.contains(sri.connection)) {
Log.w(TAG, "ChooserTargetServiceConnection " + sri.connection
+ + sri.originalTarget.getResolveInfo().activityInfo.packageName
+ " returned after being removed from active connections."
+ " Have you considered returning results faster?");
break;
@@ -474,7 +484,7 @@ public class ChooserActivity extends ResolverActivity implements
if (adapterForUserHandle != null) {
adapterForUserHandle.addServiceResults(sri.originalTarget,
sri.resultTargets, TARGET_TYPE_CHOOSER_TARGET,
- /* directShareShortcutInfoCache */ null);
+ /* directShareShortcutInfoCache */ null, mServiceConnections);
}
}
unbindService(sri.connection);
@@ -489,6 +499,7 @@ public class ChooserActivity extends ResolverActivity implements
break;
case CHOOSER_TARGET_SERVICE_WATCHDOG_MAX_TIMEOUT:
+ mMinTimeoutPassed = true;
unbindRemainingServices();
maybeStopServiceRequestTimer();
break;
@@ -513,7 +524,7 @@ public class ChooserActivity extends ResolverActivity implements
if (adapterForUserHandle != null) {
adapterForUserHandle.addServiceResults(
resultInfo.originalTarget, resultInfo.resultTargets, msg.arg1,
- mDirectShareShortcutInfoCache);
+ mDirectShareShortcutInfoCache, mServiceConnections);
}
}
break;
@@ -1481,7 +1492,7 @@ public class ChooserActivity extends ResolverActivity implements
/* origTarget */ null,
Lists.newArrayList(mCallerChooserTargets),
TARGET_TYPE_DEFAULT,
- /* directShareShortcutInfoCache */ null);
+ /* directShareShortcutInfoCache */ null, mServiceConnections);
}
}
@@ -1525,10 +1536,12 @@ public class ChooserActivity extends ResolverActivity implements
labels.add(innerInfo.getResolveInfo().loadLabel(getPackageManager()));
}
f = new ResolverTargetActionsDialogFragment(mti.getDisplayLabel(), name,
- mti.getTargets(), labels);
+ mti.getTargets(), labels,
+ mChooserMultiProfilePagerAdapter.getCurrentUserHandle());
} else {
f = new ResolverTargetActionsDialogFragment(
- ti.getResolveInfo().loadLabel(getPackageManager()), name, pinned);
+ ti.getResolveInfo().loadLabel(getPackageManager()), name, pinned,
+ mChooserMultiProfilePagerAdapter.getCurrentUserHandle());
}
f.show(getFragmentManager(), TARGET_DETAILS_FRAGMENT_TAG);
@@ -2129,7 +2142,7 @@ public class ChooserActivity extends ResolverActivity implements
return null;
}
- if (getPersonalProfileUserHandle() == userHandle) {
+ if (getPersonalProfileUserHandle().equals(userHandle)) {
if (mPersonalAppPredictor != null) {
return mPersonalAppPredictor;
}
@@ -2155,7 +2168,7 @@ public class ChooserActivity extends ResolverActivity implements
.getSystemService(AppPredictionManager.class);
AppPredictor appPredictionSession = appPredictionManager.createAppPredictionSession(
appPredictionContext);
- if (getPersonalProfileUserHandle() == userHandle) {
+ if (getPersonalProfileUserHandle().equals(userHandle)) {
mPersonalAppPredictor = appPredictionSession;
} else {
mWorkAppPredictor = appPredictionSession;
@@ -2555,7 +2568,7 @@ public class ChooserActivity extends ResolverActivity implements
ChooserListAdapter chooserListAdapter = (ChooserListAdapter) listAdapter;
if (chooserListAdapter.getUserHandle()
- == mChooserMultiProfilePagerAdapter.getCurrentUserHandle()) {
+ .equals(mChooserMultiProfilePagerAdapter.getCurrentUserHandle())) {
mChooserMultiProfilePagerAdapter.getActiveAdapterView()
.setAdapter(mChooserMultiProfilePagerAdapter.getCurrentRootAdapter());
mChooserMultiProfilePagerAdapter
@@ -3584,6 +3597,10 @@ public class ChooserActivity extends ResolverActivity implements
? mOriginalTarget.getResolveInfo().activityInfo.toString()
: "<connection destroyed>") + "}";
}
+
+ public ComponentName getComponentName() {
+ return mOriginalTarget.getResolveInfo().activityInfo.getComponentName();
+ }
}
static class ServiceResultInfo {
diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java
index 74ae29117b59..0ea855a6b7a9 100644
--- a/core/java/com/android/internal/app/ChooserListAdapter.java
+++ b/core/java/com/android/internal/app/ChooserListAdapter.java
@@ -32,8 +32,10 @@ import android.content.pm.ShortcutInfo;
import android.os.AsyncTask;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.DeviceConfig;
import android.service.chooser.ChooserTarget;
import android.util.Log;
+import android.util.Pair;
import android.view.View;
import android.view.ViewGroup;
@@ -44,17 +46,26 @@ import com.android.internal.app.chooser.DisplayResolveInfo;
import com.android.internal.app.chooser.MultiDisplayResolveInfo;
import com.android.internal.app.chooser.SelectableTargetInfo;
import com.android.internal.app.chooser.TargetInfo;
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
public class ChooserListAdapter extends ResolverListAdapter {
private static final String TAG = "ChooserListAdapter";
private static final boolean DEBUG = false;
+ private boolean mAppendDirectShareEnabled = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.APPEND_DIRECT_SHARE_ENABLED,
+ false);
+
private boolean mEnableStackedApps = true;
public static final int NO_POSITION = -1;
@@ -84,6 +95,11 @@ public class ChooserListAdapter extends ResolverListAdapter {
// Reserve spots for incoming direct share targets by adding placeholders
private ChooserTargetInfo
mPlaceHolderTargetInfo = new ChooserActivity.PlaceHolderTargetInfo();
+ private int mValidServiceTargetsNum = 0;
+ private final Map<ComponentName, Pair<List<ChooserTargetInfo>, Integer>>
+ mParkingDirectShareTargets = new HashMap<>();
+ private Set<ComponentName> mPendingChooserTargetService = new HashSet<>();
+ private Set<ComponentName> mShortcutComponents = new HashSet<>();
private final List<ChooserTargetInfo> mServiceTargets = new ArrayList<>();
private final List<TargetInfo> mCallerTargets = new ArrayList<>();
@@ -189,6 +205,9 @@ public class ChooserListAdapter extends ResolverListAdapter {
void refreshListView() {
if (mListViewDataChanged) {
+ if (mAppendDirectShareEnabled) {
+ appendServiceTargetsWithQuota();
+ }
super.notifyDataSetChanged();
}
mListViewDataChanged = false;
@@ -198,6 +217,10 @@ public class ChooserListAdapter extends ResolverListAdapter {
private void createPlaceHolders() {
mNumShortcutResults = 0;
mServiceTargets.clear();
+ mValidServiceTargetsNum = 0;
+ mParkingDirectShareTargets.clear();
+ mPendingChooserTargetService.clear();
+ mShortcutComponents.clear();
for (int i = 0; i < MAX_SERVICE_TARGETS; i++) {
mServiceTargets.add(mPlaceHolderTargetInfo);
}
@@ -393,12 +416,19 @@ public class ChooserListAdapter extends ResolverListAdapter {
*/
public void addServiceResults(DisplayResolveInfo origTarget, List<ChooserTarget> targets,
@ChooserActivity.ShareTargetType int targetType,
- Map<ChooserTarget, ShortcutInfo> directShareToShortcutInfos) {
+ Map<ChooserTarget, ShortcutInfo> directShareToShortcutInfos,
+ List<ChooserActivity.ChooserTargetServiceConnection>
+ pendingChooserTargetServiceConnections) {
if (DEBUG) {
- Log.d(TAG, "addServiceResults " + origTarget + ", " + targets.size()
+ Log.d(TAG, "addServiceResults " + origTarget.getResolvedComponentName() + ", "
+ + targets.size()
+ " targets");
}
-
+ if (mAppendDirectShareEnabled) {
+ parkTargetIntoMemory(origTarget, targets, targetType, directShareToShortcutInfos,
+ pendingChooserTargetServiceConnections);
+ return;
+ }
if (targets.size() == 0) {
return;
}
@@ -449,6 +479,126 @@ public class ChooserListAdapter extends ResolverListAdapter {
}
}
+ /**
+ * Park {@code targets} into memory for the moment to surface them later when view is refreshed.
+ * Components pending on ChooserTargetService query are also recorded.
+ */
+ private void parkTargetIntoMemory(DisplayResolveInfo origTarget, List<ChooserTarget> targets,
+ @ChooserActivity.ShareTargetType int targetType,
+ Map<ChooserTarget, ShortcutInfo> directShareToShortcutInfos,
+ List<ChooserActivity.ChooserTargetServiceConnection>
+ pendingChooserTargetServiceConnections) {
+ ComponentName origComponentName = origTarget.getResolvedComponentName();
+ mPendingChooserTargetService = pendingChooserTargetServiceConnections.stream()
+ .map(ChooserActivity.ChooserTargetServiceConnection::getComponentName)
+ .filter(componentName -> !componentName.equals(origComponentName))
+ .collect(Collectors.toSet());
+ // Park targets in memory
+ if (!targets.isEmpty() && !mParkingDirectShareTargets.containsKey(origComponentName)) {
+ final boolean isShortcutResult =
+ (targetType == TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER
+ || targetType == TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE);
+ Context contextAsUser = mContext.createContextAsUser(getUserHandle(),
+ 0 /* flags */);
+ List<ChooserTargetInfo> parkingTargetInfos = targets.stream()
+ .map(target ->
+ new SelectableTargetInfo(
+ contextAsUser, origTarget, target, target.getScore(),
+ mSelectableTargetInfoComunicator,
+ (isShortcutResult ? directShareToShortcutInfos.get(target)
+ : null))
+ )
+ .collect(Collectors.toList());
+ mParkingDirectShareTargets.put(origComponentName,
+ new Pair<>(parkingTargetInfos, 0));
+ if (isShortcutResult) {
+ mShortcutComponents.add(origComponentName);
+ }
+ }
+ notifyDataSetChanged();
+ }
+
+ /**
+ * Append targets of top ranked share app into direct share row with quota limit. Remove
+ * appended ones from memory.
+ */
+ private void appendServiceTargetsWithQuota() {
+ int maxRankedTargets = mChooserListCommunicator.getMaxRankedTargets();
+ List<ComponentName> topComponentNames = getTopComponentNames(maxRankedTargets);
+ int appRank = 0;
+ for (ComponentName component : topComponentNames) {
+ if (!mPendingChooserTargetService.contains(component)
+ && !mParkingDirectShareTargets.containsKey(component)) {
+ continue;
+ }
+ appRank++;
+ Pair<List<ChooserTargetInfo>, Integer> parkingTargetsItem =
+ mParkingDirectShareTargets.get(component);
+ if (parkingTargetsItem != null && parkingTargetsItem.second == 0) {
+ List<ChooserTargetInfo> parkingTargets = parkingTargetsItem.first;
+ int initTargetsQuota = appRank <= maxRankedTargets / 2 ? 2 : 1;
+ int insertedNum = 0;
+ while (insertedNum < initTargetsQuota && !parkingTargets.isEmpty()) {
+ if (!checkDuplicateTarget(parkingTargets.get(0))) {
+ mServiceTargets.add(mValidServiceTargetsNum, parkingTargets.get(0));
+ mValidServiceTargetsNum++;
+ insertedNum++;
+ }
+ parkingTargets.remove(0);
+ }
+ mParkingDirectShareTargets.put(component, new Pair<>(parkingTargets, insertedNum));
+ if (mShortcutComponents.contains(component)) {
+ mNumShortcutResults += insertedNum;
+ }
+ }
+ }
+ }
+
+ /**
+ * Append all remaining targets (parking in memory) into direct share row as per their ranking.
+ */
+ private void fillAllServiceTargets() {
+ int maxRankedTargets = mChooserListCommunicator.getMaxRankedTargets();
+ List<ComponentName> topComponentNames = getTopComponentNames(maxRankedTargets);
+ // Append all remaining targets of top recommended components into direct share row.
+ for (ComponentName component : topComponentNames) {
+ if (!mParkingDirectShareTargets.containsKey(component)) {
+ continue;
+ }
+ mParkingDirectShareTargets.get(component).first.stream()
+ .filter(target -> !checkDuplicateTarget(target))
+ .forEach(target -> {
+ if (mShortcutComponents.contains(component)) {
+ mNumShortcutResults++;
+ }
+ mServiceTargets.add(target);
+ });
+ mParkingDirectShareTargets.remove(component);
+ }
+ // Append all remaining shortcuts targets into direct share row.
+ List<ChooserTargetInfo> remainingTargets = new ArrayList<>();
+ mParkingDirectShareTargets.entrySet().stream()
+ .filter(entry -> mShortcutComponents.contains(entry.getKey()))
+ .map(entry -> entry.getValue())
+ .map(pair -> pair.first)
+ .forEach(remainingTargets::addAll);
+ remainingTargets.sort(
+ (t1, t2) -> -Float.compare(t1.getModifiedScore(), t2.getModifiedScore()));
+ mServiceTargets.addAll(remainingTargets);
+ mNumShortcutResults += remainingTargets.size();
+ mParkingDirectShareTargets.clear();
+ }
+
+ private boolean checkDuplicateTarget(ChooserTargetInfo chooserTargetInfo) {
+ // Check for duplicates and abort if found
+ for (ChooserTargetInfo otherTargetInfo : mServiceTargets) {
+ if (chooserTargetInfo.isSimilar(otherTargetInfo)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
int getNumShortcutResults() {
return mNumShortcutResults;
}
@@ -487,7 +637,9 @@ public class ChooserListAdapter extends ResolverListAdapter {
*/
public void completeServiceTargetLoading() {
mServiceTargets.removeIf(o -> o instanceof ChooserActivity.PlaceHolderTargetInfo);
-
+ if (mAppendDirectShareEnabled) {
+ fillAllServiceTargets();
+ }
if (mServiceTargets.isEmpty()) {
mServiceTargets.add(new ChooserActivity.EmptyTargetInfo());
}
diff --git a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
index 2167b1ebd473..c6a00f3e46b4 100644
--- a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
@@ -93,10 +93,10 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
@Override
@Nullable
ChooserListAdapter getListAdapterForUserHandle(UserHandle userHandle) {
- if (getActiveListAdapter().getUserHandle() == userHandle) {
+ if (getActiveListAdapter().getUserHandle().equals(userHandle)) {
return getActiveListAdapter();
} else if (getInactiveListAdapter() != null
- && getInactiveListAdapter().getUserHandle() == userHandle) {
+ && getInactiveListAdapter().getUserHandle().equals(userHandle)) {
return getInactiveListAdapter();
}
return null;
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
index 7a0afa2036f6..9bdfa4ad4e43 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -38,6 +38,7 @@ import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.Settings;
import android.util.Slog;
import android.widget.Toast;
@@ -153,6 +154,9 @@ public class IntentForwarderActivity extends Activity {
}
private boolean shouldShowDisclosure(@Nullable ResolveInfo ri, Intent intent) {
+ if (!isDeviceProvisioned()) {
+ return false;
+ }
if (ri == null || ri.activityInfo == null) {
return true;
}
@@ -163,6 +167,11 @@ public class IntentForwarderActivity extends Activity {
return !isTargetResolverOrChooserActivity(ri.activityInfo);
}
+ private boolean isDeviceProvisioned() {
+ return Settings.Global.getInt(getContentResolver(),
+ Settings.Global.DEVICE_PROVISIONED, /* def= */ 0) != 0;
+ }
+
private boolean isTextMessageIntent(Intent intent) {
return (Intent.ACTION_SENDTO.equals(intent.getAction()) || isViewActionIntent(intent))
&& ALLOWED_TEXT_MESSAGE_SCHEMES.contains(intent.getScheme());
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index ec371d9a7311..8e64b970441a 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -49,6 +49,7 @@ import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.graphics.Insets;
import android.net.Uri;
import android.os.Build;
@@ -65,6 +66,7 @@ import android.stats.devicepolicy.DevicePolicyEnums;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
+import android.util.TypedValue;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -1242,12 +1244,12 @@ public class ResolverActivity extends Activity implements
}
private void maybeLogCrossProfileTargetLaunch(TargetInfo cti, UserHandle currentUserHandle) {
- if (!hasWorkProfile() || currentUserHandle == getUser()) {
+ if (!hasWorkProfile() || currentUserHandle.equals(getUser())) {
return;
}
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.RESOLVER_CROSS_PROFILE_TARGET_OPENED)
- .setBoolean(currentUserHandle == getPersonalProfileUserHandle())
+ .setBoolean(currentUserHandle.equals(getPersonalProfileUserHandle()))
.setStrings(getMetricsCategory(),
cti instanceof ChooserTargetInfo ? "direct_share" : "other_target")
.write();
@@ -1303,7 +1305,7 @@ public class ResolverActivity extends Activity implements
Intent in = new Intent().setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
.setData(Uri.fromParts("package", ri.activityInfo.packageName, null))
.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
- startActivity(in);
+ startActivityAsUser(in, mMultiProfilePagerAdapter.getCurrentUserHandle());
}
@VisibleForTesting
@@ -1486,7 +1488,8 @@ public class ResolverActivity extends Activity implements
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.RESOLVER_AUTOLAUNCH_CROSS_PROFILE_TARGET)
- .setBoolean(activeListAdapter.getUserHandle() == getPersonalProfileUserHandle())
+ .setBoolean(activeListAdapter.getUserHandle()
+ .equals(getPersonalProfileUserHandle()))
.setStrings(getMetricsCategory())
.write();
safelyStartActivity(activeProfileTarget);
@@ -1605,7 +1608,10 @@ public class ResolverActivity extends Activity implements
for (int i = 0; i < tabWidget.getChildCount(); i++) {
View tabView = tabWidget.getChildAt(i);
TextView title = tabView.findViewById(android.R.id.title);
- title.setTextColor(getColor(R.color.resolver_tabs_inactive_color));
+ title.setTextAppearance(android.R.style.TextAppearance_DeviceDefault_DialogWindowTitle);
+ title.setTextColor(getAttrColor(this, android.R.attr.textColorTertiary));
+ title.setTextSize(TypedValue.COMPLEX_UNIT_PX,
+ getResources().getDimension(R.dimen.resolver_tab_text_size));
if (title.getText().equals(getString(R.string.resolver_personal_tab))) {
tabView.setContentDescription(personalContentDescription);
} else if (title.getText().equals(getString(R.string.resolver_work_tab))) {
@@ -1614,10 +1620,17 @@ public class ResolverActivity extends Activity implements
}
}
+ private static int getAttrColor(Context context, int attr) {
+ TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
+ int colorAccent = ta.getColor(0, 0);
+ ta.recycle();
+ return colorAccent;
+ }
+
private void updateActiveTabStyle(TabHost tabHost) {
TextView title = tabHost.getTabWidget().getChildAt(tabHost.getCurrentTab())
.findViewById(android.R.id.title);
- title.setTextColor(getColor(R.color.resolver_tabs_active_color));
+ title.setTextColor(getAttrColor(this, android.R.attr.colorAccent));
}
private void setupViewVisibilities() {
@@ -1778,7 +1791,7 @@ public class ResolverActivity extends Activity implements
@Override // ResolverListCommunicator
public void onHandlePackagesChanged(ResolverListAdapter listAdapter) {
if (listAdapter == mMultiProfilePagerAdapter.getActiveListAdapter()) {
- if (listAdapter.getUserHandle() == getWorkProfileUserHandle()
+ if (listAdapter.getUserHandle().equals(getWorkProfileUserHandle())
&& mMultiProfilePagerAdapter.isWaitingToEnableWorkProfile()) {
// We have just turned on the work profile and entered the pass code to start it,
// now we are waiting to receive the ACTION_USER_UNLOCKED broadcast. There is no
@@ -1819,7 +1832,7 @@ public class ResolverActivity extends Activity implements
mMultiProfilePagerAdapter.markWorkProfileEnabledBroadcastReceived();
}
if (mMultiProfilePagerAdapter.getCurrentUserHandle()
- == getWorkProfileUserHandle()) {
+ .equals(getWorkProfileUserHandle())) {
mMultiProfilePagerAdapter.rebuildActiveTab(true);
} else {
mMultiProfilePagerAdapter.clearInactiveProfileCache();
diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java
index 61a404e3bc5f..579abeecad13 100644
--- a/core/java/com/android/internal/app/ResolverListAdapter.java
+++ b/core/java/com/android/internal/app/ResolverListAdapter.java
@@ -153,6 +153,14 @@ public class ResolverListAdapter extends BaseAdapter {
return mResolverListController.getScore(target);
}
+ /**
+ * Returns the list of top K component names which have highest
+ * {@link #getScore(DisplayResolveInfo)}
+ */
+ public List<ComponentName> getTopComponentNames(int topK) {
+ return mResolverListController.getTopComponentNames(topK);
+ }
+
public void updateModel(ComponentName componentName) {
mResolverListController.updateModel(componentName);
}
diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java
index 022aded188fa..51dce5547890 100644
--- a/core/java/com/android/internal/app/ResolverListController.java
+++ b/core/java/com/android/internal/app/ResolverListController.java
@@ -367,6 +367,14 @@ public class ResolverListController {
return mResolverComparator.getScore(target.getResolvedComponentName());
}
+ /**
+ * Returns the list of top K component names which have highest
+ * {@link #getScore(DisplayResolveInfo)}
+ */
+ public List<ComponentName> getTopComponentNames(int topK) {
+ return mResolverComparator.getTopComponentNames(topK);
+ }
+
public void updateModel(ComponentName componentName) {
mResolverComparator.updateModel(componentName);
}
diff --git a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
index 0440f5e92ce4..578f6ae63ff1 100644
--- a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
@@ -103,10 +103,10 @@ public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerA
@Override
@Nullable
ResolverListAdapter getListAdapterForUserHandle(UserHandle userHandle) {
- if (getActiveListAdapter().getUserHandle() == userHandle) {
+ if (getActiveListAdapter().getUserHandle().equals(userHandle)) {
return getActiveListAdapter();
} else if (getInactiveListAdapter() != null
- && getInactiveListAdapter().getUserHandle() == userHandle) {
+ && getInactiveListAdapter().getUserHandle().equals(userHandle)) {
return getInactiveListAdapter();
}
return null;
diff --git a/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java b/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java
index 01e0622063cd..286945037ab7 100644
--- a/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java
+++ b/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java
@@ -48,6 +48,7 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
/**
* Ranks and compares packages based on usage stats and uses the {@link ResolverRankerService}.
@@ -252,6 +253,15 @@ class ResolverRankerServiceResolverComparator extends AbstractResolverComparator
return 0;
}
+ @Override
+ List<ComponentName> getTopComponentNames(int topK) {
+ return mTargetsDict.entrySet().stream()
+ .sorted((o1, o2) -> -Float.compare(getScore(o1.getKey()), getScore(o2.getKey())))
+ .limit(topK)
+ .map(Map.Entry::getKey)
+ .collect(Collectors.toList());
+ }
+
// update ranking model when the connection to it is valid.
@Override
public void updateModel(ComponentName componentName) {
diff --git a/core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java b/core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java
index 21efc78bddf8..35d9bcdbb033 100644
--- a/core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java
+++ b/core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java
@@ -27,6 +27,7 @@ import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Bundle;
+import android.os.UserHandle;
import android.provider.Settings;
import com.android.internal.R;
@@ -43,6 +44,7 @@ public class ResolverTargetActionsDialogFragment extends DialogFragment
private static final String NAME_KEY = "componentName";
private static final String TITLE_KEY = "title";
private static final String PINNED_KEY = "pinned";
+ private static final String USER_ID_KEY = "userId";
// Sync with R.array.resolver_target_actions_* resources
private static final int TOGGLE_PIN_INDEX = 0;
@@ -56,19 +58,21 @@ public class ResolverTargetActionsDialogFragment extends DialogFragment
}
public ResolverTargetActionsDialogFragment(CharSequence title, ComponentName name,
- boolean pinned) {
+ boolean pinned, UserHandle userHandle) {
Bundle args = new Bundle();
args.putCharSequence(TITLE_KEY, title);
args.putParcelable(NAME_KEY, name);
args.putBoolean(PINNED_KEY, pinned);
+ args.putParcelable(USER_ID_KEY, userHandle);
setArguments(args);
}
public ResolverTargetActionsDialogFragment(CharSequence title, ComponentName name,
- List<DisplayResolveInfo> targets, List<CharSequence> labels) {
+ List<DisplayResolveInfo> targets, List<CharSequence> labels, UserHandle userHandle) {
Bundle args = new Bundle();
args.putCharSequence(TITLE_KEY, title);
args.putParcelable(NAME_KEY, name);
+ args.putParcelable(USER_ID_KEY, userHandle);
mTargetInfos = targets;
mLabels = labels;
setArguments(args);
@@ -122,7 +126,8 @@ public class ResolverTargetActionsDialogFragment extends DialogFragment
Intent in = new Intent().setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
.setData(Uri.fromParts("package", name.getPackageName(), null))
.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
- startActivity(in);
+ UserHandle userHandle = args.getParcelable(USER_ID_KEY);
+ getActivity().startActivityAsUser(in, userHandle);
}
dismiss();
}
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 086b9d83fed5..837cc466a895 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -378,6 +378,17 @@ public final class SystemUiDeviceConfigFlags {
"nav_bar_handle_show_over_lockscreen";
/**
+ * (int) Timeout threshold, in millisecond, that Sharesheet waits for direct share targets.
+ */
+ public static final String SHARE_SHEET_DIRECT_SHARE_TIMEOUT =
+ "share_sheet_direct_share_timeout";
+
+ /**
+ * (boolean) Whether append direct share on Sharesheet is enabled.
+ */
+ public static final String APPEND_DIRECT_SHARE_ENABLED = "append_direct_share_enabled";
+
+ /**
* (boolean) Whether to enable user-drag resizing for PIP.
*/
public static final String PIP_USER_RESIZE = "pip_user_resize";
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 633aa2c5ffb4..ff03f1a1a2ab 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -205,6 +205,9 @@ public final class Zygote {
/** List of packages with the same uid, and its app data info: volume uuid and inode. */
public static final String PKG_DATA_INFO_MAP = "--pkg-data-info-map";
+ /** Bind mount app storage dirs to lower fs not via fuse */
+ public static final String BIND_MOUNT_APP_STORAGE_DIRS = "--bind-mount-storage-dirs";
+
/**
* An extraArg passed when a zygote process is forking a child-zygote, specifying a name
* in the abstract socket namespace. This socket name is what the new child zygote
@@ -310,6 +313,7 @@ public final class Zygote {
* @param isTopApp true if the process is for top (high priority) application.
* @param pkgDataInfoList A list that stores related packages and its app data
* info: volume uuid and inode.
+ * @param bindMountAppStorageDirs True if the zygote needs to mount storage dirs.
*
* @return 0 if this is the child, pid of the child
* if this is the parent, or -1 on error.
@@ -317,13 +321,13 @@ public final class Zygote {
static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
- boolean isTopApp, String[] pkgDataInfoList) {
+ boolean isTopApp, String[] pkgDataInfoList, boolean bindMountAppStorageDirs) {
ZygoteHooks.preFork();
int pid = nativeForkAndSpecialize(
uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
fdsToIgnore, startChildZygote, instructionSet, appDataDir, isTopApp,
- pkgDataInfoList);
+ pkgDataInfoList, bindMountAppStorageDirs);
if (pid == 0) {
// Note that this event ends at the end of handleChildProc,
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
@@ -339,7 +343,8 @@ public final class Zygote {
private static native int nativeForkAndSpecialize(int uid, int gid, int[] gids,
int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet,
- String appDataDir, boolean isTopApp, String[] pkgDataInfoList);
+ String appDataDir, boolean isTopApp, String[] pkgDataInfoList,
+ boolean bindMountAppStorageDirs);
/**
* Specialize an unspecialized app process. The current VM must have been started
@@ -366,14 +371,15 @@ public final class Zygote {
* volume uuid and CE dir inode. For example, pkgDataInfoList = [app_a_pkg_name,
* app_a_data_volume_uuid, app_a_ce_inode, app_b_pkg_name, app_b_data_volume_uuid,
* app_b_ce_inode, ...];
+ * @param bindMountAppStorageDirs True if the zygote needs to mount storage dirs.
*/
private static void specializeAppProcess(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName,
boolean startChildZygote, String instructionSet, String appDataDir, boolean isTopApp,
- String[] pkgDataInfoList) {
+ String[] pkgDataInfoList, boolean bindMountAppStorageDirs) {
nativeSpecializeAppProcess(uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo,
niceName, startChildZygote, instructionSet, appDataDir, isTopApp,
- pkgDataInfoList);
+ pkgDataInfoList, bindMountAppStorageDirs);
// Note that this event ends at the end of handleChildProc.
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
@@ -393,7 +399,7 @@ public final class Zygote {
private static native void nativeSpecializeAppProcess(int uid, int gid, int[] gids,
int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
boolean startChildZygote, String instructionSet, String appDataDir, boolean isTopApp,
- String[] pkgDataInfoList);
+ String[] pkgDataInfoList, boolean bindMountAppStorageDirs);
/**
* Called to do any initialization before starting an application.
@@ -718,7 +724,7 @@ public final class Zygote {
args.mRuntimeFlags, rlimits, args.mMountExternal,
args.mSeInfo, args.mNiceName, args.mStartChildZygote,
args.mInstructionSet, args.mAppDataDir, args.mIsTopApp,
- args.mPkgDataInfoList);
+ args.mPkgDataInfoList, args.mBindMountAppStorageDirs);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java
index 37f570bba238..1a63765fcaa6 100644
--- a/core/java/com/android/internal/os/ZygoteArguments.java
+++ b/core/java/com/android/internal/os/ZygoteArguments.java
@@ -227,6 +227,11 @@ class ZygoteArguments {
String[] mPkgDataInfoList;
/**
+ * @see Zygote#BIND_MOUNT_APP_STORAGE_DIRS
+ */
+ boolean mBindMountAppStorageDirs;
+
+ /**
* Constructs instance and parses args
*
* @param args zygote command-line args
@@ -447,6 +452,8 @@ class ZygoteArguments {
}
} else if (arg.startsWith(Zygote.PKG_DATA_INFO_MAP)) {
mPkgDataInfoList = getAssignmentList(arg);
+ } else if (arg.equals(Zygote.BIND_MOUNT_APP_STORAGE_DIRS)) {
+ mBindMountAppStorageDirs = true;
} else {
break;
}
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 4949811bbe17..bc8dfd4aa402 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -258,7 +258,7 @@ class ZygoteConnection {
parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo,
parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mIsTopApp,
- parsedArgs.mPkgDataInfoList);
+ parsedArgs.mPkgDataInfoList, parsedArgs.mBindMountAppStorageDirs);
try {
if (pid == 0) {
diff --git a/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java b/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java
new file mode 100644
index 000000000000..ebfea450af88
--- /dev/null
+++ b/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+/**
+ * @hide
+ */
+public class GestureNavigationSettingsObserver extends ContentObserver {
+ private Context mContext;
+ private Runnable mOnChangeRunnable;
+
+ public GestureNavigationSettingsObserver(Handler handler, Context context,
+ Runnable onChangeRunnable) {
+ super(handler);
+ mContext = context;
+ mOnChangeRunnable = onChangeRunnable;
+ }
+
+ public void register() {
+ ContentResolver r = mContext.getContentResolver();
+ r.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.BACK_GESTURE_INSET_SCALE_LEFT),
+ false, this, UserHandle.USER_ALL);
+ r.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.BACK_GESTURE_INSET_SCALE_RIGHT),
+ false, this, UserHandle.USER_ALL);
+ }
+
+ public void unregister() {
+ mContext.getContentResolver().unregisterContentObserver(this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+ if (mOnChangeRunnable != null) {
+ mOnChangeRunnable.run();
+ }
+ }
+
+ public int getLeftSensitivity(Resources userRes) {
+ return getSensitivity(userRes, Settings.Secure.BACK_GESTURE_INSET_SCALE_LEFT);
+ }
+
+ public int getRightSensitivity(Resources userRes) {
+ return getSensitivity(userRes, Settings.Secure.BACK_GESTURE_INSET_SCALE_RIGHT);
+ }
+
+ private int getSensitivity(Resources userRes, String side) {
+ final int inset = userRes.getDimensionPixelSize(
+ com.android.internal.R.dimen.config_backGestureInset);
+ final float scale = Settings.Secure.getFloatForUser(
+ mContext.getContentResolver(), side, 1.0f, UserHandle.USER_CURRENT);
+ return (int) (inset * scale);
+ }
+}
diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
index 8446bbd09df7..e4a44084e91c 100755
--- a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
+++ b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
@@ -200,8 +200,9 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object,
try {
return doInvoke();
} finally {
- if (isRecycleOnUse()) doRecycle();
- if (!isRecycled()) {
+ if (isRecycleOnUse()) {
+ doRecycle();
+ } else if (!isRecycled()) {
int argsSize = ArrayUtils.size(mArgs);
for (int i = 0; i < argsSize; i++) {
popArg(i);
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index 07be113d9a53..73da6007156a 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -122,6 +122,11 @@ public class ConversationLayout extends FrameLayout
private CharSequence mFallbackChatName;
private CharSequence mFallbackGroupChatName;
private CharSequence mConversationTitle;
+ private int mNotificationHeaderExpandedPadding;
+ private View mConversationHeader;
+ private View mContentContainer;
+ private boolean mExpandable = true;
+ private int mContentMarginEnd;
public ConversationLayout(@NonNull Context context) {
super(context);
@@ -166,12 +171,18 @@ public class ConversationLayout extends FrameLayout
});
mConversationText = findViewById(R.id.conversation_text);
mExpandButtonContainer = findViewById(R.id.expand_button_container);
+ mConversationHeader = findViewById(R.id.conversation_header);
+ mContentContainer = findViewById(R.id.notification_action_list_margin_target);
mExpandButtonAndContentContainer = findViewById(R.id.expand_button_and_content_container);
mExpandButton = findViewById(R.id.expand_button);
mExpandButtonExpandedTopMargin = getResources().getDimensionPixelSize(
R.dimen.conversation_expand_button_top_margin_expanded);
mExpandButtonExpandedSize = getResources().getDimensionPixelSize(
R.dimen.conversation_expand_button_expanded_size);
+ mNotificationHeaderExpandedPadding = getResources().getDimensionPixelSize(
+ R.dimen.conversation_header_expanded_padding_end);
+ mContentMarginEnd = getResources().getDimensionPixelSize(
+ R.dimen.notification_content_margin_end);
mBadgedSideMargins = getResources().getDimensionPixelSize(
R.dimen.conversation_badge_side_margin);
mIconSizeBadged = getResources().getDimensionPixelSize(
@@ -207,6 +218,7 @@ public class ConversationLayout extends FrameLayout
mIsCollapsed = isCollapsed;
mMessagingLinearLayout.setMaxDisplayedLines(isCollapsed ? 1 : Integer.MAX_VALUE);
updateExpandButton();
+ updateContentPaddings();
}
@RemotableViewMethod
@@ -652,6 +664,7 @@ public class ConversationLayout extends FrameLayout
mAddedGroups.add(newGroup);
}
newGroup.setDisplayImagesAtEnd(mIsCollapsed);
+ newGroup.setIsInConversation(true);
newGroup.setLayoutColor(mLayoutColor);
newGroup.setTextColors(mSenderTextColor, mMessageTextColor);
Person sender = senders.get(groupIndex);
@@ -851,10 +864,39 @@ public class ConversationLayout extends FrameLayout
mExpandButton.setLayoutParams(layoutParams);
mExpandButtonContainer.setContentDescription(mContext.getText(contentDescriptionId));
+ }
+
+ private void updateContentPaddings() {
+
+ // Let's make sure the conversation header can't run into the expand button when we're
+ // collapsed and update the paddings of the content
+ int headerPaddingEnd;
+ int contentPaddingEnd;
+ if (!mExpandable) {
+ headerPaddingEnd = 0;
+ contentPaddingEnd = mContentMarginEnd;
+ } else if (mIsCollapsed) {
+ headerPaddingEnd = 0;
+ contentPaddingEnd = 0;
+ } else {
+ headerPaddingEnd = mNotificationHeaderExpandedPadding;
+ contentPaddingEnd = mContentMarginEnd;
+ }
+ mConversationHeader.setPaddingRelative(
+ mConversationHeader.getPaddingStart(),
+ mConversationHeader.getPaddingTop(),
+ headerPaddingEnd,
+ mConversationHeader.getPaddingBottom());
+ mContentContainer.setPaddingRelative(
+ mContentContainer.getPaddingStart(),
+ mContentContainer.getPaddingTop(),
+ contentPaddingEnd,
+ mContentContainer.getPaddingBottom());
}
public void updateExpandability(boolean expandable, @Nullable OnClickListener onClickListener) {
+ mExpandable = expandable;
if (expandable) {
mExpandButtonContainer.setVisibility(VISIBLE);
mExpandButtonContainer.setOnClickListener(onClickListener);
@@ -862,5 +904,6 @@ public class ConversationLayout extends FrameLayout
// TODO: handle content paddings to end of layout
mExpandButtonContainer.setVisibility(GONE);
}
+ updateContentPaddings();
}
}
diff --git a/core/java/com/android/internal/widget/MessagingGroup.java b/core/java/com/android/internal/widget/MessagingGroup.java
index 99779032eebc..c68da97ab3b4 100644
--- a/core/java/com/android/internal/widget/MessagingGroup.java
+++ b/core/java/com/android/internal/widget/MessagingGroup.java
@@ -85,6 +85,10 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou
private int mSenderTextPaddingSingleLine;
private boolean mIsFirstGroupInLayout = true;
private boolean mCanHideSenderIfFirst;
+ private boolean mIsInConversation = true;
+ private ViewGroup mMessagingIconContainer;
+ private int mConversationContentStart;
+ private int mNonConversationMarginEnd;
public MessagingGroup(@NonNull Context context) {
super(context);
@@ -112,6 +116,7 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou
mAvatarView = findViewById(R.id.message_icon);
mImageContainer = findViewById(R.id.messaging_group_icon_container);
mSendingSpinner = findViewById(R.id.messaging_group_sending_progress);
+ mMessagingIconContainer = findViewById(R.id.message_icon_container);
mContentContainer = findViewById(R.id.messaging_group_content_container);
mSendingSpinnerContainer = findViewById(R.id.messaging_group_sending_progress_container);
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
@@ -119,6 +124,10 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou
mDisplaySize.y = displayMetrics.heightPixels;
mSenderTextPaddingSingleLine = getResources().getDimensionPixelSize(
R.dimen.messaging_group_singleline_sender_padding_end);
+ mConversationContentStart = getResources().getDimensionPixelSize(
+ R.dimen.conversation_content_start);
+ mNonConversationMarginEnd = getResources().getDimensionPixelSize(
+ R.dimen.messaging_layout_margin_end);
}
public void updateClipRect() {
@@ -644,4 +653,21 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou
public boolean isSingleLine() {
return mSingleLine;
}
+
+ /**
+ * Set this group to be displayed in a conversation and adjust the visual appearance
+ *
+ * @param isInConversation is this in a conversation
+ */
+ public void setIsInConversation(boolean isInConversation) {
+ if (mIsInConversation != isInConversation) {
+ mIsInConversation = isInConversation;
+ MarginLayoutParams layoutParams =
+ (MarginLayoutParams) mMessagingIconContainer.getLayoutParams();
+ layoutParams.width = mIsInConversation ? mConversationContentStart
+ : ViewPager.LayoutParams.WRAP_CONTENT;
+ layoutParams.setMarginEnd(mIsInConversation ? 0 : mNonConversationMarginEnd);
+ mMessagingIconContainer.setLayoutParams(layoutParams);
+ }
+ }
}
diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java
index 503f3f161c96..3fb5d43bea5a 100644
--- a/core/java/com/android/internal/widget/MessagingLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLayout.java
@@ -448,6 +448,7 @@ public class MessagingLayout extends FrameLayout
mAddedGroups.add(newGroup);
}
newGroup.setDisplayImagesAtEnd(mDisplayImagesAtEnd);
+ newGroup.setIsInConversation(false);
newGroup.setLayoutColor(mLayoutColor);
newGroup.setTextColors(mSenderTextColor, mMessageTextColor);
Person sender = senders.get(groupIndex);
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 0eb364d40920..b32b4aeedecb 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -346,6 +346,22 @@ void android_os_Process_setProcessFrozen(
}
}
+void android_os_Process_enableFreezer(
+ JNIEnv *env, jobject clazz, jboolean enable)
+{
+ bool success = true;
+
+ if (enable) {
+ success = SetTaskProfiles(0, {"FreezerFrozen"}, true);
+ } else {
+ success = SetTaskProfiles(0, {"FreezerThawed"}, true);
+ }
+
+ if (!success) {
+ jniThrowException(env, "java/lang/RuntimeException", "Unknown error");
+ }
+}
+
jint android_os_Process_getProcessGroup(JNIEnv* env, jobject clazz, jint pid)
{
SchedPolicy sp;
@@ -1344,6 +1360,7 @@ static const JNINativeMethod methods[] = {
{"sendSignal", "(II)V", (void*)android_os_Process_sendSignal},
{"sendSignalQuiet", "(II)V", (void*)android_os_Process_sendSignalQuiet},
{"setProcessFrozen", "(IIZ)V", (void*)android_os_Process_setProcessFrozen},
+ {"enableFreezer", "(Z)V", (void*)android_os_Process_enableFreezer},
{"getFreeMemory", "()J", (void*)android_os_Process_getFreeMemory},
{"getTotalMemory", "()J", (void*)android_os_Process_getTotalMemory},
{"readProcLines", "(Ljava/lang/String;[Ljava/lang/String;[J)V",
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index dafb37761d0e..ea3c0fa9fc3c 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -1567,23 +1567,6 @@ static void BindMountStorageDirs(JNIEnv* env, jobjectArray pkg_data_info_list,
auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1);
const userid_t user_id = multiuser_get_user_id(uid);
- // If FUSE is not ready for this user, skip it
- // TODO(148772775): Pass primary volume name from zygote argument to here
- std::string tmp = GetProperty("vold.fuse_running_users", "");
- std::istringstream fuse_running_users(tmp);
- bool user_found = false;
- std::string s;
- std::string user_id_str = std::to_string(user_id);
- while (!user_found && std::getline(fuse_running_users, s, ',')) {
- if (user_id_str == s) {
- user_found = true;
- }
- }
- if (!user_found) {
- ALOGI("User %d is not running fuse yet, fuse_running_users=%s", user_id, tmp.c_str());
- return;
- }
-
// Fuse is ready, so we can start using fuse path.
int size = (pkg_data_info_list != nullptr) ? env->GetArrayLength(pkg_data_info_list) : 0;
@@ -1611,7 +1594,7 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
jstring managed_nice_name, bool is_system_server,
bool is_child_zygote, jstring managed_instruction_set,
jstring managed_app_data_dir, bool is_top_app,
- jobjectArray pkg_data_info_list) {
+ jobjectArray pkg_data_info_list, bool mount_storage_dirs) {
const char* process_name = is_system_server ? "system_server" : "zygote";
auto fail_fn = std::bind(ZygoteFailure, env, process_name, managed_nice_name, _1);
auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1);
@@ -1650,10 +1633,7 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
isolateAppData(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
isolateJitProfile(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
}
-
- if ((mount_external != MOUNT_EXTERNAL_INSTALLER) &&
- GetBoolProperty(kPropFuse, false) &&
- GetBoolProperty(ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false)) {
+ if ((mount_external != MOUNT_EXTERNAL_INSTALLER) && mount_storage_dirs) {
BindMountStorageDirs(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
}
@@ -2023,7 +2003,7 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
jint mount_external, jstring se_info, jstring nice_name,
jintArray managed_fds_to_close, jintArray managed_fds_to_ignore, jboolean is_child_zygote,
jstring instruction_set, jstring app_data_dir, jboolean is_top_app,
- jobjectArray pkg_data_info_list) {
+ jobjectArray pkg_data_info_list, jboolean mount_storage_dirs) {
jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
if (UNLIKELY(managed_fds_to_close == nullptr)) {
@@ -2060,7 +2040,8 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
capabilities, capabilities,
mount_external, se_info, nice_name, false,
is_child_zygote == JNI_TRUE, instruction_set, app_data_dir,
- is_top_app == JNI_TRUE, pkg_data_info_list);
+ is_top_app == JNI_TRUE, pkg_data_info_list,
+ mount_storage_dirs == JNI_TRUE);
}
return pid;
}
@@ -2095,7 +2076,7 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer(
permitted_capabilities, effective_capabilities,
MOUNT_EXTERNAL_DEFAULT, nullptr, nullptr, true,
false, nullptr, nullptr, /* is_top_app= */ false,
- /* pkg_data_info_list */ nullptr);
+ /* pkg_data_info_list */ nullptr, false);
} else if (pid > 0) {
// The zygote process checks whether the child process has died or not.
ALOGI("System server process %d has been created", pid);
@@ -2225,14 +2206,15 @@ static void com_android_internal_os_Zygote_nativeSpecializeAppProcess(
jint runtime_flags, jobjectArray rlimits,
jint mount_external, jstring se_info, jstring nice_name,
jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app,
- jobjectArray pkg_data_info_list) {
+ jobjectArray pkg_data_info_list, jboolean mount_storage_dirs) {
jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
capabilities, capabilities,
mount_external, se_info, nice_name, false,
is_child_zygote == JNI_TRUE, instruction_set, app_data_dir,
- is_top_app == JNI_TRUE, pkg_data_info_list);
+ is_top_app == JNI_TRUE, pkg_data_info_list,
+ mount_storage_dirs == JNI_TRUE);
}
/**
@@ -2426,7 +2408,7 @@ static jint com_android_internal_os_Zygote_nativeParseSigChld(JNIEnv* env, jclas
static const JNINativeMethod gMethods[] = {
{"nativeForkAndSpecialize",
"(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/"
- "String;Z[Ljava/lang/String;)I",
+ "String;Z[Ljava/lang/String;Z)I",
(void*)com_android_internal_os_Zygote_nativeForkAndSpecialize},
{"nativeForkSystemServer", "(II[II[[IJJ)I",
(void*)com_android_internal_os_Zygote_nativeForkSystemServer},
@@ -2439,7 +2421,7 @@ static const JNINativeMethod gMethods[] = {
{"nativeForkUsap", "(II[IZ)I", (void*)com_android_internal_os_Zygote_nativeForkUsap},
{"nativeSpecializeAppProcess",
"(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/"
- "String;Z[Ljava/lang/String;)V",
+ "String;Z[Ljava/lang/String;Z)V",
(void*)com_android_internal_os_Zygote_nativeSpecializeAppProcess},
{"nativeInitNativeState", "(Z)V",
(void*)com_android_internal_os_Zygote_nativeInitNativeState},
diff --git a/core/proto/android/app/enums.proto b/core/proto/android/app/enums.proto
index 9abe92330cb0..42437d5b44a0 100644
--- a/core/proto/android/app/enums.proto
+++ b/core/proto/android/app/enums.proto
@@ -104,3 +104,108 @@ enum ProcessStateEnum {
PROCESS_STATE_NONEXISTENT = 1019;
}
+// AppOpsManager.java - operation ids for logging
+enum AppOpEnum {
+ APP_OP_NONE = -1;
+ APP_OP_COARSE_LOCATION = 0;
+ APP_OP_FINE_LOCATION = 1;
+ APP_OP_GPS = 2;
+ APP_OP_VIBRATE = 3;
+ APP_OP_READ_CONTACTS = 4;
+ APP_OP_WRITE_CONTACTS = 5;
+ APP_OP_READ_CALL_LOG = 6;
+ APP_OP_WRITE_CALL_LOG = 7;
+ APP_OP_READ_CALENDAR = 8;
+ APP_OP_WRITE_CALENDAR = 9;
+ APP_OP_WIFI_SCAN = 10;
+ APP_OP_POST_NOTIFICATION = 11;
+ APP_OP_NEIGHBORING_CELLS = 12;
+ APP_OP_CALL_PHONE = 13;
+ APP_OP_READ_SMS = 14;
+ APP_OP_WRITE_SMS = 15;
+ APP_OP_RECEIVE_SMS = 16;
+ APP_OP_RECEIVE_EMERGENCY_SMS = 17;
+ APP_OP_RECEIVE_MMS = 18;
+ APP_OP_RECEIVE_WAP_PUSH = 19;
+ APP_OP_SEND_SMS = 20;
+ APP_OP_READ_ICC_SMS = 21;
+ APP_OP_WRITE_ICC_SMS = 22;
+ APP_OP_WRITE_SETTINGS = 23;
+ APP_OP_SYSTEM_ALERT_WINDOW = 24;
+ APP_OP_ACCESS_NOTIFICATIONS = 25;
+ APP_OP_CAMERA = 26;
+ APP_OP_RECORD_AUDIO = 27;
+ APP_OP_PLAY_AUDIO = 28;
+ APP_OP_READ_CLIPBOARD = 29;
+ APP_OP_WRITE_CLIPBOARD = 30;
+ APP_OP_TAKE_MEDIA_BUTTONS = 31;
+ APP_OP_TAKE_AUDIO_FOCUS = 32;
+ APP_OP_AUDIO_MASTER_VOLUME = 33;
+ APP_OP_AUDIO_VOICE_VOLUME = 34;
+ APP_OP_AUDIO_RING_VOLUME = 35;
+ APP_OP_AUDIO_MEDIA_VOLUME = 36;
+ APP_OP_AUDIO_ALARM_VOLUME = 37;
+ APP_OP_AUDIO_NOTIFICATION_VOLUME = 38;
+ APP_OP_AUDIO_BLUETOOTH_VOLUME = 39;
+ APP_OP_WAKE_LOCK = 40;
+ APP_OP_MONITOR_LOCATION = 41;
+ APP_OP_MONITOR_HIGH_POWER_LOCATION = 42;
+ APP_OP_GET_USAGE_STATS = 43;
+ APP_OP_MUTE_MICROPHONE = 44;
+ APP_OP_TOAST_WINDOW = 45;
+ APP_OP_PROJECT_MEDIA = 46;
+ APP_OP_ACTIVATE_VPN = 47;
+ APP_OP_WRITE_WALLPAPER = 48;
+ APP_OP_ASSIST_STRUCTURE = 49;
+ APP_OP_ASSIST_SCREENSHOT = 50;
+ APP_OP_READ_PHONE_STATE = 51;
+ APP_OP_ADD_VOICEMAIL = 52;
+ APP_OP_USE_SIP = 53;
+ APP_OP_PROCESS_OUTGOING_CALLS = 54;
+ APP_OP_USE_FINGERPRINT = 55;
+ APP_OP_BODY_SENSORS = 56;
+ APP_OP_READ_CELL_BROADCASTS = 57;
+ APP_OP_MOCK_LOCATION = 58;
+ APP_OP_READ_EXTERNAL_STORAGE = 59;
+ APP_OP_WRITE_EXTERNAL_STORAGE = 60;
+ APP_OP_TURN_SCREEN_ON = 61;
+ APP_OP_GET_ACCOUNTS = 62;
+ APP_OP_RUN_IN_BACKGROUND = 63;
+ APP_OP_AUDIO_ACCESSIBILITY_VOLUME = 64;
+ APP_OP_READ_PHONE_NUMBERS = 65;
+ APP_OP_REQUEST_INSTALL_PACKAGES = 66;
+ APP_OP_PICTURE_IN_PICTURE = 67;
+ APP_OP_INSTANT_APP_START_FOREGROUND = 68;
+ APP_OP_ANSWER_PHONE_CALLS = 69;
+ APP_OP_RUN_ANY_IN_BACKGROUND = 70;
+ APP_OP_CHANGE_WIFI_STATE = 71;
+ APP_OP_REQUEST_DELETE_PACKAGES = 72;
+ APP_OP_BIND_ACCESSIBILITY_SERVICE = 73;
+ APP_OP_ACCEPT_HANDOVER = 74;
+ APP_OP_MANAGE_IPSEC_TUNNELS = 75;
+ APP_OP_START_FOREGROUND = 76;
+ APP_OP_BLUETOOTH_SCAN = 77;
+ APP_OP_USE_BIOMETRIC = 78;
+ APP_OP_ACTIVITY_RECOGNITION = 79;
+ APP_OP_SMS_FINANCIAL_TRANSACTIONS = 80;
+ APP_OP_READ_MEDIA_AUDIO = 81;
+ APP_OP_WRITE_MEDIA_AUDIO = 82;
+ APP_OP_READ_MEDIA_VIDEO = 83;
+ APP_OP_WRITE_MEDIA_VIDEO = 84;
+ APP_OP_READ_MEDIA_IMAGES = 85;
+ APP_OP_WRITE_MEDIA_IMAGES = 86;
+ APP_OP_LEGACY_STORAGE = 87;
+ APP_OP_ACCESS_ACCESSIBILITY = 88;
+ APP_OP_READ_DEVICE_IDENTIFIERS = 89;
+ APP_OP_ACCESS_MEDIA_LOCATION = 90;
+ APP_OP_QUERY_ALL_PACKAGES = 91;
+ APP_OP_MANAGE_EXTERNAL_STORAGE = 92;
+ APP_OP_INTERACT_ACROSS_PROFILES = 93;
+ APP_OP_ACTIVATE_PLATFORM_VPN = 94;
+ APP_OP_LOADER_USAGE_STATS = 95;
+ APP_OP_ACCESS_CALL_AUDIO = 96;
+ APP_OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED = 97;
+ APP_OP_AUTO_REVOKE_MANAGED_BY_INSTALLER = 98;
+}
+
+
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index d6687f08b542..64cf75d51c3d 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -376,8 +376,7 @@ message IncidentProto {
optional bytes system_trace = 3026 [
(section).type = SECTION_FILE,
(section).args = "/data/misc/perfetto-traces/incident-trace",
- (privacy).dest = DEST_AUTOMATIC,
- (section).userdebug_and_eng_only = true
+ (privacy).dest = DEST_AUTOMATIC
];
// Dropbox entries split by tags.
diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
index 3d8108d2a4c8..684a29249294 100644
--- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto
+++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
@@ -153,7 +153,7 @@ enum EventId {
CROSS_PROFILE_APPS_START_ACTIVITY_AS_USER = 126;
SET_AUTO_TIME = 127;
SET_AUTO_TIME_ZONE = 128;
- SET_PACKAGES_PROTECTED = 129;
+ SET_USER_CONTROL_DISABLED_PACKAGES = 129;
SET_FACTORY_RESET_PROTECTION = 130;
SET_COMMON_CRITERIA_MODE = 131;
ALLOW_MODIFICATION_OF_ADMIN_CONFIGURED_NETWORKS = 132;
diff --git a/core/proto/android/util/quotatracker.proto b/core/proto/android/util/quotatracker.proto
index 5d022ed6327b..d98e5eea30d2 100644
--- a/core/proto/android/util/quotatracker.proto
+++ b/core/proto/android/util/quotatracker.proto
@@ -131,93 +131,3 @@ message CountQuotaTrackerProto {
// Next tag: 4
}
-
-// A com.android.util.quota.DurationQuotaTracker object.
-message DurationQuotaTrackerProto {
- option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
- optional QuotaTrackerProto base_quota_data = 1;
-
- message DurationLimit {
- option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
- optional CategoryProto category = 1;
- optional int64 limit_ms = 2;
- optional int64 window_size_ms = 3;
- }
- repeated DurationLimit duration_limit = 2;
-
- message ExecutionStats {
- option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
- // The time after which this record should be considered invalid (out of date), in the
- // elapsed realtime timebase.
- optional int64 expiration_time_elapsed = 1;
-
- optional int32 window_size_ms = 2;
- optional int64 duration_limit_ms = 3;
-
- // The overall session duration in the window.
- optional int64 session_duration_in_window_ms = 4;
- // The number of individual long-running events in the window.
- optional int32 event_count_in_window = 5;
-
- // The time after which the app will be under the bucket quota. This is only valid if
- // session_duration_in_window_ms >= duration_limit_ms.
- optional int64 in_quota_time_elapsed = 6;
- }
-
- message Timer {
- option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
- // True if the Timer is actively tracking long-running events.
- optional bool is_active = 1;
- // The time this timer last became active. Only valid if is_active is true.
- optional int64 start_time_elapsed = 2;
- // How many long-running events are currently running. Valid only if is_active is true.
- optional int32 event_count = 3;
- }
-
- message TimingSession {
- option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
- optional int64 start_time_elapsed = 1;
- optional int64 end_time_elapsed = 2;
- // How many events started during this session. This only count long-running events, not
- // instantaneous events.
- optional int32 event_count = 3;
- }
-
- message UptcStats {
- option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
- optional UptcProto uptc = 1;
-
- // True if the UPTC has been given free quota.
- optional bool is_quota_free = 2;
-
- optional Timer timer = 3;
-
- repeated TimingSession saved_sessions = 4;
-
- repeated ExecutionStats execution_stats = 5;
- }
- repeated UptcStats uptc_stats = 3;
-
- message ReachedQuotaAlarmListener {
- option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
- optional int64 trigger_time_elapsed = 1;
-
- message UptcTimes {
- option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
- optional UptcProto uptc = 1;
- optional int64 out_of_quota_time_elapsed = 2;
- }
- repeated UptcTimes uptc_times = 2;
- }
- optional ReachedQuotaAlarmListener reached_quota_alarm_listener = 4;
-
- // Next tag: 5
-}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4001defe51a9..1f45c31f6340 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1689,6 +1689,10 @@
<permission android:name="android.permission.NETWORK_FACTORY"
android:protectionLevel="signature" />
+ <!-- @SystemApi @hide Allows applications to access network stats provider -->
+ <permission android:name="android.permission.NETWORK_STATS_PROVIDER"
+ android:protectionLevel="signature" />
+
<!-- Allows Settings and SystemUI to call methods in Networking services
<p>Not for use by third-party or privileged applications.
@SystemApi @TestApi
@@ -3756,6 +3760,12 @@
<permission android:name="android.permission.WHITELIST_RESTRICTED_PERMISSIONS"
android:protectionLevel="signature|installer" />
+ <!-- @SystemApi Allows an application to an exempt an app from having its permission be
+ auto-revoked when unused for an extended period of time.
+ @hide -->
+ <permission android:name="android.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS"
+ android:protectionLevel="signature|installer" />
+
<!-- @hide Allows an application to observe permission changes. -->
<permission android:name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS"
android:protectionLevel="signature|privileged" />
diff --git a/core/res/res/drawable/tab_indicator_resolver.xml b/core/res/res/drawable/tab_indicator_resolver.xml
index ff16d81a6383..f97773e8106e 100644
--- a/core/res/res/drawable/tab_indicator_resolver.xml
+++ b/core/res/res/drawable/tab_indicator_resolver.xml
@@ -25,7 +25,7 @@
</item>
<item android:gravity="bottom">
<shape android:shape="rectangle"
- android:tint="@color/resolver_tabs_active_color">
+ android:tint="?attr/colorAccent">
<size android:height="2dp" />
<solid android:color="@color/tab_indicator_material" />
</shape>
diff --git a/core/res/res/layout/notification_template_material_conversation.xml b/core/res/res/layout/notification_template_material_conversation.xml
index dc52e979a310..2348deeed7fb 100644
--- a/core/res/res/layout/notification_template_material_conversation.xml
+++ b/core/res/res/layout/notification_template_material_conversation.xml
@@ -93,9 +93,10 @@
android:clipChildren="false"
android:clipToPadding="false">
<!--TODO: move this into a separate layout and share logic with the header to bring back app opps etc-->
- <FrameLayout
+ <com.android.internal.widget.RemeasuringLinearLayout
android:id="@+id/notification_action_list_margin_target"
android:layout_width="0dp"
+ android:orientation="vertical"
android:layout_height="wrap_content"
android:layout_weight="1">
@@ -163,12 +164,12 @@
android:id="@+id/notification_messaging"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="40dp"
+ android:layout_marginTop="2dp"
android:spacing="@dimen/notification_messaging_spacing"
android:clipToPadding="false"
android:clipChildren="false"
/>
- </FrameLayout>
+ </com.android.internal.widget.RemeasuringLinearLayout>
<!-- Unread Count -->
<!-- <TextView /> -->
diff --git a/core/res/res/layout/notification_template_messaging_group.xml b/core/res/res/layout/notification_template_messaging_group.xml
index 15146c073e46..3188861a52a5 100644
--- a/core/res/res/layout/notification_template_messaging_group.xml
+++ b/core/res/res/layout/notification_template_messaging_group.xml
@@ -21,8 +21,9 @@
android:layout_height="wrap_content"
android:orientation="horizontal" >
<FrameLayout
+ android:id="@+id/message_icon_container"
android:layout_width="@dimen/conversation_content_start"
- android:layout_height="wrap_content"> <!--TODO: make sure to make this padding dynamic-->
+ android:layout_height="wrap_content">
<ImageView
android:layout_gravity="top|center_horizontal"
android:id="@+id/message_icon"
diff --git a/core/res/res/layout/resolver_empty_states.xml b/core/res/res/layout/resolver_empty_states.xml
index 619b3927e8d5..5fdf19042199 100644
--- a/core/res/res/layout/resolver_empty_states.xml
+++ b/core/res/res/layout/resolver_empty_states.xml
@@ -38,7 +38,7 @@
android:layout_height="wrap_content"
android:fontFamily="@string/config_headlineFontFamilyMedium"
android:textColor="@color/resolver_empty_state_text"
- android:textSize="18sp"
+ android:textSize="14sp"
android:layout_centerHorizontal="true" />
<TextView
android:id="@+id/resolver_empty_state_subtitle"
@@ -47,7 +47,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/resolver_empty_state_text"
- android:textSize="14sp"
+ android:textSize="12sp"
android:gravity="center_horizontal"
android:layout_centerHorizontal="true" />
<Button
@@ -60,7 +60,7 @@
android:background="@null"
android:fontFamily="@string/config_headlineFontFamilyMedium"
android:textSize="14sp"
- android:textColor="@color/resolver_tabs_active_color"
+ android:textColor="?attr/colorAccent"
android:layout_centerHorizontal="true" />
<ProgressBar
android:id="@+id/resolver_empty_state_progress"
@@ -71,5 +71,5 @@
android:indeterminate="true"
android:layout_centerHorizontal="true"
android:layout_below="@+id/resolver_empty_state_subtitle"
- android:indeterminateTint="@color/resolver_tabs_active_color"/>
+ android:indeterminateTint="?attr/colorAccent"/>
</RelativeLayout> \ No newline at end of file
diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml
index 7f77e6c024da..708b4f352112 100644
--- a/core/res/res/values-night/colors.xml
+++ b/core/res/res/values-night/colors.xml
@@ -33,7 +33,6 @@
<color name="chooser_gradient_background">@color/loading_gradient_background_color_dark</color>
<color name="chooser_gradient_highlight">@color/loading_gradient_highlight_color_dark</color>
- <color name="resolver_tabs_active_color">#FF8AB4F8</color>
<color name="resolver_empty_state_text">#FFFFFF</color>
<color name="resolver_empty_state_icon">#FFFFFF</color>
</resources>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index bdec096434ca..91248f1c552a 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -224,8 +224,6 @@
<!-- Resolver/Chooser -->
<color name="resolver_text_color_secondary_dark">#ffC4C6C6</color>
- <color name="resolver_tabs_active_color">#FF1A73E8</color>
- <color name="resolver_tabs_inactive_color">#FF80868B</color>
<color name="resolver_empty_state_text">#FF202124</color>
<color name="resolver_empty_state_icon">#FF5F6368</color>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 6f468e03e0b7..6060d9aa459a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4420,5 +4420,9 @@
<string name="config_customSessionPolicyProvider"></string>
<!-- The max scale for the wallpaper when it's zoomed in -->
- <item name="config_wallpaperMaxScale" format="float" type="dimen">1</item>
+ <item name="config_wallpaperMaxScale" format="float" type="dimen">1.15</item>
+
+ <!-- Package name that will receive an explicit manifest broadcast for
+ android.os.action.POWER_SAVE_MODE_CHANGED. -->
+ <string name="config_powerSaveModeChangedListenerPackage" translatable="false"></string>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index e3fe982c226d..4dedc63f2e48 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -699,6 +699,12 @@
<!-- margin on the top when the icon is centered for group conversations -->
<dimen name="conversation_icon_margin_top_centered">5dp</dimen>
+ <!-- The padding of the conversation header when expanded. This is calculated from the expand button size + notification_content_margin_end -->
+ <dimen name="conversation_header_expanded_padding_end">38dp</dimen>
+
+ <!-- margin at the end of messaging group icons when not conversations -->
+ <dimen name="messaging_layout_margin_end">12dp</dimen>
+
<!-- Padding between text and sender when singleline -->
<dimen name="messaging_group_singleline_sender_padding_end">4dp</dimen>
@@ -794,6 +800,7 @@
<dimen name="resolver_empty_state_height_with_tabs">268dp</dimen>
<dimen name="resolver_max_collapsed_height">192dp</dimen>
<dimen name="resolver_max_collapsed_height_with_tabs">248dp</dimen>
+ <dimen name="resolver_tab_text_size">14sp</dimen>
<dimen name="chooser_action_button_icon_size">18dp</dimen>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 7230cc49ac5b..cf68aff43c05 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3044,8 +3044,6 @@
<public name="config_defaultCallScreening" />
<!-- @hide @SystemApi @TestApi -->
<public name="config_systemGallery" />
- <!-- @hide @SystemApi -->
- <public name="low_memory" />
</public-group>
<public-group type="bool" first-id="0x01110005">
@@ -3073,12 +3071,6 @@
<public-group type="array" first-id="0x01070006">
<!-- @hide @SystemApi -->
<public name="simColors" />
- <!-- @hide @SystemApi -->
- <public name="config_restrictedPreinstalledCarrierApps" />
- <!-- @hide @SystemApi -->
- <public name="config_sms_enabled_single_shift_tables" />
- <!-- @hide @SystemApi -->
- <public name="config_sms_enabled_locking_shift_tables" />
</public-group>
<public-group type="string" first-id="0x0104002c">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index f101f590cab1..ff49c45b965b 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1169,7 +1169,7 @@
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
<string name="permlab_cameraOpenCloseListener">Allow an application or service to receive callbacks about camera devices being opened or closed.</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
- <string name="permdesc_cameraOpenCloseListener">This signature app can receive callbacks when any camera device is being opened (by what application package) or closed.</string>
+ <string name="permdesc_cameraOpenCloseListener">This app can receive callbacks when any camera device is being opened (by what application) or closed.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_vibrate">control vibration</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 49a0f17aad1a..9c64a70f25dd 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3615,6 +3615,7 @@
<java-symbol type="bool" name="config_batterySaverStickyBehaviourDisabled" />
<java-symbol type="integer" name="config_dynamicPowerSavingsDefaultDisableThreshold" />
<java-symbol type="string" name="config_batterySaverScheduleProvider" />
+ <java-symbol type="string" name="config_powerSaveModeChangedListenerPackage" />
<!-- For car devices -->
<java-symbol type="string" name="car_loading_profile" />
@@ -3890,6 +3891,7 @@
<java-symbol type="id" name="conversation_face_pile_top" />
<java-symbol type="id" name="conversation_face_pile" />
<java-symbol type="id" name="conversation_text" />
+ <java-symbol type="id" name="message_icon_container" />
<java-symbol type="dimen" name="conversation_expand_button_top_margin_expanded" />
<java-symbol type="dimen" name="conversation_expand_button_expanded_size" />
<java-symbol type="dimen" name="messaging_group_singleline_sender_padding_end" />
@@ -3897,12 +3899,13 @@
<java-symbol type="dimen" name="conversation_icon_size_badged" />
<java-symbol type="dimen" name="conversation_icon_size_centered" />
<java-symbol type="dimen" name="conversation_icon_margin_top_centered" />
+ <java-symbol type="dimen" name="conversation_content_start" />
+ <java-symbol type="dimen" name="messaging_layout_margin_end" />
+ <java-symbol type="dimen" name="conversation_header_expanded_padding_end" />
<java-symbol type="layout" name="notification_template_material_conversation" />
<java-symbol type="layout" name="conversation_face_pile_layout" />
<!-- Intent resolver and share sheet -->
- <java-symbol type="color" name="resolver_tabs_active_color" />
- <java-symbol type="color" name="resolver_tabs_inactive_color" />
<java-symbol type="string" name="resolver_personal_tab" />
<java-symbol type="string" name="resolver_personal_tab_accessibility" />
<java-symbol type="string" name="resolver_work_tab" />
@@ -3933,6 +3936,7 @@
<java-symbol type="dimen" name="resolver_empty_state_height_with_tabs" />
<java-symbol type="dimen" name="resolver_max_collapsed_height_with_tabs" />
<java-symbol type="bool" name="sharesheet_show_content_preview" />
+ <java-symbol type="dimen" name="resolver_tab_text_size" />
<!-- Toast message for background started foreground service while-in-use permission restriction feature -->
<java-symbol type="string" name="allow_while_in_use_permission_in_fgs" />
diff --git a/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java b/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
index 5d42915fc82b..4b42f4ae9888 100644
--- a/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
+++ b/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
@@ -268,7 +268,7 @@ public class BandwidthTest extends InstrumentationTestCase {
File snd_stat = new File (root_filepath + "tcp_snd");
int tx = BandwidthTestUtil.parseIntValueFromFile(snd_stat);
NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
- stats.addEntry(NetworkStats.IFACE_ALL, uid, NetworkStats.SET_DEFAULT,
+ stats.insertEntry(NetworkStats.IFACE_ALL, uid, NetworkStats.SET_DEFAULT,
NetworkStats.TAG_NONE, rx, 0, tx, 0, 0);
return stats;
}
diff --git a/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java b/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java
index 239f971664e9..3ebe1039d975 100644
--- a/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java
+++ b/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java
@@ -54,7 +54,7 @@ public class NetworkStatsBenchmark {
recycle.txBytes = 150000;
recycle.txPackets = 1500;
recycle.operations = 0;
- mNetworkStats.addEntry(recycle);
+ mNetworkStats.insertEntry(recycle);
if (recycle.set == 1) {
uid++;
}
@@ -70,7 +70,7 @@ public class NetworkStatsBenchmark {
recycle.txBytes = 180000 * mSize;
recycle.txPackets = 1200 * mSize;
recycle.operations = 0;
- mNetworkStats.addEntry(recycle);
+ mNetworkStats.insertEntry(recycle);
}
}
diff --git a/core/tests/coretests/src/android/util/apk/SourceStampVerifierTest.java b/core/tests/coretests/src/android/util/apk/SourceStampVerifierTest.java
index 44f64079d831..37b2817928aa 100644
--- a/core/tests/coretests/src/android/util/apk/SourceStampVerifierTest.java
+++ b/core/tests/coretests/src/android/util/apk/SourceStampVerifierTest.java
@@ -37,6 +37,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@@ -129,6 +131,48 @@ public class SourceStampVerifierTest {
assertNull(result.getCertificate());
}
+ @Test
+ public void testSourceStamp_multiApk_validStamps() throws Exception {
+ File testApk1 = getApk("SourceStampVerifierTest/valid-stamp.apk");
+ File testApk2 = getApk("SourceStampVerifierTest/valid-stamp.apk");
+ ZipFile apkZipFile = new ZipFile(testApk1);
+ ZipEntry stampCertZipEntry = apkZipFile.getEntry("stamp-cert-sha256");
+ int size = (int) stampCertZipEntry.getSize();
+ byte[] expectedStampCertHash = new byte[size];
+ try (InputStream inputStream = apkZipFile.getInputStream(stampCertZipEntry)) {
+ inputStream.read(expectedStampCertHash);
+ }
+ List<String> apkFiles = new ArrayList<>();
+ apkFiles.add(testApk1.getAbsolutePath());
+ apkFiles.add(testApk2.getAbsolutePath());
+
+ SourceStampVerificationResult result =
+ SourceStampVerifier.verify(apkFiles);
+
+ assertTrue(result.isPresent());
+ assertTrue(result.isVerified());
+ assertNotNull(result.getCertificate());
+ byte[] actualStampCertHash =
+ MessageDigest.getInstance("SHA-256").digest(result.getCertificate().getEncoded());
+ assertArrayEquals(expectedStampCertHash, actualStampCertHash);
+ }
+
+ @Test
+ public void testSourceStamp_multiApk_invalidStamps() throws Exception {
+ File testApk1 = getApk("SourceStampVerifierTest/valid-stamp.apk");
+ File testApk2 = getApk("SourceStampVerifierTest/stamp-apk-hash-mismatch.apk");
+ List<String> apkFiles = new ArrayList<>();
+ apkFiles.add(testApk1.getAbsolutePath());
+ apkFiles.add(testApk2.getAbsolutePath());
+
+ SourceStampVerificationResult result =
+ SourceStampVerifier.verify(apkFiles);
+
+ assertTrue(result.isPresent());
+ assertFalse(result.isVerified());
+ assertNull(result.getCertificate());
+ }
+
private File getApk(String apkPath) throws IOException {
File testApk = File.createTempFile("SourceStampApk", ".apk");
try (InputStream inputStream = mContext.getAssets().open(apkPath)) {
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index f1ab8ddebce6..42ab2e791641 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -58,12 +58,12 @@ import android.view.animation.LinearInterpolator;
import android.view.test.InsetsModeSession;
import android.widget.TextView;
-import com.android.server.testutils.OffsettableClock;
-import com.android.server.testutils.TestHandler;
-
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.testutils.OffsettableClock;
+import com.android.server.testutils.TestHandler;
+
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
@@ -74,7 +74,6 @@ import org.mockito.InOrder;
import org.mockito.Mockito;
import java.util.concurrent.CountDownLatch;
-import java.util.function.Supplier;
/**
* Tests for {@link InsetsController}.
@@ -199,7 +198,7 @@ public class InsetsControllerTest {
WindowInsetsAnimationControlListener mockListener =
mock(WindowInsetsAnimationControlListener.class);
mController.controlWindowInsetsAnimation(statusBars(), 10 /* durationMs */,
- new LinearInterpolator(), mockListener);
+ new LinearInterpolator(), new CancellationSignal(), mockListener);
// Ready gets deferred until next predraw
mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
@@ -219,7 +218,7 @@ public class InsetsControllerTest {
WindowInsetsAnimationControlListener controlListener =
mock(WindowInsetsAnimationControlListener.class);
mController.controlWindowInsetsAnimation(0, 0 /* durationMs */, new LinearInterpolator(),
- controlListener);
+ new CancellationSignal(), controlListener);
mController.addOnControllableInsetsChangedListener(
(controller, typeMask) -> assertEquals(0, typeMask));
verify(controlListener).onCancelled();
@@ -498,7 +497,7 @@ public class InsetsControllerTest {
WindowInsetsAnimationControlListener mockListener =
mock(WindowInsetsAnimationControlListener.class);
mController.controlWindowInsetsAnimation(statusBars(), 0 /* durationMs */,
- new LinearInterpolator(), mockListener);
+ new LinearInterpolator(), new CancellationSignal(), mockListener);
ArgumentCaptor<WindowInsetsAnimationController> controllerCaptor =
ArgumentCaptor.forClass(WindowInsetsAnimationController.class);
@@ -523,9 +522,10 @@ public class InsetsControllerTest {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
WindowInsetsAnimationControlListener mockListener =
mock(WindowInsetsAnimationControlListener.class);
- CancellationSignal cancellationSignal = mController.controlWindowInsetsAnimation(
+ CancellationSignal cancellationSignal = new CancellationSignal();
+ mController.controlWindowInsetsAnimation(
statusBars(), 0 /* durationMs */,
- new LinearInterpolator(), mockListener);
+ new LinearInterpolator(), cancellationSignal, mockListener);
// Ready gets deferred until next predraw
mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
@@ -548,7 +548,8 @@ public class InsetsControllerTest {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
WindowInsetsAnimationControlListener listener =
mock(WindowInsetsAnimationControlListener.class);
- mController.controlWindowInsetsAnimation(ime(), 0, new LinearInterpolator(), listener);
+ mController.controlWindowInsetsAnimation(ime(), 0, new LinearInterpolator(),
+ null /* cancellationSignal */, listener);
// Ready gets deferred until next predraw
mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
@@ -572,7 +573,8 @@ public class InsetsControllerTest {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
WindowInsetsAnimationControlListener listener =
mock(WindowInsetsAnimationControlListener.class);
- mController.controlWindowInsetsAnimation(ime(), 0, new LinearInterpolator(), listener);
+ mController.controlWindowInsetsAnimation(ime(), 0, new LinearInterpolator(),
+ null /* cancellationSignal */, listener);
// Ready gets deferred until next predraw
mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
@@ -592,7 +594,8 @@ public class InsetsControllerTest {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
WindowInsetsAnimationControlListener listener =
mock(WindowInsetsAnimationControlListener.class);
- mController.controlWindowInsetsAnimation(ime(), 0, new LinearInterpolator(), listener);
+ mController.controlWindowInsetsAnimation(ime(), 0, new LinearInterpolator(),
+ null /* cancellationSignal */, listener);
// Ready gets deferred until next predraw
mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
@@ -613,8 +616,10 @@ public class InsetsControllerTest {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
WindowInsetsAnimationControlListener listener =
mock(WindowInsetsAnimationControlListener.class);
- mController.controlWindowInsetsAnimation(ime(), 0, new LinearInterpolator(), listener)
- .cancel();
+ CancellationSignal cancellationSignal = new CancellationSignal();
+ mController.controlWindowInsetsAnimation(ime(), 0, new LinearInterpolator(),
+ cancellationSignal, listener);
+ cancellationSignal.cancel();
verify(listener).onCancelled();
diff --git a/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java b/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java
index 9797178fca6e..33f859ef6663 100644
--- a/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java
@@ -20,8 +20,9 @@ import static android.view.WindowInsets.Type.navigationBars;
import static android.view.WindowInsets.Type.systemBars;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
+
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
@@ -35,12 +36,12 @@ import android.platform.test.annotations.Presubmit;
import android.view.WindowInsetsController.OnControllableInsetsChangedListener;
import android.view.animation.LinearInterpolator;
+import androidx.test.runner.AndroidJUnit4;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import androidx.test.runner.AndroidJUnit4;
-
/**
* Tests for {@link PendingInsetsControllerTest}.
*
@@ -95,10 +96,11 @@ public class PendingInsetsControllerTest {
public void testControl() {
WindowInsetsAnimationControlListener listener =
mock(WindowInsetsAnimationControlListener.class);
- CancellationSignal signal = mPendingInsetsController.controlWindowInsetsAnimation(
- systemBars(), 0, new LinearInterpolator(), listener);
+ CancellationSignal cancellationSignal = new CancellationSignal();
+ mPendingInsetsController.controlWindowInsetsAnimation(
+ systemBars(), 0, new LinearInterpolator(), cancellationSignal, listener);
verify(listener).onCancelled();
- assertTrue(signal.isCanceled());
+ assertFalse(cancellationSignal.isCanceled());
}
@Test
@@ -106,10 +108,11 @@ public class PendingInsetsControllerTest {
WindowInsetsAnimationControlListener listener =
mock(WindowInsetsAnimationControlListener.class);
mPendingInsetsController.replayAndAttach(mReplayedController);
- mPendingInsetsController.controlWindowInsetsAnimation(
- systemBars(), 0L, new LinearInterpolator(), listener);
+ CancellationSignal cancellationSignal = new CancellationSignal();
+ mPendingInsetsController.controlWindowInsetsAnimation(systemBars(), 0L,
+ new LinearInterpolator(), cancellationSignal, listener);
verify(mReplayedController).controlWindowInsetsAnimation(eq(systemBars()), eq(0L), any(),
- eq(listener));
+ eq(cancellationSignal), eq(listener));
}
@Test
diff --git a/core/tests/coretests/src/android/view/textclassifier/ActionsModelParamsSupplierTest.java b/core/tests/coretests/src/android/view/textclassifier/ActionsModelParamsSupplierTest.java
deleted file mode 100644
index 87449979704d..000000000000
--- a/core/tests/coretests/src/android/view/textclassifier/ActionsModelParamsSupplierTest.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view.textclassifier;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.File;
-import java.util.Collections;
-import java.util.Locale;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class ActionsModelParamsSupplierTest {
-
- @Test
- public void getSerializedPreconditions_validActionsModelParams() {
- ModelFileManager.ModelFile modelFile = new ModelFileManager.ModelFile(
- new File("/model/file"),
- 200 /* version */,
- Collections.singletonList(Locale.forLanguageTag("en")),
- "en",
- false);
- byte[] serializedPreconditions = new byte[]{0x12, 0x24, 0x36};
- ActionsModelParamsSupplier.ActionsModelParams params =
- new ActionsModelParamsSupplier.ActionsModelParams(
- 200 /* version */,
- "en",
- serializedPreconditions);
-
- byte[] actual = params.getSerializedPreconditions(modelFile);
-
- assertThat(actual).isEqualTo(serializedPreconditions);
- }
-
- @Test
- public void getSerializedPreconditions_invalidVersion() {
- ModelFileManager.ModelFile modelFile = new ModelFileManager.ModelFile(
- new File("/model/file"),
- 201 /* version */,
- Collections.singletonList(Locale.forLanguageTag("en")),
- "en",
- false);
- byte[] serializedPreconditions = new byte[]{0x12, 0x24, 0x36};
- ActionsModelParamsSupplier.ActionsModelParams params =
- new ActionsModelParamsSupplier.ActionsModelParams(
- 200 /* version */,
- "en",
- serializedPreconditions);
-
- byte[] actual = params.getSerializedPreconditions(modelFile);
-
- assertThat(actual).isNull();
- }
-
- @Test
- public void getSerializedPreconditions_invalidLocales() {
- final String LANGUAGE_TAG = "zh";
- ModelFileManager.ModelFile modelFile = new ModelFileManager.ModelFile(
- new File("/model/file"),
- 200 /* version */,
- Collections.singletonList(Locale.forLanguageTag(LANGUAGE_TAG)),
- LANGUAGE_TAG,
- false);
- byte[] serializedPreconditions = new byte[]{0x12, 0x24, 0x36};
- ActionsModelParamsSupplier.ActionsModelParams params =
- new ActionsModelParamsSupplier.ActionsModelParams(
- 200 /* version */,
- "en",
- serializedPreconditions);
-
- byte[] actual = params.getSerializedPreconditions(modelFile);
-
- assertThat(actual).isNull();
- }
-
-}
diff --git a/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java b/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java
deleted file mode 100644
index ec7e83f1f243..000000000000
--- a/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java
+++ /dev/null
@@ -1,331 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier;
-
-import static android.view.textclassifier.ConversationActions.Message.PERSON_USER_OTHERS;
-import static android.view.textclassifier.ConversationActions.Message.PERSON_USER_SELF;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.app.PendingIntent;
-import android.app.Person;
-import android.app.RemoteAction;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.graphics.drawable.Icon;
-import android.net.Uri;
-import android.os.Bundle;
-import android.view.textclassifier.intent.LabeledIntent;
-import android.view.textclassifier.intent.TemplateIntentFactory;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.google.android.textclassifier.ActionsSuggestionsModel;
-import com.google.android.textclassifier.RemoteActionTemplate;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.time.Instant;
-import java.time.ZoneId;
-import java.time.ZonedDateTime;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-import java.util.function.Function;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class ActionsSuggestionsHelperTest {
- private static final String LOCALE_TAG = Locale.US.toLanguageTag();
- private static final Function<CharSequence, String> LANGUAGE_DETECTOR =
- charSequence -> LOCALE_TAG;
-
- @Test
- public void testToNativeMessages_emptyInput() {
- ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
- ActionsSuggestionsHelper.toNativeMessages(
- Collections.emptyList(), LANGUAGE_DETECTOR);
-
- assertThat(conversationMessages).isEmpty();
- }
-
- @Test
- public void testToNativeMessages_noTextMessages() {
- ConversationActions.Message messageWithoutText =
- new ConversationActions.Message.Builder(PERSON_USER_OTHERS).build();
-
- ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
- ActionsSuggestionsHelper.toNativeMessages(
- Collections.singletonList(messageWithoutText), LANGUAGE_DETECTOR);
-
- assertThat(conversationMessages).isEmpty();
- }
-
- @Test
- public void testToNativeMessages_userIdEncoding() {
- Person userA = new Person.Builder().setName("userA").build();
- Person userB = new Person.Builder().setName("userB").build();
-
- ConversationActions.Message firstMessage =
- new ConversationActions.Message.Builder(userB)
- .setText("first")
- .build();
- ConversationActions.Message secondMessage =
- new ConversationActions.Message.Builder(userA)
- .setText("second")
- .build();
- ConversationActions.Message thirdMessage =
- new ConversationActions.Message.Builder(PERSON_USER_SELF)
- .setText("third")
- .build();
- ConversationActions.Message fourthMessage =
- new ConversationActions.Message.Builder(userA)
- .setText("fourth")
- .build();
-
- ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
- ActionsSuggestionsHelper.toNativeMessages(
- Arrays.asList(firstMessage, secondMessage, thirdMessage, fourthMessage),
- LANGUAGE_DETECTOR);
-
- assertThat(conversationMessages).hasLength(4);
- assertNativeMessage(conversationMessages[0], firstMessage.getText(), 2, 0);
- assertNativeMessage(conversationMessages[1], secondMessage.getText(), 1, 0);
- assertNativeMessage(conversationMessages[2], thirdMessage.getText(), 0, 0);
- assertNativeMessage(conversationMessages[3], fourthMessage.getText(), 1, 0);
- }
-
- @Test
- public void testToNativeMessages_referenceTime() {
- ConversationActions.Message firstMessage =
- new ConversationActions.Message.Builder(PERSON_USER_OTHERS)
- .setText("first")
- .setReferenceTime(createZonedDateTimeFromMsUtc(1000))
- .build();
- ConversationActions.Message secondMessage =
- new ConversationActions.Message.Builder(PERSON_USER_OTHERS)
- .setText("second")
- .build();
- ConversationActions.Message thirdMessage =
- new ConversationActions.Message.Builder(PERSON_USER_OTHERS)
- .setText("third")
- .setReferenceTime(createZonedDateTimeFromMsUtc(2000))
- .build();
-
- ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
- ActionsSuggestionsHelper.toNativeMessages(
- Arrays.asList(firstMessage, secondMessage, thirdMessage),
- LANGUAGE_DETECTOR);
-
- assertThat(conversationMessages).hasLength(3);
- assertNativeMessage(conversationMessages[0], firstMessage.getText(), 1, 1000);
- assertNativeMessage(conversationMessages[1], secondMessage.getText(), 1, 0);
- assertNativeMessage(conversationMessages[2], thirdMessage.getText(), 1, 2000);
- }
-
- @Test
- public void testDeduplicateActions() {
- Bundle phoneExtras = new Bundle();
- Intent phoneIntent = new Intent();
- phoneIntent.setComponent(new ComponentName("phone", "intent"));
- ExtrasUtils.putActionIntent(phoneExtras, phoneIntent);
-
- Bundle anotherPhoneExtras = new Bundle();
- Intent anotherPhoneIntent = new Intent();
- anotherPhoneIntent.setComponent(new ComponentName("phone", "another.intent"));
- ExtrasUtils.putActionIntent(anotherPhoneExtras, anotherPhoneIntent);
-
- Bundle urlExtras = new Bundle();
- Intent urlIntent = new Intent();
- urlIntent.setComponent(new ComponentName("url", "intent"));
- ExtrasUtils.putActionIntent(urlExtras, urlIntent);
-
- PendingIntent pendingIntent = PendingIntent.getActivity(
- InstrumentationRegistry.getTargetContext(),
- 0,
- phoneIntent,
- 0);
- Icon icon = Icon.createWithData(new byte[0], 0, 0);
- ConversationAction action =
- new ConversationAction.Builder(ConversationAction.TYPE_CALL_PHONE)
- .setAction(new RemoteAction(icon, "label", "1", pendingIntent))
- .setExtras(phoneExtras)
- .build();
- ConversationAction actionWithSameLabel =
- new ConversationAction.Builder(ConversationAction.TYPE_CALL_PHONE)
- .setAction(new RemoteAction(
- icon, "label", "2", pendingIntent))
- .setExtras(phoneExtras)
- .build();
- ConversationAction actionWithSamePackageButDifferentClass =
- new ConversationAction.Builder(ConversationAction.TYPE_CALL_PHONE)
- .setAction(new RemoteAction(
- icon, "label", "3", pendingIntent))
- .setExtras(anotherPhoneExtras)
- .build();
- ConversationAction actionWithDifferentLabel =
- new ConversationAction.Builder(ConversationAction.TYPE_CALL_PHONE)
- .setAction(new RemoteAction(
- icon, "another_label", "4", pendingIntent))
- .setExtras(phoneExtras)
- .build();
- ConversationAction actionWithDifferentPackage =
- new ConversationAction.Builder(ConversationAction.TYPE_OPEN_URL)
- .setAction(new RemoteAction(icon, "label", "5", pendingIntent))
- .setExtras(urlExtras)
- .build();
- ConversationAction actionWithoutRemoteAction =
- new ConversationAction.Builder(ConversationAction.TYPE_CREATE_REMINDER)
- .build();
-
- List<ConversationAction> conversationActions =
- ActionsSuggestionsHelper.removeActionsWithDuplicates(
- Arrays.asList(action, actionWithSameLabel,
- actionWithSamePackageButDifferentClass, actionWithDifferentLabel,
- actionWithDifferentPackage, actionWithoutRemoteAction));
-
- assertThat(conversationActions).hasSize(3);
- assertThat(conversationActions.get(0).getAction().getContentDescription()).isEqualTo("4");
- assertThat(conversationActions.get(1).getAction().getContentDescription()).isEqualTo("5");
- assertThat(conversationActions.get(2).getAction()).isNull();
- }
-
- @Test
- public void testDeduplicateActions_nullComponent() {
- Bundle phoneExtras = new Bundle();
- Intent phoneIntent = new Intent(Intent.ACTION_DIAL);
- ExtrasUtils.putActionIntent(phoneExtras, phoneIntent);
- PendingIntent pendingIntent = PendingIntent.getActivity(
- InstrumentationRegistry.getTargetContext(),
- 0,
- phoneIntent,
- 0);
- Icon icon = Icon.createWithData(new byte[0], 0, 0);
- ConversationAction action =
- new ConversationAction.Builder(ConversationAction.TYPE_CALL_PHONE)
- .setAction(new RemoteAction(icon, "label", "1", pendingIntent))
- .setExtras(phoneExtras)
- .build();
- ConversationAction actionWithSameLabel =
- new ConversationAction.Builder(ConversationAction.TYPE_CALL_PHONE)
- .setAction(new RemoteAction(
- icon, "label", "2", pendingIntent))
- .setExtras(phoneExtras)
- .build();
-
- List<ConversationAction> conversationActions =
- ActionsSuggestionsHelper.removeActionsWithDuplicates(
- Arrays.asList(action, actionWithSameLabel));
-
- assertThat(conversationActions).isEmpty();
- }
-
- @Test
- public void createLabeledIntentResult_null() {
- ActionsSuggestionsModel.ActionSuggestion nativeSuggestion =
- new ActionsSuggestionsModel.ActionSuggestion(
- "text",
- ConversationAction.TYPE_OPEN_URL,
- 1.0f,
- null,
- null,
- null
- );
-
- LabeledIntent.Result labeledIntentResult =
- ActionsSuggestionsHelper.createLabeledIntentResult(
- InstrumentationRegistry.getTargetContext(),
- new TemplateIntentFactory(),
- nativeSuggestion);
-
- assertThat(labeledIntentResult).isNull();
- }
-
- @Test
- public void createLabeledIntentResult_emptyList() {
- ActionsSuggestionsModel.ActionSuggestion nativeSuggestion =
- new ActionsSuggestionsModel.ActionSuggestion(
- "text",
- ConversationAction.TYPE_OPEN_URL,
- 1.0f,
- null,
- null,
- new RemoteActionTemplate[0]
- );
-
- LabeledIntent.Result labeledIntentResult =
- ActionsSuggestionsHelper.createLabeledIntentResult(
- InstrumentationRegistry.getTargetContext(),
- new TemplateIntentFactory(),
- nativeSuggestion);
-
- assertThat(labeledIntentResult).isNull();
- }
-
- @Test
- public void createLabeledIntentResult() {
- ActionsSuggestionsModel.ActionSuggestion nativeSuggestion =
- new ActionsSuggestionsModel.ActionSuggestion(
- "text",
- ConversationAction.TYPE_OPEN_URL,
- 1.0f,
- null,
- null,
- new RemoteActionTemplate[]{
- new RemoteActionTemplate(
- "title",
- null,
- "description",
- null,
- Intent.ACTION_VIEW,
- Uri.parse("http://www.android.com").toString(),
- null,
- 0,
- null,
- null,
- null,
- 0)});
-
- LabeledIntent.Result labeledIntentResult =
- ActionsSuggestionsHelper.createLabeledIntentResult(
- InstrumentationRegistry.getTargetContext(),
- new TemplateIntentFactory(),
- nativeSuggestion);
-
- assertThat(labeledIntentResult.remoteAction.getTitle()).isEqualTo("title");
- assertThat(labeledIntentResult.resolvedIntent.getAction()).isEqualTo(Intent.ACTION_VIEW);
- }
-
- private ZonedDateTime createZonedDateTimeFromMsUtc(long msUtc) {
- return ZonedDateTime.ofInstant(Instant.ofEpochMilli(msUtc), ZoneId.of("UTC"));
- }
-
- private static void assertNativeMessage(
- ActionsSuggestionsModel.ConversationMessage nativeMessage,
- CharSequence text,
- int userId,
- long referenceTimeInMsUtc) {
- assertThat(nativeMessage.getText()).isEqualTo(text.toString());
- assertThat(nativeMessage.getUserId()).isEqualTo(userId);
- assertThat(nativeMessage.getDetectedTextLanguageTags()).isEqualTo(LOCALE_TAG);
- assertThat(nativeMessage.getReferenceTimeMsUtc()).isEqualTo(referenceTimeInMsUtc);
- }
-}
diff --git a/core/tests/coretests/src/android/view/textclassifier/ModelFileManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/ModelFileManagerTest.java
deleted file mode 100644
index 79e1406f6ae6..000000000000
--- a/core/tests/coretests/src/android/view/textclassifier/ModelFileManagerTest.java
+++ /dev/null
@@ -1,350 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.when;
-
-import android.os.LocaleList;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-import java.util.function.Supplier;
-import java.util.stream.Collectors;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class ModelFileManagerTest {
- private static final Locale DEFAULT_LOCALE = Locale.forLanguageTag("en-US");
- @Mock
- private Supplier<List<ModelFileManager.ModelFile>> mModelFileSupplier;
- private ModelFileManager.ModelFileSupplierImpl mModelFileSupplierImpl;
- private ModelFileManager mModelFileManager;
- private File mRootTestDir;
- private File mFactoryModelDir;
- private File mUpdatedModelFile;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- mModelFileManager = new ModelFileManager(mModelFileSupplier);
- mRootTestDir = InstrumentationRegistry.getContext().getCacheDir();
- mFactoryModelDir = new File(mRootTestDir, "factory");
- mUpdatedModelFile = new File(mRootTestDir, "updated.model");
-
- mModelFileSupplierImpl =
- new ModelFileManager.ModelFileSupplierImpl(
- mFactoryModelDir,
- "test\\d.model",
- mUpdatedModelFile,
- fd -> 1,
- fd -> ModelFileManager.ModelFile.LANGUAGE_INDEPENDENT
- );
-
- mRootTestDir.mkdirs();
- mFactoryModelDir.mkdirs();
-
- Locale.setDefault(DEFAULT_LOCALE);
- }
-
- @After
- public void removeTestDir() {
- recursiveDelete(mRootTestDir);
- }
-
- @Test
- public void get() {
- ModelFileManager.ModelFile modelFile =
- new ModelFileManager.ModelFile(
- new File("/path/a"), 1, Collections.emptyList(), "", true);
- when(mModelFileSupplier.get()).thenReturn(Collections.singletonList(modelFile));
-
- List<ModelFileManager.ModelFile> modelFiles = mModelFileManager.listModelFiles();
-
- assertThat(modelFiles).hasSize(1);
- assertThat(modelFiles.get(0)).isEqualTo(modelFile);
- }
-
- @Test
- public void findBestModel_versionCode() {
- ModelFileManager.ModelFile olderModelFile =
- new ModelFileManager.ModelFile(
- new File("/path/a"), 1,
- Collections.emptyList(), "", true);
-
- ModelFileManager.ModelFile newerModelFile =
- new ModelFileManager.ModelFile(
- new File("/path/b"), 2,
- Collections.emptyList(), "", true);
- when(mModelFileSupplier.get())
- .thenReturn(Arrays.asList(olderModelFile, newerModelFile));
-
- ModelFileManager.ModelFile bestModelFile =
- mModelFileManager.findBestModelFile(LocaleList.getEmptyLocaleList());
-
- assertThat(bestModelFile).isEqualTo(newerModelFile);
- }
-
- @Test
- public void findBestModel_languageDependentModelIsPreferred() {
- Locale locale = Locale.forLanguageTag("ja");
- ModelFileManager.ModelFile languageIndependentModelFile =
- new ModelFileManager.ModelFile(
- new File("/path/a"), 1,
- Collections.emptyList(), "", true);
-
- ModelFileManager.ModelFile languageDependentModelFile =
- new ModelFileManager.ModelFile(
- new File("/path/b"), 1,
- Collections.singletonList(locale), locale.toLanguageTag(), false);
- when(mModelFileSupplier.get())
- .thenReturn(
- Arrays.asList(languageIndependentModelFile, languageDependentModelFile));
-
- ModelFileManager.ModelFile bestModelFile =
- mModelFileManager.findBestModelFile(
- LocaleList.forLanguageTags(locale.toLanguageTag()));
- assertThat(bestModelFile).isEqualTo(languageDependentModelFile);
- }
-
- @Test
- public void findBestModel_noMatchedLanguageModel() {
- Locale locale = Locale.forLanguageTag("ja");
- ModelFileManager.ModelFile languageIndependentModelFile =
- new ModelFileManager.ModelFile(
- new File("/path/a"), 1,
- Collections.emptyList(), "", true);
-
- ModelFileManager.ModelFile languageDependentModelFile =
- new ModelFileManager.ModelFile(
- new File("/path/b"), 1,
- Collections.singletonList(locale), locale.toLanguageTag(), false);
-
- when(mModelFileSupplier.get())
- .thenReturn(
- Arrays.asList(languageIndependentModelFile, languageDependentModelFile));
-
- ModelFileManager.ModelFile bestModelFile =
- mModelFileManager.findBestModelFile(
- LocaleList.forLanguageTags("zh-hk"));
- assertThat(bestModelFile).isEqualTo(languageIndependentModelFile);
- }
-
- @Test
- public void findBestModel_noMatchedLanguageModel_defaultLocaleModelExists() {
- ModelFileManager.ModelFile languageIndependentModelFile =
- new ModelFileManager.ModelFile(
- new File("/path/a"), 1,
- Collections.emptyList(), "", true);
-
- ModelFileManager.ModelFile languageDependentModelFile =
- new ModelFileManager.ModelFile(
- new File("/path/b"), 1,
- Collections.singletonList(
- DEFAULT_LOCALE), DEFAULT_LOCALE.toLanguageTag(), false);
-
- when(mModelFileSupplier.get())
- .thenReturn(
- Arrays.asList(languageIndependentModelFile, languageDependentModelFile));
-
- ModelFileManager.ModelFile bestModelFile =
- mModelFileManager.findBestModelFile(
- LocaleList.forLanguageTags("zh-hk"));
- assertThat(bestModelFile).isEqualTo(languageIndependentModelFile);
- }
-
- @Test
- public void findBestModel_languageIsMoreImportantThanVersion() {
- ModelFileManager.ModelFile matchButOlderModel =
- new ModelFileManager.ModelFile(
- new File("/path/a"), 1,
- Collections.singletonList(Locale.forLanguageTag("fr")), "fr", false);
-
- ModelFileManager.ModelFile mismatchButNewerModel =
- new ModelFileManager.ModelFile(
- new File("/path/b"), 2,
- Collections.singletonList(Locale.forLanguageTag("ja")), "ja", false);
-
- when(mModelFileSupplier.get())
- .thenReturn(
- Arrays.asList(matchButOlderModel, mismatchButNewerModel));
-
- ModelFileManager.ModelFile bestModelFile =
- mModelFileManager.findBestModelFile(
- LocaleList.forLanguageTags("fr"));
- assertThat(bestModelFile).isEqualTo(matchButOlderModel);
- }
-
- @Test
- public void findBestModel_languageIsMoreImportantThanVersion_bestModelComesFirst() {
- ModelFileManager.ModelFile matchLocaleModel =
- new ModelFileManager.ModelFile(
- new File("/path/b"), 1,
- Collections.singletonList(Locale.forLanguageTag("ja")), "ja", false);
-
- ModelFileManager.ModelFile languageIndependentModel =
- new ModelFileManager.ModelFile(
- new File("/path/a"), 2,
- Collections.emptyList(), "", true);
- when(mModelFileSupplier.get())
- .thenReturn(
- Arrays.asList(matchLocaleModel, languageIndependentModel));
-
- ModelFileManager.ModelFile bestModelFile =
- mModelFileManager.findBestModelFile(
- LocaleList.forLanguageTags("ja"));
-
- assertThat(bestModelFile).isEqualTo(matchLocaleModel);
- }
-
- @Test
- public void modelFileEquals() {
- ModelFileManager.ModelFile modelA =
- new ModelFileManager.ModelFile(
- new File("/path/a"), 1,
- Collections.singletonList(Locale.forLanguageTag("ja")), "ja", false);
-
- ModelFileManager.ModelFile modelB =
- new ModelFileManager.ModelFile(
- new File("/path/a"), 1,
- Collections.singletonList(Locale.forLanguageTag("ja")), "ja", false);
-
- assertThat(modelA).isEqualTo(modelB);
- }
-
- @Test
- public void modelFile_different() {
- ModelFileManager.ModelFile modelA =
- new ModelFileManager.ModelFile(
- new File("/path/a"), 1,
- Collections.singletonList(Locale.forLanguageTag("ja")), "ja", false);
-
- ModelFileManager.ModelFile modelB =
- new ModelFileManager.ModelFile(
- new File("/path/b"), 1,
- Collections.singletonList(Locale.forLanguageTag("ja")), "ja", false);
-
- assertThat(modelA).isNotEqualTo(modelB);
- }
-
-
- @Test
- public void modelFile_getPath() {
- ModelFileManager.ModelFile modelA =
- new ModelFileManager.ModelFile(
- new File("/path/a"), 1,
- Collections.singletonList(Locale.forLanguageTag("ja")), "ja", false);
-
- assertThat(modelA.getPath()).isEqualTo("/path/a");
- }
-
- @Test
- public void modelFile_getName() {
- ModelFileManager.ModelFile modelA =
- new ModelFileManager.ModelFile(
- new File("/path/a"), 1,
- Collections.singletonList(Locale.forLanguageTag("ja")), "ja", false);
-
- assertThat(modelA.getName()).isEqualTo("a");
- }
-
- @Test
- public void modelFile_isPreferredTo_languageDependentIsBetter() {
- ModelFileManager.ModelFile modelA =
- new ModelFileManager.ModelFile(
- new File("/path/a"), 1,
- Collections.singletonList(Locale.forLanguageTag("ja")), "ja", false);
-
- ModelFileManager.ModelFile modelB =
- new ModelFileManager.ModelFile(
- new File("/path/b"), 2,
- Collections.emptyList(), "", true);
-
- assertThat(modelA.isPreferredTo(modelB)).isTrue();
- }
-
- @Test
- public void modelFile_isPreferredTo_version() {
- ModelFileManager.ModelFile modelA =
- new ModelFileManager.ModelFile(
- new File("/path/a"), 2,
- Collections.singletonList(Locale.forLanguageTag("ja")), "ja", false);
-
- ModelFileManager.ModelFile modelB =
- new ModelFileManager.ModelFile(
- new File("/path/b"), 1,
- Collections.emptyList(), "", false);
-
- assertThat(modelA.isPreferredTo(modelB)).isTrue();
- }
-
- @Test
- public void testFileSupplierImpl_updatedFileOnly() throws IOException {
- mUpdatedModelFile.createNewFile();
- File model1 = new File(mFactoryModelDir, "test1.model");
- model1.createNewFile();
- File model2 = new File(mFactoryModelDir, "test2.model");
- model2.createNewFile();
- new File(mFactoryModelDir, "not_match_regex.model").createNewFile();
-
- List<ModelFileManager.ModelFile> modelFiles = mModelFileSupplierImpl.get();
- List<String> modelFilePaths =
- modelFiles
- .stream()
- .map(modelFile -> modelFile.getPath())
- .collect(Collectors.toList());
-
- assertThat(modelFiles).hasSize(3);
- assertThat(modelFilePaths).containsExactly(
- mUpdatedModelFile.getAbsolutePath(),
- model1.getAbsolutePath(),
- model2.getAbsolutePath());
- }
-
- @Test
- public void testFileSupplierImpl_empty() {
- mFactoryModelDir.delete();
- List<ModelFileManager.ModelFile> modelFiles = mModelFileSupplierImpl.get();
-
- assertThat(modelFiles).hasSize(0);
- }
-
- private static void recursiveDelete(File f) {
- if (f.isDirectory()) {
- for (File innerFile : f.listFiles()) {
- recursiveDelete(innerFile);
- }
- }
- f.delete();
- }
-}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
index 82fa73f76207..2f21b7f0c75a 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
@@ -16,7 +16,6 @@
package android.view.textclassifier;
-import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import android.provider.DeviceConfig;
@@ -24,8 +23,6 @@ import android.provider.DeviceConfig;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.google.common.primitives.Floats;
-
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -57,17 +54,17 @@ public class TextClassificationConstantsTest {
public void testLoadFromDeviceConfig_IntValue() throws Exception {
// Saves config original value.
final String originalValue = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- TextClassificationConstants.SUGGEST_SELECTION_MAX_RANGE_LENGTH);
+ TextClassificationConstants.GENERATE_LINKS_MAX_TEXT_LENGTH);
final TextClassificationConstants constants = new TextClassificationConstants();
try {
// Sets and checks different value.
- setDeviceConfig(TextClassificationConstants.SUGGEST_SELECTION_MAX_RANGE_LENGTH, "8");
- assertWithMessage(TextClassificationConstants.SUGGEST_SELECTION_MAX_RANGE_LENGTH)
- .that(constants.getSuggestSelectionMaxRangeLength()).isEqualTo(8);
+ setDeviceConfig(TextClassificationConstants.GENERATE_LINKS_MAX_TEXT_LENGTH, "8");
+ assertWithMessage(TextClassificationConstants.GENERATE_LINKS_MAX_TEXT_LENGTH)
+ .that(constants.getGenerateLinksMaxTextLength()).isEqualTo(8);
} finally {
// Restores config original value.
- setDeviceConfig(TextClassificationConstants.SUGGEST_SELECTION_MAX_RANGE_LENGTH,
+ setDeviceConfig(TextClassificationConstants.GENERATE_LINKS_MAX_TEXT_LENGTH,
originalValue);
}
}
@@ -94,61 +91,6 @@ public class TextClassificationConstantsTest {
}
}
- @Test
- public void testLoadFromDeviceConfig_FloatValue() throws Exception {
- // Saves config original value.
- final String originalValue = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- TextClassificationConstants.LANG_ID_THRESHOLD_OVERRIDE);
-
- final TextClassificationConstants constants = new TextClassificationConstants();
- try {
- // Sets and checks different value.
- setDeviceConfig(TextClassificationConstants.LANG_ID_THRESHOLD_OVERRIDE, "2");
- assertWithMessage(TextClassificationConstants.LANG_ID_THRESHOLD_OVERRIDE)
- .that(constants.getLangIdThresholdOverride()).isWithin(EPSILON).of(2f);
- } finally {
- // Restores config original value.
- setDeviceConfig(TextClassificationConstants.LANG_ID_THRESHOLD_OVERRIDE, originalValue);
- }
- }
-
- @Test
- public void testLoadFromDeviceConfig_StringList() throws Exception {
- // Saves config original value.
- final String originalValue = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- TextClassificationConstants.ENTITY_LIST_DEFAULT);
-
- final TextClassificationConstants constants = new TextClassificationConstants();
- try {
- // Sets and checks different value.
- setDeviceConfig(TextClassificationConstants.ENTITY_LIST_DEFAULT, "email:url");
- assertWithMessage(TextClassificationConstants.ENTITY_LIST_DEFAULT)
- .that(constants.getEntityListDefault())
- .containsExactly("email", "url");
- } finally {
- // Restores config original value.
- setDeviceConfig(TextClassificationConstants.ENTITY_LIST_DEFAULT, originalValue);
- }
- }
-
- @Test
- public void testLoadFromDeviceConfig_FloatList() throws Exception {
- // Saves config original value.
- final String originalValue = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
- TextClassificationConstants.LANG_ID_CONTEXT_SETTINGS);
-
- final TextClassificationConstants constants = new TextClassificationConstants();
- try {
- // Sets and checks different value.
- setDeviceConfig(TextClassificationConstants.LANG_ID_CONTEXT_SETTINGS, "30:0.5:0.3");
- assertThat(Floats.asList(constants.getLangIdContextSettings())).containsExactly(30f,
- 0.5f, 0.3f).inOrder();
- } finally {
- // Restores config original value.
- setDeviceConfig(TextClassificationConstants.LANG_ID_CONTEXT_SETTINGS, originalValue);
- }
- }
-
private void setDeviceConfig(String key, String value) {
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key,
value, /* makeDefault */ false);
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
index 1ca46491b363..628252d8ca6c 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -16,19 +16,15 @@
package android.view.textclassifier;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotSame;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Mockito.mock;
import android.content.Context;
-import android.content.Intent;
-import android.os.LocaleList;
-import androidx.test.InstrumentationRegistry;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
@@ -38,14 +34,12 @@ import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class TextClassificationManagerTest {
- private static final LocaleList LOCALES = LocaleList.forLanguageTags("en-US");
-
private Context mContext;
private TextClassificationManager mTcm;
@Before
public void setup() {
- mContext = InstrumentationRegistry.getTargetContext();
+ mContext = ApplicationProvider.getApplicationContext();
mTcm = mContext.getSystemService(TextClassificationManager.class);
}
@@ -53,45 +47,17 @@ public class TextClassificationManagerTest {
public void testSetTextClassifier() {
TextClassifier classifier = mock(TextClassifier.class);
mTcm.setTextClassifier(classifier);
- assertEquals(classifier, mTcm.getTextClassifier());
+ assertThat(mTcm.getTextClassifier()).isEqualTo(classifier);
}
@Test
public void testGetLocalTextClassifier() {
- assertTrue(mTcm.getTextClassifier(TextClassifier.LOCAL) instanceof TextClassifierImpl);
+ assertThat(mTcm.getTextClassifier(TextClassifier.LOCAL)).isSameAs(TextClassifier.NO_OP);
}
@Test
public void testGetSystemTextClassifier() {
- assertTrue(mTcm.getTextClassifier(TextClassifier.SYSTEM) instanceof SystemTextClassifier);
- }
-
- @Test
- public void testCannotResolveIntent() {
- Context fakeContext = new FakeContextBuilder()
- .setAllIntentComponent(FakeContextBuilder.DEFAULT_COMPONENT)
- .setIntentComponent(Intent.ACTION_INSERT_OR_EDIT, null)
- .build();
-
- TextClassifier fallback = TextClassifier.NO_OP;
- TextClassifier classifier = new TextClassifierImpl(
- fakeContext, new TextClassificationConstants(), fallback);
-
- String text = "Contact me at +12122537077";
- String classifiedText = "+12122537077";
- int startIndex = text.indexOf(classifiedText);
- int endIndex = startIndex + classifiedText.length();
- TextClassification.Request request = new TextClassification.Request.Builder(
- text, startIndex, endIndex)
- .setDefaultLocales(LOCALES)
- .build();
-
- TextClassification result = classifier.classifyText(request);
- TextClassification fallbackResult = fallback.classifyText(request);
-
- // classifier should not totally fail in which case it returns a fallback result.
- // It should skip the failing intent and return a result for non-failing intents.
- assertFalse(result.getActions().isEmpty());
- assertNotSame(result, fallbackResult);
+ assertThat(mTcm.getTextClassifier(TextClassifier.SYSTEM))
+ .isInstanceOf(SystemTextClassifier.class);
}
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
deleted file mode 100644
index 372a4787cf1a..000000000000
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
+++ /dev/null
@@ -1,719 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier;
-
-import static org.hamcrest.CoreMatchers.not;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-
-import android.app.RemoteAction;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.LocaleList;
-import android.service.textclassifier.TextClassifierService;
-import android.text.Spannable;
-import android.text.SpannableString;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-
-import com.google.common.truth.Truth;
-
-import org.hamcrest.BaseMatcher;
-import org.hamcrest.Description;
-import org.hamcrest.Matcher;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Testing {@link TextClassifierTest} APIs on local and system textclassifier.
- * <p>
- * Tests are skipped if such a textclassifier does not exist.
- */
-@SmallTest
-@RunWith(Parameterized.class)
-public class TextClassifierTest {
- private static final String LOCAL = "local";
- private static final String SESSION = "session";
- private static final String DEFAULT = "default";
-
- // TODO: Add SYSTEM, which tests TextClassifier.SYSTEM.
- @Parameterized.Parameters(name = "{0}")
- public static Iterable<Object> textClassifierTypes() {
- return Arrays.asList(LOCAL, SESSION, DEFAULT);
- }
-
- @Parameterized.Parameter
- public String mTextClassifierType;
-
- private static final TextClassificationConstants TC_CONSTANTS =
- new TextClassificationConstants();
- private static final LocaleList LOCALES = LocaleList.forLanguageTags("en-US");
- private static final String NO_TYPE = null;
-
- private Context mContext;
- private TextClassificationManager mTcm;
- private TextClassifier mClassifier;
-
- @Before
- public void setup() {
- mContext = InstrumentationRegistry.getTargetContext();
- mTcm = mContext.getSystemService(TextClassificationManager.class);
-
- if (mTextClassifierType.equals(LOCAL)) {
- mClassifier = mTcm.getTextClassifier(TextClassifier.LOCAL);
- } else if (mTextClassifierType.equals(SESSION)) {
- mClassifier = mTcm.createTextClassificationSession(
- new TextClassificationContext.Builder(
- "android",
- TextClassifier.WIDGET_TYPE_NOTIFICATION)
- .build(),
- mTcm.getTextClassifier(TextClassifier.LOCAL));
- } else {
- mClassifier = TextClassifierService.getDefaultTextClassifierImplementation(mContext);
- }
- }
-
- @Test
- public void testSuggestSelection() {
- if (isTextClassifierDisabled()) return;
-
- String text = "Contact me at droid@android.com";
- String selected = "droid";
- String suggested = "droid@android.com";
- int startIndex = text.indexOf(selected);
- int endIndex = startIndex + selected.length();
- int smartStartIndex = text.indexOf(suggested);
- int smartEndIndex = smartStartIndex + suggested.length();
- TextSelection.Request request = new TextSelection.Request.Builder(
- text, startIndex, endIndex)
- .setDefaultLocales(LOCALES)
- .build();
-
- TextSelection selection = mClassifier.suggestSelection(request);
- assertThat(selection,
- isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_EMAIL));
- }
-
- @Test
- public void testSuggestSelection_url() {
- if (isTextClassifierDisabled()) return;
-
- String text = "Visit http://www.android.com for more information";
- String selected = "http";
- String suggested = "http://www.android.com";
- int startIndex = text.indexOf(selected);
- int endIndex = startIndex + selected.length();
- int smartStartIndex = text.indexOf(suggested);
- int smartEndIndex = smartStartIndex + suggested.length();
- TextSelection.Request request = new TextSelection.Request.Builder(
- text, startIndex, endIndex)
- .setDefaultLocales(LOCALES)
- .build();
-
- TextSelection selection = mClassifier.suggestSelection(request);
- assertThat(selection,
- isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_URL));
- }
-
- @Test
- public void testSmartSelection_withEmoji() {
- if (isTextClassifierDisabled()) return;
-
- String text = "\uD83D\uDE02 Hello.";
- String selected = "Hello";
- int startIndex = text.indexOf(selected);
- int endIndex = startIndex + selected.length();
- TextSelection.Request request = new TextSelection.Request.Builder(
- text, startIndex, endIndex)
- .setDefaultLocales(LOCALES)
- .build();
-
- TextSelection selection = mClassifier.suggestSelection(request);
- assertThat(selection,
- isTextSelection(startIndex, endIndex, NO_TYPE));
- }
-
- @Test
- public void testClassifyText() {
- if (isTextClassifierDisabled()) return;
-
- String text = "Contact me at droid@android.com";
- String classifiedText = "droid@android.com";
- int startIndex = text.indexOf(classifiedText);
- int endIndex = startIndex + classifiedText.length();
- TextClassification.Request request = new TextClassification.Request.Builder(
- text, startIndex, endIndex)
- .setDefaultLocales(LOCALES)
- .build();
-
- TextClassification classification = mClassifier.classifyText(request);
- assertThat(classification, isTextClassification(classifiedText, TextClassifier.TYPE_EMAIL));
- }
-
- @Test
- public void testClassifyText_url() {
- if (isTextClassifierDisabled()) return;
-
- String text = "Visit www.android.com for more information";
- String classifiedText = "www.android.com";
- int startIndex = text.indexOf(classifiedText);
- int endIndex = startIndex + classifiedText.length();
- TextClassification.Request request = new TextClassification.Request.Builder(
- text, startIndex, endIndex)
- .setDefaultLocales(LOCALES)
- .build();
-
- TextClassification classification = mClassifier.classifyText(request);
- assertThat(classification, isTextClassification(classifiedText, TextClassifier.TYPE_URL));
- assertThat(classification, containsIntentWithAction(Intent.ACTION_VIEW));
- }
-
- @Test
- public void testClassifyText_address() {
- if (isTextClassifierDisabled()) return;
-
- String text = "Brandschenkestrasse 110, Zürich, Switzerland";
- TextClassification.Request request = new TextClassification.Request.Builder(
- text, 0, text.length())
- .setDefaultLocales(LOCALES)
- .build();
-
- TextClassification classification = mClassifier.classifyText(request);
- assertThat(classification, isTextClassification(text, TextClassifier.TYPE_ADDRESS));
- }
-
- @Test
- public void testClassifyText_url_inCaps() {
- if (isTextClassifierDisabled()) return;
-
- String text = "Visit HTTP://ANDROID.COM for more information";
- String classifiedText = "HTTP://ANDROID.COM";
- int startIndex = text.indexOf(classifiedText);
- int endIndex = startIndex + classifiedText.length();
- TextClassification.Request request = new TextClassification.Request.Builder(
- text, startIndex, endIndex)
- .setDefaultLocales(LOCALES)
- .build();
-
- TextClassification classification = mClassifier.classifyText(request);
- assertThat(classification, isTextClassification(classifiedText, TextClassifier.TYPE_URL));
- assertThat(classification, containsIntentWithAction(Intent.ACTION_VIEW));
- }
-
- @Test
- public void testClassifyText_date() {
- if (isTextClassifierDisabled()) return;
-
- String text = "Let's meet on January 9, 2018.";
- String classifiedText = "January 9, 2018";
- int startIndex = text.indexOf(classifiedText);
- int endIndex = startIndex + classifiedText.length();
- TextClassification.Request request = new TextClassification.Request.Builder(
- text, startIndex, endIndex)
- .setDefaultLocales(LOCALES)
- .build();
-
- TextClassification classification = mClassifier.classifyText(request);
- assertThat(classification, isTextClassification(classifiedText, TextClassifier.TYPE_DATE));
- Bundle extras = classification.getExtras();
- List<Bundle> entities = ExtrasUtils.getEntities(extras);
- Truth.assertThat(entities).hasSize(1);
- Bundle entity = entities.get(0);
- Truth.assertThat(ExtrasUtils.getEntityType(entity)).isEqualTo(TextClassifier.TYPE_DATE);
- }
-
- @Test
- public void testClassifyText_datetime() {
- if (isTextClassifierDisabled()) return;
-
- String text = "Let's meet 2018/01/01 10:30:20.";
- String classifiedText = "2018/01/01 10:30:20";
- int startIndex = text.indexOf(classifiedText);
- int endIndex = startIndex + classifiedText.length();
- TextClassification.Request request = new TextClassification.Request.Builder(
- text, startIndex, endIndex)
- .setDefaultLocales(LOCALES)
- .build();
-
- TextClassification classification = mClassifier.classifyText(request);
- assertThat(classification,
- isTextClassification(classifiedText, TextClassifier.TYPE_DATE_TIME));
- }
-
- @Test
- public void testClassifyText_foreignText() {
- LocaleList originalLocales = LocaleList.getDefault();
- LocaleList.setDefault(LocaleList.forLanguageTags("en"));
- String japaneseText = "これは日本語のテキストです";
-
- Context context = new FakeContextBuilder()
- .setIntentComponent(Intent.ACTION_TRANSLATE, FakeContextBuilder.DEFAULT_COMPONENT)
- .build();
- TextClassifier classifier = new TextClassifierImpl(context, TC_CONSTANTS);
- TextClassification.Request request = new TextClassification.Request.Builder(
- japaneseText, 0, japaneseText.length())
- .setDefaultLocales(LOCALES)
- .build();
-
- TextClassification classification = classifier.classifyText(request);
- RemoteAction translateAction = classification.getActions().get(0);
- assertEquals(1, classification.getActions().size());
- assertEquals(
- context.getString(com.android.internal.R.string.translate),
- translateAction.getTitle());
-
- assertEquals(translateAction, ExtrasUtils.findTranslateAction(classification));
- Intent intent = ExtrasUtils.getActionsIntents(classification).get(0);
- assertEquals(Intent.ACTION_TRANSLATE, intent.getAction());
- Bundle foreignLanguageInfo = ExtrasUtils.getForeignLanguageExtra(classification);
- assertEquals("ja", ExtrasUtils.getEntityType(foreignLanguageInfo));
- assertTrue(ExtrasUtils.getScore(foreignLanguageInfo) >= 0);
- assertTrue(ExtrasUtils.getScore(foreignLanguageInfo) <= 1);
- assertTrue(intent.hasExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER));
- assertEquals("ja", ExtrasUtils.getTopLanguage(intent).getLanguage());
-
- LocaleList.setDefault(originalLocales);
- }
-
- @Test
- public void testGenerateLinks_phone() {
- if (isTextClassifierDisabled()) return;
- String text = "The number is +12122537077. See you tonight!";
- TextLinks.Request request = new TextLinks.Request.Builder(text).build();
- assertThat(mClassifier.generateLinks(request),
- isTextLinksContaining(text, "+12122537077", TextClassifier.TYPE_PHONE));
- }
-
- @Test
- public void testGenerateLinks_exclude() {
- if (isTextClassifierDisabled()) return;
- String text = "You want apple@banana.com. See you tonight!";
- List<String> hints = Collections.EMPTY_LIST;
- List<String> included = Collections.EMPTY_LIST;
- List<String> excluded = Arrays.asList(TextClassifier.TYPE_EMAIL);
- TextLinks.Request request = new TextLinks.Request.Builder(text)
- .setEntityConfig(TextClassifier.EntityConfig.create(hints, included, excluded))
- .setDefaultLocales(LOCALES)
- .build();
- assertThat(mClassifier.generateLinks(request),
- not(isTextLinksContaining(text, "apple@banana.com", TextClassifier.TYPE_EMAIL)));
- }
-
- @Test
- public void testGenerateLinks_explicit_address() {
- if (isTextClassifierDisabled()) return;
- String text = "The address is 1600 Amphitheater Parkway, Mountain View, CA. See you!";
- List<String> explicit = Arrays.asList(TextClassifier.TYPE_ADDRESS);
- TextLinks.Request request = new TextLinks.Request.Builder(text)
- .setEntityConfig(TextClassifier.EntityConfig.createWithExplicitEntityList(explicit))
- .setDefaultLocales(LOCALES)
- .build();
- assertThat(mClassifier.generateLinks(request),
- isTextLinksContaining(text, "1600 Amphitheater Parkway, Mountain View, CA",
- TextClassifier.TYPE_ADDRESS));
- }
-
- @Test
- public void testGenerateLinks_exclude_override() {
- if (isTextClassifierDisabled()) return;
- String text = "You want apple@banana.com. See you tonight!";
- List<String> hints = Collections.EMPTY_LIST;
- List<String> included = Arrays.asList(TextClassifier.TYPE_EMAIL);
- List<String> excluded = Arrays.asList(TextClassifier.TYPE_EMAIL);
- TextLinks.Request request = new TextLinks.Request.Builder(text)
- .setEntityConfig(TextClassifier.EntityConfig.create(hints, included, excluded))
- .setDefaultLocales(LOCALES)
- .build();
- assertThat(mClassifier.generateLinks(request),
- not(isTextLinksContaining(text, "apple@banana.com", TextClassifier.TYPE_EMAIL)));
- }
-
- @Test
- public void testGenerateLinks_maxLength() {
- if (isTextClassifierDisabled()) return;
- char[] manySpaces = new char[mClassifier.getMaxGenerateLinksTextLength()];
- Arrays.fill(manySpaces, ' ');
- TextLinks.Request request = new TextLinks.Request.Builder(new String(manySpaces)).build();
- TextLinks links = mClassifier.generateLinks(request);
- assertTrue(links.getLinks().isEmpty());
- }
-
- @Test
- public void testApplyLinks_unsupportedCharacter() {
- if (isTextClassifierDisabled()) return;
- Spannable url = new SpannableString("\u202Emoc.diordna.com");
- TextLinks.Request request = new TextLinks.Request.Builder(url).build();
- assertEquals(
- TextLinks.STATUS_UNSUPPORTED_CHARACTER,
- mClassifier.generateLinks(request).apply(url, 0, null));
- }
-
- @Test
- public void testGenerateLinks_tooLong() {
- if (isTextClassifierDisabled()) return;
- char[] manySpaces = new char[mClassifier.getMaxGenerateLinksTextLength() + 1];
- Arrays.fill(manySpaces, ' ');
- TextLinks.Request request = new TextLinks.Request.Builder(new String(manySpaces)).build();
- TextLinks links = mClassifier.generateLinks(request);
- assertTrue(links.getLinks().isEmpty());
- }
-
- @Test
- public void testGenerateLinks_entityData() {
- if (isTextClassifierDisabled()) return;
- String text = "The number is +12122537077.";
- Bundle extras = new Bundle();
- ExtrasUtils.putIsSerializedEntityDataEnabled(extras, true);
- TextLinks.Request request = new TextLinks.Request.Builder(text).setExtras(extras).build();
-
- TextLinks textLinks = mClassifier.generateLinks(request);
-
- Truth.assertThat(textLinks.getLinks()).hasSize(1);
- TextLinks.TextLink textLink = textLinks.getLinks().iterator().next();
- List<Bundle> entities = ExtrasUtils.getEntities(textLink.getExtras());
- Truth.assertThat(entities).hasSize(1);
- Bundle entity = entities.get(0);
- Truth.assertThat(ExtrasUtils.getEntityType(entity)).isEqualTo(TextClassifier.TYPE_PHONE);
- }
-
- @Test
- public void testGenerateLinks_entityData_disabled() {
- if (isTextClassifierDisabled()) return;
- String text = "The number is +12122537077.";
- TextLinks.Request request = new TextLinks.Request.Builder(text).build();
-
- TextLinks textLinks = mClassifier.generateLinks(request);
-
- Truth.assertThat(textLinks.getLinks()).hasSize(1);
- TextLinks.TextLink textLink = textLinks.getLinks().iterator().next();
- List<Bundle> entities = ExtrasUtils.getEntities(textLink.getExtras());
- Truth.assertThat(entities).isNull();
- }
-
- @Test
- public void testDetectLanguage() {
- if (isTextClassifierDisabled()) return;
- String text = "This is English text";
- TextLanguage.Request request = new TextLanguage.Request.Builder(text).build();
- TextLanguage textLanguage = mClassifier.detectLanguage(request);
- assertThat(textLanguage, isTextLanguage("en"));
- }
-
- @Test
- public void testDetectLanguage_japanese() {
- if (isTextClassifierDisabled()) return;
- String text = "これは日本語のテキストです";
- TextLanguage.Request request = new TextLanguage.Request.Builder(text).build();
- TextLanguage textLanguage = mClassifier.detectLanguage(request);
- assertThat(textLanguage, isTextLanguage("ja"));
- }
-
- @Ignore // Doesn't work without a language-based model.
- @Test
- public void testSuggestConversationActions_textReplyOnly_maxOne() {
- if (isTextClassifierDisabled()) return;
- ConversationActions.Message message =
- new ConversationActions.Message.Builder(
- ConversationActions.Message.PERSON_USER_OTHERS)
- .setText("Where are you?")
- .build();
- TextClassifier.EntityConfig typeConfig =
- new TextClassifier.EntityConfig.Builder().includeTypesFromTextClassifier(false)
- .setIncludedTypes(
- Collections.singletonList(ConversationAction.TYPE_TEXT_REPLY))
- .build();
- ConversationActions.Request request =
- new ConversationActions.Request.Builder(Collections.singletonList(message))
- .setMaxSuggestions(1)
- .setTypeConfig(typeConfig)
- .build();
-
- ConversationActions conversationActions = mClassifier.suggestConversationActions(request);
- Truth.assertThat(conversationActions.getConversationActions()).hasSize(1);
- ConversationAction conversationAction = conversationActions.getConversationActions().get(0);
- Truth.assertThat(conversationAction.getType()).isEqualTo(
- ConversationAction.TYPE_TEXT_REPLY);
- Truth.assertThat(conversationAction.getTextReply()).isNotNull();
- }
-
- @Ignore // Doesn't work without a language-based model.
- @Test
- public void testSuggestConversationActions_textReplyOnly_noMax() {
- if (isTextClassifierDisabled()) return;
- ConversationActions.Message message =
- new ConversationActions.Message.Builder(
- ConversationActions.Message.PERSON_USER_OTHERS)
- .setText("Where are you?")
- .build();
- TextClassifier.EntityConfig typeConfig =
- new TextClassifier.EntityConfig.Builder().includeTypesFromTextClassifier(false)
- .setIncludedTypes(
- Collections.singletonList(ConversationAction.TYPE_TEXT_REPLY))
- .build();
- ConversationActions.Request request =
- new ConversationActions.Request.Builder(Collections.singletonList(message))
- .setTypeConfig(typeConfig)
- .build();
-
- ConversationActions conversationActions = mClassifier.suggestConversationActions(request);
- assertTrue(conversationActions.getConversationActions().size() > 1);
- for (ConversationAction conversationAction :
- conversationActions.getConversationActions()) {
- assertThat(conversationAction,
- isConversationAction(ConversationAction.TYPE_TEXT_REPLY));
- }
- }
-
- @Test
- public void testSuggestConversationActions_openUrl() {
- if (isTextClassifierDisabled()) return;
- ConversationActions.Message message =
- new ConversationActions.Message.Builder(
- ConversationActions.Message.PERSON_USER_OTHERS)
- .setText("Check this out: https://www.android.com")
- .build();
- TextClassifier.EntityConfig typeConfig =
- new TextClassifier.EntityConfig.Builder().includeTypesFromTextClassifier(false)
- .setIncludedTypes(
- Collections.singletonList(ConversationAction.TYPE_OPEN_URL))
- .build();
- ConversationActions.Request request =
- new ConversationActions.Request.Builder(Collections.singletonList(message))
- .setMaxSuggestions(1)
- .setTypeConfig(typeConfig)
- .build();
-
- ConversationActions conversationActions = mClassifier.suggestConversationActions(request);
- Truth.assertThat(conversationActions.getConversationActions()).hasSize(1);
- ConversationAction conversationAction = conversationActions.getConversationActions().get(0);
- Truth.assertThat(conversationAction.getType()).isEqualTo(ConversationAction.TYPE_OPEN_URL);
- Intent actionIntent = ExtrasUtils.getActionIntent(conversationAction.getExtras());
- Truth.assertThat(actionIntent.getAction()).isEqualTo(Intent.ACTION_VIEW);
- Truth.assertThat(actionIntent.getData()).isEqualTo(Uri.parse("https://www.android.com"));
- }
-
- @Ignore // Doesn't work without a language-based model.
- @Test
- public void testSuggestConversationActions_copy() {
- if (isTextClassifierDisabled()) return;
- ConversationActions.Message message =
- new ConversationActions.Message.Builder(
- ConversationActions.Message.PERSON_USER_OTHERS)
- .setText("Authentication code: 12345")
- .build();
- TextClassifier.EntityConfig typeConfig =
- new TextClassifier.EntityConfig.Builder().includeTypesFromTextClassifier(false)
- .setIncludedTypes(
- Collections.singletonList(ConversationAction.TYPE_COPY))
- .build();
- ConversationActions.Request request =
- new ConversationActions.Request.Builder(Collections.singletonList(message))
- .setMaxSuggestions(1)
- .setTypeConfig(typeConfig)
- .build();
-
- ConversationActions conversationActions = mClassifier.suggestConversationActions(request);
- Truth.assertThat(conversationActions.getConversationActions()).hasSize(1);
- ConversationAction conversationAction = conversationActions.getConversationActions().get(0);
- Truth.assertThat(conversationAction.getType()).isEqualTo(ConversationAction.TYPE_COPY);
- Truth.assertThat(conversationAction.getTextReply()).isAnyOf(null, "");
- Truth.assertThat(conversationAction.getAction()).isNull();
- String code = ExtrasUtils.getCopyText(conversationAction.getExtras());
- Truth.assertThat(code).isEqualTo("12345");
- Truth.assertThat(
- ExtrasUtils.getSerializedEntityData(conversationAction.getExtras())).isNotEmpty();
- }
-
- @Test
- public void testSuggestConversationActions_deduplicate() {
- Context context = new FakeContextBuilder()
- .setIntentComponent(Intent.ACTION_SENDTO, FakeContextBuilder.DEFAULT_COMPONENT)
- .build();
- ConversationActions.Message message =
- new ConversationActions.Message.Builder(
- ConversationActions.Message.PERSON_USER_OTHERS)
- .setText("a@android.com b@android.com")
- .build();
- ConversationActions.Request request =
- new ConversationActions.Request.Builder(Collections.singletonList(message))
- .setMaxSuggestions(3)
- .build();
-
- TextClassifier classifier = new TextClassifierImpl(context, TC_CONSTANTS);
- ConversationActions conversationActions = classifier.suggestConversationActions(request);
-
- Truth.assertThat(conversationActions.getConversationActions()).isEmpty();
- }
-
- private boolean isTextClassifierDisabled() {
- return mClassifier == null || mClassifier == TextClassifier.NO_OP;
- }
-
- private static Matcher<TextSelection> isTextSelection(
- final int startIndex, final int endIndex, final String type) {
- return new BaseMatcher<TextSelection>() {
- @Override
- public boolean matches(Object o) {
- if (o instanceof TextSelection) {
- TextSelection selection = (TextSelection) o;
- return startIndex == selection.getSelectionStartIndex()
- && endIndex == selection.getSelectionEndIndex()
- && typeMatches(selection, type);
- }
- return false;
- }
-
- private boolean typeMatches(TextSelection selection, String type) {
- return type == null
- || (selection.getEntityCount() > 0
- && type.trim().equalsIgnoreCase(selection.getEntity(0)));
- }
-
- @Override
- public void describeTo(Description description) {
- description.appendValue(
- String.format("%d, %d, %s", startIndex, endIndex, type));
- }
- };
- }
-
- private static Matcher<TextLinks> isTextLinksContaining(
- final String text, final String substring, final String type) {
- return new BaseMatcher<TextLinks>() {
-
- @Override
- public void describeTo(Description description) {
- description.appendText("text=").appendValue(text)
- .appendText(", substring=").appendValue(substring)
- .appendText(", type=").appendValue(type);
- }
-
- @Override
- public boolean matches(Object o) {
- if (o instanceof TextLinks) {
- for (TextLinks.TextLink link : ((TextLinks) o).getLinks()) {
- if (text.subSequence(link.getStart(), link.getEnd()).equals(substring)) {
- return type.equals(link.getEntity(0));
- }
- }
- }
- return false;
- }
- };
- }
-
- private static Matcher<TextClassification> isTextClassification(
- final String text, final String type) {
- return new BaseMatcher<TextClassification>() {
- @Override
- public boolean matches(Object o) {
- if (o instanceof TextClassification) {
- TextClassification result = (TextClassification) o;
- return text.equals(result.getText())
- && result.getEntityCount() > 0
- && type.equals(result.getEntity(0));
- }
- return false;
- }
-
- @Override
- public void describeTo(Description description) {
- description.appendText("text=").appendValue(text)
- .appendText(", type=").appendValue(type);
- }
- };
- }
-
- private static Matcher<TextClassification> containsIntentWithAction(final String action) {
- return new BaseMatcher<TextClassification>() {
- @Override
- public boolean matches(Object o) {
- if (o instanceof TextClassification) {
- TextClassification result = (TextClassification) o;
- return ExtrasUtils.findAction(result, action) != null;
- }
- return false;
- }
-
- @Override
- public void describeTo(Description description) {
- description.appendText("intent action=").appendValue(action);
- }
- };
- }
-
- private static Matcher<TextLanguage> isTextLanguage(final String languageTag) {
- return new BaseMatcher<TextLanguage>() {
- @Override
- public boolean matches(Object o) {
- if (o instanceof TextLanguage) {
- TextLanguage result = (TextLanguage) o;
- return result.getLocaleHypothesisCount() > 0
- && languageTag.equals(result.getLocale(0).toLanguageTag());
- }
- return false;
- }
-
- @Override
- public void describeTo(Description description) {
- description.appendText("locale=").appendValue(languageTag);
- }
- };
- }
-
- private static Matcher<ConversationAction> isConversationAction(String actionType) {
- return new BaseMatcher<ConversationAction>() {
- @Override
- public boolean matches(Object o) {
- if (!(o instanceof ConversationAction)) {
- return false;
- }
- ConversationAction conversationAction =
- (ConversationAction) o;
- if (!actionType.equals(conversationAction.getType())) {
- return false;
- }
- if (ConversationAction.TYPE_TEXT_REPLY.equals(actionType)) {
- if (conversationAction.getTextReply() == null) {
- return false;
- }
- }
- if (conversationAction.getConfidenceScore() < 0
- || conversationAction.getConfidenceScore() > 1) {
- return false;
- }
- return true;
- }
-
- @Override
- public void describeTo(Description description) {
- description.appendText("actionType=").appendValue(actionType);
- }
- };
- }
-}
diff --git a/core/tests/coretests/src/android/view/textclassifier/intent/LabeledIntentTest.java b/core/tests/coretests/src/android/view/textclassifier/intent/LabeledIntentTest.java
deleted file mode 100644
index 3ad26f5d5108..000000000000
--- a/core/tests/coretests/src/android/view/textclassifier/intent/LabeledIntentTest.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier.intent;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.testng.Assert.assertThrows;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.view.textclassifier.FakeContextBuilder;
-import android.view.textclassifier.TextClassifier;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public final class LabeledIntentTest {
- private static final String TITLE_WITHOUT_ENTITY = "Map";
- private static final String TITLE_WITH_ENTITY = "Map NW14D1";
- private static final String DESCRIPTION = "Check the map";
- private static final String DESCRIPTION_WITH_APP_NAME = "Use %1$s to open map";
- private static final Intent INTENT =
- new Intent(Intent.ACTION_VIEW).setDataAndNormalize(Uri.parse("http://www.android.com"));
- private static final int REQUEST_CODE = 42;
- private static final Bundle TEXT_LANGUAGES_BUNDLE = Bundle.EMPTY;
- private static final String APP_LABEL = "fake";
-
- private Context mContext;
-
- @Before
- public void setup() {
- final ComponentName component = FakeContextBuilder.DEFAULT_COMPONENT;
- mContext = new FakeContextBuilder()
- .setIntentComponent(Intent.ACTION_VIEW, component)
- .setAppLabel(component.getPackageName(), APP_LABEL)
- .build();
- }
-
- @Test
- public void resolve_preferTitleWithEntity() {
- LabeledIntent labeledIntent = new LabeledIntent(
- TITLE_WITHOUT_ENTITY,
- TITLE_WITH_ENTITY,
- DESCRIPTION,
- null,
- INTENT,
- REQUEST_CODE
- );
-
- LabeledIntent.Result result = labeledIntent.resolve(
- mContext, /*titleChooser*/ null, TEXT_LANGUAGES_BUNDLE);
-
- assertThat(result).isNotNull();
- assertThat(result.remoteAction.getTitle()).isEqualTo(TITLE_WITH_ENTITY);
- assertThat(result.remoteAction.getContentDescription()).isEqualTo(DESCRIPTION);
- Intent intent = result.resolvedIntent;
- assertThat(intent.getAction()).isEqualTo(intent.getAction());
- assertThat(intent.getComponent()).isNotNull();
- assertThat(intent.hasExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER)).isTrue();
- }
-
- @Test
- public void resolve_useAvailableTitle() {
- LabeledIntent labeledIntent = new LabeledIntent(
- TITLE_WITHOUT_ENTITY,
- null,
- DESCRIPTION,
- null,
- INTENT,
- REQUEST_CODE
- );
-
- LabeledIntent.Result result = labeledIntent.resolve(
- mContext, /*titleChooser*/ null, TEXT_LANGUAGES_BUNDLE);
-
- assertThat(result).isNotNull();
- assertThat(result.remoteAction.getTitle()).isEqualTo(TITLE_WITHOUT_ENTITY);
- assertThat(result.remoteAction.getContentDescription()).isEqualTo(DESCRIPTION);
- Intent intent = result.resolvedIntent;
- assertThat(intent.getAction()).isEqualTo(intent.getAction());
- assertThat(intent.getComponent()).isNotNull();
- }
-
- @Test
- public void resolve_titleChooser() {
- LabeledIntent labeledIntent = new LabeledIntent(
- TITLE_WITHOUT_ENTITY,
- null,
- DESCRIPTION,
- null,
- INTENT,
- REQUEST_CODE
- );
-
- LabeledIntent.Result result = labeledIntent.resolve(
- mContext, (labeledIntent1, resolveInfo) -> "chooser", TEXT_LANGUAGES_BUNDLE);
-
- assertThat(result).isNotNull();
- assertThat(result.remoteAction.getTitle()).isEqualTo("chooser");
- assertThat(result.remoteAction.getContentDescription()).isEqualTo(DESCRIPTION);
- Intent intent = result.resolvedIntent;
- assertThat(intent.getAction()).isEqualTo(intent.getAction());
- assertThat(intent.getComponent()).isNotNull();
- }
-
- @Test
- public void resolve_titleChooserReturnsNull() {
- LabeledIntent labeledIntent = new LabeledIntent(
- TITLE_WITHOUT_ENTITY,
- null,
- DESCRIPTION,
- null,
- INTENT,
- REQUEST_CODE
- );
-
- LabeledIntent.Result result = labeledIntent.resolve(
- mContext, (labeledIntent1, resolveInfo) -> null, TEXT_LANGUAGES_BUNDLE);
-
- assertThat(result).isNotNull();
- assertThat(result.remoteAction.getTitle()).isEqualTo(TITLE_WITHOUT_ENTITY);
- assertThat(result.remoteAction.getContentDescription()).isEqualTo(DESCRIPTION);
- Intent intent = result.resolvedIntent;
- assertThat(intent.getAction()).isEqualTo(intent.getAction());
- assertThat(intent.getComponent()).isNotNull();
- }
-
- @Test
- public void resolve_missingTitle() {
- assertThrows(
- IllegalArgumentException.class,
- () ->
- new LabeledIntent(
- null,
- null,
- DESCRIPTION,
- null,
- INTENT,
- REQUEST_CODE
- ));
- }
-
- @Test
- public void resolve_noIntentHandler() {
- // See setup(). mContext can only resolve Intent.ACTION_VIEW.
- Intent unresolvableIntent = new Intent(Intent.ACTION_TRANSLATE);
- LabeledIntent labeledIntent = new LabeledIntent(
- TITLE_WITHOUT_ENTITY,
- null,
- DESCRIPTION,
- null,
- unresolvableIntent,
- REQUEST_CODE);
-
- LabeledIntent.Result result = labeledIntent.resolve(mContext, null, null);
-
- assertThat(result).isNull();
- }
-
- @Test
- public void resolve_descriptionWithAppName() {
- LabeledIntent labeledIntent = new LabeledIntent(
- TITLE_WITHOUT_ENTITY,
- TITLE_WITH_ENTITY,
- DESCRIPTION,
- DESCRIPTION_WITH_APP_NAME,
- INTENT,
- REQUEST_CODE
- );
-
- LabeledIntent.Result result = labeledIntent.resolve(
- mContext, /*titleChooser*/ null, TEXT_LANGUAGES_BUNDLE);
-
- assertThat(result).isNotNull();
- assertThat(result.remoteAction.getContentDescription()).isEqualTo("Use fake to open map");
- }
-}
diff --git a/core/tests/coretests/src/android/view/textclassifier/intent/LegacyIntentClassificationFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/intent/LegacyIntentClassificationFactoryTest.java
deleted file mode 100644
index 8891d3fd2dca..000000000000
--- a/core/tests/coretests/src/android/view/textclassifier/intent/LegacyIntentClassificationFactoryTest.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier.intent;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Intent;
-import android.view.textclassifier.TextClassifier;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.google.android.textclassifier.AnnotatorModel;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.List;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class LegacyIntentClassificationFactoryTest {
-
- private static final String TEXT = "text";
-
- private LegacyClassificationIntentFactory mLegacyIntentClassificationFactory;
-
- @Before
- public void setup() {
- mLegacyIntentClassificationFactory = new LegacyClassificationIntentFactory();
- }
-
- @Test
- public void create_typeDictionary() {
- AnnotatorModel.ClassificationResult classificationResult =
- new AnnotatorModel.ClassificationResult(
- TextClassifier.TYPE_DICTIONARY,
- 1.0f,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- 0L,
- 0L,
- 0d);
-
- List<LabeledIntent> intents = mLegacyIntentClassificationFactory.create(
- InstrumentationRegistry.getContext(),
- TEXT,
- /* foreignText */ false,
- null,
- classificationResult);
-
- assertThat(intents).hasSize(1);
- LabeledIntent labeledIntent = intents.get(0);
- Intent intent = labeledIntent.intent;
- assertThat(intent.getAction()).isEqualTo(Intent.ACTION_DEFINE);
- assertThat(intent.getStringExtra(Intent.EXTRA_TEXT)).isEqualTo(TEXT);
- }
-
- @Test
- public void create_translateAndDictionary() {
- AnnotatorModel.ClassificationResult classificationResult =
- new AnnotatorModel.ClassificationResult(
- TextClassifier.TYPE_DICTIONARY,
- 1.0f,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- 0L,
- 0L,
- 0d);
-
- List<LabeledIntent> intents = mLegacyIntentClassificationFactory.create(
- InstrumentationRegistry.getContext(),
- TEXT,
- /* foreignText */ true,
- null,
- classificationResult);
-
- assertThat(intents).hasSize(2);
- assertThat(intents.get(0).intent.getAction()).isEqualTo(Intent.ACTION_DEFINE);
- assertThat(intents.get(1).intent.getAction()).isEqualTo(Intent.ACTION_TRANSLATE);
- }
-}
diff --git a/core/tests/coretests/src/android/view/textclassifier/intent/TemplateClassificationIntentFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/intent/TemplateClassificationIntentFactoryTest.java
deleted file mode 100644
index bcea5fea6a13..000000000000
--- a/core/tests/coretests/src/android/view/textclassifier/intent/TemplateClassificationIntentFactoryTest.java
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier.intent;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.same;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.content.Intent;
-import android.view.textclassifier.TextClassifier;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.google.android.textclassifier.AnnotatorModel;
-import com.google.android.textclassifier.RemoteActionTemplate;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.List;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class TemplateClassificationIntentFactoryTest {
-
- private static final String TEXT = "text";
- private static final String TITLE_WITHOUT_ENTITY = "Map";
- private static final String DESCRIPTION = "Opens in Maps";
- private static final String DESCRIPTION_WITH_APP_NAME = "Use %1$s to open Map";
- private static final String ACTION = Intent.ACTION_VIEW;
-
- @Mock
- private ClassificationIntentFactory mFallback;
- private TemplateClassificationIntentFactory mTemplateClassificationIntentFactory;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- mTemplateClassificationIntentFactory = new TemplateClassificationIntentFactory(
- new TemplateIntentFactory(),
- mFallback);
- }
-
- @Test
- public void create_foreignText() {
- AnnotatorModel.ClassificationResult classificationResult =
- new AnnotatorModel.ClassificationResult(
- TextClassifier.TYPE_ADDRESS,
- 1.0f,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- createRemoteActionTemplates(),
- 0L,
- 0L,
- 0d);
-
- List<LabeledIntent> intents =
- mTemplateClassificationIntentFactory.create(
- InstrumentationRegistry.getContext(),
- TEXT,
- /* foreignText */ true,
- null,
- classificationResult);
-
- assertThat(intents).hasSize(2);
- LabeledIntent labeledIntent = intents.get(0);
- assertThat(labeledIntent.titleWithoutEntity).isEqualTo(TITLE_WITHOUT_ENTITY);
- Intent intent = labeledIntent.intent;
- assertThat(intent.getAction()).isEqualTo(ACTION);
-
- labeledIntent = intents.get(1);
- intent = labeledIntent.intent;
- assertThat(intent.getAction()).isEqualTo(Intent.ACTION_TRANSLATE);
- }
-
- @Test
- public void create_notForeignText() {
- AnnotatorModel.ClassificationResult classificationResult =
- new AnnotatorModel.ClassificationResult(
- TextClassifier.TYPE_ADDRESS,
- 1.0f,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- createRemoteActionTemplates(),
- 0L,
- 0L,
- 0d);
-
- List<LabeledIntent> intents =
- mTemplateClassificationIntentFactory.create(
- InstrumentationRegistry.getContext(),
- TEXT,
- /* foreignText */ false,
- null,
- classificationResult);
-
- assertThat(intents).hasSize(1);
- LabeledIntent labeledIntent = intents.get(0);
- assertThat(labeledIntent.titleWithoutEntity).isEqualTo(TITLE_WITHOUT_ENTITY);
- Intent intent = labeledIntent.intent;
- assertThat(intent.getAction()).isEqualTo(ACTION);
- }
-
- @Test
- public void create_nullTemplate() {
- AnnotatorModel.ClassificationResult classificationResult =
- new AnnotatorModel.ClassificationResult(
- TextClassifier.TYPE_ADDRESS,
- 1.0f,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- 0L,
- 0L,
- 0d);
-
- mTemplateClassificationIntentFactory.create(
- InstrumentationRegistry.getContext(),
- TEXT,
- /* foreignText */ false,
- null,
- classificationResult);
-
-
- verify(mFallback).create(
- same(InstrumentationRegistry.getContext()), eq(TEXT), eq(false), eq(null),
- same(classificationResult));
- }
-
- @Test
- public void create_emptyResult() {
- AnnotatorModel.ClassificationResult classificationResult =
- new AnnotatorModel.ClassificationResult(
- TextClassifier.TYPE_ADDRESS,
- 1.0f,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- new RemoteActionTemplate[0],
- 0L,
- 0L,
- 0d);
-
- mTemplateClassificationIntentFactory.create(
- InstrumentationRegistry.getContext(),
- TEXT,
- /* foreignText */ false,
- null,
- classificationResult);
-
-
- verify(mFallback, never()).create(
- any(Context.class), eq(TEXT), eq(false), eq(null),
- any(AnnotatorModel.ClassificationResult.class));
- }
-
-
- private static RemoteActionTemplate[] createRemoteActionTemplates() {
- return new RemoteActionTemplate[]{
- new RemoteActionTemplate(
- TITLE_WITHOUT_ENTITY,
- null,
- DESCRIPTION,
- DESCRIPTION_WITH_APP_NAME,
- ACTION,
- null,
- null,
- null,
- null,
- null,
- null,
- null
- )
- };
- }
-}
diff --git a/core/tests/coretests/src/android/view/textclassifier/intent/TemplateIntentFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/intent/TemplateIntentFactoryTest.java
deleted file mode 100644
index a33c35811276..000000000000
--- a/core/tests/coretests/src/android/view/textclassifier/intent/TemplateIntentFactoryTest.java
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view.textclassifier.intent;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Intent;
-import android.net.Uri;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.google.android.textclassifier.NamedVariant;
-import com.google.android.textclassifier.RemoteActionTemplate;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.MockitoAnnotations;
-
-import java.util.List;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class TemplateIntentFactoryTest {
-
- private static final String TEXT = "text";
- private static final String TITLE_WITHOUT_ENTITY = "Map";
- private static final String TITLE_WITH_ENTITY = "Map NW14D1";
- private static final String DESCRIPTION = "Check the map";
- private static final String DESCRIPTION_WITH_APP_NAME = "Use %1$s to open map";
- private static final String ACTION = Intent.ACTION_VIEW;
- private static final String DATA = Uri.parse("http://www.android.com").toString();
- private static final String TYPE = "text/html";
- private static final Integer FLAG = Intent.FLAG_ACTIVITY_NEW_TASK;
- private static final String[] CATEGORY =
- new String[]{Intent.CATEGORY_DEFAULT, Intent.CATEGORY_APP_BROWSER};
- private static final String PACKAGE_NAME = "pkg.name";
- private static final String KEY_ONE = "key1";
- private static final String VALUE_ONE = "value1";
- private static final String KEY_TWO = "key2";
- private static final int VALUE_TWO = 42;
-
- private static final NamedVariant[] NAMED_VARIANTS = new NamedVariant[]{
- new NamedVariant(KEY_ONE, VALUE_ONE),
- new NamedVariant(KEY_TWO, VALUE_TWO)
- };
- private static final Integer REQUEST_CODE = 10;
-
- private TemplateIntentFactory mTemplateIntentFactory;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- mTemplateIntentFactory = new TemplateIntentFactory();
- }
-
- @Test
- public void create_full() {
- RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate(
- TITLE_WITHOUT_ENTITY,
- TITLE_WITH_ENTITY,
- DESCRIPTION,
- DESCRIPTION_WITH_APP_NAME,
- ACTION,
- DATA,
- TYPE,
- FLAG,
- CATEGORY,
- /* packageName */ null,
- NAMED_VARIANTS,
- REQUEST_CODE
- );
-
- List<LabeledIntent> intents =
- mTemplateIntentFactory.create(new RemoteActionTemplate[]{remoteActionTemplate});
-
- assertThat(intents).hasSize(1);
- LabeledIntent labeledIntent = intents.get(0);
- assertThat(labeledIntent.titleWithoutEntity).isEqualTo(TITLE_WITHOUT_ENTITY);
- assertThat(labeledIntent.titleWithEntity).isEqualTo(TITLE_WITH_ENTITY);
- assertThat(labeledIntent.description).isEqualTo(DESCRIPTION);
- assertThat(labeledIntent.descriptionWithAppName).isEqualTo(DESCRIPTION_WITH_APP_NAME);
- assertThat(labeledIntent.requestCode).isEqualTo(REQUEST_CODE);
- Intent intent = labeledIntent.intent;
- assertThat(intent.getAction()).isEqualTo(ACTION);
- assertThat(intent.getData().toString()).isEqualTo(DATA);
- assertThat(intent.getType()).isEqualTo(TYPE);
- assertThat(intent.getFlags()).isEqualTo(FLAG);
- assertThat(intent.getCategories()).containsExactly((Object[]) CATEGORY);
- assertThat(intent.getPackage()).isNull();
- assertThat(intent.getStringExtra(KEY_ONE)).isEqualTo(VALUE_ONE);
- assertThat(intent.getIntExtra(KEY_TWO, 0)).isEqualTo(VALUE_TWO);
- }
-
- @Test
- public void normalizesScheme() {
- RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate(
- TITLE_WITHOUT_ENTITY,
- TITLE_WITH_ENTITY,
- DESCRIPTION,
- DESCRIPTION_WITH_APP_NAME,
- ACTION,
- "HTTp://www.android.com",
- TYPE,
- FLAG,
- CATEGORY,
- /* packageName */ null,
- NAMED_VARIANTS,
- REQUEST_CODE
- );
-
- List<LabeledIntent> intents =
- mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate});
-
- String data = intents.get(0).intent.getData().toString();
- assertThat(data).isEqualTo("http://www.android.com");
- }
-
- @Test
- public void create_minimal() {
- RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate(
- TITLE_WITHOUT_ENTITY,
- null,
- DESCRIPTION,
- null,
- ACTION,
- null,
- null,
- null,
- null,
- null,
- null,
- null
- );
-
- List<LabeledIntent> intents =
- mTemplateIntentFactory.create(new RemoteActionTemplate[]{remoteActionTemplate});
-
- assertThat(intents).hasSize(1);
- LabeledIntent labeledIntent = intents.get(0);
- assertThat(labeledIntent.titleWithoutEntity).isEqualTo(TITLE_WITHOUT_ENTITY);
- assertThat(labeledIntent.titleWithEntity).isNull();
- assertThat(labeledIntent.description).isEqualTo(DESCRIPTION);
- assertThat(labeledIntent.requestCode).isEqualTo(
- LabeledIntent.DEFAULT_REQUEST_CODE);
- Intent intent = labeledIntent.intent;
- assertThat(intent.getAction()).isEqualTo(ACTION);
- assertThat(intent.getData()).isNull();
- assertThat(intent.getType()).isNull();
- assertThat(intent.getFlags()).isEqualTo(0);
- assertThat(intent.getCategories()).isNull();
- assertThat(intent.getPackage()).isNull();
- }
-
- @Test
- public void invalidTemplate_nullTemplate() {
- RemoteActionTemplate remoteActionTemplate = null;
-
- List<LabeledIntent> intents =
- mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate});
-
- assertThat(intents).isEmpty();
- }
-
- @Test
- public void invalidTemplate_nonEmptyPackageName() {
- RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate(
- TITLE_WITHOUT_ENTITY,
- TITLE_WITH_ENTITY,
- DESCRIPTION,
- DESCRIPTION_WITH_APP_NAME,
- ACTION,
- DATA,
- TYPE,
- FLAG,
- CATEGORY,
- PACKAGE_NAME,
- NAMED_VARIANTS,
- REQUEST_CODE
- );
-
- List<LabeledIntent> intents =
- mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate});
-
- assertThat(intents).isEmpty();
- }
-
- @Test
- public void invalidTemplate_emptyTitle() {
- RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate(
- null,
- null,
- DESCRIPTION,
- DESCRIPTION_WITH_APP_NAME,
- ACTION,
- null,
- null,
- null,
- null,
- null,
- null,
- null
- );
-
- List<LabeledIntent> intents =
- mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate});
-
- assertThat(intents).isEmpty();
- }
-
- @Test
- public void invalidTemplate_emptyDescription() {
- RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate(
- TITLE_WITHOUT_ENTITY,
- TITLE_WITH_ENTITY,
- null,
- null,
- ACTION,
- null,
- null,
- null,
- null,
- null,
- null,
- null
- );
-
- List<LabeledIntent> intents =
- mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate});
-
- assertThat(intents).isEmpty();
- }
-
- @Test
- public void invalidTemplate_emptyIntentAction() {
- RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate(
- TITLE_WITHOUT_ENTITY,
- TITLE_WITH_ENTITY,
- DESCRIPTION,
- DESCRIPTION_WITH_APP_NAME,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null
- );
-
- List<LabeledIntent> intents =
- mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate});
-
- assertThat(intents).isEmpty();
- }
-}
diff --git a/core/tests/coretests/src/android/view/textclassifier/logging/GenerateLinksLoggerTest.java b/core/tests/coretests/src/android/view/textclassifier/logging/GenerateLinksLoggerTest.java
deleted file mode 100644
index 5e8e5823eefa..000000000000
--- a/core/tests/coretests/src/android/view/textclassifier/logging/GenerateLinksLoggerTest.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier.logging;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.mock;
-
-import android.metrics.LogMaker;
-import android.util.ArrayMap;
-import android.view.textclassifier.GenerateLinksLogger;
-import android.view.textclassifier.TextClassifier;
-import android.view.textclassifier.TextLinks;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class GenerateLinksLoggerTest {
-
- private static final String PACKAGE_NAME = "packageName";
- private static final String ZERO = "0";
- private static final int LATENCY_MS = 123;
-
- @Test
- public void testLogGenerateLinks() {
- final String phoneText = "+12122537077";
- final String addressText = "1600 Amphitheater Parkway, Mountain View, CA";
- final String testText = "The number is " + phoneText + ", the address is " + addressText;
- final int phoneOffset = testText.indexOf(phoneText);
- final int addressOffset = testText.indexOf(addressText);
-
- final Map<String, Float> phoneEntityScores = new ArrayMap<>();
- phoneEntityScores.put(TextClassifier.TYPE_PHONE, 0.9f);
- phoneEntityScores.put(TextClassifier.TYPE_OTHER, 0.1f);
- final Map<String, Float> addressEntityScores = new ArrayMap<>();
- addressEntityScores.put(TextClassifier.TYPE_ADDRESS, 1f);
-
- TextLinks links = new TextLinks.Builder(testText)
- .addLink(phoneOffset, phoneOffset + phoneText.length(), phoneEntityScores)
- .addLink(addressOffset, addressOffset + addressText.length(), addressEntityScores)
- .build();
-
- // Set up mock.
- MetricsLogger metricsLogger = mock(MetricsLogger.class);
- ArgumentCaptor<LogMaker> logMakerCapture = ArgumentCaptor.forClass(LogMaker.class);
- doNothing().when(metricsLogger).write(logMakerCapture.capture());
-
- // Generate the log.
- GenerateLinksLogger logger = new GenerateLinksLogger(1 /* sampleRate */, metricsLogger);
- logger.logGenerateLinks(testText, links, PACKAGE_NAME, LATENCY_MS);
-
- // Validate.
- List<LogMaker> logs = logMakerCapture.getAllValues();
- assertEquals(3, logs.size());
- assertHasLog(logs, "" /* entityType */, 2, phoneText.length() + addressText.length(),
- testText.length());
- assertHasLog(logs, TextClassifier.TYPE_ADDRESS, 1, addressText.length(),
- testText.length());
- assertHasLog(logs, TextClassifier.TYPE_PHONE, 1, phoneText.length(),
- testText.length());
- }
-
- private void assertHasLog(List<LogMaker> logs, String entityType, int numLinks,
- int linkTextLength, int textLength) {
- for (LogMaker log : logs) {
- if (!entityType.equals(getEntityType(log))) {
- continue;
- }
- assertEquals(PACKAGE_NAME, log.getPackageName());
- assertNotNull(Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_CALL_ID)));
- assertEquals(numLinks, getIntValue(log, MetricsEvent.FIELD_LINKIFY_NUM_LINKS));
- assertEquals(linkTextLength, getIntValue(log, MetricsEvent.FIELD_LINKIFY_LINK_LENGTH));
- assertEquals(textLength, getIntValue(log, MetricsEvent.FIELD_LINKIFY_TEXT_LENGTH));
- assertEquals(LATENCY_MS, getIntValue(log, MetricsEvent.FIELD_LINKIFY_LATENCY));
- return;
- }
- fail("No log for entity type \"" + entityType + "\"");
- }
-
- private static String getEntityType(LogMaker log) {
- return Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_ENTITY_TYPE), "");
- }
-
- private static int getIntValue(LogMaker log, int eventField) {
- return Integer.parseInt(Objects.toString(log.getTaggedData(eventField), ZERO));
- }
-}
diff --git a/core/tests/coretests/src/android/view/textclassifier/logging/SmartSelectionEventTrackerTest.java b/core/tests/coretests/src/android/view/textclassifier/logging/SmartSelectionEventTrackerTest.java
deleted file mode 100644
index 321a7f21c307..000000000000
--- a/core/tests/coretests/src/android/view/textclassifier/logging/SmartSelectionEventTrackerTest.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.view.textclassifier.logging;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.google.common.truth.Truth;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class SmartSelectionEventTrackerTest {
-
- @Test
- public void getVersionInfo_valid() {
- String signature = "a|702|b";
- String versionInfo = SmartSelectionEventTracker.SelectionEvent.getVersionInfo(signature);
- Truth.assertThat(versionInfo).isEqualTo("702");
- }
-
- @Test
- public void getVersionInfo_invalid() {
- String signature = "|702";
- String versionInfo = SmartSelectionEventTracker.SelectionEvent.getVersionInfo(signature);
- Truth.assertThat(versionInfo).isEmpty();
- }
-}
diff --git a/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java b/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java
deleted file mode 100644
index 2c540e560f6b..000000000000
--- a/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier.logging;
-
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.CONVERSATION_ACTIONS;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_EVENT_TIME;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_SCORE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_WIDGET_TYPE;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.metrics.LogMaker;
-import android.view.textclassifier.ConversationAction;
-import android.view.textclassifier.TextClassificationContext;
-import android.view.textclassifier.TextClassifierEvent;
-import android.view.textclassifier.TextClassifierEventTronLogger;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.internal.logging.MetricsLogger;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class TextClassifierEventTronLoggerTest {
- private static final String WIDGET_TYPE = "notification";
- private static final String PACKAGE_NAME = "pkg";
-
- @Mock
- private MetricsLogger mMetricsLogger;
- private TextClassifierEventTronLogger mTextClassifierEventTronLogger;
-
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- mTextClassifierEventTronLogger = new TextClassifierEventTronLogger(mMetricsLogger);
- }
-
- @Test
- public void testWriteEvent() {
- TextClassificationContext textClassificationContext =
- new TextClassificationContext.Builder(PACKAGE_NAME, WIDGET_TYPE)
- .build();
- TextClassifierEvent.ConversationActionsEvent textClassifierEvent =
- new TextClassifierEvent.ConversationActionsEvent.Builder(
- TextClassifierEvent.TYPE_SMART_ACTION)
- .setEntityTypes(ConversationAction.TYPE_CALL_PHONE)
- .setScores(0.5f)
- .setEventContext(textClassificationContext)
- .build();
-
- mTextClassifierEventTronLogger.writeEvent(textClassifierEvent);
-
- ArgumentCaptor<LogMaker> captor = ArgumentCaptor.forClass(LogMaker.class);
- Mockito.verify(mMetricsLogger).write(captor.capture());
- LogMaker logMaker = captor.getValue();
- assertThat(logMaker.getCategory()).isEqualTo(CONVERSATION_ACTIONS);
- assertThat(logMaker.getSubtype()).isEqualTo(ACTION_TEXT_SELECTION_SMART_SHARE);
- assertThat(logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE))
- .isEqualTo(ConversationAction.TYPE_CALL_PHONE);
- assertThat((float) logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_SCORE))
- .isWithin(0.00001f).of(0.5f);
- // Never write event time.
- assertThat(logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_EVENT_TIME)).isNull();
- assertThat(logMaker.getPackageName()).isEqualTo(PACKAGE_NAME);
- assertThat(logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_WIDGET_TYPE))
- .isEqualTo(WIDGET_TYPE);
-
- }
-
- @Test
- public void testWriteEvent_unsupportedCategory() {
- TextClassifierEvent.TextSelectionEvent textClassifierEvent =
- new TextClassifierEvent.TextSelectionEvent.Builder(
- TextClassifierEvent.TYPE_SMART_ACTION)
- .build();
-
- mTextClassifierEventTronLogger.writeEvent(textClassifierEvent);
-
- Mockito.verify(mMetricsLogger, Mockito.never()).write(Mockito.any(LogMaker.class));
- }
-}
diff --git a/core/tests/coretests/src/com/android/internal/app/AbstractResolverComparatorTest.java b/core/tests/coretests/src/com/android/internal/app/AbstractResolverComparatorTest.java
index 36dd3e4e72b9..03ed68cd1205 100644
--- a/core/tests/coretests/src/com/android/internal/app/AbstractResolverComparatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/AbstractResolverComparatorTest.java
@@ -98,6 +98,11 @@ public class AbstractResolverComparatorTest {
@Override
void handleResultMessage(Message message) {}
+
+ @Override
+ List<ComponentName> getTopComponentNames(int topK) {
+ return null;
+ }
};
return testComparator;
}
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index 24e96d4b8463..a6cbc1ae466d 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -987,7 +987,8 @@ public class ChooserActivityTest {
/* resolveInfoPresentationGetter */ null),
serviceTargets,
TARGET_TYPE_CHOOSER_TARGET,
- directShareToShortcutInfos)
+ directShareToShortcutInfos,
+ null)
);
// Thread.sleep shouldn't be a thing in an integration test but it's
@@ -1058,7 +1059,8 @@ public class ChooserActivityTest {
/* resolveInfoPresentationGetter */ null),
serviceTargets,
TARGET_TYPE_CHOOSER_TARGET,
- directShareToShortcutInfos)
+ directShareToShortcutInfos,
+ null)
);
// Thread.sleep shouldn't be a thing in an integration test but it's
// necessary here because of the way the code is structured
@@ -1145,7 +1147,8 @@ public class ChooserActivityTest {
/* resolveInfoPresentationGetter */ null),
serviceTargets,
TARGET_TYPE_CHOOSER_TARGET,
- directShareToShortcutInfos)
+ directShareToShortcutInfos,
+ null)
);
// Thread.sleep shouldn't be a thing in an integration test but it's
// necessary here because of the way the code is structured
diff --git a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
index abfb4fb3cedc..8cf146ea801d 100644
--- a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
@@ -48,6 +48,7 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.Settings;
import androidx.test.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
@@ -56,6 +57,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -87,6 +89,7 @@ public class IntentForwarderActivityTest {
static {
MANAGED_PROFILE_INFO.id = 10;
MANAGED_PROFILE_INFO.flags = UserInfo.FLAG_MANAGED_PROFILE;
+ MANAGED_PROFILE_INFO.userType = UserManager.USER_TYPE_PROFILE_MANAGED;
}
private static UserInfo CURRENT_USER_INFO = new UserInfo();
@@ -116,12 +119,21 @@ public class IntentForwarderActivityTest {
private Context mContext;
public static final String PHONE_NUMBER = "123-456-789";
+ private int mDeviceProvisionedInitialValue;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mContext = InstrumentationRegistry.getTargetContext();
sInjector = spy(new TestInjector());
+ mDeviceProvisionedInitialValue = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.DEVICE_PROVISIONED, /* def= */ 0);
+ }
+
+ @After
+ public void tearDown() {
+ Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED,
+ mDeviceProvisionedInitialValue);
}
@Test
@@ -533,6 +545,22 @@ public class IntentForwarderActivityTest {
}
@Test
+ public void shouldSkipDisclosure_duringDeviceSetup() throws RemoteException {
+ setupShouldSkipDisclosureTest();
+ Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED,
+ /* value= */ 0);
+ Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class)
+ .setAction(Intent.ACTION_VIEW)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .setData(Uri.fromParts("http", "apache.org", null));
+
+ mActivityRule.launchActivity(intent);
+
+ verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
+ verify(sInjector, never()).showToast(anyInt(), anyInt());
+ }
+
+ @Test
public void forwardToManagedProfile_LoggingTest() throws Exception {
sComponentName = FORWARD_TO_MANAGED_PROFILE_COMPONENT_NAME;
@@ -590,6 +618,8 @@ public class IntentForwarderActivityTest {
sComponentName = FORWARD_TO_MANAGED_PROFILE_COMPONENT_NAME;
sActivityName = "MyTestActivity";
sPackageName = "test.package.name";
+ Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED,
+ /* value= */ 1);
when(mApplicationInfo.isSystemApp()).thenReturn(true);
// Managed profile exists.
List<UserInfo> profiles = new ArrayList<>();
diff --git a/data/etc/com.android.launcher3.xml b/data/etc/com.android.launcher3.xml
index 337e153722ac..17d614e3409c 100644
--- a/data/etc/com.android.launcher3.xml
+++ b/data/etc/com.android.launcher3.xml
@@ -19,5 +19,6 @@
<permission name="android.permission.BIND_APPWIDGET"/>
<permission name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"/>
<permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
+ <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
</privapp-permissions>
</permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 487a6e912238..5466ac8e2cfa 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -62,6 +62,10 @@ applications that come with the platform
<permission name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
</privapp-permissions>
+ <privapp-permissions package="com.android.launcher3">
+ <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+ </privapp-permissions>
+
<privapp-permissions package="com.android.location.fused">
<permission name="android.permission.INSTALL_LOCATION_PROVIDER"/>
<permission name="android.permission.UPDATE_DEVICE_STATS"/>
diff --git a/keystore/java/android/security/keystore/KeymasterUtils.java b/keystore/java/android/security/keystore/KeymasterUtils.java
index bc933ff13825..670ef5e32f0c 100644
--- a/keystore/java/android/security/keystore/KeymasterUtils.java
+++ b/keystore/java/android/security/keystore/KeymasterUtils.java
@@ -82,6 +82,63 @@ public abstract class KeymasterUtils {
}
}
+ private static void addSids(KeymasterArguments args, UserAuthArgs spec) {
+ // If both biometric and credential are accepted, then just use the root sid from gatekeeper
+ if (spec.getUserAuthenticationType() == (KeyProperties.AUTH_BIOMETRIC_STRONG
+ | KeyProperties.AUTH_DEVICE_CREDENTIAL)) {
+ if (spec.getBoundToSpecificSecureUserId() != GateKeeper.INVALID_SECURE_USER_ID) {
+ args.addUnsignedLong(KeymasterDefs.KM_TAG_USER_SECURE_ID,
+ KeymasterArguments.toUint64(spec.getBoundToSpecificSecureUserId()));
+ } else {
+ // The key is authorized for use for the specified amount of time after the user has
+ // authenticated. Whatever unlocks the secure lock screen should authorize this key.
+ args.addUnsignedLong(KeymasterDefs.KM_TAG_USER_SECURE_ID,
+ KeymasterArguments.toUint64(getRootSid()));
+ }
+ } else {
+ List<Long> sids = new ArrayList<>();
+ if ((spec.getUserAuthenticationType() & KeyProperties.AUTH_BIOMETRIC_STRONG) != 0) {
+ final BiometricManager bm = KeyStore.getApplicationContext()
+ .getSystemService(BiometricManager.class);
+
+ // TODO: Restore permission check in getAuthenticatorIds once the ID is no longer
+ // needed here.
+
+ final long[] biometricSids = bm.getAuthenticatorIds();
+
+ if (biometricSids.length == 0) {
+ throw new IllegalStateException(
+ "At least one biometric must be enrolled to create keys requiring user"
+ + " authentication for every use");
+ }
+
+ if (spec.getBoundToSpecificSecureUserId() != GateKeeper.INVALID_SECURE_USER_ID) {
+ sids.add(spec.getBoundToSpecificSecureUserId());
+ } else if (spec.isInvalidatedByBiometricEnrollment()) {
+ // The biometric-only SIDs will change on biometric enrollment or removal of all
+ // enrolled templates, invalidating the key.
+ for (long sid : biometricSids) {
+ sids.add(sid);
+ }
+ } else {
+ // The root SID will *not* change on fingerprint enrollment, or removal of all
+ // enrolled fingerprints, allowing the key to remain valid.
+ sids.add(getRootSid());
+ }
+ } else if ((spec.getUserAuthenticationType() & KeyProperties.AUTH_DEVICE_CREDENTIAL)
+ != 0) {
+ sids.add(getRootSid());
+ } else {
+ throw new IllegalStateException("Invalid or no authentication type specified.");
+ }
+
+ for (int i = 0; i < sids.size(); i++) {
+ args.addUnsignedLong(KeymasterDefs.KM_TAG_USER_SECURE_ID,
+ KeymasterArguments.toUint64(sids.get(i)));
+ }
+ }
+ }
+
/**
* Adds keymaster arguments to express the key's authorization policy supported by user
* authentication.
@@ -114,40 +171,7 @@ public abstract class KeymasterUtils {
if (spec.getUserAuthenticationValidityDurationSeconds() == 0) {
// Every use of this key needs to be authorized by the user.
- final BiometricManager bm = KeyStore.getApplicationContext()
- .getSystemService(BiometricManager.class);
-
- // TODO: Restore permission check in getAuthenticatorIds once the ID is no longer
- // needed here.
-
- final long[] biometricSids = bm.getAuthenticatorIds();
-
- if (biometricSids.length == 0) {
- throw new IllegalStateException(
- "At least one biometric must be enrolled to create keys requiring user"
- + " authentication for every use");
- }
-
- List<Long> sids = new ArrayList<>();
- if (spec.getBoundToSpecificSecureUserId() != GateKeeper.INVALID_SECURE_USER_ID) {
- sids.add(spec.getBoundToSpecificSecureUserId());
- } else if (spec.isInvalidatedByBiometricEnrollment()) {
- // The biometric-only SIDs will change on biometric enrollment or removal of all
- // enrolled templates, invalidating the key.
- for (long sid : biometricSids) {
- sids.add(sid);
- }
- } else {
- // The root SID will *not* change on fingerprint enrollment, or removal of all
- // enrolled fingerprints, allowing the key to remain valid.
- sids.add(getRootSid());
- }
-
- for (int i = 0; i < sids.size(); i++) {
- args.addUnsignedLong(KeymasterDefs.KM_TAG_USER_SECURE_ID,
- KeymasterArguments.toUint64(sids.get(i)));
- }
-
+ addSids(args, spec);
args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, spec.getUserAuthenticationType());
if (spec.isUserAuthenticationValidWhileOnBody()) {
@@ -155,16 +179,7 @@ public abstract class KeymasterUtils {
+ "supported for keys requiring fingerprint authentication");
}
} else {
- long sid;
- if (spec.getBoundToSpecificSecureUserId() != GateKeeper.INVALID_SECURE_USER_ID) {
- sid = spec.getBoundToSpecificSecureUserId();
- } else {
- // The key is authorized for use for the specified amount of time after the user has
- // authenticated. Whatever unlocks the secure lock screen should authorize this key.
- sid = getRootSid();
- }
- args.addUnsignedLong(KeymasterDefs.KM_TAG_USER_SECURE_ID,
- KeymasterArguments.toUint64(sid));
+ addSids(args, spec);
args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, spec.getUserAuthenticationType());
args.addUnsignedInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT,
spec.getUserAuthenticationValidityDurationSeconds());
diff --git a/libs/androidfw/LocaleDataTables.cpp b/libs/androidfw/LocaleDataTables.cpp
index e748bd886ebc..6c6c5c9f4e22 100644
--- a/libs/androidfw/LocaleDataTables.cpp
+++ b/libs/androidfw/LocaleDataTables.cpp
@@ -15,1474 +15,1478 @@ const char SCRIPT_CODES[][4] = {
/* 11 */ {'C', 'a', 'r', 'i'},
/* 12 */ {'C', 'h', 'a', 'm'},
/* 13 */ {'C', 'h', 'e', 'r'},
- /* 14 */ {'C', 'o', 'p', 't'},
- /* 15 */ {'C', 'p', 'r', 't'},
- /* 16 */ {'C', 'y', 'r', 'l'},
- /* 17 */ {'D', 'e', 'v', 'a'},
- /* 18 */ {'E', 'g', 'y', 'p'},
- /* 19 */ {'E', 't', 'h', 'i'},
- /* 20 */ {'G', 'e', 'o', 'r'},
- /* 21 */ {'G', 'o', 'n', 'g'},
- /* 22 */ {'G', 'o', 'n', 'm'},
- /* 23 */ {'G', 'o', 't', 'h'},
- /* 24 */ {'G', 'r', 'e', 'k'},
- /* 25 */ {'G', 'u', 'j', 'r'},
- /* 26 */ {'G', 'u', 'r', 'u'},
- /* 27 */ {'H', 'a', 'n', 's'},
- /* 28 */ {'H', 'a', 'n', 't'},
- /* 29 */ {'H', 'a', 't', 'r'},
- /* 30 */ {'H', 'e', 'b', 'r'},
- /* 31 */ {'H', 'l', 'u', 'w'},
- /* 32 */ {'H', 'm', 'n', 'g'},
- /* 33 */ {'H', 'm', 'n', 'p'},
- /* 34 */ {'I', 't', 'a', 'l'},
- /* 35 */ {'J', 'p', 'a', 'n'},
- /* 36 */ {'K', 'a', 'l', 'i'},
- /* 37 */ {'K', 'a', 'n', 'a'},
- /* 38 */ {'K', 'h', 'a', 'r'},
- /* 39 */ {'K', 'h', 'm', 'r'},
- /* 40 */ {'K', 'n', 'd', 'a'},
- /* 41 */ {'K', 'o', 'r', 'e'},
- /* 42 */ {'L', 'a', 'n', 'a'},
- /* 43 */ {'L', 'a', 'o', 'o'},
- /* 44 */ {'L', 'a', 't', 'n'},
- /* 45 */ {'L', 'e', 'p', 'c'},
- /* 46 */ {'L', 'i', 'n', 'a'},
- /* 47 */ {'L', 'i', 's', 'u'},
- /* 48 */ {'L', 'y', 'c', 'i'},
- /* 49 */ {'L', 'y', 'd', 'i'},
- /* 50 */ {'M', 'a', 'n', 'd'},
- /* 51 */ {'M', 'a', 'n', 'i'},
- /* 52 */ {'M', 'e', 'r', 'c'},
- /* 53 */ {'M', 'l', 'y', 'm'},
- /* 54 */ {'M', 'o', 'n', 'g'},
- /* 55 */ {'M', 'r', 'o', 'o'},
- /* 56 */ {'M', 'y', 'm', 'r'},
- /* 57 */ {'N', 'a', 'r', 'b'},
- /* 58 */ {'N', 'k', 'o', 'o'},
- /* 59 */ {'N', 's', 'h', 'u'},
- /* 60 */ {'O', 'g', 'a', 'm'},
- /* 61 */ {'O', 'r', 'k', 'h'},
- /* 62 */ {'O', 'r', 'y', 'a'},
- /* 63 */ {'O', 's', 'g', 'e'},
- /* 64 */ {'P', 'a', 'u', 'c'},
- /* 65 */ {'P', 'h', 'l', 'i'},
- /* 66 */ {'P', 'h', 'n', 'x'},
- /* 67 */ {'P', 'l', 'r', 'd'},
- /* 68 */ {'P', 'r', 't', 'i'},
- /* 69 */ {'R', 'u', 'n', 'r'},
- /* 70 */ {'S', 'a', 'm', 'r'},
- /* 71 */ {'S', 'a', 'r', 'b'},
- /* 72 */ {'S', 'a', 'u', 'r'},
- /* 73 */ {'S', 'g', 'n', 'w'},
- /* 74 */ {'S', 'i', 'n', 'h'},
- /* 75 */ {'S', 'o', 'g', 'd'},
- /* 76 */ {'S', 'o', 'r', 'a'},
- /* 77 */ {'S', 'o', 'y', 'o'},
- /* 78 */ {'S', 'y', 'r', 'c'},
- /* 79 */ {'T', 'a', 'l', 'e'},
- /* 80 */ {'T', 'a', 'l', 'u'},
- /* 81 */ {'T', 'a', 'm', 'l'},
- /* 82 */ {'T', 'a', 'n', 'g'},
- /* 83 */ {'T', 'a', 'v', 't'},
- /* 84 */ {'T', 'e', 'l', 'u'},
- /* 85 */ {'T', 'f', 'n', 'g'},
- /* 86 */ {'T', 'h', 'a', 'a'},
- /* 87 */ {'T', 'h', 'a', 'i'},
- /* 88 */ {'T', 'i', 'b', 't'},
- /* 89 */ {'U', 'g', 'a', 'r'},
- /* 90 */ {'V', 'a', 'i', 'i'},
- /* 91 */ {'W', 'c', 'h', 'o'},
- /* 92 */ {'X', 'p', 'e', 'o'},
- /* 93 */ {'X', 's', 'u', 'x'},
- /* 94 */ {'Y', 'i', 'i', 'i'},
- /* 95 */ {'~', '~', '~', 'A'},
- /* 96 */ {'~', '~', '~', 'B'},
+ /* 14 */ {'C', 'h', 'r', 's'},
+ /* 15 */ {'C', 'o', 'p', 't'},
+ /* 16 */ {'C', 'p', 'r', 't'},
+ /* 17 */ {'C', 'y', 'r', 'l'},
+ /* 18 */ {'D', 'e', 'v', 'a'},
+ /* 19 */ {'E', 'g', 'y', 'p'},
+ /* 20 */ {'E', 't', 'h', 'i'},
+ /* 21 */ {'G', 'e', 'o', 'r'},
+ /* 22 */ {'G', 'o', 'n', 'g'},
+ /* 23 */ {'G', 'o', 'n', 'm'},
+ /* 24 */ {'G', 'o', 't', 'h'},
+ /* 25 */ {'G', 'r', 'e', 'k'},
+ /* 26 */ {'G', 'u', 'j', 'r'},
+ /* 27 */ {'G', 'u', 'r', 'u'},
+ /* 28 */ {'H', 'a', 'n', 's'},
+ /* 29 */ {'H', 'a', 'n', 't'},
+ /* 30 */ {'H', 'a', 't', 'r'},
+ /* 31 */ {'H', 'e', 'b', 'r'},
+ /* 32 */ {'H', 'l', 'u', 'w'},
+ /* 33 */ {'H', 'm', 'n', 'g'},
+ /* 34 */ {'H', 'm', 'n', 'p'},
+ /* 35 */ {'I', 't', 'a', 'l'},
+ /* 36 */ {'J', 'p', 'a', 'n'},
+ /* 37 */ {'K', 'a', 'l', 'i'},
+ /* 38 */ {'K', 'a', 'n', 'a'},
+ /* 39 */ {'K', 'h', 'a', 'r'},
+ /* 40 */ {'K', 'h', 'm', 'r'},
+ /* 41 */ {'K', 'i', 't', 's'},
+ /* 42 */ {'K', 'n', 'd', 'a'},
+ /* 43 */ {'K', 'o', 'r', 'e'},
+ /* 44 */ {'L', 'a', 'n', 'a'},
+ /* 45 */ {'L', 'a', 'o', 'o'},
+ /* 46 */ {'L', 'a', 't', 'n'},
+ /* 47 */ {'L', 'e', 'p', 'c'},
+ /* 48 */ {'L', 'i', 'n', 'a'},
+ /* 49 */ {'L', 'i', 's', 'u'},
+ /* 50 */ {'L', 'y', 'c', 'i'},
+ /* 51 */ {'L', 'y', 'd', 'i'},
+ /* 52 */ {'M', 'a', 'n', 'd'},
+ /* 53 */ {'M', 'a', 'n', 'i'},
+ /* 54 */ {'M', 'e', 'r', 'c'},
+ /* 55 */ {'M', 'l', 'y', 'm'},
+ /* 56 */ {'M', 'o', 'n', 'g'},
+ /* 57 */ {'M', 'r', 'o', 'o'},
+ /* 58 */ {'M', 'y', 'm', 'r'},
+ /* 59 */ {'N', 'a', 'r', 'b'},
+ /* 60 */ {'N', 'k', 'o', 'o'},
+ /* 61 */ {'N', 's', 'h', 'u'},
+ /* 62 */ {'O', 'g', 'a', 'm'},
+ /* 63 */ {'O', 'r', 'k', 'h'},
+ /* 64 */ {'O', 'r', 'y', 'a'},
+ /* 65 */ {'O', 's', 'g', 'e'},
+ /* 66 */ {'P', 'a', 'u', 'c'},
+ /* 67 */ {'P', 'h', 'l', 'i'},
+ /* 68 */ {'P', 'h', 'n', 'x'},
+ /* 69 */ {'P', 'l', 'r', 'd'},
+ /* 70 */ {'P', 'r', 't', 'i'},
+ /* 71 */ {'R', 'u', 'n', 'r'},
+ /* 72 */ {'S', 'a', 'm', 'r'},
+ /* 73 */ {'S', 'a', 'r', 'b'},
+ /* 74 */ {'S', 'a', 'u', 'r'},
+ /* 75 */ {'S', 'g', 'n', 'w'},
+ /* 76 */ {'S', 'i', 'n', 'h'},
+ /* 77 */ {'S', 'o', 'g', 'd'},
+ /* 78 */ {'S', 'o', 'r', 'a'},
+ /* 79 */ {'S', 'o', 'y', 'o'},
+ /* 80 */ {'S', 'y', 'r', 'c'},
+ /* 81 */ {'T', 'a', 'l', 'e'},
+ /* 82 */ {'T', 'a', 'l', 'u'},
+ /* 83 */ {'T', 'a', 'm', 'l'},
+ /* 84 */ {'T', 'a', 'n', 'g'},
+ /* 85 */ {'T', 'a', 'v', 't'},
+ /* 86 */ {'T', 'e', 'l', 'u'},
+ /* 87 */ {'T', 'f', 'n', 'g'},
+ /* 88 */ {'T', 'h', 'a', 'a'},
+ /* 89 */ {'T', 'h', 'a', 'i'},
+ /* 90 */ {'T', 'i', 'b', 't'},
+ /* 91 */ {'U', 'g', 'a', 'r'},
+ /* 92 */ {'V', 'a', 'i', 'i'},
+ /* 93 */ {'W', 'c', 'h', 'o'},
+ /* 94 */ {'X', 'p', 'e', 'o'},
+ /* 95 */ {'X', 's', 'u', 'x'},
+ /* 96 */ {'Y', 'i', 'i', 'i'},
+ /* 97 */ {'~', '~', '~', 'A'},
+ /* 98 */ {'~', '~', '~', 'B'},
};
const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({
- {0x61610000u, 44u}, // aa -> Latn
- {0xA0000000u, 44u}, // aai -> Latn
- {0xA8000000u, 44u}, // aak -> Latn
- {0xD0000000u, 44u}, // aau -> Latn
- {0x61620000u, 16u}, // ab -> Cyrl
- {0xA0200000u, 44u}, // abi -> Latn
- {0xC0200000u, 16u}, // abq -> Cyrl
- {0xC4200000u, 44u}, // abr -> Latn
- {0xCC200000u, 44u}, // abt -> Latn
- {0xE0200000u, 44u}, // aby -> Latn
- {0x8C400000u, 44u}, // acd -> Latn
- {0x90400000u, 44u}, // ace -> Latn
- {0x9C400000u, 44u}, // ach -> Latn
- {0x80600000u, 44u}, // ada -> Latn
- {0x90600000u, 44u}, // ade -> Latn
- {0xA4600000u, 44u}, // adj -> Latn
- {0xBC600000u, 88u}, // adp -> Tibt
- {0xE0600000u, 16u}, // ady -> Cyrl
- {0xE4600000u, 44u}, // adz -> Latn
+ {0x61610000u, 46u}, // aa -> Latn
+ {0xA0000000u, 46u}, // aai -> Latn
+ {0xA8000000u, 46u}, // aak -> Latn
+ {0xD0000000u, 46u}, // aau -> Latn
+ {0x61620000u, 17u}, // ab -> Cyrl
+ {0xA0200000u, 46u}, // abi -> Latn
+ {0xC0200000u, 17u}, // abq -> Cyrl
+ {0xC4200000u, 46u}, // abr -> Latn
+ {0xCC200000u, 46u}, // abt -> Latn
+ {0xE0200000u, 46u}, // aby -> Latn
+ {0x8C400000u, 46u}, // acd -> Latn
+ {0x90400000u, 46u}, // ace -> Latn
+ {0x9C400000u, 46u}, // ach -> Latn
+ {0x80600000u, 46u}, // ada -> Latn
+ {0x90600000u, 46u}, // ade -> Latn
+ {0xA4600000u, 46u}, // adj -> Latn
+ {0xBC600000u, 90u}, // adp -> Tibt
+ {0xE0600000u, 17u}, // ady -> Cyrl
+ {0xE4600000u, 46u}, // adz -> Latn
{0x61650000u, 4u}, // ae -> Avst
{0x84800000u, 1u}, // aeb -> Arab
- {0xE0800000u, 44u}, // aey -> Latn
- {0x61660000u, 44u}, // af -> Latn
- {0x88C00000u, 44u}, // agc -> Latn
- {0x8CC00000u, 44u}, // agd -> Latn
- {0x98C00000u, 44u}, // agg -> Latn
- {0xB0C00000u, 44u}, // agm -> Latn
- {0xB8C00000u, 44u}, // ago -> Latn
- {0xC0C00000u, 44u}, // agq -> Latn
- {0x80E00000u, 44u}, // aha -> Latn
- {0xACE00000u, 44u}, // ahl -> Latn
+ {0xE0800000u, 46u}, // aey -> Latn
+ {0x61660000u, 46u}, // af -> Latn
+ {0x88C00000u, 46u}, // agc -> Latn
+ {0x8CC00000u, 46u}, // agd -> Latn
+ {0x98C00000u, 46u}, // agg -> Latn
+ {0xB0C00000u, 46u}, // agm -> Latn
+ {0xB8C00000u, 46u}, // ago -> Latn
+ {0xC0C00000u, 46u}, // agq -> Latn
+ {0x80E00000u, 46u}, // aha -> Latn
+ {0xACE00000u, 46u}, // ahl -> Latn
{0xB8E00000u, 0u}, // aho -> Ahom
- {0x99200000u, 44u}, // ajg -> Latn
- {0x616B0000u, 44u}, // ak -> Latn
- {0xA9400000u, 93u}, // akk -> Xsux
- {0x81600000u, 44u}, // ala -> Latn
- {0xA1600000u, 44u}, // ali -> Latn
- {0xB5600000u, 44u}, // aln -> Latn
- {0xCD600000u, 16u}, // alt -> Cyrl
- {0x616D0000u, 19u}, // am -> Ethi
- {0xB1800000u, 44u}, // amm -> Latn
- {0xB5800000u, 44u}, // amn -> Latn
- {0xB9800000u, 44u}, // amo -> Latn
- {0xBD800000u, 44u}, // amp -> Latn
- {0x616E0000u, 44u}, // an -> Latn
- {0x89A00000u, 44u}, // anc -> Latn
- {0xA9A00000u, 44u}, // ank -> Latn
- {0xB5A00000u, 44u}, // ann -> Latn
- {0xE1A00000u, 44u}, // any -> Latn
- {0xA5C00000u, 44u}, // aoj -> Latn
- {0xB1C00000u, 44u}, // aom -> Latn
- {0xE5C00000u, 44u}, // aoz -> Latn
+ {0x99200000u, 46u}, // ajg -> Latn
+ {0x616B0000u, 46u}, // ak -> Latn
+ {0xA9400000u, 95u}, // akk -> Xsux
+ {0x81600000u, 46u}, // ala -> Latn
+ {0xA1600000u, 46u}, // ali -> Latn
+ {0xB5600000u, 46u}, // aln -> Latn
+ {0xCD600000u, 17u}, // alt -> Cyrl
+ {0x616D0000u, 20u}, // am -> Ethi
+ {0xB1800000u, 46u}, // amm -> Latn
+ {0xB5800000u, 46u}, // amn -> Latn
+ {0xB9800000u, 46u}, // amo -> Latn
+ {0xBD800000u, 46u}, // amp -> Latn
+ {0x616E0000u, 46u}, // an -> Latn
+ {0x89A00000u, 46u}, // anc -> Latn
+ {0xA9A00000u, 46u}, // ank -> Latn
+ {0xB5A00000u, 46u}, // ann -> Latn
+ {0xE1A00000u, 46u}, // any -> Latn
+ {0xA5C00000u, 46u}, // aoj -> Latn
+ {0xB1C00000u, 46u}, // aom -> Latn
+ {0xE5C00000u, 46u}, // aoz -> Latn
{0x89E00000u, 1u}, // apc -> Arab
{0x8DE00000u, 1u}, // apd -> Arab
- {0x91E00000u, 44u}, // ape -> Latn
- {0xC5E00000u, 44u}, // apr -> Latn
- {0xC9E00000u, 44u}, // aps -> Latn
- {0xE5E00000u, 44u}, // apz -> Latn
+ {0x91E00000u, 46u}, // ape -> Latn
+ {0xC5E00000u, 46u}, // apr -> Latn
+ {0xC9E00000u, 46u}, // aps -> Latn
+ {0xE5E00000u, 46u}, // apz -> Latn
{0x61720000u, 1u}, // ar -> Arab
- {0x61725842u, 96u}, // ar-XB -> ~~~B
+ {0x61725842u, 98u}, // ar-XB -> ~~~B
{0x8A200000u, 2u}, // arc -> Armi
- {0x9E200000u, 44u}, // arh -> Latn
- {0xB6200000u, 44u}, // arn -> Latn
- {0xBA200000u, 44u}, // aro -> Latn
+ {0x9E200000u, 46u}, // arh -> Latn
+ {0xB6200000u, 46u}, // arn -> Latn
+ {0xBA200000u, 46u}, // aro -> Latn
{0xC2200000u, 1u}, // arq -> Arab
{0xCA200000u, 1u}, // ars -> Arab
{0xE2200000u, 1u}, // ary -> Arab
{0xE6200000u, 1u}, // arz -> Arab
{0x61730000u, 7u}, // as -> Beng
- {0x82400000u, 44u}, // asa -> Latn
- {0x92400000u, 73u}, // ase -> Sgnw
- {0x9A400000u, 44u}, // asg -> Latn
- {0xBA400000u, 44u}, // aso -> Latn
- {0xCE400000u, 44u}, // ast -> Latn
- {0x82600000u, 44u}, // ata -> Latn
- {0x9A600000u, 44u}, // atg -> Latn
- {0xA6600000u, 44u}, // atj -> Latn
- {0xE2800000u, 44u}, // auy -> Latn
- {0x61760000u, 16u}, // av -> Cyrl
+ {0x82400000u, 46u}, // asa -> Latn
+ {0x92400000u, 75u}, // ase -> Sgnw
+ {0x9A400000u, 46u}, // asg -> Latn
+ {0xBA400000u, 46u}, // aso -> Latn
+ {0xCE400000u, 46u}, // ast -> Latn
+ {0x82600000u, 46u}, // ata -> Latn
+ {0x9A600000u, 46u}, // atg -> Latn
+ {0xA6600000u, 46u}, // atj -> Latn
+ {0xE2800000u, 46u}, // auy -> Latn
+ {0x61760000u, 17u}, // av -> Cyrl
{0xAEA00000u, 1u}, // avl -> Arab
- {0xB6A00000u, 44u}, // avn -> Latn
- {0xCEA00000u, 44u}, // avt -> Latn
- {0xD2A00000u, 44u}, // avu -> Latn
- {0x82C00000u, 17u}, // awa -> Deva
- {0x86C00000u, 44u}, // awb -> Latn
- {0xBAC00000u, 44u}, // awo -> Latn
- {0xDEC00000u, 44u}, // awx -> Latn
- {0x61790000u, 44u}, // ay -> Latn
- {0x87000000u, 44u}, // ayb -> Latn
- {0x617A0000u, 44u}, // az -> Latn
+ {0xB6A00000u, 46u}, // avn -> Latn
+ {0xCEA00000u, 46u}, // avt -> Latn
+ {0xD2A00000u, 46u}, // avu -> Latn
+ {0x82C00000u, 18u}, // awa -> Deva
+ {0x86C00000u, 46u}, // awb -> Latn
+ {0xBAC00000u, 46u}, // awo -> Latn
+ {0xDEC00000u, 46u}, // awx -> Latn
+ {0x61790000u, 46u}, // ay -> Latn
+ {0x87000000u, 46u}, // ayb -> Latn
+ {0x617A0000u, 46u}, // az -> Latn
{0x617A4951u, 1u}, // az-IQ -> Arab
{0x617A4952u, 1u}, // az-IR -> Arab
- {0x617A5255u, 16u}, // az-RU -> Cyrl
- {0x62610000u, 16u}, // ba -> Cyrl
+ {0x617A5255u, 17u}, // az-RU -> Cyrl
+ {0x62610000u, 17u}, // ba -> Cyrl
{0xAC010000u, 1u}, // bal -> Arab
- {0xB4010000u, 44u}, // ban -> Latn
- {0xBC010000u, 17u}, // bap -> Deva
- {0xC4010000u, 44u}, // bar -> Latn
- {0xC8010000u, 44u}, // bas -> Latn
- {0xD4010000u, 44u}, // bav -> Latn
+ {0xB4010000u, 46u}, // ban -> Latn
+ {0xBC010000u, 18u}, // bap -> Deva
+ {0xC4010000u, 46u}, // bar -> Latn
+ {0xC8010000u, 46u}, // bas -> Latn
+ {0xD4010000u, 46u}, // bav -> Latn
{0xDC010000u, 5u}, // bax -> Bamu
- {0x80210000u, 44u}, // bba -> Latn
- {0x84210000u, 44u}, // bbb -> Latn
- {0x88210000u, 44u}, // bbc -> Latn
- {0x8C210000u, 44u}, // bbd -> Latn
- {0xA4210000u, 44u}, // bbj -> Latn
- {0xBC210000u, 44u}, // bbp -> Latn
- {0xC4210000u, 44u}, // bbr -> Latn
- {0x94410000u, 44u}, // bcf -> Latn
- {0x9C410000u, 44u}, // bch -> Latn
- {0xA0410000u, 44u}, // bci -> Latn
- {0xB0410000u, 44u}, // bcm -> Latn
- {0xB4410000u, 44u}, // bcn -> Latn
- {0xB8410000u, 44u}, // bco -> Latn
- {0xC0410000u, 19u}, // bcq -> Ethi
- {0xD0410000u, 44u}, // bcu -> Latn
- {0x8C610000u, 44u}, // bdd -> Latn
- {0x62650000u, 16u}, // be -> Cyrl
- {0x94810000u, 44u}, // bef -> Latn
- {0x9C810000u, 44u}, // beh -> Latn
+ {0x80210000u, 46u}, // bba -> Latn
+ {0x84210000u, 46u}, // bbb -> Latn
+ {0x88210000u, 46u}, // bbc -> Latn
+ {0x8C210000u, 46u}, // bbd -> Latn
+ {0xA4210000u, 46u}, // bbj -> Latn
+ {0xBC210000u, 46u}, // bbp -> Latn
+ {0xC4210000u, 46u}, // bbr -> Latn
+ {0x94410000u, 46u}, // bcf -> Latn
+ {0x9C410000u, 46u}, // bch -> Latn
+ {0xA0410000u, 46u}, // bci -> Latn
+ {0xB0410000u, 46u}, // bcm -> Latn
+ {0xB4410000u, 46u}, // bcn -> Latn
+ {0xB8410000u, 46u}, // bco -> Latn
+ {0xC0410000u, 20u}, // bcq -> Ethi
+ {0xD0410000u, 46u}, // bcu -> Latn
+ {0x8C610000u, 46u}, // bdd -> Latn
+ {0x62650000u, 17u}, // be -> Cyrl
+ {0x94810000u, 46u}, // bef -> Latn
+ {0x9C810000u, 46u}, // beh -> Latn
{0xA4810000u, 1u}, // bej -> Arab
- {0xB0810000u, 44u}, // bem -> Latn
- {0xCC810000u, 44u}, // bet -> Latn
- {0xD8810000u, 44u}, // bew -> Latn
- {0xDC810000u, 44u}, // bex -> Latn
- {0xE4810000u, 44u}, // bez -> Latn
- {0x8CA10000u, 44u}, // bfd -> Latn
- {0xC0A10000u, 81u}, // bfq -> Taml
+ {0xB0810000u, 46u}, // bem -> Latn
+ {0xCC810000u, 46u}, // bet -> Latn
+ {0xD8810000u, 46u}, // bew -> Latn
+ {0xDC810000u, 46u}, // bex -> Latn
+ {0xE4810000u, 46u}, // bez -> Latn
+ {0x8CA10000u, 46u}, // bfd -> Latn
+ {0xC0A10000u, 83u}, // bfq -> Taml
{0xCCA10000u, 1u}, // bft -> Arab
- {0xE0A10000u, 17u}, // bfy -> Deva
- {0x62670000u, 16u}, // bg -> Cyrl
- {0x88C10000u, 17u}, // bgc -> Deva
+ {0xE0A10000u, 18u}, // bfy -> Deva
+ {0x62670000u, 17u}, // bg -> Cyrl
+ {0x88C10000u, 18u}, // bgc -> Deva
{0xB4C10000u, 1u}, // bgn -> Arab
- {0xDCC10000u, 24u}, // bgx -> Grek
- {0x84E10000u, 17u}, // bhb -> Deva
- {0x98E10000u, 44u}, // bhg -> Latn
- {0xA0E10000u, 17u}, // bhi -> Deva
- {0xACE10000u, 44u}, // bhl -> Latn
- {0xB8E10000u, 17u}, // bho -> Deva
- {0xE0E10000u, 44u}, // bhy -> Latn
- {0x62690000u, 44u}, // bi -> Latn
- {0x85010000u, 44u}, // bib -> Latn
- {0x99010000u, 44u}, // big -> Latn
- {0xA9010000u, 44u}, // bik -> Latn
- {0xB1010000u, 44u}, // bim -> Latn
- {0xB5010000u, 44u}, // bin -> Latn
- {0xB9010000u, 44u}, // bio -> Latn
- {0xC1010000u, 44u}, // biq -> Latn
- {0x9D210000u, 44u}, // bjh -> Latn
- {0xA1210000u, 19u}, // bji -> Ethi
- {0xA5210000u, 17u}, // bjj -> Deva
- {0xB5210000u, 44u}, // bjn -> Latn
- {0xB9210000u, 44u}, // bjo -> Latn
- {0xC5210000u, 44u}, // bjr -> Latn
- {0xCD210000u, 44u}, // bjt -> Latn
- {0xE5210000u, 44u}, // bjz -> Latn
- {0x89410000u, 44u}, // bkc -> Latn
- {0xB1410000u, 44u}, // bkm -> Latn
- {0xC1410000u, 44u}, // bkq -> Latn
- {0xD1410000u, 44u}, // bku -> Latn
- {0xD5410000u, 44u}, // bkv -> Latn
- {0xCD610000u, 83u}, // blt -> Tavt
- {0x626D0000u, 44u}, // bm -> Latn
- {0x9D810000u, 44u}, // bmh -> Latn
- {0xA9810000u, 44u}, // bmk -> Latn
- {0xC1810000u, 44u}, // bmq -> Latn
- {0xD1810000u, 44u}, // bmu -> Latn
+ {0xDCC10000u, 25u}, // bgx -> Grek
+ {0x84E10000u, 18u}, // bhb -> Deva
+ {0x98E10000u, 46u}, // bhg -> Latn
+ {0xA0E10000u, 18u}, // bhi -> Deva
+ {0xACE10000u, 46u}, // bhl -> Latn
+ {0xB8E10000u, 18u}, // bho -> Deva
+ {0xE0E10000u, 46u}, // bhy -> Latn
+ {0x62690000u, 46u}, // bi -> Latn
+ {0x85010000u, 46u}, // bib -> Latn
+ {0x99010000u, 46u}, // big -> Latn
+ {0xA9010000u, 46u}, // bik -> Latn
+ {0xB1010000u, 46u}, // bim -> Latn
+ {0xB5010000u, 46u}, // bin -> Latn
+ {0xB9010000u, 46u}, // bio -> Latn
+ {0xC1010000u, 46u}, // biq -> Latn
+ {0x9D210000u, 46u}, // bjh -> Latn
+ {0xA1210000u, 20u}, // bji -> Ethi
+ {0xA5210000u, 18u}, // bjj -> Deva
+ {0xB5210000u, 46u}, // bjn -> Latn
+ {0xB9210000u, 46u}, // bjo -> Latn
+ {0xC5210000u, 46u}, // bjr -> Latn
+ {0xCD210000u, 46u}, // bjt -> Latn
+ {0xE5210000u, 46u}, // bjz -> Latn
+ {0x89410000u, 46u}, // bkc -> Latn
+ {0xB1410000u, 46u}, // bkm -> Latn
+ {0xC1410000u, 46u}, // bkq -> Latn
+ {0xD1410000u, 46u}, // bku -> Latn
+ {0xD5410000u, 46u}, // bkv -> Latn
+ {0xCD610000u, 85u}, // blt -> Tavt
+ {0x626D0000u, 46u}, // bm -> Latn
+ {0x9D810000u, 46u}, // bmh -> Latn
+ {0xA9810000u, 46u}, // bmk -> Latn
+ {0xC1810000u, 46u}, // bmq -> Latn
+ {0xD1810000u, 46u}, // bmu -> Latn
{0x626E0000u, 7u}, // bn -> Beng
- {0x99A10000u, 44u}, // bng -> Latn
- {0xB1A10000u, 44u}, // bnm -> Latn
- {0xBDA10000u, 44u}, // bnp -> Latn
- {0x626F0000u, 88u}, // bo -> Tibt
- {0xA5C10000u, 44u}, // boj -> Latn
- {0xB1C10000u, 44u}, // bom -> Latn
- {0xB5C10000u, 44u}, // bon -> Latn
+ {0x99A10000u, 46u}, // bng -> Latn
+ {0xB1A10000u, 46u}, // bnm -> Latn
+ {0xBDA10000u, 46u}, // bnp -> Latn
+ {0x626F0000u, 90u}, // bo -> Tibt
+ {0xA5C10000u, 46u}, // boj -> Latn
+ {0xB1C10000u, 46u}, // bom -> Latn
+ {0xB5C10000u, 46u}, // bon -> Latn
{0xE1E10000u, 7u}, // bpy -> Beng
- {0x8A010000u, 44u}, // bqc -> Latn
+ {0x8A010000u, 46u}, // bqc -> Latn
{0xA2010000u, 1u}, // bqi -> Arab
- {0xBE010000u, 44u}, // bqp -> Latn
- {0xD6010000u, 44u}, // bqv -> Latn
- {0x62720000u, 44u}, // br -> Latn
- {0x82210000u, 17u}, // bra -> Deva
+ {0xBE010000u, 46u}, // bqp -> Latn
+ {0xD6010000u, 46u}, // bqv -> Latn
+ {0x62720000u, 46u}, // br -> Latn
+ {0x82210000u, 18u}, // bra -> Deva
{0x9E210000u, 1u}, // brh -> Arab
- {0xDE210000u, 17u}, // brx -> Deva
- {0xE6210000u, 44u}, // brz -> Latn
- {0x62730000u, 44u}, // bs -> Latn
- {0xA6410000u, 44u}, // bsj -> Latn
+ {0xDE210000u, 18u}, // brx -> Deva
+ {0xE6210000u, 46u}, // brz -> Latn
+ {0x62730000u, 46u}, // bs -> Latn
+ {0xA6410000u, 46u}, // bsj -> Latn
{0xC2410000u, 6u}, // bsq -> Bass
- {0xCA410000u, 44u}, // bss -> Latn
- {0xCE410000u, 19u}, // bst -> Ethi
- {0xBA610000u, 44u}, // bto -> Latn
- {0xCE610000u, 44u}, // btt -> Latn
- {0xD6610000u, 17u}, // btv -> Deva
- {0x82810000u, 16u}, // bua -> Cyrl
- {0x8A810000u, 44u}, // buc -> Latn
- {0x8E810000u, 44u}, // bud -> Latn
- {0x9A810000u, 44u}, // bug -> Latn
- {0xAA810000u, 44u}, // buk -> Latn
- {0xB2810000u, 44u}, // bum -> Latn
- {0xBA810000u, 44u}, // buo -> Latn
- {0xCA810000u, 44u}, // bus -> Latn
- {0xD2810000u, 44u}, // buu -> Latn
- {0x86A10000u, 44u}, // bvb -> Latn
- {0x8EC10000u, 44u}, // bwd -> Latn
- {0xC6C10000u, 44u}, // bwr -> Latn
- {0x9EE10000u, 44u}, // bxh -> Latn
- {0x93010000u, 44u}, // bye -> Latn
- {0xB7010000u, 19u}, // byn -> Ethi
- {0xC7010000u, 44u}, // byr -> Latn
- {0xCB010000u, 44u}, // bys -> Latn
- {0xD7010000u, 44u}, // byv -> Latn
- {0xDF010000u, 44u}, // byx -> Latn
- {0x83210000u, 44u}, // bza -> Latn
- {0x93210000u, 44u}, // bze -> Latn
- {0x97210000u, 44u}, // bzf -> Latn
- {0x9F210000u, 44u}, // bzh -> Latn
- {0xDB210000u, 44u}, // bzw -> Latn
- {0x63610000u, 44u}, // ca -> Latn
- {0xB4020000u, 44u}, // can -> Latn
- {0xA4220000u, 44u}, // cbj -> Latn
- {0x9C420000u, 44u}, // cch -> Latn
+ {0xCA410000u, 46u}, // bss -> Latn
+ {0xCE410000u, 20u}, // bst -> Ethi
+ {0xBA610000u, 46u}, // bto -> Latn
+ {0xCE610000u, 46u}, // btt -> Latn
+ {0xD6610000u, 18u}, // btv -> Deva
+ {0x82810000u, 17u}, // bua -> Cyrl
+ {0x8A810000u, 46u}, // buc -> Latn
+ {0x8E810000u, 46u}, // bud -> Latn
+ {0x9A810000u, 46u}, // bug -> Latn
+ {0xAA810000u, 46u}, // buk -> Latn
+ {0xB2810000u, 46u}, // bum -> Latn
+ {0xBA810000u, 46u}, // buo -> Latn
+ {0xCA810000u, 46u}, // bus -> Latn
+ {0xD2810000u, 46u}, // buu -> Latn
+ {0x86A10000u, 46u}, // bvb -> Latn
+ {0x8EC10000u, 46u}, // bwd -> Latn
+ {0xC6C10000u, 46u}, // bwr -> Latn
+ {0x9EE10000u, 46u}, // bxh -> Latn
+ {0x93010000u, 46u}, // bye -> Latn
+ {0xB7010000u, 20u}, // byn -> Ethi
+ {0xC7010000u, 46u}, // byr -> Latn
+ {0xCB010000u, 46u}, // bys -> Latn
+ {0xD7010000u, 46u}, // byv -> Latn
+ {0xDF010000u, 46u}, // byx -> Latn
+ {0x83210000u, 46u}, // bza -> Latn
+ {0x93210000u, 46u}, // bze -> Latn
+ {0x97210000u, 46u}, // bzf -> Latn
+ {0x9F210000u, 46u}, // bzh -> Latn
+ {0xDB210000u, 46u}, // bzw -> Latn
+ {0x63610000u, 46u}, // ca -> Latn
+ {0xB4020000u, 46u}, // can -> Latn
+ {0xA4220000u, 46u}, // cbj -> Latn
+ {0x9C420000u, 46u}, // cch -> Latn
{0xBC420000u, 9u}, // ccp -> Cakm
- {0x63650000u, 16u}, // ce -> Cyrl
- {0x84820000u, 44u}, // ceb -> Latn
- {0x80A20000u, 44u}, // cfa -> Latn
- {0x98C20000u, 44u}, // cgg -> Latn
- {0x63680000u, 44u}, // ch -> Latn
- {0xA8E20000u, 44u}, // chk -> Latn
- {0xB0E20000u, 16u}, // chm -> Cyrl
- {0xB8E20000u, 44u}, // cho -> Latn
- {0xBCE20000u, 44u}, // chp -> Latn
+ {0x63650000u, 17u}, // ce -> Cyrl
+ {0x84820000u, 46u}, // ceb -> Latn
+ {0x80A20000u, 46u}, // cfa -> Latn
+ {0x98C20000u, 46u}, // cgg -> Latn
+ {0x63680000u, 46u}, // ch -> Latn
+ {0xA8E20000u, 46u}, // chk -> Latn
+ {0xB0E20000u, 17u}, // chm -> Cyrl
+ {0xB8E20000u, 46u}, // cho -> Latn
+ {0xBCE20000u, 46u}, // chp -> Latn
{0xC4E20000u, 13u}, // chr -> Cher
- {0x89020000u, 44u}, // cic -> Latn
+ {0x89020000u, 46u}, // cic -> Latn
{0x81220000u, 1u}, // cja -> Arab
{0xB1220000u, 12u}, // cjm -> Cham
- {0xD5220000u, 44u}, // cjv -> Latn
+ {0xD5220000u, 46u}, // cjv -> Latn
{0x85420000u, 1u}, // ckb -> Arab
- {0xAD420000u, 44u}, // ckl -> Latn
- {0xB9420000u, 44u}, // cko -> Latn
- {0xE1420000u, 44u}, // cky -> Latn
- {0x81620000u, 44u}, // cla -> Latn
- {0x91820000u, 44u}, // cme -> Latn
- {0x99820000u, 77u}, // cmg -> Soyo
- {0x636F0000u, 44u}, // co -> Latn
- {0xBDC20000u, 14u}, // cop -> Copt
- {0xC9E20000u, 44u}, // cps -> Latn
+ {0xAD420000u, 46u}, // ckl -> Latn
+ {0xB9420000u, 46u}, // cko -> Latn
+ {0xE1420000u, 46u}, // cky -> Latn
+ {0x81620000u, 46u}, // cla -> Latn
+ {0x91820000u, 46u}, // cme -> Latn
+ {0x99820000u, 79u}, // cmg -> Soyo
+ {0x636F0000u, 46u}, // co -> Latn
+ {0xBDC20000u, 15u}, // cop -> Copt
+ {0xC9E20000u, 46u}, // cps -> Latn
{0x63720000u, 10u}, // cr -> Cans
- {0x9E220000u, 16u}, // crh -> Cyrl
+ {0x9E220000u, 17u}, // crh -> Cyrl
{0xA6220000u, 10u}, // crj -> Cans
{0xAA220000u, 10u}, // crk -> Cans
{0xAE220000u, 10u}, // crl -> Cans
{0xB2220000u, 10u}, // crm -> Cans
- {0xCA220000u, 44u}, // crs -> Latn
- {0x63730000u, 44u}, // cs -> Latn
- {0x86420000u, 44u}, // csb -> Latn
+ {0xCA220000u, 46u}, // crs -> Latn
+ {0x63730000u, 46u}, // cs -> Latn
+ {0x86420000u, 46u}, // csb -> Latn
{0xDA420000u, 10u}, // csw -> Cans
- {0x8E620000u, 64u}, // ctd -> Pauc
- {0x63750000u, 16u}, // cu -> Cyrl
- {0x63760000u, 16u}, // cv -> Cyrl
- {0x63790000u, 44u}, // cy -> Latn
- {0x64610000u, 44u}, // da -> Latn
- {0x8C030000u, 44u}, // dad -> Latn
- {0x94030000u, 44u}, // daf -> Latn
- {0x98030000u, 44u}, // dag -> Latn
- {0x9C030000u, 44u}, // dah -> Latn
- {0xA8030000u, 44u}, // dak -> Latn
- {0xC4030000u, 16u}, // dar -> Cyrl
- {0xD4030000u, 44u}, // dav -> Latn
- {0x8C230000u, 44u}, // dbd -> Latn
- {0xC0230000u, 44u}, // dbq -> Latn
+ {0x8E620000u, 66u}, // ctd -> Pauc
+ {0x63750000u, 17u}, // cu -> Cyrl
+ {0x63760000u, 17u}, // cv -> Cyrl
+ {0x63790000u, 46u}, // cy -> Latn
+ {0x64610000u, 46u}, // da -> Latn
+ {0x8C030000u, 46u}, // dad -> Latn
+ {0x94030000u, 46u}, // daf -> Latn
+ {0x98030000u, 46u}, // dag -> Latn
+ {0x9C030000u, 46u}, // dah -> Latn
+ {0xA8030000u, 46u}, // dak -> Latn
+ {0xC4030000u, 17u}, // dar -> Cyrl
+ {0xD4030000u, 46u}, // dav -> Latn
+ {0x8C230000u, 46u}, // dbd -> Latn
+ {0xC0230000u, 46u}, // dbq -> Latn
{0x88430000u, 1u}, // dcc -> Arab
- {0xB4630000u, 44u}, // ddn -> Latn
- {0x64650000u, 44u}, // de -> Latn
- {0x8C830000u, 44u}, // ded -> Latn
- {0xB4830000u, 44u}, // den -> Latn
- {0x80C30000u, 44u}, // dga -> Latn
- {0x9CC30000u, 44u}, // dgh -> Latn
- {0xA0C30000u, 44u}, // dgi -> Latn
+ {0xB4630000u, 46u}, // ddn -> Latn
+ {0x64650000u, 46u}, // de -> Latn
+ {0x8C830000u, 46u}, // ded -> Latn
+ {0xB4830000u, 46u}, // den -> Latn
+ {0x80C30000u, 46u}, // dga -> Latn
+ {0x9CC30000u, 46u}, // dgh -> Latn
+ {0xA0C30000u, 46u}, // dgi -> Latn
{0xACC30000u, 1u}, // dgl -> Arab
- {0xC4C30000u, 44u}, // dgr -> Latn
- {0xE4C30000u, 44u}, // dgz -> Latn
- {0x81030000u, 44u}, // dia -> Latn
- {0x91230000u, 44u}, // dje -> Latn
- {0xA5A30000u, 44u}, // dnj -> Latn
- {0x85C30000u, 44u}, // dob -> Latn
+ {0xC4C30000u, 46u}, // dgr -> Latn
+ {0xE4C30000u, 46u}, // dgz -> Latn
+ {0x81030000u, 46u}, // dia -> Latn
+ {0x91230000u, 46u}, // dje -> Latn
+ {0xA5A30000u, 46u}, // dnj -> Latn
+ {0x85C30000u, 46u}, // dob -> Latn
{0xA1C30000u, 1u}, // doi -> Arab
- {0xBDC30000u, 44u}, // dop -> Latn
- {0xD9C30000u, 44u}, // dow -> Latn
- {0x9E230000u, 54u}, // drh -> Mong
- {0xA2230000u, 44u}, // dri -> Latn
- {0xCA230000u, 19u}, // drs -> Ethi
- {0x86430000u, 44u}, // dsb -> Latn
- {0xB2630000u, 44u}, // dtm -> Latn
- {0xBE630000u, 44u}, // dtp -> Latn
- {0xCA630000u, 44u}, // dts -> Latn
- {0xE2630000u, 17u}, // dty -> Deva
- {0x82830000u, 44u}, // dua -> Latn
- {0x8A830000u, 44u}, // duc -> Latn
- {0x8E830000u, 44u}, // dud -> Latn
- {0x9A830000u, 44u}, // dug -> Latn
- {0x64760000u, 86u}, // dv -> Thaa
- {0x82A30000u, 44u}, // dva -> Latn
- {0xDAC30000u, 44u}, // dww -> Latn
- {0xBB030000u, 44u}, // dyo -> Latn
- {0xD3030000u, 44u}, // dyu -> Latn
- {0x647A0000u, 88u}, // dz -> Tibt
- {0x9B230000u, 44u}, // dzg -> Latn
- {0xD0240000u, 44u}, // ebu -> Latn
- {0x65650000u, 44u}, // ee -> Latn
- {0xA0A40000u, 44u}, // efi -> Latn
- {0xACC40000u, 44u}, // egl -> Latn
- {0xE0C40000u, 18u}, // egy -> Egyp
- {0x81440000u, 44u}, // eka -> Latn
- {0xE1440000u, 36u}, // eky -> Kali
- {0x656C0000u, 24u}, // el -> Grek
- {0x81840000u, 44u}, // ema -> Latn
- {0xA1840000u, 44u}, // emi -> Latn
- {0x656E0000u, 44u}, // en -> Latn
- {0x656E5841u, 95u}, // en-XA -> ~~~A
- {0xB5A40000u, 44u}, // enn -> Latn
- {0xC1A40000u, 44u}, // enq -> Latn
- {0x656F0000u, 44u}, // eo -> Latn
- {0xA2240000u, 44u}, // eri -> Latn
- {0x65730000u, 44u}, // es -> Latn
- {0x9A440000u, 22u}, // esg -> Gonm
- {0xD2440000u, 44u}, // esu -> Latn
- {0x65740000u, 44u}, // et -> Latn
- {0xC6640000u, 44u}, // etr -> Latn
- {0xCE640000u, 34u}, // ett -> Ital
- {0xD2640000u, 44u}, // etu -> Latn
- {0xDE640000u, 44u}, // etx -> Latn
- {0x65750000u, 44u}, // eu -> Latn
- {0xBAC40000u, 44u}, // ewo -> Latn
- {0xCEE40000u, 44u}, // ext -> Latn
+ {0xBDC30000u, 46u}, // dop -> Latn
+ {0xD9C30000u, 46u}, // dow -> Latn
+ {0x9E230000u, 56u}, // drh -> Mong
+ {0xA2230000u, 46u}, // dri -> Latn
+ {0xCA230000u, 20u}, // drs -> Ethi
+ {0x86430000u, 46u}, // dsb -> Latn
+ {0xB2630000u, 46u}, // dtm -> Latn
+ {0xBE630000u, 46u}, // dtp -> Latn
+ {0xCA630000u, 46u}, // dts -> Latn
+ {0xE2630000u, 18u}, // dty -> Deva
+ {0x82830000u, 46u}, // dua -> Latn
+ {0x8A830000u, 46u}, // duc -> Latn
+ {0x8E830000u, 46u}, // dud -> Latn
+ {0x9A830000u, 46u}, // dug -> Latn
+ {0x64760000u, 88u}, // dv -> Thaa
+ {0x82A30000u, 46u}, // dva -> Latn
+ {0xDAC30000u, 46u}, // dww -> Latn
+ {0xBB030000u, 46u}, // dyo -> Latn
+ {0xD3030000u, 46u}, // dyu -> Latn
+ {0x647A0000u, 90u}, // dz -> Tibt
+ {0x9B230000u, 46u}, // dzg -> Latn
+ {0xD0240000u, 46u}, // ebu -> Latn
+ {0x65650000u, 46u}, // ee -> Latn
+ {0xA0A40000u, 46u}, // efi -> Latn
+ {0xACC40000u, 46u}, // egl -> Latn
+ {0xE0C40000u, 19u}, // egy -> Egyp
+ {0x81440000u, 46u}, // eka -> Latn
+ {0xE1440000u, 37u}, // eky -> Kali
+ {0x656C0000u, 25u}, // el -> Grek
+ {0x81840000u, 46u}, // ema -> Latn
+ {0xA1840000u, 46u}, // emi -> Latn
+ {0x656E0000u, 46u}, // en -> Latn
+ {0x656E5841u, 97u}, // en-XA -> ~~~A
+ {0xB5A40000u, 46u}, // enn -> Latn
+ {0xC1A40000u, 46u}, // enq -> Latn
+ {0x656F0000u, 46u}, // eo -> Latn
+ {0xA2240000u, 46u}, // eri -> Latn
+ {0x65730000u, 46u}, // es -> Latn
+ {0x9A440000u, 23u}, // esg -> Gonm
+ {0xD2440000u, 46u}, // esu -> Latn
+ {0x65740000u, 46u}, // et -> Latn
+ {0xC6640000u, 46u}, // etr -> Latn
+ {0xCE640000u, 35u}, // ett -> Ital
+ {0xD2640000u, 46u}, // etu -> Latn
+ {0xDE640000u, 46u}, // etx -> Latn
+ {0x65750000u, 46u}, // eu -> Latn
+ {0xBAC40000u, 46u}, // ewo -> Latn
+ {0xCEE40000u, 46u}, // ext -> Latn
{0x66610000u, 1u}, // fa -> Arab
- {0x80050000u, 44u}, // faa -> Latn
- {0x84050000u, 44u}, // fab -> Latn
- {0x98050000u, 44u}, // fag -> Latn
- {0xA0050000u, 44u}, // fai -> Latn
- {0xB4050000u, 44u}, // fan -> Latn
- {0x66660000u, 44u}, // ff -> Latn
- {0xA0A50000u, 44u}, // ffi -> Latn
- {0xB0A50000u, 44u}, // ffm -> Latn
- {0x66690000u, 44u}, // fi -> Latn
+ {0x80050000u, 46u}, // faa -> Latn
+ {0x84050000u, 46u}, // fab -> Latn
+ {0x98050000u, 46u}, // fag -> Latn
+ {0xA0050000u, 46u}, // fai -> Latn
+ {0xB4050000u, 46u}, // fan -> Latn
+ {0x66660000u, 46u}, // ff -> Latn
+ {0xA0A50000u, 46u}, // ffi -> Latn
+ {0xB0A50000u, 46u}, // ffm -> Latn
+ {0x66690000u, 46u}, // fi -> Latn
{0x81050000u, 1u}, // fia -> Arab
- {0xAD050000u, 44u}, // fil -> Latn
- {0xCD050000u, 44u}, // fit -> Latn
- {0x666A0000u, 44u}, // fj -> Latn
- {0xC5650000u, 44u}, // flr -> Latn
- {0xBD850000u, 44u}, // fmp -> Latn
- {0x666F0000u, 44u}, // fo -> Latn
- {0x8DC50000u, 44u}, // fod -> Latn
- {0xB5C50000u, 44u}, // fon -> Latn
- {0xC5C50000u, 44u}, // for -> Latn
- {0x91E50000u, 44u}, // fpe -> Latn
- {0xCA050000u, 44u}, // fqs -> Latn
- {0x66720000u, 44u}, // fr -> Latn
- {0x8A250000u, 44u}, // frc -> Latn
- {0xBE250000u, 44u}, // frp -> Latn
- {0xC6250000u, 44u}, // frr -> Latn
- {0xCA250000u, 44u}, // frs -> Latn
+ {0xAD050000u, 46u}, // fil -> Latn
+ {0xCD050000u, 46u}, // fit -> Latn
+ {0x666A0000u, 46u}, // fj -> Latn
+ {0xC5650000u, 46u}, // flr -> Latn
+ {0xBD850000u, 46u}, // fmp -> Latn
+ {0x666F0000u, 46u}, // fo -> Latn
+ {0x8DC50000u, 46u}, // fod -> Latn
+ {0xB5C50000u, 46u}, // fon -> Latn
+ {0xC5C50000u, 46u}, // for -> Latn
+ {0x91E50000u, 46u}, // fpe -> Latn
+ {0xCA050000u, 46u}, // fqs -> Latn
+ {0x66720000u, 46u}, // fr -> Latn
+ {0x8A250000u, 46u}, // frc -> Latn
+ {0xBE250000u, 46u}, // frp -> Latn
+ {0xC6250000u, 46u}, // frr -> Latn
+ {0xCA250000u, 46u}, // frs -> Latn
{0x86850000u, 1u}, // fub -> Arab
- {0x8E850000u, 44u}, // fud -> Latn
- {0x92850000u, 44u}, // fue -> Latn
- {0x96850000u, 44u}, // fuf -> Latn
- {0x9E850000u, 44u}, // fuh -> Latn
- {0xC2850000u, 44u}, // fuq -> Latn
- {0xC6850000u, 44u}, // fur -> Latn
- {0xD6850000u, 44u}, // fuv -> Latn
- {0xE2850000u, 44u}, // fuy -> Latn
- {0xC6A50000u, 44u}, // fvr -> Latn
- {0x66790000u, 44u}, // fy -> Latn
- {0x67610000u, 44u}, // ga -> Latn
- {0x80060000u, 44u}, // gaa -> Latn
- {0x94060000u, 44u}, // gaf -> Latn
- {0x98060000u, 44u}, // gag -> Latn
- {0x9C060000u, 44u}, // gah -> Latn
- {0xA4060000u, 44u}, // gaj -> Latn
- {0xB0060000u, 44u}, // gam -> Latn
- {0xB4060000u, 27u}, // gan -> Hans
- {0xD8060000u, 44u}, // gaw -> Latn
- {0xE0060000u, 44u}, // gay -> Latn
- {0x80260000u, 44u}, // gba -> Latn
- {0x94260000u, 44u}, // gbf -> Latn
- {0xB0260000u, 17u}, // gbm -> Deva
- {0xE0260000u, 44u}, // gby -> Latn
+ {0x8E850000u, 46u}, // fud -> Latn
+ {0x92850000u, 46u}, // fue -> Latn
+ {0x96850000u, 46u}, // fuf -> Latn
+ {0x9E850000u, 46u}, // fuh -> Latn
+ {0xC2850000u, 46u}, // fuq -> Latn
+ {0xC6850000u, 46u}, // fur -> Latn
+ {0xD6850000u, 46u}, // fuv -> Latn
+ {0xE2850000u, 46u}, // fuy -> Latn
+ {0xC6A50000u, 46u}, // fvr -> Latn
+ {0x66790000u, 46u}, // fy -> Latn
+ {0x67610000u, 46u}, // ga -> Latn
+ {0x80060000u, 46u}, // gaa -> Latn
+ {0x94060000u, 46u}, // gaf -> Latn
+ {0x98060000u, 46u}, // gag -> Latn
+ {0x9C060000u, 46u}, // gah -> Latn
+ {0xA4060000u, 46u}, // gaj -> Latn
+ {0xB0060000u, 46u}, // gam -> Latn
+ {0xB4060000u, 28u}, // gan -> Hans
+ {0xD8060000u, 46u}, // gaw -> Latn
+ {0xE0060000u, 46u}, // gay -> Latn
+ {0x80260000u, 46u}, // gba -> Latn
+ {0x94260000u, 46u}, // gbf -> Latn
+ {0xB0260000u, 18u}, // gbm -> Deva
+ {0xE0260000u, 46u}, // gby -> Latn
{0xE4260000u, 1u}, // gbz -> Arab
- {0xC4460000u, 44u}, // gcr -> Latn
- {0x67640000u, 44u}, // gd -> Latn
- {0x90660000u, 44u}, // gde -> Latn
- {0xB4660000u, 44u}, // gdn -> Latn
- {0xC4660000u, 44u}, // gdr -> Latn
- {0x84860000u, 44u}, // geb -> Latn
- {0xA4860000u, 44u}, // gej -> Latn
- {0xAC860000u, 44u}, // gel -> Latn
- {0xE4860000u, 19u}, // gez -> Ethi
- {0xA8A60000u, 44u}, // gfk -> Latn
- {0xB4C60000u, 17u}, // ggn -> Deva
- {0xC8E60000u, 44u}, // ghs -> Latn
- {0xAD060000u, 44u}, // gil -> Latn
- {0xB1060000u, 44u}, // gim -> Latn
+ {0xC4460000u, 46u}, // gcr -> Latn
+ {0x67640000u, 46u}, // gd -> Latn
+ {0x90660000u, 46u}, // gde -> Latn
+ {0xB4660000u, 46u}, // gdn -> Latn
+ {0xC4660000u, 46u}, // gdr -> Latn
+ {0x84860000u, 46u}, // geb -> Latn
+ {0xA4860000u, 46u}, // gej -> Latn
+ {0xAC860000u, 46u}, // gel -> Latn
+ {0xE4860000u, 20u}, // gez -> Ethi
+ {0xA8A60000u, 46u}, // gfk -> Latn
+ {0xB4C60000u, 18u}, // ggn -> Deva
+ {0xC8E60000u, 46u}, // ghs -> Latn
+ {0xAD060000u, 46u}, // gil -> Latn
+ {0xB1060000u, 46u}, // gim -> Latn
{0xA9260000u, 1u}, // gjk -> Arab
- {0xB5260000u, 44u}, // gjn -> Latn
+ {0xB5260000u, 46u}, // gjn -> Latn
{0xD1260000u, 1u}, // gju -> Arab
- {0xB5460000u, 44u}, // gkn -> Latn
- {0xBD460000u, 44u}, // gkp -> Latn
- {0x676C0000u, 44u}, // gl -> Latn
+ {0xB5460000u, 46u}, // gkn -> Latn
+ {0xBD460000u, 46u}, // gkp -> Latn
+ {0x676C0000u, 46u}, // gl -> Latn
{0xA9660000u, 1u}, // glk -> Arab
- {0xB1860000u, 44u}, // gmm -> Latn
- {0xD5860000u, 19u}, // gmv -> Ethi
- {0x676E0000u, 44u}, // gn -> Latn
- {0x8DA60000u, 44u}, // gnd -> Latn
- {0x99A60000u, 44u}, // gng -> Latn
- {0x8DC60000u, 44u}, // god -> Latn
- {0x95C60000u, 19u}, // gof -> Ethi
- {0xA1C60000u, 44u}, // goi -> Latn
- {0xB1C60000u, 17u}, // gom -> Deva
- {0xB5C60000u, 84u}, // gon -> Telu
- {0xC5C60000u, 44u}, // gor -> Latn
- {0xC9C60000u, 44u}, // gos -> Latn
- {0xCDC60000u, 23u}, // got -> Goth
- {0x86260000u, 44u}, // grb -> Latn
- {0x8A260000u, 15u}, // grc -> Cprt
+ {0xB1860000u, 46u}, // gmm -> Latn
+ {0xD5860000u, 20u}, // gmv -> Ethi
+ {0x676E0000u, 46u}, // gn -> Latn
+ {0x8DA60000u, 46u}, // gnd -> Latn
+ {0x99A60000u, 46u}, // gng -> Latn
+ {0x8DC60000u, 46u}, // god -> Latn
+ {0x95C60000u, 20u}, // gof -> Ethi
+ {0xA1C60000u, 46u}, // goi -> Latn
+ {0xB1C60000u, 18u}, // gom -> Deva
+ {0xB5C60000u, 86u}, // gon -> Telu
+ {0xC5C60000u, 46u}, // gor -> Latn
+ {0xC9C60000u, 46u}, // gos -> Latn
+ {0xCDC60000u, 24u}, // got -> Goth
+ {0x86260000u, 46u}, // grb -> Latn
+ {0x8A260000u, 16u}, // grc -> Cprt
{0xCE260000u, 7u}, // grt -> Beng
- {0xDA260000u, 44u}, // grw -> Latn
- {0xDA460000u, 44u}, // gsw -> Latn
- {0x67750000u, 25u}, // gu -> Gujr
- {0x86860000u, 44u}, // gub -> Latn
- {0x8A860000u, 44u}, // guc -> Latn
- {0x8E860000u, 44u}, // gud -> Latn
- {0xC6860000u, 44u}, // gur -> Latn
- {0xDA860000u, 44u}, // guw -> Latn
- {0xDE860000u, 44u}, // gux -> Latn
- {0xE6860000u, 44u}, // guz -> Latn
- {0x67760000u, 44u}, // gv -> Latn
- {0x96A60000u, 44u}, // gvf -> Latn
- {0xC6A60000u, 17u}, // gvr -> Deva
- {0xCAA60000u, 44u}, // gvs -> Latn
+ {0xDA260000u, 46u}, // grw -> Latn
+ {0xDA460000u, 46u}, // gsw -> Latn
+ {0x67750000u, 26u}, // gu -> Gujr
+ {0x86860000u, 46u}, // gub -> Latn
+ {0x8A860000u, 46u}, // guc -> Latn
+ {0x8E860000u, 46u}, // gud -> Latn
+ {0xC6860000u, 46u}, // gur -> Latn
+ {0xDA860000u, 46u}, // guw -> Latn
+ {0xDE860000u, 46u}, // gux -> Latn
+ {0xE6860000u, 46u}, // guz -> Latn
+ {0x67760000u, 46u}, // gv -> Latn
+ {0x96A60000u, 46u}, // gvf -> Latn
+ {0xC6A60000u, 18u}, // gvr -> Deva
+ {0xCAA60000u, 46u}, // gvs -> Latn
{0x8AC60000u, 1u}, // gwc -> Arab
- {0xA2C60000u, 44u}, // gwi -> Latn
+ {0xA2C60000u, 46u}, // gwi -> Latn
{0xCEC60000u, 1u}, // gwt -> Arab
- {0xA3060000u, 44u}, // gyi -> Latn
- {0x68610000u, 44u}, // ha -> Latn
+ {0xA3060000u, 46u}, // gyi -> Latn
+ {0x68610000u, 46u}, // ha -> Latn
{0x6861434Du, 1u}, // ha-CM -> Arab
{0x68615344u, 1u}, // ha-SD -> Arab
- {0x98070000u, 44u}, // hag -> Latn
- {0xA8070000u, 27u}, // hak -> Hans
- {0xB0070000u, 44u}, // ham -> Latn
- {0xD8070000u, 44u}, // haw -> Latn
+ {0x98070000u, 46u}, // hag -> Latn
+ {0xA8070000u, 28u}, // hak -> Hans
+ {0xB0070000u, 46u}, // ham -> Latn
+ {0xD8070000u, 46u}, // haw -> Latn
{0xE4070000u, 1u}, // haz -> Arab
- {0x84270000u, 44u}, // hbb -> Latn
- {0xE0670000u, 19u}, // hdy -> Ethi
- {0x68650000u, 30u}, // he -> Hebr
- {0xE0E70000u, 44u}, // hhy -> Latn
- {0x68690000u, 17u}, // hi -> Deva
- {0x81070000u, 44u}, // hia -> Latn
- {0x95070000u, 44u}, // hif -> Latn
- {0x99070000u, 44u}, // hig -> Latn
- {0x9D070000u, 44u}, // hih -> Latn
- {0xAD070000u, 44u}, // hil -> Latn
- {0x81670000u, 44u}, // hla -> Latn
- {0xD1670000u, 31u}, // hlu -> Hluw
- {0x8D870000u, 67u}, // hmd -> Plrd
- {0xCD870000u, 44u}, // hmt -> Latn
+ {0x84270000u, 46u}, // hbb -> Latn
+ {0xE0670000u, 20u}, // hdy -> Ethi
+ {0x68650000u, 31u}, // he -> Hebr
+ {0xE0E70000u, 46u}, // hhy -> Latn
+ {0x68690000u, 18u}, // hi -> Deva
+ {0x81070000u, 46u}, // hia -> Latn
+ {0x95070000u, 46u}, // hif -> Latn
+ {0x99070000u, 46u}, // hig -> Latn
+ {0x9D070000u, 46u}, // hih -> Latn
+ {0xAD070000u, 46u}, // hil -> Latn
+ {0x81670000u, 46u}, // hla -> Latn
+ {0xD1670000u, 32u}, // hlu -> Hluw
+ {0x8D870000u, 69u}, // hmd -> Plrd
+ {0xCD870000u, 46u}, // hmt -> Latn
{0x8DA70000u, 1u}, // hnd -> Arab
- {0x91A70000u, 17u}, // hne -> Deva
- {0xA5A70000u, 32u}, // hnj -> Hmng
- {0xB5A70000u, 44u}, // hnn -> Latn
+ {0x91A70000u, 18u}, // hne -> Deva
+ {0xA5A70000u, 33u}, // hnj -> Hmng
+ {0xB5A70000u, 46u}, // hnn -> Latn
{0xB9A70000u, 1u}, // hno -> Arab
- {0x686F0000u, 44u}, // ho -> Latn
- {0x89C70000u, 17u}, // hoc -> Deva
- {0xA5C70000u, 17u}, // hoj -> Deva
- {0xCDC70000u, 44u}, // hot -> Latn
- {0x68720000u, 44u}, // hr -> Latn
- {0x86470000u, 44u}, // hsb -> Latn
- {0xB6470000u, 27u}, // hsn -> Hans
- {0x68740000u, 44u}, // ht -> Latn
- {0x68750000u, 44u}, // hu -> Latn
- {0xA2870000u, 44u}, // hui -> Latn
+ {0x686F0000u, 46u}, // ho -> Latn
+ {0x89C70000u, 18u}, // hoc -> Deva
+ {0xA5C70000u, 18u}, // hoj -> Deva
+ {0xCDC70000u, 46u}, // hot -> Latn
+ {0x68720000u, 46u}, // hr -> Latn
+ {0x86470000u, 46u}, // hsb -> Latn
+ {0xB6470000u, 28u}, // hsn -> Hans
+ {0x68740000u, 46u}, // ht -> Latn
+ {0x68750000u, 46u}, // hu -> Latn
+ {0xA2870000u, 46u}, // hui -> Latn
{0x68790000u, 3u}, // hy -> Armn
- {0x687A0000u, 44u}, // hz -> Latn
- {0x69610000u, 44u}, // ia -> Latn
- {0xB4080000u, 44u}, // ian -> Latn
- {0xC4080000u, 44u}, // iar -> Latn
- {0x80280000u, 44u}, // iba -> Latn
- {0x84280000u, 44u}, // ibb -> Latn
- {0xE0280000u, 44u}, // iby -> Latn
- {0x80480000u, 44u}, // ica -> Latn
- {0x9C480000u, 44u}, // ich -> Latn
- {0x69640000u, 44u}, // id -> Latn
- {0x8C680000u, 44u}, // idd -> Latn
- {0xA0680000u, 44u}, // idi -> Latn
- {0xD0680000u, 44u}, // idu -> Latn
- {0x90A80000u, 44u}, // ife -> Latn
- {0x69670000u, 44u}, // ig -> Latn
- {0x84C80000u, 44u}, // igb -> Latn
- {0x90C80000u, 44u}, // ige -> Latn
- {0x69690000u, 94u}, // ii -> Yiii
- {0xA5280000u, 44u}, // ijj -> Latn
- {0x696B0000u, 44u}, // ik -> Latn
- {0xA9480000u, 44u}, // ikk -> Latn
- {0xCD480000u, 44u}, // ikt -> Latn
- {0xD9480000u, 44u}, // ikw -> Latn
- {0xDD480000u, 44u}, // ikx -> Latn
- {0xB9680000u, 44u}, // ilo -> Latn
- {0xB9880000u, 44u}, // imo -> Latn
- {0x696E0000u, 44u}, // in -> Latn
- {0x9DA80000u, 16u}, // inh -> Cyrl
- {0x696F0000u, 44u}, // io -> Latn
- {0xD1C80000u, 44u}, // iou -> Latn
- {0xA2280000u, 44u}, // iri -> Latn
- {0x69730000u, 44u}, // is -> Latn
- {0x69740000u, 44u}, // it -> Latn
+ {0x687A0000u, 46u}, // hz -> Latn
+ {0x69610000u, 46u}, // ia -> Latn
+ {0xB4080000u, 46u}, // ian -> Latn
+ {0xC4080000u, 46u}, // iar -> Latn
+ {0x80280000u, 46u}, // iba -> Latn
+ {0x84280000u, 46u}, // ibb -> Latn
+ {0xE0280000u, 46u}, // iby -> Latn
+ {0x80480000u, 46u}, // ica -> Latn
+ {0x9C480000u, 46u}, // ich -> Latn
+ {0x69640000u, 46u}, // id -> Latn
+ {0x8C680000u, 46u}, // idd -> Latn
+ {0xA0680000u, 46u}, // idi -> Latn
+ {0xD0680000u, 46u}, // idu -> Latn
+ {0x90A80000u, 46u}, // ife -> Latn
+ {0x69670000u, 46u}, // ig -> Latn
+ {0x84C80000u, 46u}, // igb -> Latn
+ {0x90C80000u, 46u}, // ige -> Latn
+ {0x69690000u, 96u}, // ii -> Yiii
+ {0xA5280000u, 46u}, // ijj -> Latn
+ {0x696B0000u, 46u}, // ik -> Latn
+ {0xA9480000u, 46u}, // ikk -> Latn
+ {0xCD480000u, 46u}, // ikt -> Latn
+ {0xD9480000u, 46u}, // ikw -> Latn
+ {0xDD480000u, 46u}, // ikx -> Latn
+ {0xB9680000u, 46u}, // ilo -> Latn
+ {0xB9880000u, 46u}, // imo -> Latn
+ {0x696E0000u, 46u}, // in -> Latn
+ {0x9DA80000u, 17u}, // inh -> Cyrl
+ {0x696F0000u, 46u}, // io -> Latn
+ {0xD1C80000u, 46u}, // iou -> Latn
+ {0xA2280000u, 46u}, // iri -> Latn
+ {0x69730000u, 46u}, // is -> Latn
+ {0x69740000u, 46u}, // it -> Latn
{0x69750000u, 10u}, // iu -> Cans
- {0x69770000u, 30u}, // iw -> Hebr
- {0xB2C80000u, 44u}, // iwm -> Latn
- {0xCAC80000u, 44u}, // iws -> Latn
- {0x9F280000u, 44u}, // izh -> Latn
- {0xA3280000u, 44u}, // izi -> Latn
- {0x6A610000u, 35u}, // ja -> Jpan
- {0x84090000u, 44u}, // jab -> Latn
- {0xB0090000u, 44u}, // jam -> Latn
- {0xB8290000u, 44u}, // jbo -> Latn
- {0xD0290000u, 44u}, // jbu -> Latn
- {0xB4890000u, 44u}, // jen -> Latn
- {0xA8C90000u, 44u}, // jgk -> Latn
- {0xB8C90000u, 44u}, // jgo -> Latn
- {0x6A690000u, 30u}, // ji -> Hebr
- {0x85090000u, 44u}, // jib -> Latn
- {0x89890000u, 44u}, // jmc -> Latn
- {0xAD890000u, 17u}, // jml -> Deva
- {0x82290000u, 44u}, // jra -> Latn
- {0xCE890000u, 44u}, // jut -> Latn
- {0x6A760000u, 44u}, // jv -> Latn
- {0x6A770000u, 44u}, // jw -> Latn
- {0x6B610000u, 20u}, // ka -> Geor
- {0x800A0000u, 16u}, // kaa -> Cyrl
- {0x840A0000u, 44u}, // kab -> Latn
- {0x880A0000u, 44u}, // kac -> Latn
- {0x8C0A0000u, 44u}, // kad -> Latn
- {0xA00A0000u, 44u}, // kai -> Latn
- {0xA40A0000u, 44u}, // kaj -> Latn
- {0xB00A0000u, 44u}, // kam -> Latn
- {0xB80A0000u, 44u}, // kao -> Latn
- {0x8C2A0000u, 16u}, // kbd -> Cyrl
- {0xB02A0000u, 44u}, // kbm -> Latn
- {0xBC2A0000u, 44u}, // kbp -> Latn
- {0xC02A0000u, 44u}, // kbq -> Latn
- {0xDC2A0000u, 44u}, // kbx -> Latn
+ {0x69770000u, 31u}, // iw -> Hebr
+ {0xB2C80000u, 46u}, // iwm -> Latn
+ {0xCAC80000u, 46u}, // iws -> Latn
+ {0x9F280000u, 46u}, // izh -> Latn
+ {0xA3280000u, 46u}, // izi -> Latn
+ {0x6A610000u, 36u}, // ja -> Jpan
+ {0x84090000u, 46u}, // jab -> Latn
+ {0xB0090000u, 46u}, // jam -> Latn
+ {0xB8290000u, 46u}, // jbo -> Latn
+ {0xD0290000u, 46u}, // jbu -> Latn
+ {0xB4890000u, 46u}, // jen -> Latn
+ {0xA8C90000u, 46u}, // jgk -> Latn
+ {0xB8C90000u, 46u}, // jgo -> Latn
+ {0x6A690000u, 31u}, // ji -> Hebr
+ {0x85090000u, 46u}, // jib -> Latn
+ {0x89890000u, 46u}, // jmc -> Latn
+ {0xAD890000u, 18u}, // jml -> Deva
+ {0x82290000u, 46u}, // jra -> Latn
+ {0xCE890000u, 46u}, // jut -> Latn
+ {0x6A760000u, 46u}, // jv -> Latn
+ {0x6A770000u, 46u}, // jw -> Latn
+ {0x6B610000u, 21u}, // ka -> Geor
+ {0x800A0000u, 17u}, // kaa -> Cyrl
+ {0x840A0000u, 46u}, // kab -> Latn
+ {0x880A0000u, 46u}, // kac -> Latn
+ {0x8C0A0000u, 46u}, // kad -> Latn
+ {0xA00A0000u, 46u}, // kai -> Latn
+ {0xA40A0000u, 46u}, // kaj -> Latn
+ {0xB00A0000u, 46u}, // kam -> Latn
+ {0xB80A0000u, 46u}, // kao -> Latn
+ {0x8C2A0000u, 17u}, // kbd -> Cyrl
+ {0xB02A0000u, 46u}, // kbm -> Latn
+ {0xBC2A0000u, 46u}, // kbp -> Latn
+ {0xC02A0000u, 46u}, // kbq -> Latn
+ {0xDC2A0000u, 46u}, // kbx -> Latn
{0xE02A0000u, 1u}, // kby -> Arab
- {0x984A0000u, 44u}, // kcg -> Latn
- {0xA84A0000u, 44u}, // kck -> Latn
- {0xAC4A0000u, 44u}, // kcl -> Latn
- {0xCC4A0000u, 44u}, // kct -> Latn
- {0x906A0000u, 44u}, // kde -> Latn
+ {0x984A0000u, 46u}, // kcg -> Latn
+ {0xA84A0000u, 46u}, // kck -> Latn
+ {0xAC4A0000u, 46u}, // kcl -> Latn
+ {0xCC4A0000u, 46u}, // kct -> Latn
+ {0x906A0000u, 46u}, // kde -> Latn
{0x9C6A0000u, 1u}, // kdh -> Arab
- {0xAC6A0000u, 44u}, // kdl -> Latn
- {0xCC6A0000u, 87u}, // kdt -> Thai
- {0x808A0000u, 44u}, // kea -> Latn
- {0xB48A0000u, 44u}, // ken -> Latn
- {0xE48A0000u, 44u}, // kez -> Latn
- {0xB8AA0000u, 44u}, // kfo -> Latn
- {0xC4AA0000u, 17u}, // kfr -> Deva
- {0xE0AA0000u, 17u}, // kfy -> Deva
- {0x6B670000u, 44u}, // kg -> Latn
- {0x90CA0000u, 44u}, // kge -> Latn
- {0x94CA0000u, 44u}, // kgf -> Latn
- {0xBCCA0000u, 44u}, // kgp -> Latn
- {0x80EA0000u, 44u}, // kha -> Latn
- {0x84EA0000u, 80u}, // khb -> Talu
- {0xB4EA0000u, 17u}, // khn -> Deva
- {0xC0EA0000u, 44u}, // khq -> Latn
- {0xC8EA0000u, 44u}, // khs -> Latn
- {0xCCEA0000u, 56u}, // kht -> Mymr
+ {0xAC6A0000u, 46u}, // kdl -> Latn
+ {0xCC6A0000u, 89u}, // kdt -> Thai
+ {0x808A0000u, 46u}, // kea -> Latn
+ {0xB48A0000u, 46u}, // ken -> Latn
+ {0xE48A0000u, 46u}, // kez -> Latn
+ {0xB8AA0000u, 46u}, // kfo -> Latn
+ {0xC4AA0000u, 18u}, // kfr -> Deva
+ {0xE0AA0000u, 18u}, // kfy -> Deva
+ {0x6B670000u, 46u}, // kg -> Latn
+ {0x90CA0000u, 46u}, // kge -> Latn
+ {0x94CA0000u, 46u}, // kgf -> Latn
+ {0xBCCA0000u, 46u}, // kgp -> Latn
+ {0x80EA0000u, 46u}, // kha -> Latn
+ {0x84EA0000u, 82u}, // khb -> Talu
+ {0xB4EA0000u, 18u}, // khn -> Deva
+ {0xC0EA0000u, 46u}, // khq -> Latn
+ {0xC8EA0000u, 46u}, // khs -> Latn
+ {0xCCEA0000u, 58u}, // kht -> Mymr
{0xD8EA0000u, 1u}, // khw -> Arab
- {0xE4EA0000u, 44u}, // khz -> Latn
- {0x6B690000u, 44u}, // ki -> Latn
- {0xA50A0000u, 44u}, // kij -> Latn
- {0xD10A0000u, 44u}, // kiu -> Latn
- {0xD90A0000u, 44u}, // kiw -> Latn
- {0x6B6A0000u, 44u}, // kj -> Latn
- {0x8D2A0000u, 44u}, // kjd -> Latn
- {0x992A0000u, 43u}, // kjg -> Laoo
- {0xC92A0000u, 44u}, // kjs -> Latn
- {0xE12A0000u, 44u}, // kjy -> Latn
- {0x6B6B0000u, 16u}, // kk -> Cyrl
+ {0xE4EA0000u, 46u}, // khz -> Latn
+ {0x6B690000u, 46u}, // ki -> Latn
+ {0xA50A0000u, 46u}, // kij -> Latn
+ {0xD10A0000u, 46u}, // kiu -> Latn
+ {0xD90A0000u, 46u}, // kiw -> Latn
+ {0x6B6A0000u, 46u}, // kj -> Latn
+ {0x8D2A0000u, 46u}, // kjd -> Latn
+ {0x992A0000u, 45u}, // kjg -> Laoo
+ {0xC92A0000u, 46u}, // kjs -> Latn
+ {0xE12A0000u, 46u}, // kjy -> Latn
+ {0x6B6B0000u, 17u}, // kk -> Cyrl
{0x6B6B4146u, 1u}, // kk-AF -> Arab
{0x6B6B434Eu, 1u}, // kk-CN -> Arab
{0x6B6B4952u, 1u}, // kk-IR -> Arab
{0x6B6B4D4Eu, 1u}, // kk-MN -> Arab
- {0x894A0000u, 44u}, // kkc -> Latn
- {0xA54A0000u, 44u}, // kkj -> Latn
- {0x6B6C0000u, 44u}, // kl -> Latn
- {0xB56A0000u, 44u}, // kln -> Latn
- {0xC16A0000u, 44u}, // klq -> Latn
- {0xCD6A0000u, 44u}, // klt -> Latn
- {0xDD6A0000u, 44u}, // klx -> Latn
- {0x6B6D0000u, 39u}, // km -> Khmr
- {0x858A0000u, 44u}, // kmb -> Latn
- {0x9D8A0000u, 44u}, // kmh -> Latn
- {0xB98A0000u, 44u}, // kmo -> Latn
- {0xC98A0000u, 44u}, // kms -> Latn
- {0xD18A0000u, 44u}, // kmu -> Latn
- {0xD98A0000u, 44u}, // kmw -> Latn
- {0x6B6E0000u, 40u}, // kn -> Knda
- {0x95AA0000u, 44u}, // knf -> Latn
- {0xBDAA0000u, 44u}, // knp -> Latn
- {0x6B6F0000u, 41u}, // ko -> Kore
- {0xA1CA0000u, 16u}, // koi -> Cyrl
- {0xA9CA0000u, 17u}, // kok -> Deva
- {0xADCA0000u, 44u}, // kol -> Latn
- {0xC9CA0000u, 44u}, // kos -> Latn
- {0xE5CA0000u, 44u}, // koz -> Latn
- {0x91EA0000u, 44u}, // kpe -> Latn
- {0x95EA0000u, 44u}, // kpf -> Latn
- {0xB9EA0000u, 44u}, // kpo -> Latn
- {0xC5EA0000u, 44u}, // kpr -> Latn
- {0xDDEA0000u, 44u}, // kpx -> Latn
- {0x860A0000u, 44u}, // kqb -> Latn
- {0x960A0000u, 44u}, // kqf -> Latn
- {0xCA0A0000u, 44u}, // kqs -> Latn
- {0xE20A0000u, 19u}, // kqy -> Ethi
- {0x6B720000u, 44u}, // kr -> Latn
- {0x8A2A0000u, 16u}, // krc -> Cyrl
- {0xA22A0000u, 44u}, // kri -> Latn
- {0xA62A0000u, 44u}, // krj -> Latn
- {0xAE2A0000u, 44u}, // krl -> Latn
- {0xCA2A0000u, 44u}, // krs -> Latn
- {0xD22A0000u, 17u}, // kru -> Deva
+ {0x894A0000u, 46u}, // kkc -> Latn
+ {0xA54A0000u, 46u}, // kkj -> Latn
+ {0x6B6C0000u, 46u}, // kl -> Latn
+ {0xB56A0000u, 46u}, // kln -> Latn
+ {0xC16A0000u, 46u}, // klq -> Latn
+ {0xCD6A0000u, 46u}, // klt -> Latn
+ {0xDD6A0000u, 46u}, // klx -> Latn
+ {0x6B6D0000u, 40u}, // km -> Khmr
+ {0x858A0000u, 46u}, // kmb -> Latn
+ {0x9D8A0000u, 46u}, // kmh -> Latn
+ {0xB98A0000u, 46u}, // kmo -> Latn
+ {0xC98A0000u, 46u}, // kms -> Latn
+ {0xD18A0000u, 46u}, // kmu -> Latn
+ {0xD98A0000u, 46u}, // kmw -> Latn
+ {0x6B6E0000u, 42u}, // kn -> Knda
+ {0x95AA0000u, 46u}, // knf -> Latn
+ {0xBDAA0000u, 46u}, // knp -> Latn
+ {0x6B6F0000u, 43u}, // ko -> Kore
+ {0xA1CA0000u, 17u}, // koi -> Cyrl
+ {0xA9CA0000u, 18u}, // kok -> Deva
+ {0xADCA0000u, 46u}, // kol -> Latn
+ {0xC9CA0000u, 46u}, // kos -> Latn
+ {0xE5CA0000u, 46u}, // koz -> Latn
+ {0x91EA0000u, 46u}, // kpe -> Latn
+ {0x95EA0000u, 46u}, // kpf -> Latn
+ {0xB9EA0000u, 46u}, // kpo -> Latn
+ {0xC5EA0000u, 46u}, // kpr -> Latn
+ {0xDDEA0000u, 46u}, // kpx -> Latn
+ {0x860A0000u, 46u}, // kqb -> Latn
+ {0x960A0000u, 46u}, // kqf -> Latn
+ {0xCA0A0000u, 46u}, // kqs -> Latn
+ {0xE20A0000u, 20u}, // kqy -> Ethi
+ {0x6B720000u, 46u}, // kr -> Latn
+ {0x8A2A0000u, 17u}, // krc -> Cyrl
+ {0xA22A0000u, 46u}, // kri -> Latn
+ {0xA62A0000u, 46u}, // krj -> Latn
+ {0xAE2A0000u, 46u}, // krl -> Latn
+ {0xCA2A0000u, 46u}, // krs -> Latn
+ {0xD22A0000u, 18u}, // kru -> Deva
{0x6B730000u, 1u}, // ks -> Arab
- {0x864A0000u, 44u}, // ksb -> Latn
- {0x8E4A0000u, 44u}, // ksd -> Latn
- {0x964A0000u, 44u}, // ksf -> Latn
- {0x9E4A0000u, 44u}, // ksh -> Latn
- {0xA64A0000u, 44u}, // ksj -> Latn
- {0xC64A0000u, 44u}, // ksr -> Latn
- {0x866A0000u, 19u}, // ktb -> Ethi
- {0xB26A0000u, 44u}, // ktm -> Latn
- {0xBA6A0000u, 44u}, // kto -> Latn
- {0xC66A0000u, 44u}, // ktr -> Latn
- {0x6B750000u, 44u}, // ku -> Latn
+ {0x864A0000u, 46u}, // ksb -> Latn
+ {0x8E4A0000u, 46u}, // ksd -> Latn
+ {0x964A0000u, 46u}, // ksf -> Latn
+ {0x9E4A0000u, 46u}, // ksh -> Latn
+ {0xA64A0000u, 46u}, // ksj -> Latn
+ {0xC64A0000u, 46u}, // ksr -> Latn
+ {0x866A0000u, 20u}, // ktb -> Ethi
+ {0xB26A0000u, 46u}, // ktm -> Latn
+ {0xBA6A0000u, 46u}, // kto -> Latn
+ {0xC66A0000u, 46u}, // ktr -> Latn
+ {0x6B750000u, 46u}, // ku -> Latn
{0x6B754952u, 1u}, // ku-IR -> Arab
{0x6B754C42u, 1u}, // ku-LB -> Arab
- {0x868A0000u, 44u}, // kub -> Latn
- {0x8E8A0000u, 44u}, // kud -> Latn
- {0x928A0000u, 44u}, // kue -> Latn
- {0xA68A0000u, 44u}, // kuj -> Latn
- {0xB28A0000u, 16u}, // kum -> Cyrl
- {0xB68A0000u, 44u}, // kun -> Latn
- {0xBE8A0000u, 44u}, // kup -> Latn
- {0xCA8A0000u, 44u}, // kus -> Latn
- {0x6B760000u, 16u}, // kv -> Cyrl
- {0x9AAA0000u, 44u}, // kvg -> Latn
- {0xC6AA0000u, 44u}, // kvr -> Latn
+ {0x868A0000u, 46u}, // kub -> Latn
+ {0x8E8A0000u, 46u}, // kud -> Latn
+ {0x928A0000u, 46u}, // kue -> Latn
+ {0xA68A0000u, 46u}, // kuj -> Latn
+ {0xB28A0000u, 17u}, // kum -> Cyrl
+ {0xB68A0000u, 46u}, // kun -> Latn
+ {0xBE8A0000u, 46u}, // kup -> Latn
+ {0xCA8A0000u, 46u}, // kus -> Latn
+ {0x6B760000u, 17u}, // kv -> Cyrl
+ {0x9AAA0000u, 46u}, // kvg -> Latn
+ {0xC6AA0000u, 46u}, // kvr -> Latn
{0xDEAA0000u, 1u}, // kvx -> Arab
- {0x6B770000u, 44u}, // kw -> Latn
- {0xA6CA0000u, 44u}, // kwj -> Latn
- {0xBACA0000u, 44u}, // kwo -> Latn
- {0xC2CA0000u, 44u}, // kwq -> Latn
- {0x82EA0000u, 44u}, // kxa -> Latn
- {0x8AEA0000u, 19u}, // kxc -> Ethi
- {0x92EA0000u, 44u}, // kxe -> Latn
- {0xB2EA0000u, 87u}, // kxm -> Thai
+ {0x6B770000u, 46u}, // kw -> Latn
+ {0xA6CA0000u, 46u}, // kwj -> Latn
+ {0xBACA0000u, 46u}, // kwo -> Latn
+ {0xC2CA0000u, 46u}, // kwq -> Latn
+ {0x82EA0000u, 46u}, // kxa -> Latn
+ {0x8AEA0000u, 20u}, // kxc -> Ethi
+ {0x92EA0000u, 46u}, // kxe -> Latn
+ {0xB2EA0000u, 89u}, // kxm -> Thai
{0xBEEA0000u, 1u}, // kxp -> Arab
- {0xDAEA0000u, 44u}, // kxw -> Latn
- {0xE6EA0000u, 44u}, // kxz -> Latn
- {0x6B790000u, 16u}, // ky -> Cyrl
+ {0xDAEA0000u, 46u}, // kxw -> Latn
+ {0xE6EA0000u, 46u}, // kxz -> Latn
+ {0x6B790000u, 17u}, // ky -> Cyrl
{0x6B79434Eu, 1u}, // ky-CN -> Arab
- {0x6B795452u, 44u}, // ky-TR -> Latn
- {0x930A0000u, 44u}, // kye -> Latn
- {0xDF0A0000u, 44u}, // kyx -> Latn
- {0xA72A0000u, 44u}, // kzj -> Latn
- {0xC72A0000u, 44u}, // kzr -> Latn
- {0xCF2A0000u, 44u}, // kzt -> Latn
- {0x6C610000u, 44u}, // la -> Latn
- {0x840B0000u, 46u}, // lab -> Lina
- {0x8C0B0000u, 30u}, // lad -> Hebr
- {0x980B0000u, 44u}, // lag -> Latn
+ {0x6B795452u, 46u}, // ky-TR -> Latn
+ {0x930A0000u, 46u}, // kye -> Latn
+ {0xDF0A0000u, 46u}, // kyx -> Latn
+ {0xA72A0000u, 46u}, // kzj -> Latn
+ {0xC72A0000u, 46u}, // kzr -> Latn
+ {0xCF2A0000u, 46u}, // kzt -> Latn
+ {0x6C610000u, 46u}, // la -> Latn
+ {0x840B0000u, 48u}, // lab -> Lina
+ {0x8C0B0000u, 31u}, // lad -> Hebr
+ {0x980B0000u, 46u}, // lag -> Latn
{0x9C0B0000u, 1u}, // lah -> Arab
- {0xA40B0000u, 44u}, // laj -> Latn
- {0xC80B0000u, 44u}, // las -> Latn
- {0x6C620000u, 44u}, // lb -> Latn
- {0x902B0000u, 16u}, // lbe -> Cyrl
- {0xD02B0000u, 44u}, // lbu -> Latn
- {0xD82B0000u, 44u}, // lbw -> Latn
- {0xB04B0000u, 44u}, // lcm -> Latn
- {0xBC4B0000u, 87u}, // lcp -> Thai
- {0x846B0000u, 44u}, // ldb -> Latn
- {0x8C8B0000u, 44u}, // led -> Latn
- {0x908B0000u, 44u}, // lee -> Latn
- {0xB08B0000u, 44u}, // lem -> Latn
- {0xBC8B0000u, 45u}, // lep -> Lepc
- {0xC08B0000u, 44u}, // leq -> Latn
- {0xD08B0000u, 44u}, // leu -> Latn
- {0xE48B0000u, 16u}, // lez -> Cyrl
- {0x6C670000u, 44u}, // lg -> Latn
- {0x98CB0000u, 44u}, // lgg -> Latn
- {0x6C690000u, 44u}, // li -> Latn
- {0x810B0000u, 44u}, // lia -> Latn
- {0x8D0B0000u, 44u}, // lid -> Latn
- {0x950B0000u, 17u}, // lif -> Deva
- {0x990B0000u, 44u}, // lig -> Latn
- {0x9D0B0000u, 44u}, // lih -> Latn
- {0xA50B0000u, 44u}, // lij -> Latn
- {0xC90B0000u, 47u}, // lis -> Lisu
- {0xBD2B0000u, 44u}, // ljp -> Latn
+ {0xA40B0000u, 46u}, // laj -> Latn
+ {0xC80B0000u, 46u}, // las -> Latn
+ {0x6C620000u, 46u}, // lb -> Latn
+ {0x902B0000u, 17u}, // lbe -> Cyrl
+ {0xD02B0000u, 46u}, // lbu -> Latn
+ {0xD82B0000u, 46u}, // lbw -> Latn
+ {0xB04B0000u, 46u}, // lcm -> Latn
+ {0xBC4B0000u, 89u}, // lcp -> Thai
+ {0x846B0000u, 46u}, // ldb -> Latn
+ {0x8C8B0000u, 46u}, // led -> Latn
+ {0x908B0000u, 46u}, // lee -> Latn
+ {0xB08B0000u, 46u}, // lem -> Latn
+ {0xBC8B0000u, 47u}, // lep -> Lepc
+ {0xC08B0000u, 46u}, // leq -> Latn
+ {0xD08B0000u, 46u}, // leu -> Latn
+ {0xE48B0000u, 17u}, // lez -> Cyrl
+ {0x6C670000u, 46u}, // lg -> Latn
+ {0x98CB0000u, 46u}, // lgg -> Latn
+ {0x6C690000u, 46u}, // li -> Latn
+ {0x810B0000u, 46u}, // lia -> Latn
+ {0x8D0B0000u, 46u}, // lid -> Latn
+ {0x950B0000u, 18u}, // lif -> Deva
+ {0x990B0000u, 46u}, // lig -> Latn
+ {0x9D0B0000u, 46u}, // lih -> Latn
+ {0xA50B0000u, 46u}, // lij -> Latn
+ {0xC90B0000u, 49u}, // lis -> Lisu
+ {0xBD2B0000u, 46u}, // ljp -> Latn
{0xA14B0000u, 1u}, // lki -> Arab
- {0xCD4B0000u, 44u}, // lkt -> Latn
- {0x916B0000u, 44u}, // lle -> Latn
- {0xB56B0000u, 44u}, // lln -> Latn
- {0xB58B0000u, 84u}, // lmn -> Telu
- {0xB98B0000u, 44u}, // lmo -> Latn
- {0xBD8B0000u, 44u}, // lmp -> Latn
- {0x6C6E0000u, 44u}, // ln -> Latn
- {0xC9AB0000u, 44u}, // lns -> Latn
- {0xD1AB0000u, 44u}, // lnu -> Latn
- {0x6C6F0000u, 43u}, // lo -> Laoo
- {0xA5CB0000u, 44u}, // loj -> Latn
- {0xA9CB0000u, 44u}, // lok -> Latn
- {0xADCB0000u, 44u}, // lol -> Latn
- {0xC5CB0000u, 44u}, // lor -> Latn
- {0xC9CB0000u, 44u}, // los -> Latn
- {0xE5CB0000u, 44u}, // loz -> Latn
+ {0xCD4B0000u, 46u}, // lkt -> Latn
+ {0x916B0000u, 46u}, // lle -> Latn
+ {0xB56B0000u, 46u}, // lln -> Latn
+ {0xB58B0000u, 86u}, // lmn -> Telu
+ {0xB98B0000u, 46u}, // lmo -> Latn
+ {0xBD8B0000u, 46u}, // lmp -> Latn
+ {0x6C6E0000u, 46u}, // ln -> Latn
+ {0xC9AB0000u, 46u}, // lns -> Latn
+ {0xD1AB0000u, 46u}, // lnu -> Latn
+ {0x6C6F0000u, 45u}, // lo -> Laoo
+ {0xA5CB0000u, 46u}, // loj -> Latn
+ {0xA9CB0000u, 46u}, // lok -> Latn
+ {0xADCB0000u, 46u}, // lol -> Latn
+ {0xC5CB0000u, 46u}, // lor -> Latn
+ {0xC9CB0000u, 46u}, // los -> Latn
+ {0xE5CB0000u, 46u}, // loz -> Latn
{0x8A2B0000u, 1u}, // lrc -> Arab
- {0x6C740000u, 44u}, // lt -> Latn
- {0x9A6B0000u, 44u}, // ltg -> Latn
- {0x6C750000u, 44u}, // lu -> Latn
- {0x828B0000u, 44u}, // lua -> Latn
- {0xBA8B0000u, 44u}, // luo -> Latn
- {0xE28B0000u, 44u}, // luy -> Latn
+ {0x6C740000u, 46u}, // lt -> Latn
+ {0x9A6B0000u, 46u}, // ltg -> Latn
+ {0x6C750000u, 46u}, // lu -> Latn
+ {0x828B0000u, 46u}, // lua -> Latn
+ {0xBA8B0000u, 46u}, // luo -> Latn
+ {0xE28B0000u, 46u}, // luy -> Latn
{0xE68B0000u, 1u}, // luz -> Arab
- {0x6C760000u, 44u}, // lv -> Latn
- {0xAECB0000u, 87u}, // lwl -> Thai
- {0x9F2B0000u, 27u}, // lzh -> Hans
- {0xE72B0000u, 44u}, // lzz -> Latn
- {0x8C0C0000u, 44u}, // mad -> Latn
- {0x940C0000u, 44u}, // maf -> Latn
- {0x980C0000u, 17u}, // mag -> Deva
- {0xA00C0000u, 17u}, // mai -> Deva
- {0xA80C0000u, 44u}, // mak -> Latn
- {0xB40C0000u, 44u}, // man -> Latn
- {0xB40C474Eu, 58u}, // man-GN -> Nkoo
- {0xC80C0000u, 44u}, // mas -> Latn
- {0xD80C0000u, 44u}, // maw -> Latn
- {0xE40C0000u, 44u}, // maz -> Latn
- {0x9C2C0000u, 44u}, // mbh -> Latn
- {0xB82C0000u, 44u}, // mbo -> Latn
- {0xC02C0000u, 44u}, // mbq -> Latn
- {0xD02C0000u, 44u}, // mbu -> Latn
- {0xD82C0000u, 44u}, // mbw -> Latn
- {0xA04C0000u, 44u}, // mci -> Latn
- {0xBC4C0000u, 44u}, // mcp -> Latn
- {0xC04C0000u, 44u}, // mcq -> Latn
- {0xC44C0000u, 44u}, // mcr -> Latn
- {0xD04C0000u, 44u}, // mcu -> Latn
- {0x806C0000u, 44u}, // mda -> Latn
+ {0x6C760000u, 46u}, // lv -> Latn
+ {0xAECB0000u, 89u}, // lwl -> Thai
+ {0x9F2B0000u, 28u}, // lzh -> Hans
+ {0xE72B0000u, 46u}, // lzz -> Latn
+ {0x8C0C0000u, 46u}, // mad -> Latn
+ {0x940C0000u, 46u}, // maf -> Latn
+ {0x980C0000u, 18u}, // mag -> Deva
+ {0xA00C0000u, 18u}, // mai -> Deva
+ {0xA80C0000u, 46u}, // mak -> Latn
+ {0xB40C0000u, 46u}, // man -> Latn
+ {0xB40C474Eu, 60u}, // man-GN -> Nkoo
+ {0xC80C0000u, 46u}, // mas -> Latn
+ {0xD80C0000u, 46u}, // maw -> Latn
+ {0xE40C0000u, 46u}, // maz -> Latn
+ {0x9C2C0000u, 46u}, // mbh -> Latn
+ {0xB82C0000u, 46u}, // mbo -> Latn
+ {0xC02C0000u, 46u}, // mbq -> Latn
+ {0xD02C0000u, 46u}, // mbu -> Latn
+ {0xD82C0000u, 46u}, // mbw -> Latn
+ {0xA04C0000u, 46u}, // mci -> Latn
+ {0xBC4C0000u, 46u}, // mcp -> Latn
+ {0xC04C0000u, 46u}, // mcq -> Latn
+ {0xC44C0000u, 46u}, // mcr -> Latn
+ {0xD04C0000u, 46u}, // mcu -> Latn
+ {0x806C0000u, 46u}, // mda -> Latn
{0x906C0000u, 1u}, // mde -> Arab
- {0x946C0000u, 16u}, // mdf -> Cyrl
- {0x9C6C0000u, 44u}, // mdh -> Latn
- {0xA46C0000u, 44u}, // mdj -> Latn
- {0xC46C0000u, 44u}, // mdr -> Latn
- {0xDC6C0000u, 19u}, // mdx -> Ethi
- {0x8C8C0000u, 44u}, // med -> Latn
- {0x908C0000u, 44u}, // mee -> Latn
- {0xA88C0000u, 44u}, // mek -> Latn
- {0xB48C0000u, 44u}, // men -> Latn
- {0xC48C0000u, 44u}, // mer -> Latn
- {0xCC8C0000u, 44u}, // met -> Latn
- {0xD08C0000u, 44u}, // meu -> Latn
+ {0x946C0000u, 17u}, // mdf -> Cyrl
+ {0x9C6C0000u, 46u}, // mdh -> Latn
+ {0xA46C0000u, 46u}, // mdj -> Latn
+ {0xC46C0000u, 46u}, // mdr -> Latn
+ {0xDC6C0000u, 20u}, // mdx -> Ethi
+ {0x8C8C0000u, 46u}, // med -> Latn
+ {0x908C0000u, 46u}, // mee -> Latn
+ {0xA88C0000u, 46u}, // mek -> Latn
+ {0xB48C0000u, 46u}, // men -> Latn
+ {0xC48C0000u, 46u}, // mer -> Latn
+ {0xCC8C0000u, 46u}, // met -> Latn
+ {0xD08C0000u, 46u}, // meu -> Latn
{0x80AC0000u, 1u}, // mfa -> Arab
- {0x90AC0000u, 44u}, // mfe -> Latn
- {0xB4AC0000u, 44u}, // mfn -> Latn
- {0xB8AC0000u, 44u}, // mfo -> Latn
- {0xC0AC0000u, 44u}, // mfq -> Latn
- {0x6D670000u, 44u}, // mg -> Latn
- {0x9CCC0000u, 44u}, // mgh -> Latn
- {0xACCC0000u, 44u}, // mgl -> Latn
- {0xB8CC0000u, 44u}, // mgo -> Latn
- {0xBCCC0000u, 17u}, // mgp -> Deva
- {0xE0CC0000u, 44u}, // mgy -> Latn
- {0x6D680000u, 44u}, // mh -> Latn
- {0xA0EC0000u, 44u}, // mhi -> Latn
- {0xACEC0000u, 44u}, // mhl -> Latn
- {0x6D690000u, 44u}, // mi -> Latn
- {0x950C0000u, 44u}, // mif -> Latn
- {0xB50C0000u, 44u}, // min -> Latn
- {0xC90C0000u, 29u}, // mis -> Hatr
- {0xD90C0000u, 44u}, // miw -> Latn
- {0x6D6B0000u, 16u}, // mk -> Cyrl
+ {0x90AC0000u, 46u}, // mfe -> Latn
+ {0xB4AC0000u, 46u}, // mfn -> Latn
+ {0xB8AC0000u, 46u}, // mfo -> Latn
+ {0xC0AC0000u, 46u}, // mfq -> Latn
+ {0x6D670000u, 46u}, // mg -> Latn
+ {0x9CCC0000u, 46u}, // mgh -> Latn
+ {0xACCC0000u, 46u}, // mgl -> Latn
+ {0xB8CC0000u, 46u}, // mgo -> Latn
+ {0xBCCC0000u, 18u}, // mgp -> Deva
+ {0xE0CC0000u, 46u}, // mgy -> Latn
+ {0x6D680000u, 46u}, // mh -> Latn
+ {0xA0EC0000u, 46u}, // mhi -> Latn
+ {0xACEC0000u, 46u}, // mhl -> Latn
+ {0x6D690000u, 46u}, // mi -> Latn
+ {0x950C0000u, 46u}, // mif -> Latn
+ {0xB50C0000u, 46u}, // min -> Latn
+ {0xC90C0000u, 30u}, // mis -> Hatr
+ {0xD90C0000u, 46u}, // miw -> Latn
+ {0x6D6B0000u, 17u}, // mk -> Cyrl
{0xA14C0000u, 1u}, // mki -> Arab
- {0xAD4C0000u, 44u}, // mkl -> Latn
- {0xBD4C0000u, 44u}, // mkp -> Latn
- {0xD94C0000u, 44u}, // mkw -> Latn
- {0x6D6C0000u, 53u}, // ml -> Mlym
- {0x916C0000u, 44u}, // mle -> Latn
- {0xBD6C0000u, 44u}, // mlp -> Latn
- {0xC96C0000u, 44u}, // mls -> Latn
- {0xB98C0000u, 44u}, // mmo -> Latn
- {0xD18C0000u, 44u}, // mmu -> Latn
- {0xDD8C0000u, 44u}, // mmx -> Latn
- {0x6D6E0000u, 16u}, // mn -> Cyrl
- {0x6D6E434Eu, 54u}, // mn-CN -> Mong
- {0x81AC0000u, 44u}, // mna -> Latn
- {0x95AC0000u, 44u}, // mnf -> Latn
+ {0xAD4C0000u, 46u}, // mkl -> Latn
+ {0xBD4C0000u, 46u}, // mkp -> Latn
+ {0xD94C0000u, 46u}, // mkw -> Latn
+ {0x6D6C0000u, 55u}, // ml -> Mlym
+ {0x916C0000u, 46u}, // mle -> Latn
+ {0xBD6C0000u, 46u}, // mlp -> Latn
+ {0xC96C0000u, 46u}, // mls -> Latn
+ {0xB98C0000u, 46u}, // mmo -> Latn
+ {0xD18C0000u, 46u}, // mmu -> Latn
+ {0xDD8C0000u, 46u}, // mmx -> Latn
+ {0x6D6E0000u, 17u}, // mn -> Cyrl
+ {0x6D6E434Eu, 56u}, // mn-CN -> Mong
+ {0x81AC0000u, 46u}, // mna -> Latn
+ {0x95AC0000u, 46u}, // mnf -> Latn
{0xA1AC0000u, 7u}, // mni -> Beng
- {0xD9AC0000u, 56u}, // mnw -> Mymr
- {0x6D6F0000u, 44u}, // mo -> Latn
- {0x81CC0000u, 44u}, // moa -> Latn
- {0x91CC0000u, 44u}, // moe -> Latn
- {0x9DCC0000u, 44u}, // moh -> Latn
- {0xC9CC0000u, 44u}, // mos -> Latn
- {0xDDCC0000u, 44u}, // mox -> Latn
- {0xBDEC0000u, 44u}, // mpp -> Latn
- {0xC9EC0000u, 44u}, // mps -> Latn
- {0xCDEC0000u, 44u}, // mpt -> Latn
- {0xDDEC0000u, 44u}, // mpx -> Latn
- {0xAE0C0000u, 44u}, // mql -> Latn
- {0x6D720000u, 17u}, // mr -> Deva
- {0x8E2C0000u, 17u}, // mrd -> Deva
- {0xA62C0000u, 16u}, // mrj -> Cyrl
- {0xBA2C0000u, 55u}, // mro -> Mroo
- {0x6D730000u, 44u}, // ms -> Latn
+ {0xD9AC0000u, 58u}, // mnw -> Mymr
+ {0x6D6F0000u, 46u}, // mo -> Latn
+ {0x81CC0000u, 46u}, // moa -> Latn
+ {0x91CC0000u, 46u}, // moe -> Latn
+ {0x9DCC0000u, 46u}, // moh -> Latn
+ {0xC9CC0000u, 46u}, // mos -> Latn
+ {0xDDCC0000u, 46u}, // mox -> Latn
+ {0xBDEC0000u, 46u}, // mpp -> Latn
+ {0xC9EC0000u, 46u}, // mps -> Latn
+ {0xCDEC0000u, 46u}, // mpt -> Latn
+ {0xDDEC0000u, 46u}, // mpx -> Latn
+ {0xAE0C0000u, 46u}, // mql -> Latn
+ {0x6D720000u, 18u}, // mr -> Deva
+ {0x8E2C0000u, 18u}, // mrd -> Deva
+ {0xA62C0000u, 17u}, // mrj -> Cyrl
+ {0xBA2C0000u, 57u}, // mro -> Mroo
+ {0x6D730000u, 46u}, // ms -> Latn
{0x6D734343u, 1u}, // ms-CC -> Arab
{0x6D734944u, 1u}, // ms-ID -> Arab
- {0x6D740000u, 44u}, // mt -> Latn
- {0x8A6C0000u, 44u}, // mtc -> Latn
- {0x966C0000u, 44u}, // mtf -> Latn
- {0xA26C0000u, 44u}, // mti -> Latn
- {0xC66C0000u, 17u}, // mtr -> Deva
- {0x828C0000u, 44u}, // mua -> Latn
- {0xC68C0000u, 44u}, // mur -> Latn
- {0xCA8C0000u, 44u}, // mus -> Latn
- {0x82AC0000u, 44u}, // mva -> Latn
- {0xB6AC0000u, 44u}, // mvn -> Latn
+ {0x6D740000u, 46u}, // mt -> Latn
+ {0x8A6C0000u, 46u}, // mtc -> Latn
+ {0x966C0000u, 46u}, // mtf -> Latn
+ {0xA26C0000u, 46u}, // mti -> Latn
+ {0xC66C0000u, 18u}, // mtr -> Deva
+ {0x828C0000u, 46u}, // mua -> Latn
+ {0xC68C0000u, 46u}, // mur -> Latn
+ {0xCA8C0000u, 46u}, // mus -> Latn
+ {0x82AC0000u, 46u}, // mva -> Latn
+ {0xB6AC0000u, 46u}, // mvn -> Latn
{0xE2AC0000u, 1u}, // mvy -> Arab
- {0xAACC0000u, 44u}, // mwk -> Latn
- {0xC6CC0000u, 17u}, // mwr -> Deva
- {0xD6CC0000u, 44u}, // mwv -> Latn
- {0xDACC0000u, 33u}, // mww -> Hmnp
- {0x8AEC0000u, 44u}, // mxc -> Latn
- {0xB2EC0000u, 44u}, // mxm -> Latn
- {0x6D790000u, 56u}, // my -> Mymr
- {0xAB0C0000u, 44u}, // myk -> Latn
- {0xB30C0000u, 19u}, // mym -> Ethi
- {0xD70C0000u, 16u}, // myv -> Cyrl
- {0xDB0C0000u, 44u}, // myw -> Latn
- {0xDF0C0000u, 44u}, // myx -> Latn
- {0xE70C0000u, 50u}, // myz -> Mand
- {0xAB2C0000u, 44u}, // mzk -> Latn
- {0xB32C0000u, 44u}, // mzm -> Latn
+ {0xAACC0000u, 46u}, // mwk -> Latn
+ {0xC6CC0000u, 18u}, // mwr -> Deva
+ {0xD6CC0000u, 46u}, // mwv -> Latn
+ {0xDACC0000u, 34u}, // mww -> Hmnp
+ {0x8AEC0000u, 46u}, // mxc -> Latn
+ {0xB2EC0000u, 46u}, // mxm -> Latn
+ {0x6D790000u, 58u}, // my -> Mymr
+ {0xAB0C0000u, 46u}, // myk -> Latn
+ {0xB30C0000u, 20u}, // mym -> Ethi
+ {0xD70C0000u, 17u}, // myv -> Cyrl
+ {0xDB0C0000u, 46u}, // myw -> Latn
+ {0xDF0C0000u, 46u}, // myx -> Latn
+ {0xE70C0000u, 52u}, // myz -> Mand
+ {0xAB2C0000u, 46u}, // mzk -> Latn
+ {0xB32C0000u, 46u}, // mzm -> Latn
{0xB72C0000u, 1u}, // mzn -> Arab
- {0xBF2C0000u, 44u}, // mzp -> Latn
- {0xDB2C0000u, 44u}, // mzw -> Latn
- {0xE72C0000u, 44u}, // mzz -> Latn
- {0x6E610000u, 44u}, // na -> Latn
- {0x880D0000u, 44u}, // nac -> Latn
- {0x940D0000u, 44u}, // naf -> Latn
- {0xA80D0000u, 44u}, // nak -> Latn
- {0xB40D0000u, 27u}, // nan -> Hans
- {0xBC0D0000u, 44u}, // nap -> Latn
- {0xC00D0000u, 44u}, // naq -> Latn
- {0xC80D0000u, 44u}, // nas -> Latn
- {0x6E620000u, 44u}, // nb -> Latn
- {0x804D0000u, 44u}, // nca -> Latn
- {0x904D0000u, 44u}, // nce -> Latn
- {0x944D0000u, 44u}, // ncf -> Latn
- {0x9C4D0000u, 44u}, // nch -> Latn
- {0xB84D0000u, 44u}, // nco -> Latn
- {0xD04D0000u, 44u}, // ncu -> Latn
- {0x6E640000u, 44u}, // nd -> Latn
- {0x886D0000u, 44u}, // ndc -> Latn
- {0xC86D0000u, 44u}, // nds -> Latn
- {0x6E650000u, 17u}, // ne -> Deva
- {0x848D0000u, 44u}, // neb -> Latn
- {0xD88D0000u, 17u}, // new -> Deva
- {0xDC8D0000u, 44u}, // nex -> Latn
- {0xC4AD0000u, 44u}, // nfr -> Latn
- {0x6E670000u, 44u}, // ng -> Latn
- {0x80CD0000u, 44u}, // nga -> Latn
- {0x84CD0000u, 44u}, // ngb -> Latn
- {0xACCD0000u, 44u}, // ngl -> Latn
- {0x84ED0000u, 44u}, // nhb -> Latn
- {0x90ED0000u, 44u}, // nhe -> Latn
- {0xD8ED0000u, 44u}, // nhw -> Latn
- {0x950D0000u, 44u}, // nif -> Latn
- {0xA10D0000u, 44u}, // nii -> Latn
- {0xA50D0000u, 44u}, // nij -> Latn
- {0xB50D0000u, 44u}, // nin -> Latn
- {0xD10D0000u, 44u}, // niu -> Latn
- {0xE10D0000u, 44u}, // niy -> Latn
- {0xE50D0000u, 44u}, // niz -> Latn
- {0xB92D0000u, 44u}, // njo -> Latn
- {0x994D0000u, 44u}, // nkg -> Latn
- {0xB94D0000u, 44u}, // nko -> Latn
- {0x6E6C0000u, 44u}, // nl -> Latn
- {0x998D0000u, 44u}, // nmg -> Latn
- {0xE58D0000u, 44u}, // nmz -> Latn
- {0x6E6E0000u, 44u}, // nn -> Latn
- {0x95AD0000u, 44u}, // nnf -> Latn
- {0x9DAD0000u, 44u}, // nnh -> Latn
- {0xA9AD0000u, 44u}, // nnk -> Latn
- {0xB1AD0000u, 44u}, // nnm -> Latn
- {0xBDAD0000u, 91u}, // nnp -> Wcho
- {0x6E6F0000u, 44u}, // no -> Latn
- {0x8DCD0000u, 42u}, // nod -> Lana
- {0x91CD0000u, 17u}, // noe -> Deva
- {0xB5CD0000u, 69u}, // non -> Runr
- {0xBDCD0000u, 44u}, // nop -> Latn
- {0xD1CD0000u, 44u}, // nou -> Latn
- {0xBA0D0000u, 58u}, // nqo -> Nkoo
- {0x6E720000u, 44u}, // nr -> Latn
- {0x862D0000u, 44u}, // nrb -> Latn
+ {0xBF2C0000u, 46u}, // mzp -> Latn
+ {0xDB2C0000u, 46u}, // mzw -> Latn
+ {0xE72C0000u, 46u}, // mzz -> Latn
+ {0x6E610000u, 46u}, // na -> Latn
+ {0x880D0000u, 46u}, // nac -> Latn
+ {0x940D0000u, 46u}, // naf -> Latn
+ {0xA80D0000u, 46u}, // nak -> Latn
+ {0xB40D0000u, 28u}, // nan -> Hans
+ {0xBC0D0000u, 46u}, // nap -> Latn
+ {0xC00D0000u, 46u}, // naq -> Latn
+ {0xC80D0000u, 46u}, // nas -> Latn
+ {0x6E620000u, 46u}, // nb -> Latn
+ {0x804D0000u, 46u}, // nca -> Latn
+ {0x904D0000u, 46u}, // nce -> Latn
+ {0x944D0000u, 46u}, // ncf -> Latn
+ {0x9C4D0000u, 46u}, // nch -> Latn
+ {0xB84D0000u, 46u}, // nco -> Latn
+ {0xD04D0000u, 46u}, // ncu -> Latn
+ {0x6E640000u, 46u}, // nd -> Latn
+ {0x886D0000u, 46u}, // ndc -> Latn
+ {0xC86D0000u, 46u}, // nds -> Latn
+ {0x6E650000u, 18u}, // ne -> Deva
+ {0x848D0000u, 46u}, // neb -> Latn
+ {0xD88D0000u, 18u}, // new -> Deva
+ {0xDC8D0000u, 46u}, // nex -> Latn
+ {0xC4AD0000u, 46u}, // nfr -> Latn
+ {0x6E670000u, 46u}, // ng -> Latn
+ {0x80CD0000u, 46u}, // nga -> Latn
+ {0x84CD0000u, 46u}, // ngb -> Latn
+ {0xACCD0000u, 46u}, // ngl -> Latn
+ {0x84ED0000u, 46u}, // nhb -> Latn
+ {0x90ED0000u, 46u}, // nhe -> Latn
+ {0xD8ED0000u, 46u}, // nhw -> Latn
+ {0x950D0000u, 46u}, // nif -> Latn
+ {0xA10D0000u, 46u}, // nii -> Latn
+ {0xA50D0000u, 46u}, // nij -> Latn
+ {0xB50D0000u, 46u}, // nin -> Latn
+ {0xD10D0000u, 46u}, // niu -> Latn
+ {0xE10D0000u, 46u}, // niy -> Latn
+ {0xE50D0000u, 46u}, // niz -> Latn
+ {0xB92D0000u, 46u}, // njo -> Latn
+ {0x994D0000u, 46u}, // nkg -> Latn
+ {0xB94D0000u, 46u}, // nko -> Latn
+ {0x6E6C0000u, 46u}, // nl -> Latn
+ {0x998D0000u, 46u}, // nmg -> Latn
+ {0xE58D0000u, 46u}, // nmz -> Latn
+ {0x6E6E0000u, 46u}, // nn -> Latn
+ {0x95AD0000u, 46u}, // nnf -> Latn
+ {0x9DAD0000u, 46u}, // nnh -> Latn
+ {0xA9AD0000u, 46u}, // nnk -> Latn
+ {0xB1AD0000u, 46u}, // nnm -> Latn
+ {0xBDAD0000u, 93u}, // nnp -> Wcho
+ {0x6E6F0000u, 46u}, // no -> Latn
+ {0x8DCD0000u, 44u}, // nod -> Lana
+ {0x91CD0000u, 18u}, // noe -> Deva
+ {0xB5CD0000u, 71u}, // non -> Runr
+ {0xBDCD0000u, 46u}, // nop -> Latn
+ {0xD1CD0000u, 46u}, // nou -> Latn
+ {0xBA0D0000u, 60u}, // nqo -> Nkoo
+ {0x6E720000u, 46u}, // nr -> Latn
+ {0x862D0000u, 46u}, // nrb -> Latn
{0xAA4D0000u, 10u}, // nsk -> Cans
- {0xB64D0000u, 44u}, // nsn -> Latn
- {0xBA4D0000u, 44u}, // nso -> Latn
- {0xCA4D0000u, 44u}, // nss -> Latn
- {0xB26D0000u, 44u}, // ntm -> Latn
- {0xC66D0000u, 44u}, // ntr -> Latn
- {0xA28D0000u, 44u}, // nui -> Latn
- {0xBE8D0000u, 44u}, // nup -> Latn
- {0xCA8D0000u, 44u}, // nus -> Latn
- {0xD68D0000u, 44u}, // nuv -> Latn
- {0xDE8D0000u, 44u}, // nux -> Latn
- {0x6E760000u, 44u}, // nv -> Latn
- {0x86CD0000u, 44u}, // nwb -> Latn
- {0xC2ED0000u, 44u}, // nxq -> Latn
- {0xC6ED0000u, 44u}, // nxr -> Latn
- {0x6E790000u, 44u}, // ny -> Latn
- {0xB30D0000u, 44u}, // nym -> Latn
- {0xB70D0000u, 44u}, // nyn -> Latn
- {0xA32D0000u, 44u}, // nzi -> Latn
- {0x6F630000u, 44u}, // oc -> Latn
- {0x88CE0000u, 44u}, // ogc -> Latn
- {0xC54E0000u, 44u}, // okr -> Latn
- {0xD54E0000u, 44u}, // okv -> Latn
- {0x6F6D0000u, 44u}, // om -> Latn
- {0x99AE0000u, 44u}, // ong -> Latn
- {0xB5AE0000u, 44u}, // onn -> Latn
- {0xC9AE0000u, 44u}, // ons -> Latn
- {0xB1EE0000u, 44u}, // opm -> Latn
- {0x6F720000u, 62u}, // or -> Orya
- {0xBA2E0000u, 44u}, // oro -> Latn
+ {0xB64D0000u, 46u}, // nsn -> Latn
+ {0xBA4D0000u, 46u}, // nso -> Latn
+ {0xCA4D0000u, 46u}, // nss -> Latn
+ {0xB26D0000u, 46u}, // ntm -> Latn
+ {0xC66D0000u, 46u}, // ntr -> Latn
+ {0xA28D0000u, 46u}, // nui -> Latn
+ {0xBE8D0000u, 46u}, // nup -> Latn
+ {0xCA8D0000u, 46u}, // nus -> Latn
+ {0xD68D0000u, 46u}, // nuv -> Latn
+ {0xDE8D0000u, 46u}, // nux -> Latn
+ {0x6E760000u, 46u}, // nv -> Latn
+ {0x86CD0000u, 46u}, // nwb -> Latn
+ {0xC2ED0000u, 46u}, // nxq -> Latn
+ {0xC6ED0000u, 46u}, // nxr -> Latn
+ {0x6E790000u, 46u}, // ny -> Latn
+ {0xB30D0000u, 46u}, // nym -> Latn
+ {0xB70D0000u, 46u}, // nyn -> Latn
+ {0xA32D0000u, 46u}, // nzi -> Latn
+ {0x6F630000u, 46u}, // oc -> Latn
+ {0x88CE0000u, 46u}, // ogc -> Latn
+ {0xC54E0000u, 46u}, // okr -> Latn
+ {0xD54E0000u, 46u}, // okv -> Latn
+ {0x6F6D0000u, 46u}, // om -> Latn
+ {0x99AE0000u, 46u}, // ong -> Latn
+ {0xB5AE0000u, 46u}, // onn -> Latn
+ {0xC9AE0000u, 46u}, // ons -> Latn
+ {0xB1EE0000u, 46u}, // opm -> Latn
+ {0x6F720000u, 64u}, // or -> Orya
+ {0xBA2E0000u, 46u}, // oro -> Latn
{0xD22E0000u, 1u}, // oru -> Arab
- {0x6F730000u, 16u}, // os -> Cyrl
- {0x824E0000u, 63u}, // osa -> Osge
+ {0x6F730000u, 17u}, // os -> Cyrl
+ {0x824E0000u, 65u}, // osa -> Osge
{0x826E0000u, 1u}, // ota -> Arab
- {0xAA6E0000u, 61u}, // otk -> Orkh
- {0xB32E0000u, 44u}, // ozm -> Latn
- {0x70610000u, 26u}, // pa -> Guru
+ {0xAA6E0000u, 63u}, // otk -> Orkh
+ {0xB32E0000u, 46u}, // ozm -> Latn
+ {0x70610000u, 27u}, // pa -> Guru
{0x7061504Bu, 1u}, // pa-PK -> Arab
- {0x980F0000u, 44u}, // pag -> Latn
- {0xAC0F0000u, 65u}, // pal -> Phli
- {0xB00F0000u, 44u}, // pam -> Latn
- {0xBC0F0000u, 44u}, // pap -> Latn
- {0xD00F0000u, 44u}, // pau -> Latn
- {0xA02F0000u, 44u}, // pbi -> Latn
- {0x8C4F0000u, 44u}, // pcd -> Latn
- {0xB04F0000u, 44u}, // pcm -> Latn
- {0x886F0000u, 44u}, // pdc -> Latn
- {0xCC6F0000u, 44u}, // pdt -> Latn
- {0x8C8F0000u, 44u}, // ped -> Latn
- {0xB88F0000u, 92u}, // peo -> Xpeo
- {0xDC8F0000u, 44u}, // pex -> Latn
- {0xACAF0000u, 44u}, // pfl -> Latn
+ {0x980F0000u, 46u}, // pag -> Latn
+ {0xAC0F0000u, 67u}, // pal -> Phli
+ {0xB00F0000u, 46u}, // pam -> Latn
+ {0xBC0F0000u, 46u}, // pap -> Latn
+ {0xD00F0000u, 46u}, // pau -> Latn
+ {0xA02F0000u, 46u}, // pbi -> Latn
+ {0x8C4F0000u, 46u}, // pcd -> Latn
+ {0xB04F0000u, 46u}, // pcm -> Latn
+ {0x886F0000u, 46u}, // pdc -> Latn
+ {0xCC6F0000u, 46u}, // pdt -> Latn
+ {0x8C8F0000u, 46u}, // ped -> Latn
+ {0xB88F0000u, 94u}, // peo -> Xpeo
+ {0xDC8F0000u, 46u}, // pex -> Latn
+ {0xACAF0000u, 46u}, // pfl -> Latn
{0xACEF0000u, 1u}, // phl -> Arab
- {0xB4EF0000u, 66u}, // phn -> Phnx
- {0xAD0F0000u, 44u}, // pil -> Latn
- {0xBD0F0000u, 44u}, // pip -> Latn
+ {0xB4EF0000u, 68u}, // phn -> Phnx
+ {0xAD0F0000u, 46u}, // pil -> Latn
+ {0xBD0F0000u, 46u}, // pip -> Latn
{0x814F0000u, 8u}, // pka -> Brah
- {0xB94F0000u, 44u}, // pko -> Latn
- {0x706C0000u, 44u}, // pl -> Latn
- {0x816F0000u, 44u}, // pla -> Latn
- {0xC98F0000u, 44u}, // pms -> Latn
- {0x99AF0000u, 44u}, // png -> Latn
- {0xB5AF0000u, 44u}, // pnn -> Latn
- {0xCDAF0000u, 24u}, // pnt -> Grek
- {0xB5CF0000u, 44u}, // pon -> Latn
- {0x81EF0000u, 17u}, // ppa -> Deva
- {0xB9EF0000u, 44u}, // ppo -> Latn
- {0x822F0000u, 38u}, // pra -> Khar
+ {0xB94F0000u, 46u}, // pko -> Latn
+ {0x706C0000u, 46u}, // pl -> Latn
+ {0x816F0000u, 46u}, // pla -> Latn
+ {0xC98F0000u, 46u}, // pms -> Latn
+ {0x99AF0000u, 46u}, // png -> Latn
+ {0xB5AF0000u, 46u}, // pnn -> Latn
+ {0xCDAF0000u, 25u}, // pnt -> Grek
+ {0xB5CF0000u, 46u}, // pon -> Latn
+ {0x81EF0000u, 18u}, // ppa -> Deva
+ {0xB9EF0000u, 46u}, // ppo -> Latn
+ {0x822F0000u, 39u}, // pra -> Khar
{0x8E2F0000u, 1u}, // prd -> Arab
- {0x9A2F0000u, 44u}, // prg -> Latn
+ {0x9A2F0000u, 46u}, // prg -> Latn
{0x70730000u, 1u}, // ps -> Arab
- {0xCA4F0000u, 44u}, // pss -> Latn
- {0x70740000u, 44u}, // pt -> Latn
- {0xBE6F0000u, 44u}, // ptp -> Latn
- {0xD28F0000u, 44u}, // puu -> Latn
- {0x82CF0000u, 44u}, // pwa -> Latn
- {0x71750000u, 44u}, // qu -> Latn
- {0x8A900000u, 44u}, // quc -> Latn
- {0x9A900000u, 44u}, // qug -> Latn
- {0xA0110000u, 44u}, // rai -> Latn
- {0xA4110000u, 17u}, // raj -> Deva
- {0xB8110000u, 44u}, // rao -> Latn
- {0x94510000u, 44u}, // rcf -> Latn
- {0xA4910000u, 44u}, // rej -> Latn
- {0xAC910000u, 44u}, // rel -> Latn
- {0xC8910000u, 44u}, // res -> Latn
- {0xB4D10000u, 44u}, // rgn -> Latn
+ {0xCA4F0000u, 46u}, // pss -> Latn
+ {0x70740000u, 46u}, // pt -> Latn
+ {0xBE6F0000u, 46u}, // ptp -> Latn
+ {0xD28F0000u, 46u}, // puu -> Latn
+ {0x82CF0000u, 46u}, // pwa -> Latn
+ {0x71750000u, 46u}, // qu -> Latn
+ {0x8A900000u, 46u}, // quc -> Latn
+ {0x9A900000u, 46u}, // qug -> Latn
+ {0xA0110000u, 46u}, // rai -> Latn
+ {0xA4110000u, 18u}, // raj -> Deva
+ {0xB8110000u, 46u}, // rao -> Latn
+ {0x94510000u, 46u}, // rcf -> Latn
+ {0xA4910000u, 46u}, // rej -> Latn
+ {0xAC910000u, 46u}, // rel -> Latn
+ {0xC8910000u, 46u}, // res -> Latn
+ {0xB4D10000u, 46u}, // rgn -> Latn
{0x98F10000u, 1u}, // rhg -> Arab
- {0x81110000u, 44u}, // ria -> Latn
- {0x95110000u, 85u}, // rif -> Tfng
- {0x95114E4Cu, 44u}, // rif-NL -> Latn
- {0xC9310000u, 17u}, // rjs -> Deva
+ {0x81110000u, 46u}, // ria -> Latn
+ {0x95110000u, 87u}, // rif -> Tfng
+ {0x95114E4Cu, 46u}, // rif-NL -> Latn
+ {0xC9310000u, 18u}, // rjs -> Deva
{0xCD510000u, 7u}, // rkt -> Beng
- {0x726D0000u, 44u}, // rm -> Latn
- {0x95910000u, 44u}, // rmf -> Latn
- {0xB9910000u, 44u}, // rmo -> Latn
+ {0x726D0000u, 46u}, // rm -> Latn
+ {0x95910000u, 46u}, // rmf -> Latn
+ {0xB9910000u, 46u}, // rmo -> Latn
{0xCD910000u, 1u}, // rmt -> Arab
- {0xD1910000u, 44u}, // rmu -> Latn
- {0x726E0000u, 44u}, // rn -> Latn
- {0x81B10000u, 44u}, // rna -> Latn
- {0x99B10000u, 44u}, // rng -> Latn
- {0x726F0000u, 44u}, // ro -> Latn
- {0x85D10000u, 44u}, // rob -> Latn
- {0x95D10000u, 44u}, // rof -> Latn
- {0xB9D10000u, 44u}, // roo -> Latn
- {0xBA310000u, 44u}, // rro -> Latn
- {0xB2710000u, 44u}, // rtm -> Latn
- {0x72750000u, 16u}, // ru -> Cyrl
- {0x92910000u, 16u}, // rue -> Cyrl
- {0x9A910000u, 44u}, // rug -> Latn
- {0x72770000u, 44u}, // rw -> Latn
- {0xAAD10000u, 44u}, // rwk -> Latn
- {0xBAD10000u, 44u}, // rwo -> Latn
- {0xD3110000u, 37u}, // ryu -> Kana
- {0x73610000u, 17u}, // sa -> Deva
- {0x94120000u, 44u}, // saf -> Latn
- {0x9C120000u, 16u}, // sah -> Cyrl
- {0xC0120000u, 44u}, // saq -> Latn
- {0xC8120000u, 44u}, // sas -> Latn
- {0xCC120000u, 44u}, // sat -> Latn
- {0xD4120000u, 44u}, // sav -> Latn
- {0xE4120000u, 72u}, // saz -> Saur
- {0x80320000u, 44u}, // sba -> Latn
- {0x90320000u, 44u}, // sbe -> Latn
- {0xBC320000u, 44u}, // sbp -> Latn
- {0x73630000u, 44u}, // sc -> Latn
- {0xA8520000u, 17u}, // sck -> Deva
+ {0xD1910000u, 46u}, // rmu -> Latn
+ {0x726E0000u, 46u}, // rn -> Latn
+ {0x81B10000u, 46u}, // rna -> Latn
+ {0x99B10000u, 46u}, // rng -> Latn
+ {0x726F0000u, 46u}, // ro -> Latn
+ {0x85D10000u, 46u}, // rob -> Latn
+ {0x95D10000u, 46u}, // rof -> Latn
+ {0xB9D10000u, 46u}, // roo -> Latn
+ {0xBA310000u, 46u}, // rro -> Latn
+ {0xB2710000u, 46u}, // rtm -> Latn
+ {0x72750000u, 17u}, // ru -> Cyrl
+ {0x92910000u, 17u}, // rue -> Cyrl
+ {0x9A910000u, 46u}, // rug -> Latn
+ {0x72770000u, 46u}, // rw -> Latn
+ {0xAAD10000u, 46u}, // rwk -> Latn
+ {0xBAD10000u, 46u}, // rwo -> Latn
+ {0xD3110000u, 38u}, // ryu -> Kana
+ {0x73610000u, 18u}, // sa -> Deva
+ {0x94120000u, 46u}, // saf -> Latn
+ {0x9C120000u, 17u}, // sah -> Cyrl
+ {0xC0120000u, 46u}, // saq -> Latn
+ {0xC8120000u, 46u}, // sas -> Latn
+ {0xCC120000u, 46u}, // sat -> Latn
+ {0xD4120000u, 46u}, // sav -> Latn
+ {0xE4120000u, 74u}, // saz -> Saur
+ {0x80320000u, 46u}, // sba -> Latn
+ {0x90320000u, 46u}, // sbe -> Latn
+ {0xBC320000u, 46u}, // sbp -> Latn
+ {0x73630000u, 46u}, // sc -> Latn
+ {0xA8520000u, 18u}, // sck -> Deva
{0xAC520000u, 1u}, // scl -> Arab
- {0xB4520000u, 44u}, // scn -> Latn
- {0xB8520000u, 44u}, // sco -> Latn
- {0xC8520000u, 44u}, // scs -> Latn
+ {0xB4520000u, 46u}, // scn -> Latn
+ {0xB8520000u, 46u}, // sco -> Latn
+ {0xC8520000u, 46u}, // scs -> Latn
{0x73640000u, 1u}, // sd -> Arab
- {0x88720000u, 44u}, // sdc -> Latn
+ {0x88720000u, 46u}, // sdc -> Latn
{0x9C720000u, 1u}, // sdh -> Arab
- {0x73650000u, 44u}, // se -> Latn
- {0x94920000u, 44u}, // sef -> Latn
- {0x9C920000u, 44u}, // seh -> Latn
- {0xA0920000u, 44u}, // sei -> Latn
- {0xC8920000u, 44u}, // ses -> Latn
- {0x73670000u, 44u}, // sg -> Latn
- {0x80D20000u, 60u}, // sga -> Ogam
- {0xC8D20000u, 44u}, // sgs -> Latn
- {0xD8D20000u, 19u}, // sgw -> Ethi
- {0xE4D20000u, 44u}, // sgz -> Latn
- {0x73680000u, 44u}, // sh -> Latn
- {0xA0F20000u, 85u}, // shi -> Tfng
- {0xA8F20000u, 44u}, // shk -> Latn
- {0xB4F20000u, 56u}, // shn -> Mymr
+ {0x73650000u, 46u}, // se -> Latn
+ {0x94920000u, 46u}, // sef -> Latn
+ {0x9C920000u, 46u}, // seh -> Latn
+ {0xA0920000u, 46u}, // sei -> Latn
+ {0xC8920000u, 46u}, // ses -> Latn
+ {0x73670000u, 46u}, // sg -> Latn
+ {0x80D20000u, 62u}, // sga -> Ogam
+ {0xC8D20000u, 46u}, // sgs -> Latn
+ {0xD8D20000u, 20u}, // sgw -> Ethi
+ {0xE4D20000u, 46u}, // sgz -> Latn
+ {0x73680000u, 46u}, // sh -> Latn
+ {0xA0F20000u, 87u}, // shi -> Tfng
+ {0xA8F20000u, 46u}, // shk -> Latn
+ {0xB4F20000u, 58u}, // shn -> Mymr
{0xD0F20000u, 1u}, // shu -> Arab
- {0x73690000u, 74u}, // si -> Sinh
- {0x8D120000u, 44u}, // sid -> Latn
- {0x99120000u, 44u}, // sig -> Latn
- {0xAD120000u, 44u}, // sil -> Latn
- {0xB1120000u, 44u}, // sim -> Latn
- {0xC5320000u, 44u}, // sjr -> Latn
- {0x736B0000u, 44u}, // sk -> Latn
- {0x89520000u, 44u}, // skc -> Latn
+ {0x73690000u, 76u}, // si -> Sinh
+ {0x8D120000u, 46u}, // sid -> Latn
+ {0x99120000u, 46u}, // sig -> Latn
+ {0xAD120000u, 46u}, // sil -> Latn
+ {0xB1120000u, 46u}, // sim -> Latn
+ {0xC5320000u, 46u}, // sjr -> Latn
+ {0x736B0000u, 46u}, // sk -> Latn
+ {0x89520000u, 46u}, // skc -> Latn
{0xC5520000u, 1u}, // skr -> Arab
- {0xC9520000u, 44u}, // sks -> Latn
- {0x736C0000u, 44u}, // sl -> Latn
- {0x8D720000u, 44u}, // sld -> Latn
- {0xA1720000u, 44u}, // sli -> Latn
- {0xAD720000u, 44u}, // sll -> Latn
- {0xE1720000u, 44u}, // sly -> Latn
- {0x736D0000u, 44u}, // sm -> Latn
- {0x81920000u, 44u}, // sma -> Latn
- {0xA5920000u, 44u}, // smj -> Latn
- {0xB5920000u, 44u}, // smn -> Latn
- {0xBD920000u, 70u}, // smp -> Samr
- {0xC1920000u, 44u}, // smq -> Latn
- {0xC9920000u, 44u}, // sms -> Latn
- {0x736E0000u, 44u}, // sn -> Latn
- {0x89B20000u, 44u}, // snc -> Latn
- {0xA9B20000u, 44u}, // snk -> Latn
- {0xBDB20000u, 44u}, // snp -> Latn
- {0xDDB20000u, 44u}, // snx -> Latn
- {0xE1B20000u, 44u}, // sny -> Latn
- {0x736F0000u, 44u}, // so -> Latn
- {0x99D20000u, 75u}, // sog -> Sogd
- {0xA9D20000u, 44u}, // sok -> Latn
- {0xC1D20000u, 44u}, // soq -> Latn
- {0xD1D20000u, 87u}, // sou -> Thai
- {0xE1D20000u, 44u}, // soy -> Latn
- {0x8DF20000u, 44u}, // spd -> Latn
- {0xADF20000u, 44u}, // spl -> Latn
- {0xC9F20000u, 44u}, // sps -> Latn
- {0x73710000u, 44u}, // sq -> Latn
- {0x73720000u, 16u}, // sr -> Cyrl
- {0x73724D45u, 44u}, // sr-ME -> Latn
- {0x7372524Fu, 44u}, // sr-RO -> Latn
- {0x73725255u, 44u}, // sr-RU -> Latn
- {0x73725452u, 44u}, // sr-TR -> Latn
- {0x86320000u, 76u}, // srb -> Sora
- {0xB6320000u, 44u}, // srn -> Latn
- {0xC6320000u, 44u}, // srr -> Latn
- {0xDE320000u, 17u}, // srx -> Deva
- {0x73730000u, 44u}, // ss -> Latn
- {0x8E520000u, 44u}, // ssd -> Latn
- {0x9A520000u, 44u}, // ssg -> Latn
- {0xE2520000u, 44u}, // ssy -> Latn
- {0x73740000u, 44u}, // st -> Latn
- {0xAA720000u, 44u}, // stk -> Latn
- {0xC2720000u, 44u}, // stq -> Latn
- {0x73750000u, 44u}, // su -> Latn
- {0x82920000u, 44u}, // sua -> Latn
- {0x92920000u, 44u}, // sue -> Latn
- {0xAA920000u, 44u}, // suk -> Latn
- {0xC6920000u, 44u}, // sur -> Latn
- {0xCA920000u, 44u}, // sus -> Latn
- {0x73760000u, 44u}, // sv -> Latn
- {0x73770000u, 44u}, // sw -> Latn
+ {0xC9520000u, 46u}, // sks -> Latn
+ {0x736C0000u, 46u}, // sl -> Latn
+ {0x8D720000u, 46u}, // sld -> Latn
+ {0xA1720000u, 46u}, // sli -> Latn
+ {0xAD720000u, 46u}, // sll -> Latn
+ {0xE1720000u, 46u}, // sly -> Latn
+ {0x736D0000u, 46u}, // sm -> Latn
+ {0x81920000u, 46u}, // sma -> Latn
+ {0xA5920000u, 46u}, // smj -> Latn
+ {0xB5920000u, 46u}, // smn -> Latn
+ {0xBD920000u, 72u}, // smp -> Samr
+ {0xC1920000u, 46u}, // smq -> Latn
+ {0xC9920000u, 46u}, // sms -> Latn
+ {0x736E0000u, 46u}, // sn -> Latn
+ {0x89B20000u, 46u}, // snc -> Latn
+ {0xA9B20000u, 46u}, // snk -> Latn
+ {0xBDB20000u, 46u}, // snp -> Latn
+ {0xDDB20000u, 46u}, // snx -> Latn
+ {0xE1B20000u, 46u}, // sny -> Latn
+ {0x736F0000u, 46u}, // so -> Latn
+ {0x99D20000u, 77u}, // sog -> Sogd
+ {0xA9D20000u, 46u}, // sok -> Latn
+ {0xC1D20000u, 46u}, // soq -> Latn
+ {0xD1D20000u, 89u}, // sou -> Thai
+ {0xE1D20000u, 46u}, // soy -> Latn
+ {0x8DF20000u, 46u}, // spd -> Latn
+ {0xADF20000u, 46u}, // spl -> Latn
+ {0xC9F20000u, 46u}, // sps -> Latn
+ {0x73710000u, 46u}, // sq -> Latn
+ {0x73720000u, 17u}, // sr -> Cyrl
+ {0x73724D45u, 46u}, // sr-ME -> Latn
+ {0x7372524Fu, 46u}, // sr-RO -> Latn
+ {0x73725255u, 46u}, // sr-RU -> Latn
+ {0x73725452u, 46u}, // sr-TR -> Latn
+ {0x86320000u, 78u}, // srb -> Sora
+ {0xB6320000u, 46u}, // srn -> Latn
+ {0xC6320000u, 46u}, // srr -> Latn
+ {0xDE320000u, 18u}, // srx -> Deva
+ {0x73730000u, 46u}, // ss -> Latn
+ {0x8E520000u, 46u}, // ssd -> Latn
+ {0x9A520000u, 46u}, // ssg -> Latn
+ {0xE2520000u, 46u}, // ssy -> Latn
+ {0x73740000u, 46u}, // st -> Latn
+ {0xAA720000u, 46u}, // stk -> Latn
+ {0xC2720000u, 46u}, // stq -> Latn
+ {0x73750000u, 46u}, // su -> Latn
+ {0x82920000u, 46u}, // sua -> Latn
+ {0x92920000u, 46u}, // sue -> Latn
+ {0xAA920000u, 46u}, // suk -> Latn
+ {0xC6920000u, 46u}, // sur -> Latn
+ {0xCA920000u, 46u}, // sus -> Latn
+ {0x73760000u, 46u}, // sv -> Latn
+ {0x73770000u, 46u}, // sw -> Latn
{0x86D20000u, 1u}, // swb -> Arab
- {0x8AD20000u, 44u}, // swc -> Latn
- {0x9AD20000u, 44u}, // swg -> Latn
- {0xBED20000u, 44u}, // swp -> Latn
- {0xD6D20000u, 17u}, // swv -> Deva
- {0xB6F20000u, 44u}, // sxn -> Latn
- {0xDAF20000u, 44u}, // sxw -> Latn
+ {0x8AD20000u, 46u}, // swc -> Latn
+ {0x9AD20000u, 46u}, // swg -> Latn
+ {0xBED20000u, 46u}, // swp -> Latn
+ {0xD6D20000u, 18u}, // swv -> Deva
+ {0xB6F20000u, 46u}, // sxn -> Latn
+ {0xDAF20000u, 46u}, // sxw -> Latn
{0xAF120000u, 7u}, // syl -> Beng
- {0xC7120000u, 78u}, // syr -> Syrc
- {0xAF320000u, 44u}, // szl -> Latn
- {0x74610000u, 81u}, // ta -> Taml
- {0xA4130000u, 17u}, // taj -> Deva
- {0xAC130000u, 44u}, // tal -> Latn
- {0xB4130000u, 44u}, // tan -> Latn
- {0xC0130000u, 44u}, // taq -> Latn
- {0x88330000u, 44u}, // tbc -> Latn
- {0x8C330000u, 44u}, // tbd -> Latn
- {0x94330000u, 44u}, // tbf -> Latn
- {0x98330000u, 44u}, // tbg -> Latn
- {0xB8330000u, 44u}, // tbo -> Latn
- {0xD8330000u, 44u}, // tbw -> Latn
- {0xE4330000u, 44u}, // tbz -> Latn
- {0xA0530000u, 44u}, // tci -> Latn
- {0xE0530000u, 40u}, // tcy -> Knda
- {0x8C730000u, 79u}, // tdd -> Tale
- {0x98730000u, 17u}, // tdg -> Deva
- {0x9C730000u, 17u}, // tdh -> Deva
- {0xD0730000u, 44u}, // tdu -> Latn
- {0x74650000u, 84u}, // te -> Telu
- {0x8C930000u, 44u}, // ted -> Latn
- {0xB0930000u, 44u}, // tem -> Latn
- {0xB8930000u, 44u}, // teo -> Latn
- {0xCC930000u, 44u}, // tet -> Latn
- {0xA0B30000u, 44u}, // tfi -> Latn
- {0x74670000u, 16u}, // tg -> Cyrl
+ {0xC7120000u, 80u}, // syr -> Syrc
+ {0xAF320000u, 46u}, // szl -> Latn
+ {0x74610000u, 83u}, // ta -> Taml
+ {0xA4130000u, 18u}, // taj -> Deva
+ {0xAC130000u, 46u}, // tal -> Latn
+ {0xB4130000u, 46u}, // tan -> Latn
+ {0xC0130000u, 46u}, // taq -> Latn
+ {0x88330000u, 46u}, // tbc -> Latn
+ {0x8C330000u, 46u}, // tbd -> Latn
+ {0x94330000u, 46u}, // tbf -> Latn
+ {0x98330000u, 46u}, // tbg -> Latn
+ {0xB8330000u, 46u}, // tbo -> Latn
+ {0xD8330000u, 46u}, // tbw -> Latn
+ {0xE4330000u, 46u}, // tbz -> Latn
+ {0xA0530000u, 46u}, // tci -> Latn
+ {0xE0530000u, 42u}, // tcy -> Knda
+ {0x8C730000u, 81u}, // tdd -> Tale
+ {0x98730000u, 18u}, // tdg -> Deva
+ {0x9C730000u, 18u}, // tdh -> Deva
+ {0xD0730000u, 46u}, // tdu -> Latn
+ {0x74650000u, 86u}, // te -> Telu
+ {0x8C930000u, 46u}, // ted -> Latn
+ {0xB0930000u, 46u}, // tem -> Latn
+ {0xB8930000u, 46u}, // teo -> Latn
+ {0xCC930000u, 46u}, // tet -> Latn
+ {0xA0B30000u, 46u}, // tfi -> Latn
+ {0x74670000u, 17u}, // tg -> Cyrl
{0x7467504Bu, 1u}, // tg-PK -> Arab
- {0x88D30000u, 44u}, // tgc -> Latn
- {0xB8D30000u, 44u}, // tgo -> Latn
- {0xD0D30000u, 44u}, // tgu -> Latn
- {0x74680000u, 87u}, // th -> Thai
- {0xACF30000u, 17u}, // thl -> Deva
- {0xC0F30000u, 17u}, // thq -> Deva
- {0xC4F30000u, 17u}, // thr -> Deva
- {0x74690000u, 19u}, // ti -> Ethi
- {0x95130000u, 44u}, // tif -> Latn
- {0x99130000u, 19u}, // tig -> Ethi
- {0xA9130000u, 44u}, // tik -> Latn
- {0xB1130000u, 44u}, // tim -> Latn
- {0xB9130000u, 44u}, // tio -> Latn
- {0xD5130000u, 44u}, // tiv -> Latn
- {0x746B0000u, 44u}, // tk -> Latn
- {0xAD530000u, 44u}, // tkl -> Latn
- {0xC5530000u, 44u}, // tkr -> Latn
- {0xCD530000u, 17u}, // tkt -> Deva
- {0x746C0000u, 44u}, // tl -> Latn
- {0x95730000u, 44u}, // tlf -> Latn
- {0xDD730000u, 44u}, // tlx -> Latn
- {0xE1730000u, 44u}, // tly -> Latn
- {0x9D930000u, 44u}, // tmh -> Latn
- {0xE1930000u, 44u}, // tmy -> Latn
- {0x746E0000u, 44u}, // tn -> Latn
- {0x9DB30000u, 44u}, // tnh -> Latn
- {0x746F0000u, 44u}, // to -> Latn
- {0x95D30000u, 44u}, // tof -> Latn
- {0x99D30000u, 44u}, // tog -> Latn
- {0xC1D30000u, 44u}, // toq -> Latn
- {0xA1F30000u, 44u}, // tpi -> Latn
- {0xB1F30000u, 44u}, // tpm -> Latn
- {0xE5F30000u, 44u}, // tpz -> Latn
- {0xBA130000u, 44u}, // tqo -> Latn
- {0x74720000u, 44u}, // tr -> Latn
- {0xD2330000u, 44u}, // tru -> Latn
- {0xD6330000u, 44u}, // trv -> Latn
+ {0x88D30000u, 46u}, // tgc -> Latn
+ {0xB8D30000u, 46u}, // tgo -> Latn
+ {0xD0D30000u, 46u}, // tgu -> Latn
+ {0x74680000u, 89u}, // th -> Thai
+ {0xACF30000u, 18u}, // thl -> Deva
+ {0xC0F30000u, 18u}, // thq -> Deva
+ {0xC4F30000u, 18u}, // thr -> Deva
+ {0x74690000u, 20u}, // ti -> Ethi
+ {0x95130000u, 46u}, // tif -> Latn
+ {0x99130000u, 20u}, // tig -> Ethi
+ {0xA9130000u, 46u}, // tik -> Latn
+ {0xB1130000u, 46u}, // tim -> Latn
+ {0xB9130000u, 46u}, // tio -> Latn
+ {0xD5130000u, 46u}, // tiv -> Latn
+ {0x746B0000u, 46u}, // tk -> Latn
+ {0xAD530000u, 46u}, // tkl -> Latn
+ {0xC5530000u, 46u}, // tkr -> Latn
+ {0xCD530000u, 18u}, // tkt -> Deva
+ {0x746C0000u, 46u}, // tl -> Latn
+ {0x95730000u, 46u}, // tlf -> Latn
+ {0xDD730000u, 46u}, // tlx -> Latn
+ {0xE1730000u, 46u}, // tly -> Latn
+ {0x9D930000u, 46u}, // tmh -> Latn
+ {0xE1930000u, 46u}, // tmy -> Latn
+ {0x746E0000u, 46u}, // tn -> Latn
+ {0x9DB30000u, 46u}, // tnh -> Latn
+ {0x746F0000u, 46u}, // to -> Latn
+ {0x95D30000u, 46u}, // tof -> Latn
+ {0x99D30000u, 46u}, // tog -> Latn
+ {0xC1D30000u, 46u}, // toq -> Latn
+ {0xA1F30000u, 46u}, // tpi -> Latn
+ {0xB1F30000u, 46u}, // tpm -> Latn
+ {0xE5F30000u, 46u}, // tpz -> Latn
+ {0xBA130000u, 46u}, // tqo -> Latn
+ {0x74720000u, 46u}, // tr -> Latn
+ {0xD2330000u, 46u}, // tru -> Latn
+ {0xD6330000u, 46u}, // trv -> Latn
{0xDA330000u, 1u}, // trw -> Arab
- {0x74730000u, 44u}, // ts -> Latn
- {0x8E530000u, 24u}, // tsd -> Grek
- {0x96530000u, 17u}, // tsf -> Deva
- {0x9A530000u, 44u}, // tsg -> Latn
- {0xA6530000u, 88u}, // tsj -> Tibt
- {0xDA530000u, 44u}, // tsw -> Latn
- {0x74740000u, 16u}, // tt -> Cyrl
- {0x8E730000u, 44u}, // ttd -> Latn
- {0x92730000u, 44u}, // tte -> Latn
- {0xA6730000u, 44u}, // ttj -> Latn
- {0xC6730000u, 44u}, // ttr -> Latn
- {0xCA730000u, 87u}, // tts -> Thai
- {0xCE730000u, 44u}, // ttt -> Latn
- {0x9E930000u, 44u}, // tuh -> Latn
- {0xAE930000u, 44u}, // tul -> Latn
- {0xB2930000u, 44u}, // tum -> Latn
- {0xC2930000u, 44u}, // tuq -> Latn
- {0x8EB30000u, 44u}, // tvd -> Latn
- {0xAEB30000u, 44u}, // tvl -> Latn
- {0xD2B30000u, 44u}, // tvu -> Latn
- {0x9ED30000u, 44u}, // twh -> Latn
- {0xC2D30000u, 44u}, // twq -> Latn
- {0x9AF30000u, 82u}, // txg -> Tang
- {0x74790000u, 44u}, // ty -> Latn
- {0x83130000u, 44u}, // tya -> Latn
- {0xD7130000u, 16u}, // tyv -> Cyrl
- {0xB3330000u, 44u}, // tzm -> Latn
- {0xD0340000u, 44u}, // ubu -> Latn
- {0xB0740000u, 16u}, // udm -> Cyrl
+ {0x74730000u, 46u}, // ts -> Latn
+ {0x8E530000u, 25u}, // tsd -> Grek
+ {0x96530000u, 18u}, // tsf -> Deva
+ {0x9A530000u, 46u}, // tsg -> Latn
+ {0xA6530000u, 90u}, // tsj -> Tibt
+ {0xDA530000u, 46u}, // tsw -> Latn
+ {0x74740000u, 17u}, // tt -> Cyrl
+ {0x8E730000u, 46u}, // ttd -> Latn
+ {0x92730000u, 46u}, // tte -> Latn
+ {0xA6730000u, 46u}, // ttj -> Latn
+ {0xC6730000u, 46u}, // ttr -> Latn
+ {0xCA730000u, 89u}, // tts -> Thai
+ {0xCE730000u, 46u}, // ttt -> Latn
+ {0x9E930000u, 46u}, // tuh -> Latn
+ {0xAE930000u, 46u}, // tul -> Latn
+ {0xB2930000u, 46u}, // tum -> Latn
+ {0xC2930000u, 46u}, // tuq -> Latn
+ {0x8EB30000u, 46u}, // tvd -> Latn
+ {0xAEB30000u, 46u}, // tvl -> Latn
+ {0xD2B30000u, 46u}, // tvu -> Latn
+ {0x9ED30000u, 46u}, // twh -> Latn
+ {0xC2D30000u, 46u}, // twq -> Latn
+ {0x9AF30000u, 84u}, // txg -> Tang
+ {0x74790000u, 46u}, // ty -> Latn
+ {0x83130000u, 46u}, // tya -> Latn
+ {0xD7130000u, 17u}, // tyv -> Cyrl
+ {0xB3330000u, 46u}, // tzm -> Latn
+ {0xD0340000u, 46u}, // ubu -> Latn
+ {0xB0740000u, 17u}, // udm -> Cyrl
{0x75670000u, 1u}, // ug -> Arab
- {0x75674B5Au, 16u}, // ug-KZ -> Cyrl
- {0x75674D4Eu, 16u}, // ug-MN -> Cyrl
- {0x80D40000u, 89u}, // uga -> Ugar
- {0x756B0000u, 16u}, // uk -> Cyrl
- {0xA1740000u, 44u}, // uli -> Latn
- {0x85940000u, 44u}, // umb -> Latn
+ {0x75674B5Au, 17u}, // ug-KZ -> Cyrl
+ {0x75674D4Eu, 17u}, // ug-MN -> Cyrl
+ {0x80D40000u, 91u}, // uga -> Ugar
+ {0x756B0000u, 17u}, // uk -> Cyrl
+ {0xA1740000u, 46u}, // uli -> Latn
+ {0x85940000u, 46u}, // umb -> Latn
{0xC5B40000u, 7u}, // unr -> Beng
- {0xC5B44E50u, 17u}, // unr-NP -> Deva
+ {0xC5B44E50u, 18u}, // unr-NP -> Deva
{0xDDB40000u, 7u}, // unx -> Beng
- {0xA9D40000u, 44u}, // uok -> Latn
+ {0xA9D40000u, 46u}, // uok -> Latn
{0x75720000u, 1u}, // ur -> Arab
- {0xA2340000u, 44u}, // uri -> Latn
- {0xCE340000u, 44u}, // urt -> Latn
- {0xDA340000u, 44u}, // urw -> Latn
- {0x82540000u, 44u}, // usa -> Latn
- {0xC6740000u, 44u}, // utr -> Latn
- {0x9EB40000u, 44u}, // uvh -> Latn
- {0xAEB40000u, 44u}, // uvl -> Latn
- {0x757A0000u, 44u}, // uz -> Latn
+ {0xA2340000u, 46u}, // uri -> Latn
+ {0xCE340000u, 46u}, // urt -> Latn
+ {0xDA340000u, 46u}, // urw -> Latn
+ {0x82540000u, 46u}, // usa -> Latn
+ {0xC6740000u, 46u}, // utr -> Latn
+ {0x9EB40000u, 46u}, // uvh -> Latn
+ {0xAEB40000u, 46u}, // uvl -> Latn
+ {0x757A0000u, 46u}, // uz -> Latn
{0x757A4146u, 1u}, // uz-AF -> Arab
- {0x757A434Eu, 16u}, // uz-CN -> Cyrl
- {0x98150000u, 44u}, // vag -> Latn
- {0xA0150000u, 90u}, // vai -> Vaii
- {0xB4150000u, 44u}, // van -> Latn
- {0x76650000u, 44u}, // ve -> Latn
- {0x88950000u, 44u}, // vec -> Latn
- {0xBC950000u, 44u}, // vep -> Latn
- {0x76690000u, 44u}, // vi -> Latn
- {0x89150000u, 44u}, // vic -> Latn
- {0xD5150000u, 44u}, // viv -> Latn
- {0xC9750000u, 44u}, // vls -> Latn
- {0x95950000u, 44u}, // vmf -> Latn
- {0xD9950000u, 44u}, // vmw -> Latn
- {0x766F0000u, 44u}, // vo -> Latn
- {0xCDD50000u, 44u}, // vot -> Latn
- {0xBA350000u, 44u}, // vro -> Latn
- {0xB6950000u, 44u}, // vun -> Latn
- {0xCE950000u, 44u}, // vut -> Latn
- {0x77610000u, 44u}, // wa -> Latn
- {0x90160000u, 44u}, // wae -> Latn
- {0xA4160000u, 44u}, // waj -> Latn
- {0xAC160000u, 19u}, // wal -> Ethi
- {0xB4160000u, 44u}, // wan -> Latn
- {0xC4160000u, 44u}, // war -> Latn
- {0xBC360000u, 44u}, // wbp -> Latn
- {0xC0360000u, 84u}, // wbq -> Telu
- {0xC4360000u, 17u}, // wbr -> Deva
- {0xA0560000u, 44u}, // wci -> Latn
- {0xC4960000u, 44u}, // wer -> Latn
- {0xA0D60000u, 44u}, // wgi -> Latn
- {0x98F60000u, 44u}, // whg -> Latn
- {0x85160000u, 44u}, // wib -> Latn
- {0xD1160000u, 44u}, // wiu -> Latn
- {0xD5160000u, 44u}, // wiv -> Latn
- {0x81360000u, 44u}, // wja -> Latn
- {0xA1360000u, 44u}, // wji -> Latn
- {0xC9760000u, 44u}, // wls -> Latn
- {0xB9960000u, 44u}, // wmo -> Latn
- {0x89B60000u, 44u}, // wnc -> Latn
+ {0x757A434Eu, 17u}, // uz-CN -> Cyrl
+ {0x98150000u, 46u}, // vag -> Latn
+ {0xA0150000u, 92u}, // vai -> Vaii
+ {0xB4150000u, 46u}, // van -> Latn
+ {0x76650000u, 46u}, // ve -> Latn
+ {0x88950000u, 46u}, // vec -> Latn
+ {0xBC950000u, 46u}, // vep -> Latn
+ {0x76690000u, 46u}, // vi -> Latn
+ {0x89150000u, 46u}, // vic -> Latn
+ {0xD5150000u, 46u}, // viv -> Latn
+ {0xC9750000u, 46u}, // vls -> Latn
+ {0x95950000u, 46u}, // vmf -> Latn
+ {0xD9950000u, 46u}, // vmw -> Latn
+ {0x766F0000u, 46u}, // vo -> Latn
+ {0xCDD50000u, 46u}, // vot -> Latn
+ {0xBA350000u, 46u}, // vro -> Latn
+ {0xB6950000u, 46u}, // vun -> Latn
+ {0xCE950000u, 46u}, // vut -> Latn
+ {0x77610000u, 46u}, // wa -> Latn
+ {0x90160000u, 46u}, // wae -> Latn
+ {0xA4160000u, 46u}, // waj -> Latn
+ {0xAC160000u, 20u}, // wal -> Ethi
+ {0xB4160000u, 46u}, // wan -> Latn
+ {0xC4160000u, 46u}, // war -> Latn
+ {0xBC360000u, 46u}, // wbp -> Latn
+ {0xC0360000u, 86u}, // wbq -> Telu
+ {0xC4360000u, 18u}, // wbr -> Deva
+ {0xA0560000u, 46u}, // wci -> Latn
+ {0xC4960000u, 46u}, // wer -> Latn
+ {0xA0D60000u, 46u}, // wgi -> Latn
+ {0x98F60000u, 46u}, // whg -> Latn
+ {0x85160000u, 46u}, // wib -> Latn
+ {0xD1160000u, 46u}, // wiu -> Latn
+ {0xD5160000u, 46u}, // wiv -> Latn
+ {0x81360000u, 46u}, // wja -> Latn
+ {0xA1360000u, 46u}, // wji -> Latn
+ {0xC9760000u, 46u}, // wls -> Latn
+ {0xB9960000u, 46u}, // wmo -> Latn
+ {0x89B60000u, 46u}, // wnc -> Latn
{0xA1B60000u, 1u}, // wni -> Arab
- {0xD1B60000u, 44u}, // wnu -> Latn
- {0x776F0000u, 44u}, // wo -> Latn
- {0x85D60000u, 44u}, // wob -> Latn
- {0xC9D60000u, 44u}, // wos -> Latn
- {0xCA360000u, 44u}, // wrs -> Latn
- {0x9A560000u, 21u}, // wsg -> Gong
- {0xAA560000u, 44u}, // wsk -> Latn
- {0xB2760000u, 17u}, // wtm -> Deva
- {0xD2960000u, 27u}, // wuu -> Hans
- {0xD6960000u, 44u}, // wuv -> Latn
- {0x82D60000u, 44u}, // wwa -> Latn
- {0xD4170000u, 44u}, // xav -> Latn
- {0xA0370000u, 44u}, // xbi -> Latn
+ {0xD1B60000u, 46u}, // wnu -> Latn
+ {0x776F0000u, 46u}, // wo -> Latn
+ {0x85D60000u, 46u}, // wob -> Latn
+ {0xC9D60000u, 46u}, // wos -> Latn
+ {0xCA360000u, 46u}, // wrs -> Latn
+ {0x9A560000u, 22u}, // wsg -> Gong
+ {0xAA560000u, 46u}, // wsk -> Latn
+ {0xB2760000u, 18u}, // wtm -> Deva
+ {0xD2960000u, 28u}, // wuu -> Hans
+ {0xD6960000u, 46u}, // wuv -> Latn
+ {0x82D60000u, 46u}, // wwa -> Latn
+ {0xD4170000u, 46u}, // xav -> Latn
+ {0xA0370000u, 46u}, // xbi -> Latn
+ {0xB8570000u, 14u}, // xco -> Chrs
{0xC4570000u, 11u}, // xcr -> Cari
- {0xC8970000u, 44u}, // xes -> Latn
- {0x78680000u, 44u}, // xh -> Latn
- {0x81770000u, 44u}, // xla -> Latn
- {0x89770000u, 48u}, // xlc -> Lyci
- {0x8D770000u, 49u}, // xld -> Lydi
- {0x95970000u, 20u}, // xmf -> Geor
- {0xB5970000u, 51u}, // xmn -> Mani
- {0xC5970000u, 52u}, // xmr -> Merc
- {0x81B70000u, 57u}, // xna -> Narb
- {0xC5B70000u, 17u}, // xnr -> Deva
- {0x99D70000u, 44u}, // xog -> Latn
- {0xB5D70000u, 44u}, // xon -> Latn
- {0xC5F70000u, 68u}, // xpr -> Prti
- {0x86370000u, 44u}, // xrb -> Latn
- {0x82570000u, 71u}, // xsa -> Sarb
- {0xA2570000u, 44u}, // xsi -> Latn
- {0xB2570000u, 44u}, // xsm -> Latn
- {0xC6570000u, 17u}, // xsr -> Deva
- {0x92D70000u, 44u}, // xwe -> Latn
- {0xB0180000u, 44u}, // yam -> Latn
- {0xB8180000u, 44u}, // yao -> Latn
- {0xBC180000u, 44u}, // yap -> Latn
- {0xC8180000u, 44u}, // yas -> Latn
- {0xCC180000u, 44u}, // yat -> Latn
- {0xD4180000u, 44u}, // yav -> Latn
- {0xE0180000u, 44u}, // yay -> Latn
- {0xE4180000u, 44u}, // yaz -> Latn
- {0x80380000u, 44u}, // yba -> Latn
- {0x84380000u, 44u}, // ybb -> Latn
- {0xE0380000u, 44u}, // yby -> Latn
- {0xC4980000u, 44u}, // yer -> Latn
- {0xC4D80000u, 44u}, // ygr -> Latn
- {0xD8D80000u, 44u}, // ygw -> Latn
- {0x79690000u, 30u}, // yi -> Hebr
- {0xB9580000u, 44u}, // yko -> Latn
- {0x91780000u, 44u}, // yle -> Latn
- {0x99780000u, 44u}, // ylg -> Latn
- {0xAD780000u, 44u}, // yll -> Latn
- {0xAD980000u, 44u}, // yml -> Latn
- {0x796F0000u, 44u}, // yo -> Latn
- {0xB5D80000u, 44u}, // yon -> Latn
- {0x86380000u, 44u}, // yrb -> Latn
- {0x92380000u, 44u}, // yre -> Latn
- {0xAE380000u, 44u}, // yrl -> Latn
- {0xCA580000u, 44u}, // yss -> Latn
- {0x82980000u, 44u}, // yua -> Latn
- {0x92980000u, 28u}, // yue -> Hant
- {0x9298434Eu, 27u}, // yue-CN -> Hans
- {0xA6980000u, 44u}, // yuj -> Latn
- {0xCE980000u, 44u}, // yut -> Latn
- {0xDA980000u, 44u}, // yuw -> Latn
- {0x7A610000u, 44u}, // za -> Latn
- {0x98190000u, 44u}, // zag -> Latn
+ {0xC8970000u, 46u}, // xes -> Latn
+ {0x78680000u, 46u}, // xh -> Latn
+ {0x81770000u, 46u}, // xla -> Latn
+ {0x89770000u, 50u}, // xlc -> Lyci
+ {0x8D770000u, 51u}, // xld -> Lydi
+ {0x95970000u, 21u}, // xmf -> Geor
+ {0xB5970000u, 53u}, // xmn -> Mani
+ {0xC5970000u, 54u}, // xmr -> Merc
+ {0x81B70000u, 59u}, // xna -> Narb
+ {0xC5B70000u, 18u}, // xnr -> Deva
+ {0x99D70000u, 46u}, // xog -> Latn
+ {0xB5D70000u, 46u}, // xon -> Latn
+ {0xC5F70000u, 70u}, // xpr -> Prti
+ {0x86370000u, 46u}, // xrb -> Latn
+ {0x82570000u, 73u}, // xsa -> Sarb
+ {0xA2570000u, 46u}, // xsi -> Latn
+ {0xB2570000u, 46u}, // xsm -> Latn
+ {0xC6570000u, 18u}, // xsr -> Deva
+ {0x92D70000u, 46u}, // xwe -> Latn
+ {0xB0180000u, 46u}, // yam -> Latn
+ {0xB8180000u, 46u}, // yao -> Latn
+ {0xBC180000u, 46u}, // yap -> Latn
+ {0xC8180000u, 46u}, // yas -> Latn
+ {0xCC180000u, 46u}, // yat -> Latn
+ {0xD4180000u, 46u}, // yav -> Latn
+ {0xE0180000u, 46u}, // yay -> Latn
+ {0xE4180000u, 46u}, // yaz -> Latn
+ {0x80380000u, 46u}, // yba -> Latn
+ {0x84380000u, 46u}, // ybb -> Latn
+ {0xE0380000u, 46u}, // yby -> Latn
+ {0xC4980000u, 46u}, // yer -> Latn
+ {0xC4D80000u, 46u}, // ygr -> Latn
+ {0xD8D80000u, 46u}, // ygw -> Latn
+ {0x79690000u, 31u}, // yi -> Hebr
+ {0xB9580000u, 46u}, // yko -> Latn
+ {0x91780000u, 46u}, // yle -> Latn
+ {0x99780000u, 46u}, // ylg -> Latn
+ {0xAD780000u, 46u}, // yll -> Latn
+ {0xAD980000u, 46u}, // yml -> Latn
+ {0x796F0000u, 46u}, // yo -> Latn
+ {0xB5D80000u, 46u}, // yon -> Latn
+ {0x86380000u, 46u}, // yrb -> Latn
+ {0x92380000u, 46u}, // yre -> Latn
+ {0xAE380000u, 46u}, // yrl -> Latn
+ {0xCA580000u, 46u}, // yss -> Latn
+ {0x82980000u, 46u}, // yua -> Latn
+ {0x92980000u, 29u}, // yue -> Hant
+ {0x9298434Eu, 28u}, // yue-CN -> Hans
+ {0xA6980000u, 46u}, // yuj -> Latn
+ {0xCE980000u, 46u}, // yut -> Latn
+ {0xDA980000u, 46u}, // yuw -> Latn
+ {0x7A610000u, 46u}, // za -> Latn
+ {0x98190000u, 46u}, // zag -> Latn
{0xA4790000u, 1u}, // zdj -> Arab
- {0x80990000u, 44u}, // zea -> Latn
- {0x9CD90000u, 85u}, // zgh -> Tfng
- {0x7A680000u, 27u}, // zh -> Hans
- {0x7A684155u, 28u}, // zh-AU -> Hant
- {0x7A68424Eu, 28u}, // zh-BN -> Hant
- {0x7A684742u, 28u}, // zh-GB -> Hant
- {0x7A684746u, 28u}, // zh-GF -> Hant
- {0x7A68484Bu, 28u}, // zh-HK -> Hant
- {0x7A684944u, 28u}, // zh-ID -> Hant
- {0x7A684D4Fu, 28u}, // zh-MO -> Hant
- {0x7A684D59u, 28u}, // zh-MY -> Hant
- {0x7A685041u, 28u}, // zh-PA -> Hant
- {0x7A685046u, 28u}, // zh-PF -> Hant
- {0x7A685048u, 28u}, // zh-PH -> Hant
- {0x7A685352u, 28u}, // zh-SR -> Hant
- {0x7A685448u, 28u}, // zh-TH -> Hant
- {0x7A685457u, 28u}, // zh-TW -> Hant
- {0x7A685553u, 28u}, // zh-US -> Hant
- {0x7A68564Eu, 28u}, // zh-VN -> Hant
- {0xDCF90000u, 59u}, // zhx -> Nshu
- {0x81190000u, 44u}, // zia -> Latn
- {0xB1790000u, 44u}, // zlm -> Latn
- {0xA1990000u, 44u}, // zmi -> Latn
- {0x91B90000u, 44u}, // zne -> Latn
- {0x7A750000u, 44u}, // zu -> Latn
- {0x83390000u, 44u}, // zza -> Latn
+ {0x80990000u, 46u}, // zea -> Latn
+ {0x9CD90000u, 87u}, // zgh -> Tfng
+ {0x7A680000u, 28u}, // zh -> Hans
+ {0x7A684155u, 29u}, // zh-AU -> Hant
+ {0x7A68424Eu, 29u}, // zh-BN -> Hant
+ {0x7A684742u, 29u}, // zh-GB -> Hant
+ {0x7A684746u, 29u}, // zh-GF -> Hant
+ {0x7A68484Bu, 29u}, // zh-HK -> Hant
+ {0x7A684944u, 29u}, // zh-ID -> Hant
+ {0x7A684D4Fu, 29u}, // zh-MO -> Hant
+ {0x7A684D59u, 29u}, // zh-MY -> Hant
+ {0x7A685041u, 29u}, // zh-PA -> Hant
+ {0x7A685046u, 29u}, // zh-PF -> Hant
+ {0x7A685048u, 29u}, // zh-PH -> Hant
+ {0x7A685352u, 29u}, // zh-SR -> Hant
+ {0x7A685448u, 29u}, // zh-TH -> Hant
+ {0x7A685457u, 29u}, // zh-TW -> Hant
+ {0x7A685553u, 29u}, // zh-US -> Hant
+ {0x7A68564Eu, 29u}, // zh-VN -> Hant
+ {0xDCF90000u, 61u}, // zhx -> Nshu
+ {0x81190000u, 46u}, // zia -> Latn
+ {0xCD590000u, 41u}, // zkt -> Kits
+ {0xB1790000u, 46u}, // zlm -> Latn
+ {0xA1990000u, 46u}, // zmi -> Latn
+ {0x91B90000u, 46u}, // zne -> Latn
+ {0x7A750000u, 46u}, // zu -> Latn
+ {0x83390000u, 46u}, // zza -> Latn
});
std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({
@@ -1829,6 +1833,7 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({
0xC66A4D594C61746ELLU, // ktr_Latn_MY
0x6B75495141726162LLU, // ku_Arab_IQ
0x6B7554524C61746ELLU, // ku_Latn_TR
+ 0x6B75474559657A69LLU, // ku_Yezi_GE
0xB28A52554379726CLLU, // kum_Cyrl_RU
0x6B7652554379726CLLU, // kv_Cyrl_RU
0xC6AA49444C61746ELLU, // kvr_Latn_ID
@@ -2199,6 +2204,7 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({
0xB276494E44657661LLU, // wtm_Deva_IN
0xD296434E48616E73LLU, // wuu_Hans_CN
0xD41742524C61746ELLU, // xav_Latn_BR
+ 0xB857555A43687273LLU, // xco_Chrs_UZ
0xC457545243617269LLU, // xcr_Cari_TR
0x78685A414C61746ELLU, // xh_Latn_ZA
0x897754524C796369LLU, // xlc_Lyci_TR
@@ -2231,6 +2237,7 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({
0x7A68434E48616E73LLU, // zh_Hans_CN
0x7A68545748616E74LLU, // zh_Hant_TW
0xDCF9434E4E736875LLU, // zhx_Nshu_CN
+ 0xCD59434E4B697473LLU, // zkt_Kits_CN
0xB17954474C61746ELLU, // zlm_Latn_TG
0xA1994D594C61746ELLU, // zmi_Latn_MY
0x7A755A414C61746ELLU, // zu_Latn_ZA
diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java
index 3a771bba03b9..ad9486cc6597 100644
--- a/media/java/android/media/MediaCas.java
+++ b/media/java/android/media/MediaCas.java
@@ -870,16 +870,17 @@ public final class MediaCas implements AutoCloseable {
private int getSessionResourceId() throws MediaCasException {
validateInternalStates();
- int[] sessionResourceId = new int[1];
- sessionResourceId[0] = -1;
+ int[] sessionResourceHandle = new int[1];
+ sessionResourceHandle[0] = -1;
if (mTunerResourceManager != null) {
CasSessionRequest casSessionRequest = new CasSessionRequest(mClientId, mCasSystemId);
- if (!mTunerResourceManager.requestCasSession(casSessionRequest, sessionResourceId)) {
- throw new MediaCasException.ResourceBusyException(
+ if (!mTunerResourceManager
+ .requestCasSession(casSessionRequest, sessionResourceHandle)) {
+ throw new MediaCasException.InsufficientResourceException(
"insufficient resource to Open Session");
}
}
- return sessionResourceId[0];
+ return sessionResourceHandle[0];
}
private void addSessionToResourceMap(Session session, int sessionResourceId) {
@@ -905,6 +906,10 @@ public final class MediaCas implements AutoCloseable {
* Open a session to descramble one or more streams scrambled by the
* conditional access system.
*
+ * <p>Tuner resource manager (TRM) uses the client priority value to decide whether it is able
+ * to get cas session resource if cas session resources is limited. If the client can't get the
+ * resource, this call returns {@link MediaCasException.InsufficientResourceException }.
+ *
* @return session the newly opened session.
*
* @throws IllegalStateException if the MediaCas instance is not valid.
@@ -930,6 +935,10 @@ public final class MediaCas implements AutoCloseable {
* Open a session with usage and scrambling information, so that descrambler can be configured
* to descramble one or more streams scrambled by the conditional access system.
*
+ * <p>Tuner resource manager (TRM) uses the client priority value to decide whether it is able
+ * to get cas session resource if cas session resources is limited. If the client can't get the
+ * resource, this call returns {@link MediaCasException.InsufficientResourceException}.
+ *
* @param sessionUsage used for the created session.
* @param scramblingMode used for the created session.
*
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index 8e39dfae7c57..fb95e98ebf47 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -466,10 +466,6 @@ public abstract class MediaRoute2ProviderService extends Service {
return;
}
- List<RoutingSessionInfo> sessionInfos;
- synchronized (mSessionLock) {
- sessionInfos = new ArrayList<>(mSessionInfo.values());
- }
try {
mRemoteCallback.updateState(mProviderInfo);
} catch (RemoteException ex) {
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 37e2ab53a1a1..bde45d74d29d 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -58,7 +58,7 @@ import java.util.stream.Collectors;
// TODO: Add method names at the beginning of log messages. (e.g. updateControllerOnHandler)
// Not only MediaRouter2, but also to service / manager / provider.
// TODO: ensure thread-safe and document it
-public class MediaRouter2 {
+public final class MediaRouter2 {
private static final String TAG = "MR2";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final Object sRouterLock = new Object();
@@ -93,9 +93,9 @@ public class MediaRouter2 {
MediaRouter2Stub mStub;
@GuardedBy("sRouterLock")
- private Map<String, RoutingController> mRoutingControllers = new ArrayMap<>();
+ private final Map<String, RoutingController> mRoutingControllers = new ArrayMap<>();
- private AtomicInteger mControllerCreationRequestCnt = new AtomicInteger(1);
+ private final AtomicInteger mControllerCreationRequestCnt = new AtomicInteger(1);
final Handler mHandler;
@GuardedBy("sRouterLock")
@@ -419,8 +419,7 @@ public class MediaRouter2 {
controller.release();
- final int requestId;
- requestId = mControllerCreationRequestCnt.getAndIncrement();
+ final int requestId = mControllerCreationRequestCnt.getAndIncrement();
ControllerCreationRequest request =
new ControllerCreationRequest(requestId, controller, route);
@@ -610,10 +609,16 @@ public class MediaRouter2 {
}
if (sessionInfo != null) {
- RoutingController newController = new RoutingController(sessionInfo);
- synchronized (sRouterLock) {
- mRoutingControllers.put(newController.getId(), newController);
+ RoutingController newController;
+ if (sessionInfo.isSystemSession()) {
+ newController = getSystemController();
+ } else {
+ newController = new RoutingController(sessionInfo);
+ synchronized (sRouterLock) {
+ mRoutingControllers.put(newController.getId(), newController);
+ }
}
+ //TODO: Determine oldController properly when transfer is launched by Output Switcher.
notifyTransferred(matchingRequest != null ? matchingRequest.mController :
getSystemController(), newController);
}
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 11c9fe199521..88bcd6aaad95 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -50,7 +50,7 @@ import java.util.stream.Collectors;
* A class that monitors and controls media routing of other apps.
* @hide
*/
-public class MediaRouter2Manager {
+public final class MediaRouter2Manager {
private static final String TAG = "MR2Manager";
private static final Object sLock = new Object();
@@ -61,7 +61,7 @@ public class MediaRouter2Manager {
final String mPackageName;
- private Context mContext;
+ private final Context mContext;
@GuardedBy("sLock")
private Client mClient;
private final IMediaRouterService mMediaRouterService;
@@ -74,7 +74,7 @@ public class MediaRouter2Manager {
@NonNull
final ConcurrentMap<String, List<String>> mPreferredFeaturesMap = new ConcurrentHashMap<>();
- private AtomicInteger mNextRequestId = new AtomicInteger(1);
+ private final AtomicInteger mNextRequestId = new AtomicInteger(1);
/**
* Gets an instance of media router manager that controls media route of other applications.
diff --git a/media/java/android/media/audiofx/Visualizer.java b/media/java/android/media/audiofx/Visualizer.java
index 392e8fe7543f..a5da648cf14a 100644
--- a/media/java/android/media/audiofx/Visualizer.java
+++ b/media/java/android/media/audiofx/Visualizer.java
@@ -20,9 +20,10 @@ import android.app.ActivityThread;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Handler;
import android.os.Looper;
-import android.os.Message;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+
import java.lang.ref.WeakReference;
/**
@@ -158,6 +159,7 @@ public class Visualizer {
/**
* Indicates the state of the Visualizer instance
*/
+ @GuardedBy("mStateLock")
private int mState = STATE_UNINITIALIZED;
/**
* Lock to synchronize access to mState
@@ -166,6 +168,7 @@ public class Visualizer {
/**
* System wide unique Identifier of the visualizer engine used by this Visualizer instance
*/
+ @GuardedBy("mStateLock")
@UnsupportedAppUsage
private int mId;
@@ -176,19 +179,24 @@ public class Visualizer {
/**
* Handler for events coming from the native code
*/
- private NativeEventHandler mNativeEventHandler = null;
+ @GuardedBy("mListenerLock")
+ private Handler mNativeEventHandler = null;
/**
* PCM and FFT capture listener registered by client
*/
+ @GuardedBy("mListenerLock")
private OnDataCaptureListener mCaptureListener = null;
/**
* Server Died listener registered by client
*/
+ @GuardedBy("mListenerLock")
private OnServerDiedListener mServerDiedListener = null;
// accessed by native methods
- private long mNativeVisualizer;
- private long mJniData;
+ private long mNativeVisualizer; // guarded by a static lock in native code
+ private long mJniData; // set in native_setup, _release;
+ // get in native_release, _setEnabled, _setPeriodicCapture
+ // thus, effectively guarded by mStateLock
//--------------------------------------------------------------------------
// Constructor, Finalize
@@ -244,7 +252,9 @@ public class Visualizer {
@Override
protected void finalize() {
- native_finalize();
+ synchronized (mStateLock) {
+ native_finalize();
+ }
}
/**
@@ -601,25 +611,28 @@ public class Visualizer {
*/
public int setDataCaptureListener(OnDataCaptureListener listener,
int rate, boolean waveform, boolean fft) {
- synchronized (mListenerLock) {
- mCaptureListener = listener;
- }
if (listener == null) {
// make sure capture callback is stopped in native code
waveform = false;
fft = false;
}
- int status = native_setPeriodicCapture(rate, waveform, fft);
+ int status;
+ synchronized (mStateLock) {
+ status = native_setPeriodicCapture(rate, waveform, fft);
+ }
if (status == SUCCESS) {
- if ((listener != null) && (mNativeEventHandler == null)) {
- Looper looper;
- if ((looper = Looper.myLooper()) != null) {
- mNativeEventHandler = new NativeEventHandler(this, looper);
- } else if ((looper = Looper.getMainLooper()) != null) {
- mNativeEventHandler = new NativeEventHandler(this, looper);
- } else {
- mNativeEventHandler = null;
- status = ERROR_NO_INIT;
+ synchronized (mListenerLock) {
+ mCaptureListener = listener;
+ if ((listener != null) && (mNativeEventHandler == null)) {
+ Looper looper;
+ if ((looper = Looper.myLooper()) != null) {
+ mNativeEventHandler = new Handler(looper);
+ } else if ((looper = Looper.getMainLooper()) != null) {
+ mNativeEventHandler = new Handler(looper);
+ } else {
+ mNativeEventHandler = null;
+ status = ERROR_NO_INIT;
+ }
}
}
}
@@ -663,112 +676,61 @@ public class Visualizer {
return SUCCESS;
}
- /**
- * Helper class to handle the forwarding of native events to the appropriate listeners
- */
- private class NativeEventHandler extends Handler
- {
- private Visualizer mVisualizer;
-
- public NativeEventHandler(Visualizer v, Looper looper) {
- super(looper);
- mVisualizer = v;
- }
-
- private void handleCaptureMessage(Message msg) {
- OnDataCaptureListener l = null;
- synchronized (mListenerLock) {
- l = mVisualizer.mCaptureListener;
- }
-
- if (l != null) {
- byte[] data = (byte[])msg.obj;
- int samplingRate = msg.arg1;
-
- switch(msg.what) {
- case NATIVE_EVENT_PCM_CAPTURE:
- l.onWaveFormDataCapture(mVisualizer, data, samplingRate);
- break;
- case NATIVE_EVENT_FFT_CAPTURE:
- l.onFftDataCapture(mVisualizer, data, samplingRate);
- break;
- default:
- Log.e(TAG,"Unknown native event in handleCaptureMessge: "+msg.what);
- break;
- }
- }
- }
-
- private void handleServerDiedMessage(Message msg) {
- OnServerDiedListener l = null;
- synchronized (mListenerLock) {
- l = mVisualizer.mServerDiedListener;
- }
-
- if (l != null)
- l.onServerDied();
- }
-
- @Override
- public void handleMessage(Message msg) {
- if (mVisualizer == null) {
- return;
- }
-
- switch(msg.what) {
- case NATIVE_EVENT_PCM_CAPTURE:
- case NATIVE_EVENT_FFT_CAPTURE:
- handleCaptureMessage(msg);
- break;
- case NATIVE_EVENT_SERVER_DIED:
- handleServerDiedMessage(msg);
- break;
- default:
- Log.e(TAG,"Unknown native event: "+msg.what);
- break;
- }
- }
- }
-
//---------------------------------------------------------
// Interface definitions
//--------------------
private static native final void native_init();
+ @GuardedBy("mStateLock")
private native final int native_setup(Object audioeffect_this,
int audioSession,
int[] id,
String opPackageName);
+ @GuardedBy("mStateLock")
private native final void native_finalize();
+ @GuardedBy("mStateLock")
private native final void native_release();
+ @GuardedBy("mStateLock")
private native final int native_setEnabled(boolean enabled);
+ @GuardedBy("mStateLock")
private native final boolean native_getEnabled();
+ @GuardedBy("mStateLock")
private native final int native_setCaptureSize(int size);
+ @GuardedBy("mStateLock")
private native final int native_getCaptureSize();
+ @GuardedBy("mStateLock")
private native final int native_setScalingMode(int mode);
+ @GuardedBy("mStateLock")
private native final int native_getScalingMode();
+ @GuardedBy("mStateLock")
private native final int native_setMeasurementMode(int mode);
+ @GuardedBy("mStateLock")
private native final int native_getMeasurementMode();
+ @GuardedBy("mStateLock")
private native final int native_getSamplingRate();
+ @GuardedBy("mStateLock")
private native final int native_getWaveForm(byte[] waveform);
+ @GuardedBy("mStateLock")
private native final int native_getFft(byte[] fft);
+ @GuardedBy("mStateLock")
private native final int native_getPeakRms(MeasurementPeakRms measurement);
+ @GuardedBy("mStateLock")
private native final int native_setPeriodicCapture(int rate, boolean waveForm, boolean fft);
//---------------------------------------------------------
@@ -776,17 +738,47 @@ public class Visualizer {
//--------------------
@SuppressWarnings("unused")
private static void postEventFromNative(Object effect_ref,
- int what, int arg1, int arg2, Object obj) {
- Visualizer visu = (Visualizer)((WeakReference)effect_ref).get();
- if (visu == null) {
- return;
- }
+ int what, int samplingRate, byte[] data) {
+ final Visualizer visualizer = (Visualizer) ((WeakReference) effect_ref).get();
+ if (visualizer == null) return;
- if (visu.mNativeEventHandler != null) {
- Message m = visu.mNativeEventHandler.obtainMessage(what, arg1, arg2, obj);
- visu.mNativeEventHandler.sendMessage(m);
+ final Handler handler;
+ synchronized (visualizer.mListenerLock) {
+ handler = visualizer.mNativeEventHandler;
}
+ if (handler == null) return;
+ switch (what) {
+ case NATIVE_EVENT_PCM_CAPTURE:
+ case NATIVE_EVENT_FFT_CAPTURE:
+ handler.post(() -> {
+ final OnDataCaptureListener l;
+ synchronized (visualizer.mListenerLock) {
+ l = visualizer.mCaptureListener;
+ }
+ if (l != null) {
+ if (what == NATIVE_EVENT_PCM_CAPTURE) {
+ l.onWaveFormDataCapture(visualizer, data, samplingRate);
+ } else { // what == NATIVE_EVENT_FFT_CAPTURE
+ l.onFftDataCapture(visualizer, data, samplingRate);
+ }
+ }
+ });
+ break;
+ case NATIVE_EVENT_SERVER_DIED:
+ handler.post(() -> {
+ final OnServerDiedListener l;
+ synchronized (visualizer.mListenerLock) {
+ l = visualizer.mServerDiedListener;
+ }
+ if (l != null) {
+ l.onServerDied();
+ }
+ });
+ break;
+ default:
+ Log.e(TAG, "Unknown native event in postEventFromNative: " + what);
+ break;
+ }
}
}
-
diff --git a/media/java/android/media/tv/tuner/Descrambler.java b/media/java/android/media/tv/tuner/Descrambler.java
index 40add566e858..975604c1c2c7 100644
--- a/media/java/android/media/tv/tuner/Descrambler.java
+++ b/media/java/android/media/tv/tuner/Descrambler.java
@@ -20,7 +20,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.media.tv.tuner.TunerConstants.Result;
+import android.media.tv.tuner.Tuner.Result;
import android.media.tv.tuner.filter.Filter;
import java.lang.annotation.Retention;
diff --git a/media/java/android/media/tv/tuner/Lnb.java b/media/java/android/media/tv/tuner/Lnb.java
index ea066321f376..525ee4d05ade 100644
--- a/media/java/android/media/tv/tuner/Lnb.java
+++ b/media/java/android/media/tv/tuner/Lnb.java
@@ -23,7 +23,7 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
import android.hardware.tv.tuner.V1_0.Constants;
-import android.media.tv.tuner.TunerConstants.Result;
+import android.media.tv.tuner.Tuner.Result;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 08a33f1f861c..d24d75260395 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -26,7 +26,6 @@ import android.annotation.SystemApi;
import android.content.Context;
import android.hardware.tv.tuner.V1_0.Constants;
import android.media.tv.TvInputService;
-import android.media.tv.tuner.TunerConstants.Result;
import android.media.tv.tuner.dvr.DvrPlayback;
import android.media.tv.tuner.dvr.DvrRecorder;
import android.media.tv.tuner.dvr.OnPlaybackStatusChangedListener;
@@ -44,12 +43,14 @@ import android.media.tv.tuner.frontend.FrontendStatus.FrontendStatusType;
import android.media.tv.tuner.frontend.OnTuneEventListener;
import android.media.tv.tuner.frontend.ScanCallback;
import android.media.tv.tunerresourcemanager.ResourceClientProfile;
+import android.media.tv.tunerresourcemanager.TunerFrontendRequest;
import android.media.tv.tunerresourcemanager.TunerLnbRequest;
import android.media.tv.tunerresourcemanager.TunerResourceManager;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Looper;
import android.os.Message;
+import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -68,6 +69,96 @@ import java.util.concurrent.Executor;
*/
@SystemApi
public class Tuner implements AutoCloseable {
+ /**
+ * Invalid TS packet ID.
+ */
+ public static final int INVALID_TS_PID = Constants.Constant.INVALID_TS_PID;
+ /**
+ * Invalid stream ID.
+ */
+ public static final int INVALID_STREAM_ID = Constants.Constant.INVALID_STREAM_ID;
+ /**
+ * Invalid filter ID.
+ */
+ public static final int INVALID_FILTER_ID = Constants.Constant.INVALID_FILTER_ID;
+ /**
+ * Invalid AV Sync ID.
+ */
+ public static final int INVALID_AV_SYNC_ID = Constants.Constant.INVALID_AV_SYNC_ID;
+ /**
+ * Invalid timestamp.
+ *
+ * <p>Returned by {@link android.media.tv.tuner.filter.TimeFilter#getSourceTime()},
+ * {@link android.media.tv.tuner.filter.TimeFilter#getTimeStamp()}, or
+ * {@link Tuner#getAvSyncTime(int)} when the requested timestamp is not available.
+ *
+ * @see android.media.tv.tuner.filter.TimeFilter#getSourceTime()
+ * @see android.media.tv.tuner.filter.TimeFilter#getTimeStamp()
+ * @see Tuner#getAvSyncTime(int)
+ */
+ public static final long INVALID_TIMESTAMP = -1L;
+
+
+ /** @hide */
+ @IntDef(prefix = "SCAN_TYPE_", value = {SCAN_TYPE_UNDEFINED, SCAN_TYPE_AUTO, SCAN_TYPE_BLIND})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ScanType {}
+ /**
+ * Scan type undefined.
+ */
+ public static final int SCAN_TYPE_UNDEFINED = Constants.FrontendScanType.SCAN_UNDEFINED;
+ /**
+ * Scan type auto.
+ *
+ * <p> Tuner will send {@link android.media.tv.tuner.frontend.ScanCallback#onLocked}
+ */
+ public static final int SCAN_TYPE_AUTO = Constants.FrontendScanType.SCAN_AUTO;
+ /**
+ * Blind scan.
+ *
+ * <p>Frequency range is not specified. The {@link android.media.tv.tuner.Tuner} will scan an
+ * implementation specific range.
+ */
+ public static final int SCAN_TYPE_BLIND = Constants.FrontendScanType.SCAN_BLIND;
+
+
+ /** @hide */
+ @IntDef({RESULT_SUCCESS, RESULT_UNAVAILABLE, RESULT_NOT_INITIALIZED, RESULT_INVALID_STATE,
+ RESULT_INVALID_ARGUMENT, RESULT_OUT_OF_MEMORY, RESULT_UNKNOWN_ERROR})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Result {}
+
+ /**
+ * Operation succeeded.
+ */
+ public static final int RESULT_SUCCESS = Constants.Result.SUCCESS;
+ /**
+ * Operation failed because the corresponding resources are not available.
+ */
+ public static final int RESULT_UNAVAILABLE = Constants.Result.UNAVAILABLE;
+ /**
+ * Operation failed because the corresponding resources are not initialized.
+ */
+ public static final int RESULT_NOT_INITIALIZED = Constants.Result.NOT_INITIALIZED;
+ /**
+ * Operation failed because it's not in a valid state.
+ */
+ public static final int RESULT_INVALID_STATE = Constants.Result.INVALID_STATE;
+ /**
+ * Operation failed because there are invalid arguments.
+ */
+ public static final int RESULT_INVALID_ARGUMENT = Constants.Result.INVALID_ARGUMENT;
+ /**
+ * Memory allocation failed.
+ */
+ public static final int RESULT_OUT_OF_MEMORY = Constants.Result.OUT_OF_MEMORY;
+ /**
+ * Operation failed due to unknown errors.
+ */
+ public static final int RESULT_UNKNOWN_ERROR = Constants.Result.UNKNOWN_ERROR;
+
+
+
private static final String TAG = "MediaTvTuner";
private static final boolean DEBUG = false;
@@ -93,23 +184,27 @@ public class Tuner implements AutoCloseable {
public static final int DVR_TYPE_PLAYBACK = Constants.DvrType.PLAYBACK;
static {
- System.loadLibrary("media_tv_tuner");
- nativeInit();
+ try {
+ System.loadLibrary("media_tv_tuner");
+ nativeInit();
+ } catch (UnsatisfiedLinkError e) {
+ Log.d(TAG, "tuner JNI library not found!");
+ }
}
private final Context mContext;
private final TunerResourceManager mTunerResourceManager;
private final int mClientId;
- private List<Integer> mFrontendIds;
private Frontend mFrontend;
private EventHandler mHandler;
@Nullable
private FrontendInfo mFrontendInfo;
+ private Integer mFrontendHandle;
+ private int mFrontendType = FrontendSettings.TYPE_UNDEFINED;
- private List<Integer> mLnbIds;
private Lnb mLnb;
- private Integer mLnbId;
+ private Integer mLnbHandle;
@Nullable
private OnTuneEventListener mOnTuneEventListener;
@Nullable
@@ -233,7 +328,7 @@ public class Tuner implements AutoCloseable {
/**
* Native method to open frontend of the given ID.
*/
- private native Frontend nativeOpenFrontendById(int id);
+ private native Frontend nativeOpenFrontendByHandle(int handle);
private native int nativeTune(int type, FrontendSettings settings);
private native int nativeStopTune();
private native int nativeScan(int settingsType, FrontendSettings settings, int scanType);
@@ -250,7 +345,7 @@ public class Tuner implements AutoCloseable {
private native TimeFilter nativeOpenTimeFilter();
private native List<Integer> nativeGetLnbIds();
- private native Lnb nativeOpenLnbById(int id);
+ private native Lnb nativeOpenLnbByHandle(int handle);
private native Lnb nativeOpenLnbByName(String name);
private native Descrambler nativeOpenDescrambler();
@@ -359,6 +454,10 @@ public class Tuner implements AutoCloseable {
/**
* Tunes the frontend to using the settings given.
*
+ * <p>Tuner resource manager (TRM) uses the client priority value to decide whether it is able
+ * to get frontend resource. If the client can't get the resource, this call returns {@link
+ * Result#RESULT_UNAVAILABLE}.
+ *
* <p>
* This locks the frontend to a frequency by providing signal
* delivery information. If previous tuning isn't completed, this stop the previous tuning, and
@@ -378,7 +477,8 @@ public class Tuner implements AutoCloseable {
@RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Result
public int tune(@NonNull FrontendSettings settings) {
- TunerUtils.checkTunerPermission(mContext);
+ mFrontendType = settings.getType();
+ checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND);
mFrontendInfo = null;
return nativeTune(settings.getType(), settings);
}
@@ -411,14 +511,15 @@ public class Tuner implements AutoCloseable {
*/
@RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Result
- public int scan(@NonNull FrontendSettings settings, @TunerConstants.ScanType int scanType,
+ public int scan(@NonNull FrontendSettings settings, @ScanType int scanType,
@NonNull @CallbackExecutor Executor executor, @NonNull ScanCallback scanCallback) {
- TunerUtils.checkTunerPermission(mContext);
if (mScanCallback != null || mScanCallbackExecutor != null) {
throw new IllegalStateException(
"Scan already in progress. stopScan must be called before a new scan can be "
+ "started.");
}
+ mFrontendType = settings.getType();
+ checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND);
mScanCallback = scanCallback;
mScanCallbackExecutor = executor;
mFrontendInfo = null;
@@ -446,6 +547,16 @@ public class Tuner implements AutoCloseable {
return retVal;
}
+ private boolean requestFrontend() {
+ int[] feId = new int[1];
+ TunerFrontendRequest request = new TunerFrontendRequest(mClientId, mFrontendType);
+ boolean granted = mTunerResourceManager.requestFrontend(request, feId);
+ if (granted) {
+ mFrontendHandle = feId[0];
+ }
+ return granted;
+ }
+
/**
* Sets Low-Noise Block downconverter (LNB) for satellite frontend.
*
@@ -498,7 +609,7 @@ public class Tuner implements AutoCloseable {
public int getAvSyncHwId(@NonNull Filter filter) {
TunerUtils.checkTunerPermission(mContext);
Integer id = nativeGetAvSyncHwId(filter);
- return id == null ? TunerConstants.INVALID_AV_SYNC_ID : id;
+ return id == null ? INVALID_AV_SYNC_ID : id;
}
/**
@@ -514,7 +625,7 @@ public class Tuner implements AutoCloseable {
public long getAvSyncTime(int avSyncHwId) {
TunerUtils.checkTunerPermission(mContext);
Long time = nativeGetAvSyncTime(avSyncHwId);
- return time == null ? TunerConstants.TIMESTAMP_UNAVAILABLE : time;
+ return time == null ? INVALID_TIMESTAMP : time;
}
/**
@@ -579,22 +690,6 @@ public class Tuner implements AutoCloseable {
return nativeGetDemuxCapabilities();
}
- private List<Integer> getFrontendIds() {
- mFrontendIds = nativeGetFrontendIds();
- return mFrontendIds;
- }
-
- private Frontend openFrontendById(int id) {
- if (mFrontendIds == null) {
- mFrontendIds = getFrontendIds();
- }
- if (!mFrontendIds.contains(id)) {
- return null;
- }
- mFrontend = nativeOpenFrontendById(id);
- return mFrontend;
- }
-
private void onFrontendEvent(int eventType) {
if (mOnTunerEventExecutor != null && mOnTuneEventListener != null) {
mOnTunerEventExecutor.execute(() -> mOnTuneEventListener.onTuneEvent(eventType));
@@ -731,11 +826,9 @@ public class Tuner implements AutoCloseable {
public Lnb openLnb(@CallbackExecutor @NonNull Executor executor, @NonNull LnbCallback cb) {
Objects.requireNonNull(executor, "executor must not be null");
Objects.requireNonNull(cb, "LnbCallback must not be null");
- TunerUtils.checkTunerPermission(mContext);
- if (mLnbId == null && !requestLnb()) {
- return null;
- }
- return nativeOpenLnbById(mLnbId);
+ checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_LNB);
+ // TODO: update JNI code for LNB handle,
+ return nativeOpenLnbByHandle(mLnbHandle);
}
/**
@@ -763,7 +856,7 @@ public class Tuner implements AutoCloseable {
TunerLnbRequest request = new TunerLnbRequest(mClientId);
boolean granted = mTunerResourceManager.requestLnb(request, lnbId);
if (granted) {
- mLnbId = lnbId[0];
+ mLnbHandle = lnbId[0];
}
return granted;
}
@@ -778,22 +871,6 @@ public class Tuner implements AutoCloseable {
return nativeOpenTimeFilter();
}
- private List<Integer> getLnbIds() {
- mLnbIds = nativeGetLnbIds();
- return mLnbIds;
- }
-
- private Lnb openLnbById(int id) {
- if (mLnbIds == null) {
- mLnbIds = getLnbIds();
- }
- if (!mLnbIds.contains(id)) {
- return null;
- }
- mLnb = nativeOpenLnbById(id);
- return mLnb;
- }
-
private void onLnbEvent(int eventType) {
if (mHandler != null) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_ON_LNB_EVENT, eventType, 0));
@@ -857,4 +934,23 @@ public class Tuner implements AutoCloseable {
DvrPlayback dvr = nativeOpenDvrPlayback(bufferSize);
return dvr;
}
+
+ private boolean checkResource(int resourceType) {
+ // TODO: demux and descrambler
+ switch (resourceType) {
+ case TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND: {
+ if (mFrontendHandle == null && !requestFrontend()) {
+ return false;
+ }
+ break;
+ }
+ case TunerResourceManager.TUNER_RESOURCE_TYPE_LNB: {
+ if (mLnbHandle == null && !requestLnb()) {
+ return false;
+ }
+ break;
+ }
+ }
+ return true;
+ }
}
diff --git a/media/java/android/media/tv/tuner/TunerConstants.java b/media/java/android/media/tv/tuner/TunerConstants.java
deleted file mode 100644
index 6d89962d190f..000000000000
--- a/media/java/android/media/tv/tuner/TunerConstants.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.tv.tuner;
-
-import android.annotation.IntDef;
-import android.annotation.SystemApi;
-import android.hardware.tv.tuner.V1_0.Constants;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Constants for tuner framework.
- *
- * @hide
- */
-@SystemApi
-public final class TunerConstants {
- /**
- * Invalid TS packet ID.
- */
- public static final int INVALID_TS_PID = Constants.Constant.INVALID_TS_PID;
- /**
- * Invalid stream ID.
- */
- public static final int INVALID_STREAM_ID = Constants.Constant.INVALID_STREAM_ID;
- /**
- * Invalid filter ID.
- */
- public static final int INVALID_FILTER_ID = Constants.Constant.INVALID_FILTER_ID;
- /**
- * Invalid AV Sync ID.
- */
- public static final int INVALID_AV_SYNC_ID = Constants.Constant.INVALID_AV_SYNC_ID;
- /**
- * Timestamp is unavailable.
- *
- * <p>Returned by {@link android.media.tv.tuner.filter.TimeFilter#getSourceTime()},
- * {@link android.media.tv.tuner.filter.TimeFilter#getTimeStamp()}, or
- * {@link Tuner#getAvSyncTime(int)} when the requested timestamp is not available.
- *
- * @see android.media.tv.tuner.filter.TimeFilter#getSourceTime()
- * @see android.media.tv.tuner.filter.TimeFilter#getTimeStamp()
- * @see Tuner#getAvSyncTime(int)
- * @hide
- */
- public static final long TIMESTAMP_UNAVAILABLE = -1L;
-
- /** @hide */
- @IntDef(prefix = "SCAN_TYPE_", value = {SCAN_TYPE_UNDEFINED, SCAN_TYPE_AUTO, SCAN_TYPE_BLIND})
- @Retention(RetentionPolicy.SOURCE)
- public @interface ScanType {}
- /**
- * Scan type undefined.
- */
- public static final int SCAN_TYPE_UNDEFINED = Constants.FrontendScanType.SCAN_UNDEFINED;
- /**
- * Scan type auto.
- *
- * <p> Tuner will send {@link android.media.tv.tuner.frontend.ScanCallback#onLocked}
- */
- public static final int SCAN_TYPE_AUTO = Constants.FrontendScanType.SCAN_AUTO;
- /**
- * Blind scan.
- *
- * <p>Frequency range is not specified. The {@link android.media.tv.tuner.Tuner} will scan an
- * implementation specific range.
- */
- public static final int SCAN_TYPE_BLIND = Constants.FrontendScanType.SCAN_BLIND;
-
- /** @hide */
- @IntDef({RESULT_SUCCESS, RESULT_UNAVAILABLE, RESULT_NOT_INITIALIZED, RESULT_INVALID_STATE,
- RESULT_INVALID_ARGUMENT, RESULT_OUT_OF_MEMORY, RESULT_UNKNOWN_ERROR})
- @Retention(RetentionPolicy.SOURCE)
- public @interface Result {}
-
- /**
- * Operation succeeded.
- */
- public static final int RESULT_SUCCESS = Constants.Result.SUCCESS;
- /**
- * Operation failed because the corresponding resources are not available.
- */
- public static final int RESULT_UNAVAILABLE = Constants.Result.UNAVAILABLE;
- /**
- * Operation failed because the corresponding resources are not initialized.
- */
- public static final int RESULT_NOT_INITIALIZED = Constants.Result.NOT_INITIALIZED;
- /**
- * Operation failed because it's not in a valid state.
- */
- public static final int RESULT_INVALID_STATE = Constants.Result.INVALID_STATE;
- /**
- * Operation failed because there are invalid arguments.
- */
- public static final int RESULT_INVALID_ARGUMENT = Constants.Result.INVALID_ARGUMENT;
- /**
- * Memory allocation failed.
- */
- public static final int RESULT_OUT_OF_MEMORY = Constants.Result.OUT_OF_MEMORY;
- /**
- * Operation failed due to unknown errors.
- */
- public static final int RESULT_UNKNOWN_ERROR = Constants.Result.UNKNOWN_ERROR;
-
- private TunerConstants() {
- }
-}
diff --git a/media/java/android/media/tv/tuner/TunerUtils.java b/media/java/android/media/tv/tuner/TunerUtils.java
index 2258ee559ea0..c3be12a0bf5e 100644
--- a/media/java/android/media/tv/tuner/TunerUtils.java
+++ b/media/java/android/media/tv/tuner/TunerUtils.java
@@ -171,22 +171,22 @@ public final class TunerUtils {
*/
@Nullable
public static void throwExceptionForResult(
- @TunerConstants.Result int r, @Nullable String msg) {
+ @Tuner.Result int r, @Nullable String msg) {
if (msg == null) {
msg = "";
}
switch (r) {
- case TunerConstants.RESULT_INVALID_ARGUMENT:
+ case Tuner.RESULT_INVALID_ARGUMENT:
throw new IllegalArgumentException(msg);
- case TunerConstants.RESULT_INVALID_STATE:
+ case Tuner.RESULT_INVALID_STATE:
throw new IllegalStateException(msg);
- case TunerConstants.RESULT_NOT_INITIALIZED:
+ case Tuner.RESULT_NOT_INITIALIZED:
throw new IllegalStateException("Invalid state: not initialized. " + msg);
- case TunerConstants.RESULT_OUT_OF_MEMORY:
+ case Tuner.RESULT_OUT_OF_MEMORY:
throw new OutOfMemoryError(msg);
- case TunerConstants.RESULT_UNAVAILABLE:
+ case Tuner.RESULT_UNAVAILABLE:
throw new IllegalStateException("Invalid state: resource unavailable. " + msg);
- case TunerConstants.RESULT_UNKNOWN_ERROR:
+ case Tuner.RESULT_UNKNOWN_ERROR:
throw new RuntimeException("Unknown error" + msg);
default:
break;
diff --git a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
index 37a016ecb4ce..0d10d941a15d 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
@@ -21,7 +21,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.hardware.tv.tuner.V1_0.Constants;
-import android.media.tv.tuner.TunerConstants.Result;
+import android.media.tv.tuner.Tuner.Result;
import android.media.tv.tuner.filter.Filter;
import android.os.ParcelFileDescriptor;
diff --git a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
index d06356cfb6de..dbda7bbad5f2 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
@@ -19,7 +19,7 @@ package android.media.tv.tuner.dvr;
import android.annotation.BytesLong;
import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.media.tv.tuner.TunerConstants.Result;
+import android.media.tv.tuner.Tuner.Result;
import android.media.tv.tuner.filter.Filter;
import android.os.ParcelFileDescriptor;
diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
index 4777fe861420..8dc062274641 100644
--- a/media/java/android/media/tv/tuner/filter/Filter.java
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -22,7 +22,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.hardware.tv.tuner.V1_0.Constants;
-import android.media.tv.tuner.TunerConstants.Result;
+import android.media.tv.tuner.Tuner.Result;
import android.media.tv.tuner.TunerUtils;
import java.lang.annotation.Retention;
diff --git a/media/java/android/media/tv/tuner/filter/TimeFilter.java b/media/java/android/media/tv/tuner/filter/TimeFilter.java
index 371ccc44337a..be0a055b41ef 100644
--- a/media/java/android/media/tv/tuner/filter/TimeFilter.java
+++ b/media/java/android/media/tv/tuner/filter/TimeFilter.java
@@ -17,8 +17,8 @@
package android.media.tv.tuner.filter;
import android.annotation.SystemApi;
-import android.media.tv.tuner.TunerConstants;
-import android.media.tv.tuner.TunerConstants.Result;
+import android.media.tv.tuner.Tuner;
+import android.media.tv.tuner.Tuner.Result;
import android.media.tv.tuner.TunerUtils;
/**
@@ -35,17 +35,6 @@ import android.media.tv.tuner.TunerUtils;
@SystemApi
public class TimeFilter implements AutoCloseable {
- /**
- * Timestamp is unavailable.
- *
- * <p>Returned by {@link #getSourceTime()} or {@link #getTimeStamp()} when the requested
- * timestamp is not available.
- *
- * @see #getSourceTime()
- * @see #getTimeStamp()
- */
- public static final long TIMESTAMP_UNAVAILABLE = -1L;
-
private native int nativeSetTimestamp(long timestamp);
private native int nativeClearTimestamp();
@@ -108,12 +97,12 @@ public class TimeFilter implements AutoCloseable {
*
* @return current timestamp in the time filter. It's based on the 90KHz counter, and it's
* the same format as PTS (Presentation Time Stamp) defined in ISO/IEC 13818-1:2019. The
- * timestamps may or may not be related to PTS or DTS. Returns {@link #TIMESTAMP_UNAVAILABLE}
- * if the timestamp is never set.
+ * timestamps may or may not be related to PTS or DTS. Returns
+ * {@link Tuner#INVALID_TIMESTAMP} if the timestamp is never set.
*/
public long getTimeStamp() {
if (!mEnable) {
- return TIMESTAMP_UNAVAILABLE;
+ return Tuner.INVALID_TIMESTAMP;
}
return nativeGetTimestamp();
}
@@ -126,11 +115,11 @@ public class TimeFilter implements AutoCloseable {
* @return first timestamp of incoming data stream. It's based on the 90KHz counter, and
* it's the same format as PTS (Presentation Time Stamp) defined in ISO/IEC 13818-1:2019.
* The timestamps may or may not be related to PTS or DTS. Returns
- * {@link #TIMESTAMP_UNAVAILABLE} if the timestamp is not available.
+ * {@link Tuner#INVALID_TIMESTAMP} if the timestamp is not available.
*/
public long getSourceTime() {
if (!mEnable) {
- return TIMESTAMP_UNAVAILABLE;
+ return Tuner.INVALID_TIMESTAMP;
}
return nativeGetSourceTime();
}
@@ -144,7 +133,7 @@ public class TimeFilter implements AutoCloseable {
@Override
public void close() {
int res = nativeClose();
- if (res != TunerConstants.RESULT_SUCCESS) {
+ if (res != Tuner.RESULT_SUCCESS) {
TunerUtils.throwExceptionForResult(res, "Failed to close time filter.");
}
}
diff --git a/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java
index 7b85fa81adb6..382cc8559843 100644
--- a/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java
@@ -17,6 +17,7 @@
package android.media.tv.tuner.frontend;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
@@ -213,13 +214,24 @@ public class AnalogFrontendSettings extends FrontendSettings {
/**
* Builder for {@link AnalogFrontendSettings}.
*/
- public static class Builder extends FrontendSettings.Builder<Builder> {
+ public static class Builder {
+ private int mFrequency;
private int mSignalType;
private int mSifStandard;
private Builder() {}
/**
+ * Sets frequency in Hz.
+ */
+ @NonNull
+ @IntRange(from = 1)
+ public Builder setFrequency(int frequency) {
+ mFrequency = frequency;
+ return this;
+ }
+
+ /**
* Sets analog signal type.
*/
@NonNull
@@ -244,10 +256,5 @@ public class AnalogFrontendSettings extends FrontendSettings {
public AnalogFrontendSettings build() {
return new AnalogFrontendSettings(mFrequency, mSignalType, mSifStandard);
}
-
- @Override
- Builder self() {
- return this;
- }
}
}
diff --git a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
index b40ab00afb30..1394716a1aac 100644
--- a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
@@ -17,6 +17,7 @@
package android.media.tv.tuner.frontend;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
@@ -325,7 +326,8 @@ public class Atsc3FrontendSettings extends FrontendSettings {
/**
* Builder for {@link Atsc3FrontendSettings}.
*/
- public static class Builder extends FrontendSettings.Builder<Builder> {
+ public static class Builder {
+ private int mFrequency;
private int mBandwidth;
private int mDemodOutputFormat;
private Atsc3PlpSettings[] mPlpSettings;
@@ -334,6 +336,16 @@ public class Atsc3FrontendSettings extends FrontendSettings {
}
/**
+ * Sets frequency in Hz.
+ */
+ @NonNull
+ @IntRange(from = 1)
+ public Builder setFrequency(int frequency) {
+ mFrequency = frequency;
+ return this;
+ }
+
+ /**
* Sets bandwidth.
*/
@NonNull
@@ -366,11 +378,6 @@ public class Atsc3FrontendSettings extends FrontendSettings {
return new Atsc3FrontendSettings(
mFrequency, mBandwidth, mDemodOutputFormat, mPlpSettings);
}
-
- @Override
- Builder self() {
- return this;
- }
}
@Override
diff --git a/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java
index fc82a1c7a6a0..53352f0b0d23 100644
--- a/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java
@@ -17,6 +17,7 @@
package android.media.tv.tuner.frontend;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
@@ -91,13 +92,24 @@ public class AtscFrontendSettings extends FrontendSettings {
/**
* Builder for {@link AtscFrontendSettings}.
*/
- public static class Builder extends FrontendSettings.Builder<Builder> {
+ public static class Builder {
+ private int mFrequency;
private int mModulation;
private Builder() {
}
/**
+ * Sets frequency in Hz.
+ */
+ @NonNull
+ @IntRange(from = 1)
+ public Builder setFrequency(int frequency) {
+ mFrequency = frequency;
+ return this;
+ }
+
+ /**
* Sets Modulation.
*/
@NonNull
@@ -113,11 +125,6 @@ public class AtscFrontendSettings extends FrontendSettings {
public AtscFrontendSettings build() {
return new AtscFrontendSettings(mFrequency, mModulation);
}
-
- @Override
- Builder self() {
- return this;
- }
}
@Override
diff --git a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
index 705d5207cc5f..6d585702aaf3 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
@@ -17,6 +17,7 @@
package android.media.tv.tuner.frontend;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
@@ -144,17 +145,17 @@ public class DvbcFrontendSettings extends FrontendSettings {
private final int mModulation;
- private final long mFec;
+ private final long mInnerFec;
private final int mSymbolRate;
private final int mOuterFec;
private final int mAnnex;
private final int mSpectralInversion;
- private DvbcFrontendSettings(int frequency, int modulation, long fec, int symbolRate,
+ private DvbcFrontendSettings(int frequency, int modulation, long innerFec, int symbolRate,
int outerFec, int annex, int spectralInversion) {
super(frequency);
mModulation = modulation;
- mFec = fec;
+ mInnerFec = innerFec;
mSymbolRate = symbolRate;
mOuterFec = outerFec;
mAnnex = annex;
@@ -172,8 +173,8 @@ public class DvbcFrontendSettings extends FrontendSettings {
* Gets Inner Forward Error Correction.
*/
@InnerFec
- public long getFec() {
- return mFec;
+ public long getInnerFec() {
+ return mInnerFec;
}
/**
* Gets Symbol Rate in symbols per second.
@@ -218,9 +219,10 @@ public class DvbcFrontendSettings extends FrontendSettings {
/**
* Builder for {@link DvbcFrontendSettings}.
*/
- public static class Builder extends FrontendSettings.Builder<Builder> {
+ public static class Builder {
+ private int mFrequency;
private int mModulation;
- private long mFec;
+ private long mInnerFec;
private int mSymbolRate;
private int mOuterFec;
private int mAnnex;
@@ -230,6 +232,16 @@ public class DvbcFrontendSettings extends FrontendSettings {
}
/**
+ * Sets frequency in Hz.
+ */
+ @NonNull
+ @IntRange(from = 1)
+ public Builder setFrequency(int frequency) {
+ mFrequency = frequency;
+ return this;
+ }
+
+ /**
* Sets Modulation.
*/
@NonNull
@@ -241,8 +253,8 @@ public class DvbcFrontendSettings extends FrontendSettings {
* Sets Inner Forward Error Correction.
*/
@NonNull
- public Builder setFec(@InnerFec long fec) {
- mFec = fec;
+ public Builder setInnerFec(@InnerFec long fec) {
+ mInnerFec = fec;
return this;
}
/**
@@ -283,13 +295,8 @@ public class DvbcFrontendSettings extends FrontendSettings {
*/
@NonNull
public DvbcFrontendSettings build() {
- return new DvbcFrontendSettings(mFrequency, mModulation, mFec, mSymbolRate, mOuterFec,
- mAnnex, mSpectralInversion);
- }
-
- @Override
- Builder self() {
- return this;
+ return new DvbcFrontendSettings(mFrequency, mModulation, mInnerFec, mSymbolRate,
+ mOuterFec, mAnnex, mSpectralInversion);
}
}
diff --git a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
index 4a4fed596f21..9c45dd11a37b 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
@@ -17,6 +17,7 @@
package android.media.tv.tuner.frontend;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -303,7 +304,8 @@ public class DvbsFrontendSettings extends FrontendSettings {
/**
* Builder for {@link DvbsFrontendSettings}.
*/
- public static class Builder extends FrontendSettings.Builder<Builder> {
+ public static class Builder {
+ private int mFrequency;
private int mModulation;
private DvbsCodeRate mCodeRate;
private int mSymbolRate;
@@ -317,6 +319,16 @@ public class DvbsFrontendSettings extends FrontendSettings {
}
/**
+ * Sets frequency in Hz.
+ */
+ @NonNull
+ @IntRange(from = 1)
+ public Builder setFrequency(int frequency) {
+ mFrequency = frequency;
+ return this;
+ }
+
+ /**
* Sets Modulation.
*/
@NonNull
@@ -389,11 +401,6 @@ public class DvbsFrontendSettings extends FrontendSettings {
return new DvbsFrontendSettings(mFrequency, mModulation, mCodeRate, mSymbolRate,
mRolloff, mPilot, mInputStreamId, mStandard, mVcmMode);
}
-
- @Override
- Builder self() {
- return this;
- }
}
@Override
diff --git a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
index 1510b2d399f7..4accabbc557b 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
@@ -17,6 +17,7 @@
package android.media.tv.tuner.frontend;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
@@ -506,7 +507,8 @@ public class DvbtFrontendSettings extends FrontendSettings {
/**
* Builder for {@link DvbtFrontendSettings}.
*/
- public static class Builder extends FrontendSettings.Builder<Builder> {
+ public static class Builder {
+ private int mFrequency;
private int mTransmissionMode;
private int mBandwidth;
private int mConstellation;
@@ -525,6 +527,16 @@ public class DvbtFrontendSettings extends FrontendSettings {
}
/**
+ * Sets frequency in Hz.
+ */
+ @NonNull
+ @IntRange(from = 1)
+ public Builder setFrequency(int frequency) {
+ mFrequency = frequency;
+ return this;
+ }
+
+ /**
* Sets Transmission Mode.
*/
@NonNull
@@ -638,11 +650,6 @@ public class DvbtFrontendSettings extends FrontendSettings {
mConstellation, mHierarchy, mHpCodeRate, mLpCodeRate, mGuardInterval,
mIsHighPriority, mStandard, mIsMiso, mPlpMode, mPlpId, mPlpGroupId);
}
-
- @Override
- Builder self() {
- return this;
- }
}
@Override
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
index 9071526e79f4..2f2fa97f4e7a 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
@@ -17,9 +17,7 @@
package android.media.tv.tuner.frontend;
import android.annotation.IntDef;
-import android.annotation.IntRange;
import android.annotation.LongDef;
-import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.hardware.tv.tuner.V1_0.Constants;
@@ -265,27 +263,4 @@ public abstract class FrontendSettings {
public int getFrequency() {
return mFrequency;
}
-
- /**
- * Builder for {@link FrontendSettings}.
- *
- * @param <T> The subclass to be built.
- */
- public abstract static class Builder<T extends Builder<T>> {
- /* package */ int mFrequency;
-
- /* package */ Builder() {}
-
- /**
- * Sets frequency in Hz.
- */
- @NonNull
- @IntRange(from = 1)
- public T setFrequency(int frequency) {
- mFrequency = frequency;
- return self();
- }
-
- /* package */ abstract T self();
- }
}
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
index 63de03344219..ea91ee9250b5 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
@@ -304,7 +304,7 @@ public class FrontendStatus {
* and ETSI EN 302 307-2 V1.1.1.
*/
@FrontendSettings.InnerFec
- public long getFec() {
+ public long getInnerFec() {
if (mInnerFec == null) {
throw new IllegalStateException();
}
diff --git a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
index 9b0e53351f59..f385379265ce 100644
--- a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
@@ -17,6 +17,7 @@
package android.media.tv.tuner.frontend;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
@@ -224,7 +225,8 @@ public class Isdbs3FrontendSettings extends FrontendSettings {
/**
* Builder for {@link Isdbs3FrontendSettings}.
*/
- public static class Builder extends FrontendSettings.Builder<Builder> {
+ public static class Builder {
+ private int mFrequency;
private int mStreamId;
private int mStreamIdType;
private int mModulation;
@@ -236,6 +238,16 @@ public class Isdbs3FrontendSettings extends FrontendSettings {
}
/**
+ * Sets frequency in Hz.
+ */
+ @NonNull
+ @IntRange(from = 1)
+ public Builder setFrequency(int frequency) {
+ mFrequency = frequency;
+ return this;
+ }
+
+ /**
* Sets Stream ID.
*/
@NonNull
@@ -292,11 +304,6 @@ public class Isdbs3FrontendSettings extends FrontendSettings {
return new Isdbs3FrontendSettings(mFrequency, mStreamId, mStreamIdType, mModulation,
mCodeRate, mSymbolRate, mRolloff);
}
-
- @Override
- Builder self() {
- return this;
- }
}
@Override
diff --git a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
index 14c08b1af582..4667b9b952e0 100644
--- a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
@@ -17,6 +17,7 @@
package android.media.tv.tuner.frontend;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
@@ -208,7 +209,8 @@ public class IsdbsFrontendSettings extends FrontendSettings {
/**
* Builder for {@link IsdbsFrontendSettings}.
*/
- public static class Builder extends FrontendSettings.Builder<Builder> {
+ public static class Builder {
+ private int mFrequency;
private int mStreamId;
private int mStreamIdType;
private int mModulation;
@@ -220,6 +222,16 @@ public class IsdbsFrontendSettings extends FrontendSettings {
}
/**
+ * Sets frequency in Hz.
+ */
+ @NonNull
+ @IntRange(from = 1)
+ public Builder setFrequency(int frequency) {
+ mFrequency = frequency;
+ return this;
+ }
+
+ /**
* Sets Stream ID.
*/
@NonNull
@@ -276,11 +288,6 @@ public class IsdbsFrontendSettings extends FrontendSettings {
return new IsdbsFrontendSettings(mFrequency, mStreamId, mStreamIdType, mModulation,
mCodeRate, mSymbolRate, mRolloff);
}
-
- @Override
- Builder self() {
- return this;
- }
}
@Override
diff --git a/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java
index de3c80dda440..b607623290e6 100644
--- a/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java
@@ -17,6 +17,7 @@
package android.media.tv.tuner.frontend;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
@@ -202,7 +203,8 @@ public class IsdbtFrontendSettings extends FrontendSettings {
/**
* Builder for {@link IsdbtFrontendSettings}.
*/
- public static class Builder extends FrontendSettings.Builder<Builder> {
+ public static class Builder {
+ private int mFrequency;
private int mModulation;
private int mBandwidth;
private int mMode;
@@ -214,6 +216,16 @@ public class IsdbtFrontendSettings extends FrontendSettings {
}
/**
+ * Sets frequency in Hz.
+ */
+ @NonNull
+ @IntRange(from = 1)
+ public Builder setFrequency(int frequency) {
+ mFrequency = frequency;
+ return this;
+ }
+
+ /**
* Sets Modulation.
*/
@NonNull
@@ -270,11 +282,6 @@ public class IsdbtFrontendSettings extends FrontendSettings {
return new IsdbtFrontendSettings(mFrequency, mModulation, mBandwidth, mMode, mCodeRate,
mGuardInterval, mServiceAreaId);
}
-
- @Override
- Builder self() {
- return this;
- }
}
@Override
diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
index 524b6c2e26e1..63a71e272e53 100644
--- a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
+++ b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
@@ -17,6 +17,7 @@
package android.media.tv.tunerresourcemanager;
import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresFeature;
@@ -27,6 +28,8 @@ import android.os.Binder;
import android.os.RemoteException;
import android.util.Log;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.Executor;
/**
@@ -60,12 +63,25 @@ public class TunerResourceManager {
private static final String TAG = "TunerResourceManager";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- public static final int INVALID_FRONTEND_ID = -1;
- public static final int INVALID_CAS_SESSION_RESOURCE_ID = -1;
- public static final int INVALID_LNB_ID = -1;
- public static final int INVALID_TV_INPUT_DEVICE_ID = -1;
- public static final int INVALID_TV_INPUT_PORT_ID = -1;
public static final int INVALID_RESOURCE_HANDLE = -1;
+ /**
+ * Tuner resource type to help generate resource handle
+ */
+ @IntDef({
+ TUNER_RESOURCE_TYPE_FRONTEND,
+ TUNER_RESOURCE_TYPE_DEMUX,
+ TUNER_RESOURCE_TYPE_DESCRAMBLER,
+ TUNER_RESOURCE_TYPE_LNB,
+ TUNER_RESOURCE_TYPE_CAS_SESSION,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TunerResourceType {}
+
+ public static final int TUNER_RESOURCE_TYPE_FRONTEND = 0;
+ public static final int TUNER_RESOURCE_TYPE_DEMUX = 1;
+ public static final int TUNER_RESOURCE_TYPE_DESCRAMBLER = 2;
+ public static final int TUNER_RESOURCE_TYPE_LNB = 3;
+ public static final int TUNER_RESOURCE_TYPE_CAS_SESSION = 4;
private final ITunerResourceManager mService;
private final int mUserId;
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index a37c9e52f5dd..bbeb45179c78 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -1410,6 +1410,10 @@ static sp<IDescrambler> getDescrambler(JNIEnv *env, jobject descrambler) {
return (IDescrambler *)env->GetLongField(descrambler, gFields.descramblerContext);
}
+static uint32_t getResourceIdFromHandle(jint handle) {
+ return (handle & 0x00ff0000) >> 16;
+}
+
static DemuxPid getDemuxPid(int pidType, int pid) {
DemuxPid demuxPid;
if ((int)pidType == 1) {
@@ -1968,8 +1972,10 @@ static jobject android_media_tv_Tuner_get_frontend_ids(JNIEnv *env, jobject thiz
return tuner->getFrontendIds();
}
-static jobject android_media_tv_Tuner_open_frontend_by_id(JNIEnv *env, jobject thiz, jint id) {
+static jobject android_media_tv_Tuner_open_frontend_by_handle(
+ JNIEnv *env, jobject thiz, jint handle) {
sp<JTuner> tuner = getTuner(env, thiz);
+ uint32_t id = getResourceIdFromHandle(handle);
return tuner->openFrontendById(id);
}
@@ -2045,8 +2051,9 @@ static jobject android_media_tv_Tuner_get_lnb_ids(JNIEnv *env, jobject thiz) {
return tuner->getLnbIds();
}
-static jobject android_media_tv_Tuner_open_lnb_by_id(JNIEnv *env, jobject thiz, jint id) {
+static jobject android_media_tv_Tuner_open_lnb_by_handle(JNIEnv *env, jobject thiz, jint handle) {
sp<JTuner> tuner = getTuner(env, thiz);
+ uint32_t id = getResourceIdFromHandle(handle);
return tuner->openLnbById(id);
}
@@ -2924,8 +2931,8 @@ static const JNINativeMethod gTunerMethods[] = {
{ "nativeSetup", "()V", (void *)android_media_tv_Tuner_native_setup },
{ "nativeGetFrontendIds", "()Ljava/util/List;",
(void *)android_media_tv_Tuner_get_frontend_ids },
- { "nativeOpenFrontendById", "(I)Landroid/media/tv/tuner/Tuner$Frontend;",
- (void *)android_media_tv_Tuner_open_frontend_by_id },
+ { "nativeOpenFrontendByHandle", "(I)Landroid/media/tv/tuner/Tuner$Frontend;",
+ (void *)android_media_tv_Tuner_open_frontend_by_handle },
{ "nativeTune", "(ILandroid/media/tv/tuner/frontend/FrontendSettings;)I",
(void *)android_media_tv_Tuner_tune },
{ "nativeStopTune", "()I", (void *)android_media_tv_Tuner_stop_tune },
@@ -2950,8 +2957,8 @@ static const JNINativeMethod gTunerMethods[] = {
(void *)android_media_tv_Tuner_open_time_filter },
{ "nativeGetLnbIds", "()Ljava/util/List;",
(void *)android_media_tv_Tuner_get_lnb_ids },
- { "nativeOpenLnbById", "(I)Landroid/media/tv/tuner/Lnb;",
- (void *)android_media_tv_Tuner_open_lnb_by_id },
+ { "nativeOpenLnbByHandle", "(I)Landroid/media/tv/tuner/Lnb;",
+ (void *)android_media_tv_Tuner_open_lnb_by_handle },
{ "nativeOpenLnbByName", "(Ljava/lang/String;)Landroid/media/tv/tuner/Lnb;",
(void *)android_media_tv_Tuner_open_lnb_by_name },
{ "nativeOpenDescrambler", "()Landroid/media/tv/tuner/Descrambler;",
diff --git a/media/jni/audioeffect/Visualizer.cpp b/media/jni/audioeffect/Visualizer.cpp
index 83f3b6ed6217..efeb3352d393 100644
--- a/media/jni/audioeffect/Visualizer.cpp
+++ b/media/jni/audioeffect/Visualizer.cpp
@@ -120,8 +120,9 @@ status_t Visualizer::setCaptureCallBack(capture_cbk_t cbk, void* user, uint32_t
}
if (mCaptureThread != 0) {
+ sp<CaptureThread> t = mCaptureThread;
mCaptureLock.unlock();
- mCaptureThread->requestExitAndWait();
+ t->requestExitAndWait();
mCaptureLock.lock();
}
diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp
index 1362433bb26e..f9a77f474c50 100644
--- a/media/jni/audioeffect/android_media_Visualizer.cpp
+++ b/media/jni/audioeffect/android_media_Visualizer.cpp
@@ -196,7 +196,6 @@ static void captureCallback(void* user,
callbackInfo->visualizer_ref,
NATIVE_EVENT_PCM_CAPTURE,
samplingrate,
- 0,
jArray);
}
}
@@ -217,7 +216,6 @@ static void captureCallback(void* user,
callbackInfo->visualizer_ref,
NATIVE_EVENT_FFT_CAPTURE,
samplingrate,
- 0,
jArray);
}
}
@@ -286,7 +284,7 @@ android_media_visualizer_native_init(JNIEnv *env)
// Get the postEvent method
fields.midPostNativeEvent = env->GetStaticMethodID(
fields.clazzEffect,
- "postEventFromNative", "(Ljava/lang/Object;IIILjava/lang/Object;)V");
+ "postEventFromNative", "(Ljava/lang/Object;II[B)V");
if (fields.midPostNativeEvent == NULL) {
ALOGE("Can't find Visualizer.%s", "postEventFromNative");
return;
@@ -343,7 +341,7 @@ static void android_media_visualizer_effect_callback(int32_t event,
fields.midPostNativeEvent,
callbackInfo->visualizer_ref,
NATIVE_EVENT_SERVER_DIED,
- 0, 0, NULL);
+ 0, NULL);
}
}
diff --git a/media/tests/MediaRouter/AndroidManifest.xml b/media/tests/MediaRouter/AndroidManifest.xml
index 95465007d5ea..d9806f3cbb1d 100644
--- a/media/tests/MediaRouter/AndroidManifest.xml
+++ b/media/tests/MediaRouter/AndroidManifest.xml
@@ -19,7 +19,7 @@
<application android:label="@string/app_name">
<uses-library android:name="android.test.runner" />
- <service android:name=".SampleMediaRoute2ProviderService"
+ <service android:name=".StubMediaRoute2ProviderService"
android:exported="true">
<intent-filter>
<action android:name="android.media.MediaRoute2ProviderService" />
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
index 230b9e4bfaad..a97baafac992 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
@@ -22,16 +22,16 @@ import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE;
import static android.media.MediaRoute2ProviderService.REASON_REJECTED;
import static android.media.MediaRoute2ProviderService.REQUEST_ID_NONE;
-import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.FEATURE_SAMPLE;
-import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.FEATURE_SPECIAL;
-import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.ROUTE_ID1;
-import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.ROUTE_ID2;
-import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.ROUTE_ID5_TO_TRANSFER_TO;
-import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.ROUTE_ID_FIXED_VOLUME;
-import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.ROUTE_ID_SPECIAL_FEATURE;
-import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.ROUTE_ID_VARIABLE_VOLUME;
-import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.ROUTE_NAME2;
-import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.VOLUME_MAX;
+import static com.android.mediaroutertest.StubMediaRoute2ProviderService.FEATURE_SAMPLE;
+import static com.android.mediaroutertest.StubMediaRoute2ProviderService.FEATURE_SPECIAL;
+import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_ID1;
+import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_ID2;
+import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_ID5_TO_TRANSFER_TO;
+import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_ID_FIXED_VOLUME;
+import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_ID_SPECIAL_FEATURE;
+import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_ID_VARIABLE_VOLUME;
+import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_NAME2;
+import static com.android.mediaroutertest.StubMediaRoute2ProviderService.VOLUME_MAX;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -115,7 +115,7 @@ public class MediaRouter2ManagerTest {
// unregister callbacks
clearCallbacks();
- SampleMediaRoute2ProviderService instance = SampleMediaRoute2ProviderService.getInstance();
+ StubMediaRoute2ProviderService instance = StubMediaRoute2ProviderService.getInstance();
if (instance != null) {
instance.setProxy(null);
}
@@ -161,8 +161,8 @@ public class MediaRouter2ManagerTest {
MediaRoute2Info routeToRemove = routes.get(ROUTE_ID2);
- SampleMediaRoute2ProviderService sInstance =
- SampleMediaRoute2ProviderService.getInstance();
+ StubMediaRoute2ProviderService sInstance =
+ StubMediaRoute2ProviderService.getInstance();
assertNotNull(sInstance);
sInstance.removeRoute(ROUTE_ID2);
assertTrue(removedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
@@ -413,12 +413,12 @@ public class MediaRouter2ManagerTest {
Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
MediaRoute2Info volRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
- SampleMediaRoute2ProviderService instance = SampleMediaRoute2ProviderService.getInstance();
+ StubMediaRoute2ProviderService instance = StubMediaRoute2ProviderService.getInstance();
assertNotNull(instance);
final List<Long> requestIds = new ArrayList<>();
final CountDownLatch onSetRouteVolumeLatch = new CountDownLatch(1);
- instance.setProxy(new SampleMediaRoute2ProviderService.Proxy() {
+ instance.setProxy(new StubMediaRoute2ProviderService.Proxy() {
@Override
public void onSetRouteVolume(String routeId, int volume, long requestId) {
requestIds.add(requestId);
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/StubMediaRoute2ProviderService.java
index f05d8ad79dee..6d46ba582ddc 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/StubMediaRoute2ProviderService.java
@@ -36,7 +36,7 @@ import java.util.Objects;
import javax.annotation.concurrent.GuardedBy;
-public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService {
+public class StubMediaRoute2ProviderService extends MediaRoute2ProviderService {
private static final String TAG = "SampleMR2ProviderSvc";
private static final Object sLock = new Object();
@@ -74,7 +74,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService
private int mNextSessionId = 1000;
@GuardedBy("sLock")
- private static SampleMediaRoute2ProviderService sInstance;
+ private static StubMediaRoute2ProviderService sInstance;
private Proxy mProxy;
private void initializeRoutes() {
@@ -127,7 +127,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService
mRoutes.put(variableVolumeRoute.getId(), variableVolumeRoute);
}
- public static SampleMediaRoute2ProviderService getInstance() {
+ public static StubMediaRoute2ProviderService getInstance() {
synchronized (sLock) {
return sInstance;
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
index 8292d304af2d..14d5bd53a9ab 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
@@ -27,9 +27,11 @@ import com.android.systemui.car.CarDeviceProvisionedControllerImpl;
import com.android.systemui.dagger.SystemUIRootComponent;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerImpl;
+import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.EnhancedEstimates;
import com.android.systemui.power.EnhancedEstimatesImpl;
+import com.android.systemui.qs.tileimpl.QSFactoryImpl;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsImplementation;
import com.android.systemui.stackdivider.DividerModule;
@@ -107,6 +109,10 @@ abstract class CarSystemUIModule {
BatteryControllerImpl controllerImpl);
@Binds
+ @Singleton
+ public abstract QSFactory provideQSFactory(QSFactoryImpl qsFactoryImpl);
+
+ @Binds
abstract DockManager bindDockManager(DockManagerImpl dockManager);
@Binds
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
index b63162ba8090..3ee92bd7f3d0 100644
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
@@ -238,10 +238,12 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks
}
buildNavBarContent();
- // If the UI was rebuilt (day/night change) while the keyguard was up we need to
- // correctly respect that state.
+ // If the UI was rebuilt (day/night change or user change) while the keyguard was up we need
+ // to correctly respect that state.
if (mKeyguardStateControllerLazy.get().isShowing()) {
mCarNavigationBarController.showAllKeyguardButtons(isDeviceSetupForUser());
+ } else {
+ mCarNavigationBarController.hideAllKeyguardButtons(isDeviceSetupForUser());
}
// Upon restarting the Navigation Bar, CarFacetButtonController should immediately apply the
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarTest.java
index c04e47f557f7..f2748b89c95c 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarTest.java
@@ -46,8 +46,6 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import dagger.Lazy;
-
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
@@ -68,12 +66,8 @@ public class CarNavigationBarTest extends SysuiTestCase {
@Mock
private ButtonSelectionStateListener mButtonSelectionStateListener;
@Mock
- private Lazy<KeyguardStateController> mKeyguardStateControllerLazy;
- @Mock
private KeyguardStateController mKeyguardStateController;
@Mock
- private Lazy<NavigationBarController> mNavigationBarControllerLazy;
- @Mock
private NavigationBarController mNavigationBarController;
@Mock
private SuperStatusBarViewFactory mSuperStatusBarViewFactory;
@@ -89,13 +83,11 @@ public class CarNavigationBarTest extends SysuiTestCase {
mCarNavigationBar = new CarNavigationBar(mContext, mCarNavigationBarController,
mWindowManager, mDeviceProvisionedController, new CommandQueue(mContext),
mAutoHideController, mButtonSelectionStateListener, mHandler,
- mKeyguardStateControllerLazy, mNavigationBarControllerLazy,
+ () -> mKeyguardStateController, () -> mNavigationBarController,
mSuperStatusBarViewFactory, mButtonSelectionStateController);
StatusBarWindowView statusBarWindowView = (StatusBarWindowView) LayoutInflater.from(
mContext).inflate(R.layout.super_status_bar, /* root= */ null);
when(mSuperStatusBarViewFactory.getStatusBarWindowView()).thenReturn(statusBarWindowView);
- when(mNavigationBarControllerLazy.get()).thenReturn(mNavigationBarController);
- when(mKeyguardStateControllerLazy.get()).thenReturn(mKeyguardStateController);
when(mKeyguardStateController.isShowing()).thenReturn(false);
mDependency.injectMockDependency(WindowManager.class);
// Needed to inflate top navigation bar.
@@ -119,4 +111,44 @@ public class CarNavigationBarTest extends SysuiTestCase {
verify(mButtonSelectionStateListener).onTaskStackChanged();
}
+
+ @Test
+ public void restartNavBars_newUserNotSetupWithKeyguardShowing_showsKeyguardButtons() {
+ ArgumentCaptor<CarDeviceProvisionedController.DeviceProvisionedListener>
+ deviceProvisionedCallbackCaptor = ArgumentCaptor.forClass(
+ CarDeviceProvisionedController.DeviceProvisionedListener.class);
+ when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true);
+ mCarNavigationBar.start();
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ // switching the currentUserSetup value to force restart the navbars.
+ when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(false);
+ verify(mDeviceProvisionedController).addCallback(deviceProvisionedCallbackCaptor.capture());
+
+ deviceProvisionedCallbackCaptor.getValue().onUserSwitched();
+ waitForIdleSync(mHandler);
+
+ verify(mCarNavigationBarController).showAllKeyguardButtons(false);
+ }
+
+ @Test
+ public void restartNavbars_newUserIsSetupWithKeyguardHidden_hidesKeyguardButtons() {
+ ArgumentCaptor<CarDeviceProvisionedController.DeviceProvisionedListener>
+ deviceProvisionedCallbackCaptor = ArgumentCaptor.forClass(
+ CarDeviceProvisionedController.DeviceProvisionedListener.class);
+ when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true);
+ mCarNavigationBar.start();
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ // switching the currentUserSetup value to force restart the navbars.
+ when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(false);
+ verify(mDeviceProvisionedController).addCallback(deviceProvisionedCallbackCaptor.capture());
+ deviceProvisionedCallbackCaptor.getValue().onUserSwitched();
+ waitForIdleSync(mHandler);
+ when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true);
+ when(mKeyguardStateController.isShowing()).thenReturn(false);
+
+ deviceProvisionedCallbackCaptor.getValue().onUserSetupChanged();
+ waitForIdleSync(mHandler);
+
+ verify(mCarNavigationBarController).hideAllKeyguardButtons(true);
+ }
}
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 00f6bcb6c548..a7970668124b 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1039,7 +1039,7 @@
<!-- Title for the accessibility preference to configure display color space correction. [CHAR LIMIT=NONE] -->
<string name="accessibility_display_daltonizer_preference_title">Color correction</string>
<!-- Subtitle for the accessibility preference to configure display color space correction. [CHAR LIMIT=NONE] -->
- <string name="accessibility_display_daltonizer_preference_subtitle">Color correction helps the device display more accurate colors. Color correction may be helpful for people with colorblindness.</string>
+ <string name="accessibility_display_daltonizer_preference_subtitle"><![CDATA[Color correction allows you to adjust how colors are displayed on your device]]></string>
<!-- Summary shown for color space correction preference when its value is overridden by another preference [CHAR LIMIT=35] -->
<string name="daltonizer_type_overridden">Overridden by <xliff:g id="title" example="Simulate color space">%1$s</xliff:g></string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index 18eb1879d7cf..3c7856048860 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -218,7 +218,7 @@ public class A2dpProfile implements LocalBluetoothProfile {
}
public boolean supportsHighQualityAudio(BluetoothDevice device) {
- BluetoothDevice bluetoothDevice = (device == null) ? device : mService.getActiveDevice();
+ BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice();
if (bluetoothDevice == null) {
return false;
}
@@ -227,7 +227,7 @@ public class A2dpProfile implements LocalBluetoothProfile {
}
public boolean isHighQualityAudioEnabled(BluetoothDevice device) {
- BluetoothDevice bluetoothDevice = (device == null) ? device : mService.getActiveDevice();
+ BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice();
if (bluetoothDevice == null) {
return false;
}
@@ -253,7 +253,7 @@ public class A2dpProfile implements LocalBluetoothProfile {
}
public void setHighQualityAudioEnabled(BluetoothDevice device, boolean enabled) {
- BluetoothDevice bluetoothDevice = (device == null) ? device : mService.getActiveDevice();
+ BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice();
if (bluetoothDevice == null) {
return;
}
@@ -272,7 +272,7 @@ public class A2dpProfile implements LocalBluetoothProfile {
}
public String getHighQualityAudioOptionLabel(BluetoothDevice device) {
- BluetoothDevice bluetoothDevice = (device == null) ? device : mService.getActiveDevice();
+ BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice();
int unknownCodecId = R.string.bluetooth_profile_a2dp_high_quality_unknown_codec;
if (bluetoothDevice == null || !supportsHighQualityAudio(device)
|| getConnectionStatus(device) != BluetoothProfile.STATE_CONNECTED) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 4a0d16c8d2db..9ae9b4a46227 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -49,11 +49,13 @@ public class LocalMediaManager implements BluetoothCallback {
@Retention(RetentionPolicy.SOURCE)
@IntDef({MediaDeviceState.STATE_CONNECTED,
MediaDeviceState.STATE_CONNECTING,
- MediaDeviceState.STATE_DISCONNECTED})
+ MediaDeviceState.STATE_DISCONNECTED,
+ MediaDeviceState.STATE_CONNECTING_FAILED})
public @interface MediaDeviceState {
- int STATE_CONNECTED = 1;
- int STATE_CONNECTING = 2;
- int STATE_DISCONNECTED = 3;
+ int STATE_CONNECTED = 0;
+ int STATE_CONNECTING = 1;
+ int STATE_DISCONNECTED = 2;
+ int STATE_CONNECTING_FAILED = 3;
}
private final Collection<DeviceCallback> mCallbacks = new CopyOnWriteArrayList<>();
@@ -93,6 +95,15 @@ public class LocalMediaManager implements BluetoothCallback {
mCallbacks.remove(callback);
}
+ /**
+ * Creates a LocalMediaManager with references to given managers.
+ *
+ * It will obtain a {@link LocalBluetoothManager} by calling
+ * {@link LocalBluetoothManager#getInstance} and create an {@link InfoMediaManager} passing
+ * that bluetooth manager.
+ *
+ * It will use {@link BluetoothAdapter#getDefaultAdapter()] for setting the bluetooth adapter.
+ */
public LocalMediaManager(Context context, String packageName, Notification notification) {
mContext = context;
mPackageName = packageName;
@@ -108,12 +119,18 @@ public class LocalMediaManager implements BluetoothCallback {
new InfoMediaManager(context, packageName, notification, mLocalBluetoothManager);
}
+ /**
+ * Creates a LocalMediaManager with references to given managers.
+ *
+ * It will use {@link BluetoothAdapter#getDefaultAdapter()] for setting the bluetooth adapter.
+ */
public LocalMediaManager(Context context, LocalBluetoothManager localBluetoothManager,
InfoMediaManager infoMediaManager, String packageName) {
mContext = context;
mLocalBluetoothManager = localBluetoothManager;
mInfoMediaManager = infoMediaManager;
mPackageName = packageName;
+ mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
}
/**
@@ -126,6 +143,7 @@ public class LocalMediaManager implements BluetoothCallback {
final CachedBluetoothDevice cachedDevice =
((BluetoothMediaDevice) device).getCachedDevice();
if (!cachedDevice.isConnected() && !cachedDevice.isBusy()) {
+ device.setState(MediaDeviceState.STATE_CONNECTING);
cachedDevice.connect();
return;
}
@@ -140,6 +158,7 @@ public class LocalMediaManager implements BluetoothCallback {
mCurrentConnectedDevice.disconnect();
}
+ device.setState(MediaDeviceState.STATE_CONNECTING);
if (TextUtils.isEmpty(mPackageName)) {
mInfoMediaManager.connectDeviceWithoutPackageName(device);
} else {
@@ -421,6 +440,7 @@ public class LocalMediaManager implements BluetoothCallback {
MediaDevice connectDevice = getMediaDeviceById(mMediaDevices, id);
connectDevice = connectDevice != null
? connectDevice : updateCurrentConnectedDevice();
+ connectDevice.setState(MediaDeviceState.STATE_CONNECTED);
if (connectDevice == mCurrentConnectedDevice) {
Log.d(TAG, "onConnectedDeviceChanged() this device all ready connected!");
@@ -438,6 +458,11 @@ public class LocalMediaManager implements BluetoothCallback {
@Override
public void onRequestFailed(int reason) {
+ for (MediaDevice device : mMediaDevices) {
+ if (device.getState() == MediaDeviceState.STATE_CONNECTING) {
+ device.setState(MediaDeviceState.STATE_CONNECTING_FAILED);
+ }
+ }
dispatchOnRequestFailed(reason);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 33c3d7e039b3..39e6a129a992 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -51,6 +51,7 @@ public abstract class MediaDevice implements Comparable<MediaDevice> {
int mType;
private int mConnectedRecord;
+ private int mState;
protected final Context mContext;
protected final MediaRoute2Info mRouteInfo;
@@ -200,6 +201,22 @@ public abstract class MediaDevice implements Comparable<MediaDevice> {
}
/**
+ * Set current device's state
+ */
+ public void setState(@LocalMediaManager.MediaDeviceState int state) {
+ mState = state;
+ }
+
+ /**
+ * Get current device's state
+ *
+ * @return state of device
+ */
+ public @LocalMediaManager.MediaDeviceState int getState() {
+ return mState;
+ }
+
+ /**
* Rules:
* 1. If there is one of the connected devices identified as a carkit, this carkit will
* be always on the top of the device list. Rule 2 and Rule 3 can’t overrule this rule.
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
index 18d8f14eb88d..559187d2c38c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
@@ -16,6 +16,8 @@
package com.android.settingslib.media;
+import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -27,6 +29,8 @@ import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
+import android.media.MediaRoute2Info;
+import android.media.MediaRouter2Manager;
import com.android.settingslib.bluetooth.A2dpProfile;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -53,10 +57,13 @@ import java.util.List;
@Config(shadows = {ShadowBluetoothAdapter.class})
public class LocalMediaManagerTest {
+ private static final String TEST_DEVICE_NAME_1 = "device_name_1";
+ private static final String TEST_DEVICE_NAME_2 = "device_name_2";
private static final String TEST_DEVICE_ID_1 = "device_id_1";
private static final String TEST_DEVICE_ID_2 = "device_id_2";
private static final String TEST_DEVICE_ID_3 = "device_id_3";
private static final String TEST_CURRENT_DEVICE_ID = "currentDevice_id";
+ private static final String TEST_PACKAGE_NAME = "com.test.playmusic";
@Mock
private BluetoothMediaManager mBluetoothMediaManager;
@@ -72,10 +79,18 @@ public class LocalMediaManagerTest {
private A2dpProfile mA2dpProfile;
@Mock
private LocalBluetoothProfileManager mLocalProfileManager;
+ @Mock
+ private MediaRouter2Manager mMediaRouter2Manager;
+ @Mock
+ private MediaRoute2Info mRouteInfo1;
+ @Mock
+ private MediaRoute2Info mRouteInfo2;
private Context mContext;
private LocalMediaManager mLocalMediaManager;
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
+ private InfoMediaDevice mInfoMediaDevice1;
+ private InfoMediaDevice mInfoMediaDevice2;
@Before
public void setUp() {
@@ -84,14 +99,20 @@ public class LocalMediaManagerTest {
final List<BluetoothDevice> bluetoothDevices = new ArrayList<>();
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
mShadowBluetoothAdapter.setMostRecentlyConnectedDevices(bluetoothDevices);
-
+ when(mRouteInfo1.getName()).thenReturn(TEST_DEVICE_NAME_1);
+ when(mRouteInfo1.getId()).thenReturn(TEST_DEVICE_ID_1);
+ when(mRouteInfo2.getName()).thenReturn(TEST_DEVICE_NAME_2);
+ when(mRouteInfo2.getId()).thenReturn(TEST_DEVICE_ID_2);
when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalProfileManager);
when(mLocalProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
when(mLocalProfileManager.getHearingAidProfile()).thenReturn(mHapProfile);
+ mInfoMediaDevice1 = new InfoMediaDevice(mContext, mMediaRouter2Manager, mRouteInfo1,
+ TEST_PACKAGE_NAME);
+ mInfoMediaDevice2 = new InfoMediaDevice(mContext, mMediaRouter2Manager, mRouteInfo2,
+ TEST_PACKAGE_NAME);
mLocalMediaManager = new LocalMediaManager(mContext, mLocalBluetoothManager,
mInfoMediaManager, "com.test.packagename");
- mLocalMediaManager.mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
}
@Test
@@ -123,6 +144,32 @@ public class LocalMediaManagerTest {
}
@Test
+ public void connectDevice_deviceNotEqualCurrentConnectedDevice_isConnectingState() {
+ mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice1);
+ mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice2);
+ mLocalMediaManager.mCurrentConnectedDevice = mInfoMediaDevice1;
+
+ mLocalMediaManager.registerCallback(mCallback);
+ mLocalMediaManager.connectDevice(mInfoMediaDevice2);
+
+ assertThat(mInfoMediaDevice2.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
+ .STATE_CONNECTING);
+ }
+
+ @Test
+ public void connectDevice_deviceEqualCurrentConnectedDevice_notConnectingState() {
+ mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice1);
+ mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice2);
+ mLocalMediaManager.mCurrentConnectedDevice = mInfoMediaDevice1;
+
+ mLocalMediaManager.registerCallback(mCallback);
+ mLocalMediaManager.connectDevice(mInfoMediaDevice1);
+
+ assertThat(mInfoMediaDevice1.getState()).isNotEqualTo(LocalMediaManager.MediaDeviceState
+ .STATE_CONNECTING);
+ }
+
+ @Test
public void connectDevice_bluetoothDeviceNotConnected_connectBluetoothDevice() {
final MediaDevice device = mock(BluetoothMediaDevice.class);
final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
@@ -388,6 +435,21 @@ public class LocalMediaManagerTest {
}
@Test
+ public void onConnectedDeviceChanged_isConnectedState() {
+ mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice1);
+ mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice2);
+ mInfoMediaDevice1.setState(LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED);
+
+ assertThat(mInfoMediaDevice1.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
+ .STATE_DISCONNECTED);
+ mLocalMediaManager.registerCallback(mCallback);
+ mLocalMediaManager.mMediaDeviceCallback.onConnectedDeviceChanged(TEST_DEVICE_ID_1);
+
+ assertThat(mInfoMediaDevice1.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
+ .STATE_CONNECTED);
+ }
+
+ @Test
public void onDeviceAttributesChanged_shouldDispatchDeviceListUpdate() {
mLocalMediaManager.registerCallback(mCallback);
@@ -397,6 +459,26 @@ public class LocalMediaManagerTest {
}
@Test
+ public void onRequestFailed_checkDevicesState() {
+ mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice1);
+ mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice2);
+ mInfoMediaDevice1.setState(LocalMediaManager.MediaDeviceState.STATE_CONNECTING);
+ mInfoMediaDevice2.setState(LocalMediaManager.MediaDeviceState.STATE_CONNECTED);
+
+ assertThat(mInfoMediaDevice1.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
+ .STATE_CONNECTING);
+ assertThat(mInfoMediaDevice2.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
+ .STATE_CONNECTED);
+ mLocalMediaManager.registerCallback(mCallback);
+ mLocalMediaManager.mMediaDeviceCallback.onRequestFailed(REASON_UNKNOWN_ERROR);
+
+ assertThat(mInfoMediaDevice1.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
+ .STATE_CONNECTING_FAILED);
+ assertThat(mInfoMediaDevice2.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
+ .STATE_CONNECTED);
+ }
+
+ @Test
public void getActiveMediaDevice_checkList() {
final List<MediaDevice> devices = new ArrayList<>();
final MediaDevice device1 = mock(MediaDevice.class);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
index 3f29b72b978d..4b08387275be 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
@@ -448,4 +448,23 @@ public class MediaDeviceTest {
assertThat(mInfoMediaDevice1.getClientAppLabel()).isEqualTo(
mContext.getResources().getString(R.string.unknown));
}
+
+ @Test
+ public void setState_verifyGetState() {
+ mInfoMediaDevice1.setState(LocalMediaManager.MediaDeviceState.STATE_CONNECTED);
+ assertThat(mInfoMediaDevice1.getState()).isEqualTo(
+ LocalMediaManager.MediaDeviceState.STATE_CONNECTED);
+
+ mInfoMediaDevice1.setState(LocalMediaManager.MediaDeviceState.STATE_CONNECTING);
+ assertThat(mInfoMediaDevice1.getState()).isEqualTo(
+ LocalMediaManager.MediaDeviceState.STATE_CONNECTING);
+
+ mInfoMediaDevice1.setState(LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED);
+ assertThat(mInfoMediaDevice1.getState()).isEqualTo(
+ LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED);
+
+ mInfoMediaDevice1.setState(LocalMediaManager.MediaDeviceState.STATE_CONNECTING_FAILED);
+ assertThat(mInfoMediaDevice1.getState()).isEqualTo(
+ LocalMediaManager.MediaDeviceState.STATE_CONNECTING_FAILED);
+ }
}
diff --git a/packages/SystemUI/res/drawable/ic_bubble_overflow_button.xml b/packages/SystemUI/res/drawable/ic_bubble_overflow_button.xml
index 64b57c5aac2b..3acebc12a807 100644
--- a/packages/SystemUI/res/drawable/ic_bubble_overflow_button.xml
+++ b/packages/SystemUI/res/drawable/ic_bubble_overflow_button.xml
@@ -14,10 +14,11 @@ 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:viewportHeight="24"
- android:viewportWidth="24"
- android:height="52dp"
- android:width="52dp">
- <path android:fillColor="#1A73E8"
- android:pathData="M6,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM18,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
-</vector> \ No newline at end of file
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:width="24dp"
+ android:height="24dp">
+ <path
+ android:fillColor="#1A73E8"
+ android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/global_screenshot.xml b/packages/SystemUI/res/layout/global_screenshot.xml
index a76f96115fa9..d506e7e8e700 100644
--- a/packages/SystemUI/res/layout/global_screenshot.xml
+++ b/packages/SystemUI/res/layout/global_screenshot.xml
@@ -58,13 +58,15 @@
android:elevation="@dimen/screenshot_preview_elevation"
android:visibility="gone"
android:background="@drawable/screenshot_rounded_corners"
- android:adjustViewBounds="true"/>
+ android:adjustViewBounds="true"
+ android:contentDescription="@string/screenshot_preview_description"/>
<FrameLayout
android:id="@+id/global_screenshot_dismiss_button"
android:layout_width="@dimen/screenshot_dismiss_button_tappable_size"
android:layout_height="@dimen/screenshot_dismiss_button_tappable_size"
android:elevation="7dp"
- android:visibility="gone">
+ android:visibility="gone"
+ android:contentDescription="@string/screenshot_dismiss_ui_description">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 217abbe3778a..06e027d7cae7 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -117,7 +117,7 @@
<!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
<string name="quick_settings_tiles_stock" translatable="false">
- wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,screenrecord
+ wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,screenrecord,reverse
</string>
<!-- The tiles to display in QuickSettings -->
@@ -477,6 +477,9 @@
<!-- Allow PIP to resize to a slightly bigger state upon touch/showing the menu -->
<bool name="config_pipEnableResizeForMenu">true</bool>
+ <!-- Allow PIP to enable round corner, see also R.dimen.pip_corner_radius -->
+ <bool name="config_pipEnableRoundCorner">false</bool>
+
<!-- SystemUI Plugins that can be loaded on user builds. -->
<string-array name="config_pluginWhitelist" translatable="false">
<item>com.android.systemui</item>
@@ -526,4 +529,7 @@
<!-- Flag to turn on the rendering of the above path or not -->
<bool name="config_enableDisplayCutoutProtection">false</bool>
+ <!-- Respect drawable/rounded.xml intrinsic size for multiple radius corner path customization -->
+ <bool name="config_roundedCornerMultipleRadius">false</bool>
+
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 35430733a118..93bafdbee91d 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -233,6 +233,10 @@
<!-- Notification text displayed when we fail to take a screenshot. [CHAR LIMIT=100] -->
<string name="screenshot_failed_to_capture_text">Taking screenshots isn\'t allowed by the app or
your organization</string>
+ <!-- Content description indicating that tapping a button will dismiss the screenshots UI [CHAR LIMIT=NONE] -->
+ <string name="screenshot_dismiss_ui_description">Dismiss screenshot</string>
+ <!-- Content description indicating that tapping will open an app to view/edit the screenshot. [CHAR LIMIT=NONE] -->
+ <string name="screenshot_preview_description">Open screenshot</string>
<!-- Notification title displayed for screen recording [CHAR LIMIT=50]-->
<string name="screenrecord_name">Screen Recorder</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 4885ade23f6a..07bd3a0567e6 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -78,6 +78,9 @@ public class QuickStepContract {
public static final int SYSUI_STATE_QUICK_SETTINGS_EXPANDED = 1 << 11;
// Winscope tracing is enabled
public static final int SYSUI_STATE_TRACING_ENABLED = 1 << 12;
+ // The Assistant gesture should be constrained. It is up to the launcher implementation to
+ // decide how to constrain it
+ public static final int SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED = 1 << 13;
@Retention(RetentionPolicy.SOURCE)
@IntDef({SYSUI_STATE_SCREEN_PINNING,
@@ -92,7 +95,8 @@ public class QuickStepContract {
SYSUI_STATE_OVERVIEW_DISABLED,
SYSUI_STATE_HOME_DISABLED,
SYSUI_STATE_SEARCH_DISABLED,
- SYSUI_STATE_TRACING_ENABLED
+ SYSUI_STATE_TRACING_ENABLED,
+ SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED
})
public @interface SystemUiStateFlags {}
@@ -112,6 +116,8 @@ public class QuickStepContract {
str.add((flags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0 ? "a11y_click" : "");
str.add((flags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0 ? "a11y_long_click" : "");
str.add((flags & SYSUI_STATE_TRACING_ENABLED) != 0 ? "tracing" : "");
+ str.add((flags & SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED) != 0
+ ? "asst_gesture_constrain" : "");
return str.toString();
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestReceiver.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestReceiver.java
index d33c653f2cb7..29100ef8f70f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestReceiver.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestReceiver.java
@@ -90,7 +90,7 @@ public class SurfaceViewRequestReceiver {
view.setTranslationX((surfaceControl.getWidth() - scale * viewSize.getWidth()) / 2);
view.setTranslationY((surfaceControl.getHeight() - scale * viewSize.getHeight()) / 2);
- mSurfaceControlViewHost.addView(view, layoutParams);
+ mSurfaceControlViewHost.setView(view, layoutParams);
}
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 431c4519bd81..90df124c85ca 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -2740,6 +2740,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver);
mBroadcastDispatcher.unregisterReceiver(mBroadcastAllReceiver);
+
+ mHandler.removeCallbacksAndMessages(null);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 537a812d19cb..a8a3caecc780 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -50,6 +50,7 @@ import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
+import android.graphics.drawable.VectorDrawable;
import android.hardware.display.DisplayManager;
import android.os.Handler;
import android.os.HandlerThread;
@@ -126,8 +127,9 @@ public class ScreenDecorations extends SystemUI implements Tunable {
private WindowManager mWindowManager;
private int mRotation;
private SecureSetting mColorInversionSetting;
- private boolean mPendingRotationChange;
private Handler mHandler;
+ private boolean mPendingRotationChange;
+ private boolean mIsRoundedCornerMultipleRadius;
private CameraAvailabilityListener.CameraTransitionCallback mCameraTransitionCallback =
new CameraAvailabilityListener.CameraTransitionCallback() {
@@ -194,6 +196,8 @@ public class ScreenDecorations extends SystemUI implements Tunable {
mRotation = mContext.getDisplay().getRotation();
mWindowManager = mContext.getSystemService(WindowManager.class);
mDisplayManager = mContext.getSystemService(DisplayManager.class);
+ mIsRoundedCornerMultipleRadius = mContext.getResources().getBoolean(
+ R.bool.config_roundedCornerMultipleRadius);
updateRoundedCornerRadii();
setupDecorations();
setupCameraListener();
@@ -572,15 +576,22 @@ public class ScreenDecorations extends SystemUI implements Tunable {
com.android.internal.R.dimen.rounded_corner_radius_top);
final int newRoundedDefaultBottom = mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.rounded_corner_radius_bottom);
-
final boolean roundedCornersChanged = mRoundedDefault != newRoundedDefault
|| mRoundedDefaultBottom != newRoundedDefaultBottom
|| mRoundedDefaultTop != newRoundedDefaultTop;
if (roundedCornersChanged) {
- mRoundedDefault = newRoundedDefault;
- mRoundedDefaultTop = newRoundedDefaultTop;
- mRoundedDefaultBottom = newRoundedDefaultBottom;
+ // If config_roundedCornerMultipleRadius set as true, ScreenDecorations respect the
+ // max(width, height) size of drawable/rounded.xml instead of rounded_corner_radius
+ if (mIsRoundedCornerMultipleRadius) {
+ final VectorDrawable d = (VectorDrawable) mContext.getDrawable(R.drawable.rounded);
+ mRoundedDefault = Math.max(d.getIntrinsicWidth(), d.getIntrinsicHeight());
+ mRoundedDefaultTop = mRoundedDefaultBottom = mRoundedDefault;
+ } else {
+ mRoundedDefault = newRoundedDefault;
+ mRoundedDefaultTop = newRoundedDefaultTop;
+ mRoundedDefaultBottom = newRoundedDefaultBottom;
+ }
onTuningChanged(SIZE, null);
}
}
@@ -631,7 +642,8 @@ public class ScreenDecorations extends SystemUI implements Tunable {
}
private boolean hasRoundedCorners() {
- return mRoundedDefault > 0 || mRoundedDefaultBottom > 0 || mRoundedDefaultTop > 0;
+ return mRoundedDefault > 0 || mRoundedDefaultBottom > 0 || mRoundedDefaultTop > 0
+ || mIsRoundedCornerMultipleRadius;
}
private boolean shouldShowRoundedCorner(@BoundsPosition int pos) {
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index 5077e18b06e4..cc4ee89f2208 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -1,6 +1,9 @@
package com.android.systemui.assist;
+import static android.view.Display.DEFAULT_DISPLAY;
+
import static com.android.systemui.DejankUtils.whitelistIpcs;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -43,6 +46,7 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.settingslib.applications.InterestingConfigChanges;
import com.android.systemui.R;
import com.android.systemui.assist.ui.DefaultUiController;
+import com.android.systemui.model.SysUiState;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -51,6 +55,8 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import javax.inject.Inject;
import javax.inject.Singleton;
+import dagger.Lazy;
+
/**
* Class to manage everything related to assist in SystemUI.
*/
@@ -98,6 +104,9 @@ public class AssistManager {
public static final String INVOCATION_TYPE_KEY = "invocation_type";
protected static final String ACTION_KEY = "action";
protected static final String SHOW_ASSIST_HANDLES_ACTION = "show_assist_handles";
+ protected static final String SET_ASSIST_GESTURE_CONSTRAINED_ACTION =
+ "set_assist_gesture_constrained";
+ protected static final String CONSTRAINED_KEY = "should_constrain";
public static final int INVOCATION_TYPE_GESTURE = 1;
public static final int INVOCATION_TYPE_ACTIVE_EDGE = 2;
@@ -120,6 +129,7 @@ public class AssistManager {
private final PhoneStateMonitor mPhoneStateMonitor;
private final AssistHandleBehaviorController mHandleController;
private final UiController mUiController;
+ protected final Lazy<SysUiState> mSysUiState;
private AssistOrbContainer mView;
private final DeviceProvisionedController mDeviceProvisionedController;
@@ -185,7 +195,8 @@ public class AssistManager {
CommandQueue commandQueue,
PhoneStateMonitor phoneStateMonitor,
OverviewProxyService overviewProxyService,
- ConfigurationController configurationController) {
+ ConfigurationController configurationController,
+ Lazy<SysUiState> sysUiState) {
mContext = context;
mDeviceProvisionedController = controller;
mCommandQueue = commandQueue;
@@ -206,6 +217,8 @@ public class AssistManager {
mUiController = new DefaultUiController(mContext);
+ mSysUiState = sysUiState;
+
overviewProxyService.addCallback(new OverviewProxyService.OverviewProxyListener() {
@Override
public void onAssistantProgress(float progress) {
@@ -243,8 +256,16 @@ public class AssistManager {
if (VERBOSE) {
Log.v(TAG, "UI hints received");
}
- if (SHOW_ASSIST_HANDLES_ACTION.equals(hints.getString(ACTION_KEY))) {
+
+ String action = hints.getString(ACTION_KEY);
+ if (SHOW_ASSIST_HANDLES_ACTION.equals(action)) {
requestAssistHandles();
+ } else if (SET_ASSIST_GESTURE_CONSTRAINED_ACTION.equals(action)) {
+ mSysUiState.get()
+ .setFlag(
+ SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED,
+ hints.getBoolean(CONSTRAINED_KEY, false))
+ .commitUpdate(DEFAULT_DISPLAY);
}
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 406e7cef9bfa..b39dd1aea4a9 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -52,6 +52,7 @@ import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.ZenModeConfig;
import android.util.ArraySet;
@@ -146,13 +147,15 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
private BubbleData mBubbleData;
@Nullable private BubbleStackView mStackView;
private BubbleIconFactory mBubbleIconFactory;
- private int mMaxBubbles;
// Tracks the id of the current (foreground) user.
private int mCurrentUserId;
// Saves notification keys of active bubbles when users are switched.
private final SparseSetArray<String> mSavedBubbleKeysPerUser;
+ // Used when ranking updates occur and we check if things should bubble / unbubble
+ private NotificationListenerService.Ranking mTmpRanking;
+
// Saves notification keys of user created "fake" bubbles so that we can allow notifications
// like these to bubble by default. Doesn't persist across reboots, not a long-term solution.
private final HashSet<String> mUserCreatedBubbles;
@@ -338,7 +341,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
configurationController.addCallback(this /* configurationListener */);
- mMaxBubbles = mContext.getResources().getInteger(R.integer.bubbles_max_rendered);
mBubbleData = data;
mBubbleData.setListener(mBubbleDataListener);
mBubbleData.setSuppressionChangedListener(new NotificationSuppressionChangedListener() {
@@ -939,9 +941,29 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
}
+ /**
+ * Called when NotificationListener has received adjusted notification rank and reapplied
+ * filtering and sorting. This is used to dismiss or create bubbles based on changes in
+ * permissions on the notification channel or the global setting.
+ *
+ * @param rankingMap the updated ranking map from NotificationListenerService
+ */
private void onRankingUpdated(RankingMap rankingMap) {
- // Forward to BubbleData to block any bubbles which should no longer be shown
- mBubbleData.notificationRankingUpdated(rankingMap);
+ if (mTmpRanking == null) {
+ mTmpRanking = new NotificationListenerService.Ranking();
+ }
+ String[] orderedKeys = rankingMap.getOrderedKeys();
+ for (int i = 0; i < orderedKeys.length; i++) {
+ String key = orderedKeys[i];
+ NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(key);
+ rankingMap.getRanking(key, mTmpRanking);
+ if (mBubbleData.hasBubbleWithKey(key) && !mTmpRanking.canBubble()) {
+ mBubbleData.notificationEntryRemoved(entry, BubbleController.DISMISS_BLOCKED);
+ } else if (entry != null && mTmpRanking.isBubble()) {
+ entry.setFlagBubble(true);
+ onEntryUpdated(entry);
+ }
+ }
}
@SuppressWarnings("FieldCanBeLocal")
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index cf5a4d3840cc..ff5e13c25859 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -26,7 +26,6 @@ import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.service.notification.NotificationListenerService;
-import android.service.notification.NotificationListenerService.RankingMap;
import android.util.Log;
import android.util.Pair;
@@ -207,12 +206,13 @@ public class BubbleData {
// Preserve new order for next repack, which sorts by last updated time.
bubble.markUpdatedAt(mTimeSource.currentTimeMillis());
- setSelectedBubbleInternal(bubble);
mOverflowBubbles.remove(bubble);
-
bubble.inflate(
- b -> notificationEntryUpdated(bubble, /* suppressFlyout */
- false, /* showInShade */ true),
+ b -> {
+ notificationEntryUpdated(bubble, /* suppressFlyout */
+ false, /* showInShade */ true);
+ setSelectedBubbleInternal(bubble);
+ },
mContext, stack, factory);
dispatchPendingChanges();
}
@@ -225,6 +225,14 @@ public class BubbleData {
Bubble getOrCreateBubble(NotificationEntry entry) {
Bubble bubble = getBubbleWithKey(entry.getKey());
if (bubble == null) {
+ for (int i = 0; i < mOverflowBubbles.size(); i++) {
+ Bubble b = mOverflowBubbles.get(i);
+ if (b.getKey().equals(entry.getKey())) {
+ mOverflowBubbles.remove(b);
+ mPendingBubbles.add(b);
+ return b;
+ }
+ }
// Check for it in pending
for (int i = 0; i < mPendingBubbles.size(); i++) {
Bubble b = mPendingBubbles.get(i);
@@ -289,31 +297,6 @@ public class BubbleData {
}
/**
- * Called when NotificationListener has received adjusted notification rank and reapplied
- * filtering and sorting. This is used to dismiss any bubbles which should no longer be shown
- * due to changes in permissions on the notification channel or the global setting.
- *
- * @param rankingMap the updated ranking map from NotificationListenerService
- */
- public void notificationRankingUpdated(RankingMap rankingMap) {
- if (mTmpRanking == null) {
- mTmpRanking = new NotificationListenerService.Ranking();
- }
-
- String[] orderedKeys = rankingMap.getOrderedKeys();
- for (int i = 0; i < orderedKeys.length; i++) {
- String key = orderedKeys[i];
- if (hasBubbleWithKey(key)) {
- rankingMap.getRanking(key, mTmpRanking);
- if (!mTmpRanking.canBubble()) {
- doRemove(key, BubbleController.DISMISS_BLOCKED);
- }
- }
- }
- dispatchPendingChanges();
- }
-
- /**
* Adds a group key indicating that the summary for this group should be suppressed.
*
* @param groupKey the group key of the group whose summary should be suppressed.
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 0778d5b54493..e666fb5f1af9 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -471,7 +471,8 @@ public class BubbleStackView extends FrameLayout {
mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding);
int elevation = res.getDimensionPixelSize(R.dimen.bubble_elevation);
- mStackAnimationController = new StackAnimationController(floatingContentCoordinator);
+ mStackAnimationController = new StackAnimationController(
+ floatingContentCoordinator, this::getBubbleCount);
mExpandedAnimationController = new ExpandedAnimationController(
mDisplaySize, mExpandedViewPadding, res.getConfiguration().orientation);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
index 3eaa90c985d7..9b5dc31eea2f 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
@@ -409,11 +409,8 @@ public class ExpandedAnimationController
mBubblesMaxRendered = res.getInteger(R.integer.bubbles_max_rendered);
// Includes overflow button.
- // TODO(b/148675523) this is a temporary work around; change back once we have proper fix.
-// float totalGapWidth = getWidthForDisplayingBubbles() - (mExpandedViewPadding * 2)
-// - (mBubblesMaxRendered + 1) * mBubbleSizePx;
- float totalGapWidth = getAvailableScreenWidth(true /* includeStableInsets */)
- - (mExpandedViewPadding * 2) - (mBubblesMaxRendered + 1) * mBubbleSizePx;
+ float totalGapWidth = getWidthForDisplayingBubbles() - (mExpandedViewPadding * 2)
+ - (mBubblesMaxRendered + 1) * mBubbleSizePx;
mSpaceBetweenBubbles = totalGapWidth / mBubblesMaxRendered;
// Ensure that all child views are at 1x scale, and visible, in case they were animating
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
index 86387f1cc546..7ee162e03dbc 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
@@ -43,6 +43,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Set;
+import java.util.function.IntSupplier;
/**
* Animation controller for bubbles when they're in their stacked state. Stacked bubbles sit atop
@@ -88,6 +89,9 @@ public class StackAnimationController extends
private static final int SPRING_AFTER_FLING_STIFFNESS = 750;
private static final float SPRING_AFTER_FLING_DAMPING_RATIO = 0.85f;
+ /** Sentinel value for unset position value. */
+ private static final float UNSET = -Float.MIN_VALUE;
+
/**
* Minimum fling velocity required to trigger moving the stack from one side of the screen to
* the other.
@@ -132,7 +136,7 @@ public class StackAnimationController extends
* The Y position of the stack before the IME became visible, or {@link Float#MIN_VALUE} if the
* IME is not visible or the user moved the stack since the IME became visible.
*/
- private float mPreImeY = Float.MIN_VALUE;
+ private float mPreImeY = UNSET;
/**
* Animations on the stack position itself, which would have been started in
@@ -241,9 +245,15 @@ public class StackAnimationController extends
}
};
+ /** Returns the number of 'real' bubbles (excluding the overflow bubble). */
+ private IntSupplier mBubbleCountSupplier;
+
public StackAnimationController(
- FloatingContentCoordinator floatingContentCoordinator) {
+ FloatingContentCoordinator floatingContentCoordinator,
+ IntSupplier bubbleCountSupplier) {
mFloatingContentCoordinator = floatingContentCoordinator;
+ mBubbleCountSupplier = bubbleCountSupplier;
+
}
/**
@@ -256,7 +266,7 @@ public class StackAnimationController extends
// If we manually move the bubbles with the IME open, clear the return point since we don't
// want the stack to snap away from the new position.
- mPreImeY = Float.MIN_VALUE;
+ mPreImeY = UNSET;
moveFirstBubbleWithStackFollowing(DynamicAnimation.TRANSLATION_X, x);
moveFirstBubbleWithStackFollowing(DynamicAnimation.TRANSLATION_Y, y);
@@ -505,26 +515,27 @@ public class StackAnimationController extends
* Animates the stack either away from the newly visible IME, or back to its original position
* due to the IME going away.
*
- * @return The destination Y value of the stack due to the IME movement.
+ * @return The destination Y value of the stack due to the IME movement (or the current position
+ * of the stack if it's not moving).
*/
public float animateForImeVisibility(boolean imeVisible) {
final float maxBubbleY = getAllowableStackPositionRegion().bottom;
- float destinationY = Float.MIN_VALUE;
+ float destinationY = UNSET;
if (imeVisible) {
// Stack is lower than it should be and overlaps the now-visible IME.
- if (mStackPosition.y > maxBubbleY && mPreImeY == Float.MIN_VALUE) {
+ if (mStackPosition.y > maxBubbleY && mPreImeY == UNSET) {
mPreImeY = mStackPosition.y;
destinationY = maxBubbleY;
}
} else {
- if (mPreImeY > Float.MIN_VALUE) {
+ if (mPreImeY != UNSET) {
destinationY = mPreImeY;
- mPreImeY = Float.MIN_VALUE;
+ mPreImeY = UNSET;
}
}
- if (destinationY > Float.MIN_VALUE) {
+ if (destinationY != UNSET) {
springFirstBubbleWithStackFollowing(
DynamicAnimation.TRANSLATION_Y,
getSpringForce(DynamicAnimation.TRANSLATION_Y, /* view */ null)
@@ -535,7 +546,7 @@ public class StackAnimationController extends
notifyFloatingCoordinatorStackAnimatingTo(mStackPosition.x, destinationY);
}
- return destinationY;
+ return destinationY != UNSET ? destinationY : mStackPosition.y;
}
/**
@@ -588,7 +599,7 @@ public class StackAnimationController extends
mLayout.getHeight()
- mBubbleSize
- mBubblePaddingTop
- - (mImeHeight > Float.MIN_VALUE ? mImeHeight + mBubblePaddingTop : 0f)
+ - (mImeHeight != UNSET ? mImeHeight + mBubblePaddingTop : 0f)
- Math.max(
insets.getStableInsetBottom(),
insets.getDisplayCutout() != null
@@ -669,6 +680,8 @@ public class StackAnimationController extends
new SpringAnimation(this, firstBubbleProperty)
.setSpring(spring)
.addEndListener((dynamicAnimation, b, v, v1) -> {
+ mRestingStackPosition.set(mStackPosition);
+
if (after != null) {
for (Runnable callback : after) {
callback.run();
@@ -736,7 +749,7 @@ public class StackAnimationController extends
return;
}
- if (mLayout.getChildCount() == 1) {
+ if (getBubbleCount() == 1) {
// If this is the first child added, position the stack in its starting position.
moveStackToStartPosition();
} else if (isStackPositionSet() && mLayout.indexOfChild(child) == 0) {
@@ -758,7 +771,7 @@ public class StackAnimationController extends
.start();
// If there are other bubbles, pull them into the correct position.
- if (mLayout.getChildCount() > 0) {
+ if (getBubbleCount() > 0) {
animationForChildAtIndex(0).translationX(mStackPosition.x).start();
} else {
// When all children are removed ensure stack position is sane
@@ -979,6 +992,11 @@ public class StackAnimationController extends
return mMagnetizedStack;
}
+ /** Returns the number of 'real' bubbles (excluding overflow). */
+ private int getBubbleCount() {
+ return mBubbleCountSupplier.getAsInt();
+ }
+
/**
* FloatProperty that uses {@link #moveFirstBubbleWithStackFollowing} to set the first bubble's
* translation and animate the rest of the stack with it. A DynamicAnimation can animate this
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index 956b4aa177ea..8c572fe8f842 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -26,9 +26,11 @@ import androidx.annotation.Nullable;
import com.android.keyguard.KeyguardViewController;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerImpl;
+import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.EnhancedEstimates;
import com.android.systemui.power.EnhancedEstimatesImpl;
+import com.android.systemui.qs.tileimpl.QSFactoryImpl;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsImplementation;
import com.android.systemui.stackdivider.DividerModule;
@@ -85,6 +87,10 @@ public abstract class SystemUIDefaultModule {
BatteryControllerImpl controllerImpl);
@Binds
+ @Singleton
+ public abstract QSFactory provideQSFactory(QSFactoryImpl qsFactoryImpl);
+
+ @Binds
abstract DockManager bindDockManager(DockManagerImpl dockManager);
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 59b28b4372b4..b99d7655c425 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -99,6 +99,7 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
import com.android.systemui.plugins.GlobalActionsPanelPlugin;
+import com.android.systemui.statusbar.BlurUtils;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.ScrimController;
@@ -115,16 +116,17 @@ import java.util.concurrent.Executor;
import javax.inject.Inject;
/**
- * Helper to show the global actions dialog. Each item is an {@link Action} that
- * may show depending on whether the keyguard is showing, and whether the device
- * is provisioned.
+ * Helper to show the global actions dialog. Each item is an {@link Action} that may show depending
+ * on whether the keyguard is showing, and whether the device is provisioned.
*/
public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
- DialogInterface.OnShowListener, ConfigurationController.ConfigurationListener {
+ DialogInterface.OnShowListener,
+ ConfigurationController.ConfigurationListener,
+ GlobalActionsPanelPlugin.Callbacks {
- static public final String SYSTEM_DIALOG_REASON_KEY = "reason";
- static public final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
- static public final String SYSTEM_DIALOG_REASON_DREAM = "dream";
+ public static final String SYSTEM_DIALOG_REASON_KEY = "reason";
+ public static final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
+ public static final String SYSTEM_DIALOG_REASON_DREAM = "dream";
private static final String TAG = "GlobalActionsDialog";
@@ -163,6 +165,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private final TelecomManager mTelecomManager;
private final MetricsLogger mMetricsLogger;
private final NotificationShadeDepthController mDepthController;
+ private final BlurUtils mBlurUtils;
private ArrayList<Action> mItems;
private ActionsDialog mDialog;
@@ -208,7 +211,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
TrustManager trustManager, IActivityManager iActivityManager,
@Nullable TelecomManager telecomManager, MetricsLogger metricsLogger,
NotificationShadeDepthController depthController, SysuiColorExtractor colorExtractor,
- IStatusBarService statusBarService,
+ IStatusBarService statusBarService, BlurUtils blurUtils,
NotificationShadeWindowController notificationShadeWindowController,
ControlsUiController controlsUiController, IWindowManager iWindowManager,
@Background Executor backgroundExecutor,
@@ -237,6 +240,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mIWindowManager = iWindowManager;
mBackgroundExecutor = backgroundExecutor;
mControlsListingController = controlsListingController;
+ mBlurUtils = blurUtils;
// receive broadcasts
IntentFilter filter = new IntentFilter();
@@ -268,8 +272,9 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
@Override
public void onUnlockedChanged() {
if (mDialog != null && mDialog.mPanelController != null) {
- boolean locked = !keyguardStateController.canDismissLockScreen();
- mDialog.mPanelController.onDeviceLockStateChanged(locked);
+ boolean unlocked = keyguardStateController.isUnlocked()
+ || keyguardStateController.canDismissLockScreen();
+ mDialog.mPanelController.onDeviceLockStateChanged(unlocked);
}
}
});
@@ -383,7 +388,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mItems.add(getSettingsAction());
} else if (GLOBAL_ACTION_KEY_LOCKDOWN.equals(actionKey)) {
if (Settings.Secure.getIntForUser(mContentResolver,
- Settings.Secure.LOCKDOWN_IN_POWER_MENU, 0, getCurrentUser().id) != 0
+ Settings.Secure.LOCKDOWN_IN_POWER_MENU, 0, getCurrentUser().id) != 0
&& shouldDisplayLockdown()) {
mItems.add(getLockdownAction());
}
@@ -417,29 +422,10 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mAdapter = new MyAdapter();
- GlobalActionsPanelPlugin.PanelViewController panelViewController =
- mPanelPlugin != null
- ? mPanelPlugin.onPanelShown(
- new GlobalActionsPanelPlugin.Callbacks() {
- @Override
- public void dismissGlobalActionsMenu() {
- dismissDialog();
- }
-
- @Override
- public void startPendingIntentDismissingKeyguard(
- PendingIntent intent) {
- mActivityStarter
- .startPendingIntentDismissingKeyguard(intent);
- }
- },
- !mKeyguardStateController.isUnlocked())
- : null;
-
- ActionsDialog dialog = new ActionsDialog(mContext, mAdapter, panelViewController,
+ ActionsDialog dialog = new ActionsDialog(mContext, mAdapter, getWalletPanelViewController(),
mDepthController, mSysuiColorExtractor, mStatusBarService,
mNotificationShadeWindowController,
- shouldShowControls() ? mControlsUiController : null);
+ shouldShowControls() ? mControlsUiController : null, mBlurUtils);
dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.
dialog.setKeyguardShowing(mKeyguardShowing);
@@ -474,6 +460,33 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mConfigurationController.removeCallback(this);
}
+ @Nullable
+ private GlobalActionsPanelPlugin.PanelViewController getWalletPanelViewController() {
+ if (mPanelPlugin == null) {
+ return null;
+ }
+ return mPanelPlugin.onPanelShown(this, !mKeyguardStateController.isUnlocked());
+ }
+
+ /**
+ * Implements {@link GlobalActionsPanelPlugin.Callbacks#dismissGlobalActionsMenu()}, which is
+ * called when the quick access wallet requests dismissal.
+ */
+ @Override
+ public void dismissGlobalActionsMenu() {
+ dismissDialog();
+ }
+
+ /**
+ * Implements {@link GlobalActionsPanelPlugin.Callbacks#dismissGlobalActionsMenu()}, which is
+ * called when the quick access wallet requests that an intent be started (with lock screen
+ * shown first if needed).
+ */
+ @Override
+ public void startPendingIntentDismissingKeyguard(PendingIntent pendingIntent) {
+ mActivityStarter.startPendingIntentDismissingKeyguard(pendingIntent);
+ }
+
private final class PowerAction extends SinglePressAction implements LongPressAction {
private PowerAction() {
super(R.drawable.ic_lock_power_off,
@@ -916,7 +929,9 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
}
- /** {@inheritDoc} */
+ /**
+ * {@inheritDoc}
+ */
public void onDismiss(DialogInterface dialog) {
if (mDialog == dialog) {
mDialog = null;
@@ -932,17 +947,19 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
}
- /** {@inheritDoc} */
+ /**
+ * {@inheritDoc}
+ */
public void onShow(DialogInterface dialog) {
mMetricsLogger.visible(MetricsEvent.POWER_MENU);
}
/**
- * The adapter used for the list within the global actions dialog, taking
- * into account whether the keyguard is showing via
- * {@link com.android.systemui.globalactions.GlobalActionsDialog#mKeyguardShowing} and whether
- * the device is provisioned
- * via {@link com.android.systemui.globalactions.GlobalActionsDialog#mDeviceProvisioned}.
+ * The adapter used for the list within the global actions dialog, taking into account whether
+ * the keyguard is showing via
+ * {@link com.android.systemui.globalactions.GlobalActionsDialog#mKeyguardShowing}
+ * and whether the device is provisioned via
+ * {@link com.android.systemui.globalactions.GlobalActionsDialog#mDeviceProvisioned}.
*/
public class MyAdapter extends MultiListAdapter {
private int countItems(boolean separated) {
@@ -1073,8 +1090,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
*/
public interface Action {
/**
- * @return Text that will be announced when dialog is created. null
- * for none.
+ * @return Text that will be announced when dialog is created. null for none.
*/
CharSequence getLabelForAccessibility(Context context);
@@ -1083,15 +1099,13 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
void onPress();
/**
- * @return whether this action should appear in the dialog when the keygaurd
- * is showing.
+ * @return whether this action should appear in the dialog when the keygaurd is showing.
*/
boolean showDuringKeyguard();
/**
- * @return whether this action should appear in the dialog before the
- * device is provisioned.onlongpress
- *
+ * @return whether this action should appear in the dialog before the device is
+ * provisioned.onlongpress
*/
boolean showBeforeProvisioning();
@@ -1110,8 +1124,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
/**
- * A single press action maintains no state, just responds to a press
- * and takes an action.
+ * A single press action maintains no state, just responds to a press and takes an action.
*/
private abstract class SinglePressAction implements Action {
@@ -1185,8 +1198,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
/**
- * A toggle action knows whether it is on or off, and displays an icon
- * and status message accordingly.
+ * A toggle action knows whether it is on or off, and displays an icon and status message
+ * accordingly.
*/
private static abstract class ToggleAction implements Action {
@@ -1236,8 +1249,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
/**
- * Override to make changes to resource IDs just before creating the
- * View.
+ * Override to make changes to resource IDs just before creating the View.
*/
void willCreate() {
@@ -1293,9 +1305,9 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
/**
- * Implementations may override this if their state can be in on of the intermediate
- * states until some notification is received (e.g airplane mode is 'turning off' until
- * we know the wireless connections are back online
+ * Implementations may override this if their state can be in on of the intermediate states
+ * until some notification is received (e.g airplane mode is 'turning off' until we know the
+ * wireless connections are back online
*
* @param buttonOn Whether the button was turned on or off
*/
@@ -1313,12 +1325,13 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private class AirplaneModeAction extends ToggleAction {
AirplaneModeAction() {
super(
- R.drawable.ic_lock_airplane_mode,
- R.drawable.ic_lock_airplane_mode_off,
- R.string.global_actions_toggle_airplane_mode,
- R.string.global_actions_airplane_mode_on_status,
- R.string.global_actions_airplane_mode_off_status);
+ R.drawable.ic_lock_airplane_mode,
+ R.drawable.ic_lock_airplane_mode_off,
+ R.string.global_actions_toggle_airplane_mode,
+ R.string.global_actions_airplane_mode_on_status,
+ R.string.global_actions_airplane_mode_off_status);
}
+
void onToggle(boolean on) {
if (mHasTelephony && TelephonyProperties.in_ecm_mode().orElse(false)) {
mIsWaitingForEcmExit = true;
@@ -1571,6 +1584,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private boolean mHadTopUi;
private final NotificationShadeWindowController mNotificationShadeWindowController;
private final NotificationShadeDepthController mDepthController;
+ private final BlurUtils mBlurUtils;
private ControlsUiController mControlsUiController;
private ViewGroup mControlsView;
@@ -1580,7 +1594,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
NotificationShadeDepthController depthController,
SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
NotificationShadeWindowController notificationShadeWindowController,
- ControlsUiController controlsUiController) {
+ ControlsUiController controlsUiController, BlurUtils blurUtils) {
super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions);
mContext = context;
mAdapter = adapter;
@@ -1589,6 +1603,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mStatusBarService = statusBarService;
mNotificationShadeWindowController = notificationShadeWindowController;
mControlsUiController = controlsUiController;
+ mBlurUtils = blurUtils;
// Window initialization
Window window = getWindow();
@@ -1602,11 +1617,11 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
window.addFlags(
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
- | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
- | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
- | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
+ | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+ | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
window.getAttributes().setFitInsetsTypes(0 /* types */);
setTitle(R.string.global_actions);
@@ -1697,7 +1712,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
if (mBackgroundDrawable == null) {
mBackgroundDrawable = new ScrimDrawable();
- mScrimAlpha = ScrimController.BUSY_SCRIM_ALPHA;
+ mScrimAlpha = mBlurUtils.supportsBlursOnWindows()
+ ? ScrimController.BLUR_SCRIM_ALPHA : ScrimController.BUSY_SCRIM_ALPHA;
}
getWindow().setBackgroundDrawable(mBackgroundDrawable);
}
@@ -1749,7 +1765,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
/**
* Updates background and system bars according to current GradientColors.
- * @param colors Colors and hints to use.
+ *
+ * @param colors Colors and hints to use.
* @param animate Interpolates gradient if true, just sets otherwise.
*/
private void updateColors(GradientColors colors, boolean animate) {
@@ -1915,8 +1932,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
/**
- * Determines whether or not the Global Actions menu should be forced to
- * use the newer grid-style layout.
+ * Determines whether or not the Global Actions menu should be forced to use the newer
+ * grid-style layout.
*/
private static boolean isForceGridEnabled(Context context) {
return isPanelDebugModeEnabled(context);
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index bbb57bdb33f7..12955a153360 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -149,7 +149,7 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks
}
if (mBlurUtils.supportsBlursOnWindows()) {
- background.setAlpha((int) (ScrimController.BUSY_SCRIM_ALPHA * 255));
+ background.setAlpha((int) (ScrimController.BLUR_SCRIM_ALPHA * 255));
mBlurUtils.applyBlur(d.getWindow().getDecorView().getViewRootImpl(),
mBlurUtils.blurRadiusOfRatio(1));
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 3d708a91dbf0..226ac16cf1f2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -91,8 +91,10 @@ import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.dagger.KeyguardModule;
import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.phone.NavigationModeController;
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -374,6 +376,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable {
private boolean mLockLater;
private boolean mShowHomeOverLockscreen;
+ private boolean mInGestureNavigationMode;
private boolean mWakeAndUnlocking;
private IKeyguardDrawnCallback mDrawnCallback;
@@ -720,7 +723,8 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable {
KeyguardUpdateMonitor keyguardUpdateMonitor, DumpManager dumpManager,
@UiBackground Executor uiBgExecutor, PowerManager powerManager,
TrustManager trustManager,
- DeviceConfigProxy deviceConfig) {
+ DeviceConfigProxy deviceConfig,
+ NavigationModeController navigationModeController) {
super(context);
mFalsingManager = falsingManager;
mLockPatternUtils = lockPatternUtils;
@@ -742,6 +746,10 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable {
DeviceConfig.NAMESPACE_SYSTEMUI,
mHandler::post,
mOnPropertiesChangedListener);
+ mInGestureNavigationMode =
+ QuickStepContract.isGesturalMode(navigationModeController.addListener(mode -> {
+ mInGestureNavigationMode = QuickStepContract.isGesturalMode(mode);
+ }));
}
public void userActivity() {
@@ -2013,7 +2021,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable {
// windows that appear on top, ever
int flags = StatusBarManager.DISABLE_NONE;
if (forceHideHomeRecentsButtons || isShowingAndNotOccluded()) {
- if (!mShowHomeOverLockscreen) {
+ if (!mShowHomeOverLockscreen || !mInGestureNavigationMode) {
flags |= StatusBarManager.DISABLE_HOME;
}
flags |= StatusBarManager.DISABLE_RECENT;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 9be478639ed8..7a63a5e406f6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -29,6 +29,7 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.statusbar.phone.NavigationModeController;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.util.DeviceConfigProxy;
@@ -64,7 +65,8 @@ public class KeyguardModule {
PowerManager powerManager,
TrustManager trustManager,
@UiBackground Executor uiBgExecutor,
- DeviceConfigProxy deviceConfig) {
+ DeviceConfigProxy deviceConfig,
+ NavigationModeController navigationModeController) {
return new KeyguardViewMediator(
context,
falsingManager,
@@ -78,6 +80,7 @@ public class KeyguardModule {
uiBgExecutor,
powerManager,
trustManager,
- deviceConfig);
+ deviceConfig,
+ navigationModeController);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index a161d037fb50..e208ee2b4ab7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -33,7 +33,6 @@ import android.media.MediaMetadata;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
-import android.os.Handler;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -64,6 +63,7 @@ import java.util.concurrent.Executor;
public class MediaControlPanel implements NotificationMediaManager.MediaListener {
private static final String TAG = "MediaControlPanel";
private final NotificationMediaManager mMediaManager;
+ private final Executor mForegroundExecutor;
private final Executor mBackgroundExecutor;
private Context mContext;
@@ -102,15 +102,18 @@ public class MediaControlPanel implements NotificationMediaManager.MediaListener
* @param manager
* @param layoutId layout resource to use for this control panel
* @param actionIds resource IDs for action buttons in the layout
+ * @param foregroundExecutor foreground executor
* @param backgroundExecutor background executor, used for processing artwork
*/
public MediaControlPanel(Context context, ViewGroup parent, NotificationMediaManager manager,
- @LayoutRes int layoutId, int[] actionIds, Executor backgroundExecutor) {
+ @LayoutRes int layoutId, int[] actionIds, Executor foregroundExecutor,
+ Executor backgroundExecutor) {
mContext = context;
LayoutInflater inflater = LayoutInflater.from(mContext);
mMediaNotifView = (LinearLayout) inflater.inflate(layoutId, parent, false);
mMediaManager = manager;
mActionIds = actionIds;
+ mForegroundExecutor = foregroundExecutor;
mBackgroundExecutor = backgroundExecutor;
}
@@ -176,15 +179,17 @@ public class MediaControlPanel implements NotificationMediaManager.MediaListener
mMediaNotifView.setBackgroundTintList(ColorStateList.valueOf(mBackgroundColor));
// Click action
- mMediaNotifView.setOnClickListener(v -> {
- try {
- contentIntent.send();
- // Also close shade
- mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
- } catch (PendingIntent.CanceledException e) {
- Log.e(TAG, "Pending intent was canceled", e);
- }
- });
+ if (contentIntent != null) {
+ mMediaNotifView.setOnClickListener(v -> {
+ try {
+ contentIntent.send();
+ // Also close shade
+ mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+ } catch (PendingIntent.CanceledException e) {
+ Log.e(TAG, "Pending intent was canceled", e);
+ }
+ });
+ }
// App icon
ImageView appIcon = mMediaNotifView.findViewById(R.id.icon);
@@ -316,7 +321,7 @@ public class MediaControlPanel implements NotificationMediaManager.MediaListener
// Now that it's resized, update the UI
final RoundedBitmapDrawable result = roundedDrawable;
- albumView.getHandler().post(() -> {
+ mForegroundExecutor.execute(() -> {
if (result != null) {
albumView.setImageDrawable(result);
albumView.setVisibility(View.VISIBLE);
@@ -335,8 +340,7 @@ public class MediaControlPanel implements NotificationMediaManager.MediaListener
if (mSeamless == null) {
return;
}
- Handler handler = mSeamless.getHandler();
- handler.post(() -> {
+ mForegroundExecutor.execute(() -> {
updateChipInternal(device);
});
}
@@ -401,12 +405,15 @@ public class MediaControlPanel implements NotificationMediaManager.MediaListener
new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
mContext.sendBroadcast(intent);
} else {
- Log.d(TAG, "No receiver to restart");
// If we don't have a receiver, try relaunching the activity instead
- try {
- mController.getSessionActivity().send();
- } catch (PendingIntent.CanceledException e) {
- Log.e(TAG, "Pending intent was canceled", e);
+ if (mController.getSessionActivity() != null) {
+ try {
+ mController.getSessionActivity().send();
+ } catch (PendingIntent.CanceledException e) {
+ Log.e(TAG, "Pending intent was canceled", e);
+ }
+ } else {
+ Log.e(TAG, "No receiver or activity to restart");
}
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
index 41bace74bd46..4bfcf2229e3e 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
@@ -30,6 +30,8 @@ import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import javax.inject.Inject;
+
/**
* Controller class of PiP animations (both from and to PiP mode).
*/
@@ -62,12 +64,15 @@ public class PipAnimationController {
@interface TransitionDirection {}
private final Interpolator mFastOutSlowInInterpolator;
+ private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
private PipTransitionAnimator mCurrentAnimator;
- PipAnimationController(Context context) {
+ @Inject
+ PipAnimationController(Context context, PipSurfaceTransactionHelper helper) {
mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
com.android.internal.R.interpolator.fast_out_slow_in);
+ mSurfaceTransactionHelper = helper;
}
@SuppressWarnings("unchecked")
@@ -110,6 +115,7 @@ public class PipAnimationController {
}
private PipTransitionAnimator setupPipTransitionAnimator(PipTransitionAnimator animator) {
+ animator.setSurfaceTransactionHelper(mSurfaceTransactionHelper);
animator.setInterpolator(mFastOutSlowInInterpolator);
animator.setFloatValues(FRACTION_START, FRACTION_END);
return animator;
@@ -151,9 +157,10 @@ public class PipAnimationController {
private T mEndValue;
private T mCurrentValue;
private PipAnimationCallback mPipAnimationCallback;
- private SurfaceControlTransactionFactory mSurfaceControlTransactionFactory;
+ private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
+ mSurfaceControlTransactionFactory;
+ private PipSurfaceTransactionHelper mSurfaceTransactionHelper;
private @TransitionDirection int mTransitionDirection;
- private int mCornerRadius;
private PipTransitionAnimator(SurfaceControl leash, @AnimationType int animationType,
Rect destinationBounds, T startValue, T endValue) {
@@ -242,15 +249,6 @@ public class PipAnimationController {
mCurrentValue = value;
}
- int getCornerRadius() {
- return mCornerRadius;
- }
-
- PipTransitionAnimator<T> setCornerRadius(int cornerRadius) {
- mCornerRadius = cornerRadius;
- return this;
- }
-
boolean shouldApplyCornerRadius() {
return mTransitionDirection != TRANSITION_DIRECTION_TO_FULLSCREEN;
}
@@ -273,10 +271,19 @@ public class PipAnimationController {
}
@VisibleForTesting
- void setSurfaceControlTransactionFactory(SurfaceControlTransactionFactory factory) {
+ void setSurfaceControlTransactionFactory(
+ PipSurfaceTransactionHelper.SurfaceControlTransactionFactory factory) {
mSurfaceControlTransactionFactory = factory;
}
+ PipSurfaceTransactionHelper getSurfaceTransactionHelper() {
+ return mSurfaceTransactionHelper;
+ }
+
+ void setSurfaceTransactionHelper(PipSurfaceTransactionHelper helper) {
+ mSurfaceTransactionHelper = helper;
+ }
+
abstract void applySurfaceControlTransaction(SurfaceControl leash,
SurfaceControl.Transaction tx, float fraction);
@@ -289,14 +296,11 @@ public class PipAnimationController {
SurfaceControl.Transaction tx, float fraction) {
final float alpha = getStartValue() * (1 - fraction) + getEndValue() * fraction;
setCurrentValue(alpha);
- tx.setAlpha(leash, alpha);
+ getSurfaceTransactionHelper().alpha(tx, leash, alpha);
if (Float.compare(fraction, FRACTION_START) == 0) {
- // Ensure the start condition
- final Rect bounds = getDestinationBounds();
- tx.setPosition(leash, bounds.left, bounds.top)
- .setWindowCrop(leash, bounds.width(), bounds.height());
- tx.setCornerRadius(leash,
- shouldApplyCornerRadius() ? getCornerRadius() : 0);
+ getSurfaceTransactionHelper()
+ .crop(tx, leash, getDestinationBounds())
+ .round(tx, leash, shouldApplyCornerRadius());
}
tx.apply();
}
@@ -325,21 +329,16 @@ public class PipAnimationController {
getCastedFractionValue(start.right, end.right, fraction),
getCastedFractionValue(start.bottom, end.bottom, fraction));
setCurrentValue(mTmpRect);
- tx.setPosition(leash, mTmpRect.left, mTmpRect.top)
- .setWindowCrop(leash, mTmpRect.width(), mTmpRect.height());
+ getSurfaceTransactionHelper().crop(tx, leash, mTmpRect);
if (Float.compare(fraction, FRACTION_START) == 0) {
// Ensure the start condition
- tx.setAlpha(leash, 1f);
- tx.setCornerRadius(leash,
- shouldApplyCornerRadius() ? getCornerRadius() : 0);
+ getSurfaceTransactionHelper()
+ .alpha(tx, leash, 1f)
+ .round(tx, leash, shouldApplyCornerRadius());
}
tx.apply();
}
};
}
}
-
- interface SurfaceControlTransactionFactory {
- SurfaceControl.Transaction getTransaction();
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
index fb348f483c46..8be2502f98eb 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
@@ -67,7 +67,7 @@ public class PipBoundsHandler {
private ComponentName mLastPipComponentName;
private float mReentrySnapFraction = INVALID_SNAP_FRACTION;
- private Size mReentrySize = null;
+ private Size mReentrySize;
private float mDefaultAspectRatio;
private float mMinAspectRatio;
@@ -77,6 +77,7 @@ public class PipBoundsHandler {
private int mDefaultMinSize;
private Point mScreenEdgeInsets;
private int mCurrentMinSize;
+ private Size mOverrideMinimalSize;
private boolean mIsImeShowing;
private int mImeHeight;
@@ -226,11 +227,14 @@ public class PipBoundsHandler {
/**
* @return {@link Rect} of the destination PiP window bounds.
*/
- Rect getDestinationBounds(float aspectRatio, Rect bounds) {
+ Rect getDestinationBounds(float aspectRatio, Rect bounds, Size minimalSize) {
final Rect destinationBounds;
- final Rect defaultBounds = getDefaultBounds(mReentrySnapFraction, mReentrySize);
if (bounds == null) {
+ final Rect defaultBounds = getDefaultBounds(mReentrySnapFraction, mReentrySize);
destinationBounds = new Rect(defaultBounds);
+ if (mReentrySnapFraction == INVALID_SNAP_FRACTION && mReentrySize == null) {
+ mOverrideMinimalSize = minimalSize;
+ }
} else {
destinationBounds = new Rect(bounds);
}
@@ -335,7 +339,6 @@ public class PipBoundsHandler {
*/
private void transformBoundsToAspectRatio(Rect stackBounds, float aspectRatio,
boolean useCurrentMinEdgeSize) {
-
// Save the snap fraction and adjust the size based on the new aspect ratio.
final float snapFraction = mSnapAlgorithm.getSnapFraction(stackBounds,
getMovementBounds(stackBounds));
@@ -354,10 +357,38 @@ public class PipBoundsHandler {
final int left = (int) (stackBounds.centerX() - size.getWidth() / 2f);
final int top = (int) (stackBounds.centerY() - size.getHeight() / 2f);
stackBounds.set(left, top, left + size.getWidth(), top + size.getHeight());
+ // apply the override minimal size if applicable, this minimal size is specified by app
+ if (mOverrideMinimalSize != null) {
+ transformBoundsToMinimalSize(stackBounds, aspectRatio, mOverrideMinimalSize);
+ }
mSnapAlgorithm.applySnapFraction(stackBounds, getMovementBounds(stackBounds), snapFraction);
}
/**
+ * Transforms a given bounds to meet the minimal size constraints.
+ * This function assumes the given {@param stackBounds} qualifies {@param aspectRatio}.
+ */
+ private void transformBoundsToMinimalSize(Rect stackBounds, float aspectRatio,
+ Size minimalSize) {
+ if (minimalSize == null) return;
+ final Size adjustedMinimalSize;
+ final float minimalSizeAspectRatio =
+ minimalSize.getWidth() / (float) minimalSize.getHeight();
+ if (minimalSizeAspectRatio > aspectRatio) {
+ // minimal size is wider, fixed the width and increase the height
+ adjustedMinimalSize = new Size(
+ minimalSize.getWidth(), (int) (minimalSize.getWidth() / aspectRatio));
+ } else {
+ adjustedMinimalSize = new Size(
+ (int) (minimalSize.getHeight() * aspectRatio), minimalSize.getHeight());
+ }
+ final Rect containerBounds = new Rect(stackBounds);
+ Gravity.apply(mDefaultStackGravity,
+ adjustedMinimalSize.getWidth(), adjustedMinimalSize.getHeight(),
+ containerBounds, stackBounds);
+ }
+
+ /**
* @return the default bounds to show the PIP, if a {@param snapFraction} and {@param size} are
* provided, then it will apply the default bounds to the provided snap fraction and size.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java
new file mode 100644
index 000000000000..21f9301fe794
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.pip;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.view.SurfaceControl;
+
+import com.android.systemui.R;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Abstracts the common operations on {@link SurfaceControl.Transaction} for PiP transition.
+ */
+@Singleton
+public class PipSurfaceTransactionHelper {
+
+ private final boolean mEnableCornerRadius;
+ private final int mCornerRadius;
+
+ @Inject
+ public PipSurfaceTransactionHelper(Context context) {
+ final Resources res = context.getResources();
+ mEnableCornerRadius = res.getBoolean(R.bool.config_pipEnableRoundCorner);
+ mCornerRadius = res.getDimensionPixelSize(R.dimen.pip_corner_radius);
+ }
+
+ /**
+ * Operates the alpha on a given transaction and leash
+ * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
+ */
+ PipSurfaceTransactionHelper alpha(SurfaceControl.Transaction tx, SurfaceControl leash,
+ float alpha) {
+ tx.setAlpha(leash, alpha);
+ return this;
+ }
+
+ /**
+ * Operates the crop (and position) on a given transaction and leash
+ * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
+ */
+ PipSurfaceTransactionHelper crop(SurfaceControl.Transaction tx, SurfaceControl leash,
+ Rect destinationBounds) {
+ tx.setWindowCrop(leash, destinationBounds.width(), destinationBounds.height())
+ .setPosition(leash, destinationBounds.left, destinationBounds.top);
+ return this;
+ }
+
+ /**
+ * Operates the round corner radius on a given transaction and leash
+ * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
+ */
+ PipSurfaceTransactionHelper round(SurfaceControl.Transaction tx, SurfaceControl leash,
+ boolean applyCornerRadius) {
+ if (mEnableCornerRadius) {
+ tx.setCornerRadius(leash, applyCornerRadius ? mCornerRadius : 0);
+ }
+ return this;
+ }
+
+ interface SurfaceControlTransactionFactory {
+ SurfaceControl.Transaction getTransaction();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index 90b657077b0d..dc1b5d70089b 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -30,12 +30,14 @@ import android.app.ActivityTaskManager;
import android.app.ITaskOrganizerController;
import android.app.PictureInPictureParams;
import android.content.Context;
+import android.content.pm.ActivityInfo;
import android.graphics.Rect;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
+import android.util.Size;
import android.view.ITaskOrganizer;
import android.view.IWindowContainer;
import android.view.SurfaceControl;
@@ -78,9 +80,9 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
private final PipAnimationController mPipAnimationController;
private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>();
private final Rect mLastReportedBounds = new Rect();
- private final int mCornerRadius;
- private final Map<IBinder, Rect> mBoundsToRestore = new HashMap<>();
private final int mEnterExitAnimationDuration;
+ private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
+ private final Map<IBinder, Rect> mBoundsToRestore = new HashMap<>();
// These callbacks are called on the update thread
private final PipAnimationController.PipAnimationCallback mPipAnimationCallback =
@@ -172,16 +174,20 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
private SurfaceControl mLeash;
private boolean mInPip;
private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
+ private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
+ mSurfaceControlTransactionFactory;
- public PipTaskOrganizer(Context context, @NonNull PipBoundsHandler boundsHandler) {
+ public PipTaskOrganizer(Context context, @NonNull PipBoundsHandler boundsHandler,
+ @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper) {
mMainHandler = new Handler(Looper.getMainLooper());
mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks);
mTaskOrganizerController = ActivityTaskManager.getTaskOrganizerController();
mPipBoundsHandler = boundsHandler;
- mPipAnimationController = new PipAnimationController(context);
- mCornerRadius = context.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius);
mEnterExitAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipResizeAnimationDuration);
+ mSurfaceTransactionHelper = surfaceTransactionHelper;
+ mPipAnimationController = new PipAnimationController(context, surfaceTransactionHelper);
+ mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
}
public Handler getUpdateHandler() {
@@ -204,26 +210,12 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
mOneShotAnimationType = animationType;
}
- /**
- * Callback to issue the final {@link WindowContainerTransaction} on end of movements.
- * @param destinationBounds the final bounds.
- */
- public void onMotionMovementEnd(Rect destinationBounds) {
- try {
- mLastReportedBounds.set(destinationBounds);
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.setBounds(mToken, destinationBounds);
- mTaskOrganizerController.applyContainerTransaction(wct, null /* ITaskOrganizer */);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to apply window container transaction", e);
- }
- }
-
@Override
public void taskAppeared(ActivityManager.RunningTaskInfo info) {
Objects.requireNonNull(info, "Requires RunningTaskInfo");
final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
- getAspectRatioOrDefault(info.pictureInPictureParams), null /* bounds */);
+ getAspectRatioOrDefault(info.pictureInPictureParams),
+ null /* bounds */, getMinimalSize(info.topActivityInfo));
Objects.requireNonNull(destinationBounds, "Missing destination bounds");
mTaskInfo = info;
mToken = mTaskInfo.token;
@@ -242,7 +234,6 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
mUpdateHandler.post(() -> mPipAnimationController
.getAnimator(mLeash, destinationBounds, 0f, 1f)
.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
- .setCornerRadius(mCornerRadius)
.setPipAnimationCallback(mPipAnimationCallback)
.setDuration(mEnterExitAnimationDuration)
.start());
@@ -260,7 +251,7 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
Log.wtf(TAG, "Unrecognized token: " + token);
return;
}
- final Rect boundsToRestore = mBoundsToRestore.remove(mToken.asBinder());
+ final Rect boundsToRestore = mBoundsToRestore.remove(token.asBinder());
scheduleAnimateResizePip(mLastReportedBounds, boundsToRestore,
TRANSITION_DIRECTION_TO_FULLSCREEN, mEnterExitAnimationDuration, null);
mInPip = false;
@@ -278,7 +269,8 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
return;
}
final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
- getAspectRatioOrDefault(newParams), null /* bounds */);
+ getAspectRatioOrDefault(newParams),
+ null /* bounds */, getMinimalSize(info.topActivityInfo));
Objects.requireNonNull(destinationBounds, "Missing destination bounds");
scheduleAnimateResizePip(destinationBounds, mEnterExitAnimationDuration, null);
}
@@ -307,7 +299,6 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
private void scheduleAnimateResizePip(Rect currentBounds, Rect destinationBounds,
@PipAnimationController.TransitionDirection int direction, int durationMs,
Consumer<Rect> updateBoundsCallback) {
- Objects.requireNonNull(mToken, "Requires valid IWindowContainer");
if (!mInPip) {
// Ignore animation when we are no longer in PIP
return;
@@ -326,7 +317,6 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
* {@link WindowContainerTransaction} until {@link #scheduleFinishResizePip} is called.
*/
public void scheduleResizePip(Rect toBounds, Consumer<Rect> updateBoundsCallback) {
- Objects.requireNonNull(mToken, "Requires valid IWindowContainer");
SomeArgs args = SomeArgs.obtain();
args.arg1 = updateBoundsCallback;
args.arg2 = toBounds;
@@ -334,22 +324,20 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
}
/**
- * Finish a intermediate resize operation. This is expected to be called after
+ * Finish an intermediate resize operation. This is expected to be called after
* {@link #scheduleResizePip}.
*/
public void scheduleFinishResizePip(Rect destinationBounds) {
- Objects.requireNonNull(mToken, "Requires valid IWindowContainer");
- SurfaceControl.Transaction tx = new SurfaceControl.Transaction()
- .setPosition(mLeash, destinationBounds.left, destinationBounds.top)
- .setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height())
- .setCornerRadius(mLeash, mInPip ? mCornerRadius : 0);
+ final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
+ mSurfaceTransactionHelper
+ .crop(tx, mLeash, destinationBounds)
+ .round(tx, mLeash, mInPip);
scheduleFinishResizePip(tx, destinationBounds, TRANSITION_DIRECTION_NONE, null);
}
private void scheduleFinishResizePip(SurfaceControl.Transaction tx,
Rect destinationBounds, @PipAnimationController.TransitionDirection int direction,
Consumer<Rect> updateBoundsCallback) {
- Objects.requireNonNull(mToken, "Requires valid IWindowContainer");
SomeArgs args = SomeArgs.obtain();
args.arg1 = updateBoundsCallback;
args.arg2 = tx;
@@ -367,7 +355,6 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
// Ignore offsets when we are no longer in PIP
return;
}
- Objects.requireNonNull(mToken, "Requires valid IWindowContainer");
SomeArgs args = SomeArgs.obtain();
args.arg1 = updateBoundsCallback;
args.arg2 = originalBounds;
@@ -396,17 +383,16 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
throw new RuntimeException("Callers should call scheduleResizePip() instead of this "
+ "directly");
}
- Objects.requireNonNull(mToken, "Requires valid IWindowContainer");
// Could happen when dismissPip
if (mToken == null || mLeash == null) {
Log.w(TAG, "Abort animation, invalid leash");
return;
}
- new SurfaceControl.Transaction()
- .setPosition(mLeash, destinationBounds.left, destinationBounds.top)
- .setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height())
- .setCornerRadius(mLeash, mInPip ? mCornerRadius : 0)
- .apply();
+ final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
+ mSurfaceTransactionHelper
+ .crop(tx, mLeash, destinationBounds)
+ .round(tx, mLeash, mInPip);
+ tx.apply();
}
private void finishResize(SurfaceControl.Transaction tx, Rect destinationBounds,
@@ -449,12 +435,19 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
mUpdateHandler.post(() -> mPipAnimationController
.getAnimator(mLeash, currentBounds, destinationBounds)
.setTransitionDirection(direction)
- .setCornerRadius(mCornerRadius)
.setPipAnimationCallback(mPipAnimationCallback)
.setDuration(durationMs)
.start());
}
+ private Size getMinimalSize(ActivityInfo activityInfo) {
+ if (activityInfo == null || activityInfo.windowLayout == null) {
+ return null;
+ }
+ final ActivityInfo.WindowLayout windowLayout = activityInfo.windowLayout;
+ return new Size(windowLayout.minWidth, windowLayout.minHeight);
+ }
+
private float getAspectRatioOrDefault(@Nullable PictureInPictureParams params) {
return params == null
? mPipBoundsHandler.getDefaultAspectRatio()
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index 2aac188c130d..020627a20458 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -42,6 +42,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.pip.BasePipManager;
import com.android.systemui.pip.PipBoundsHandler;
import com.android.systemui.pip.PipSnapAlgorithm;
+import com.android.systemui.pip.PipSurfaceTransactionHelper;
import com.android.systemui.pip.PipTaskOrganizer;
import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -210,7 +211,8 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
FloatingContentCoordinator floatingContentCoordinator,
DeviceConfigProxy deviceConfig,
PipBoundsHandler pipBoundsHandler,
- PipSnapAlgorithm pipSnapAlgorithm) {
+ PipSnapAlgorithm pipSnapAlgorithm,
+ PipSurfaceTransactionHelper surfaceTransactionHelper) {
mContext = context;
mActivityManager = ActivityManager.getService();
@@ -224,7 +226,8 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
final IActivityTaskManager activityTaskManager = ActivityTaskManager.getService();
mPipBoundsHandler = pipBoundsHandler;
- mPipTaskOrganizer = new PipTaskOrganizer(mContext, mPipBoundsHandler);
+ mPipTaskOrganizer = new PipTaskOrganizer(context, pipBoundsHandler,
+ surfaceTransactionHelper);
mPipTaskOrganizer.registerPipTransitionCallback(this);
mInputConsumerController = InputConsumerController.getPipInputConsumer();
mMediaController = new PipMediaController(context, mActivityManager, broadcastDispatcher);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index 33760be3f469..449a2bca6abb 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -78,10 +78,10 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
private final Rect mBounds = new Rect();
/** The bounds within which PIP's top-left coordinate is allowed to move. */
- private Rect mMovementBounds = new Rect();
+ private final Rect mMovementBounds = new Rect();
/** The region that all of PIP must stay within. */
- private Rect mFloatingAllowedArea = new Rect();
+ private final Rect mFloatingAllowedArea = new Rect();
/**
* Bounds that are animated using the physics animator.
@@ -89,7 +89,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
private final Rect mAnimatedBounds = new Rect();
/** The destination bounds to which PIP is animating. */
- private Rect mAnimatingToBounds = new Rect();
+ private final Rect mAnimatingToBounds = new Rect();
/** Coordinator instance for resolving conflicts with other floating content. */
private FloatingContentCoordinator mFloatingContentCoordinator;
@@ -120,7 +120,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
new PhysicsAnimator.SpringConfig(
SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
- private final Consumer<Rect> mUpdateBoundsCallback = (toBounds) -> mBounds.set(toBounds);
+ private final Consumer<Rect> mUpdateBoundsCallback = mBounds::set;
public PipMotionHelper(Context context, IActivityTaskManager activityTaskManager,
PipTaskOrganizer pipTaskOrganizer, PipMenuActivityController menuController,
@@ -426,7 +426,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
cancelAnimations();
mAnimatedBoundsPhysicsAnimator
- .withEndActions(() -> mPipTaskOrganizer.onMotionMovementEnd(mAnimatedBounds))
+ .withEndActions(() -> mPipTaskOrganizer.scheduleFinishResizePip(mAnimatedBounds))
.addUpdateListener(mResizePipUpdateListener)
.start();
}
@@ -437,7 +437,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
* {@link FloatingContentCoordinator.FloatingContent#getFloatingBoundsOnScreen()}.
*/
private void setAnimatingToBounds(Rect bounds) {
- mAnimatingToBounds = bounds;
+ mAnimatingToBounds.set(bounds);
mFloatingContentCoordinator.onContentMoved(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
index 8fff419af5a1..25acce678594 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
@@ -65,6 +65,7 @@ public class PipResizeGestureHandler {
private final PointF mDownPoint = new PointF();
private final Point mMaxSize = new Point();
private final Point mMinSize = new Point();
+ private final Rect mLastResizeBounds = new Rect();
private final Rect mTmpBounds = new Rect();
private final int mDelta;
@@ -187,17 +188,13 @@ public class PipResizeGestureHandler {
private void onMotionEvent(MotionEvent ev) {
int action = ev.getActionMasked();
if (action == MotionEvent.ACTION_DOWN) {
+ mLastResizeBounds.setEmpty();
mAllowGesture = isWithinTouchRegion((int) ev.getX(), (int) ev.getY());
if (mAllowGesture) {
mDownPoint.set(ev.getX(), ev.getY());
}
} else if (mAllowGesture) {
- final Rect currentPipBounds = mMotionHelper.getBounds();
- Rect newSize = TaskResizingAlgorithm.resizeDrag(ev.getX(), ev.getY(), mDownPoint.x,
- mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x, mMinSize.y, mMaxSize,
- true, true);
- mPipBoundsHandler.transformBoundsToAspectRatio(newSize);
switch (action) {
case MotionEvent.ACTION_POINTER_DOWN:
// We do not support multi touch for resizing via drag
@@ -206,11 +203,16 @@ public class PipResizeGestureHandler {
case MotionEvent.ACTION_MOVE:
// Capture inputs
mInputMonitor.pilferPointers();
- //TODO: Actually do resize here.
+ final Rect currentPipBounds = mMotionHelper.getBounds();
+ mLastResizeBounds.set(TaskResizingAlgorithm.resizeDrag(ev.getX(), ev.getY(),
+ mDownPoint.x, mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x,
+ mMinSize.y, mMaxSize, true, true));
+ mPipBoundsHandler.transformBoundsToAspectRatio(mLastResizeBounds);
+ mPipTaskOrganizer.scheduleResizePip(mLastResizeBounds, null);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
- //TODO: Finish resize operation here.
+ mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds);
mMotionHelper.synchronizePinnedStackBounds();
mCtrlType = CTRL_NONE;
mAllowGesture = false;
@@ -223,7 +225,7 @@ public class PipResizeGestureHandler {
mMaxSize.set(maxX, maxY);
}
- void updateMiniSize(int minX, int minY) {
+ void updateMinSize(int minX, int minY) {
mMinSize.set(minX, minY);
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index b5fb1a9dba9a..9b67d8020c32 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -42,6 +42,7 @@ import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.logging.MetricsLoggerWrapper;
import com.android.systemui.R;
import com.android.systemui.pip.PipBoundsHandler;
@@ -74,7 +75,7 @@ public class PipTouchHandler {
private final Context mContext;
private final IActivityManager mActivityManager;
private final PipBoundsHandler mPipBoundsHandler;
- private final PipResizeGestureHandler mPipResizeGestureHandler;
+ private PipResizeGestureHandler mPipResizeGestureHandler;
private IPinnedStackController mPinnedStackController;
private final PipMenuActivityController mMenuController;
@@ -85,14 +86,17 @@ public class PipTouchHandler {
// The current movement bounds
private Rect mMovementBounds = new Rect();
+ // The current resized bounds, changed by user resize.
+ // This is used during expand/un-expand to save/restore the user's resized size.
+ @VisibleForTesting Rect mResizedBounds = new Rect();
// The reference inset bounds, used to determine the dismiss fraction
private Rect mInsetBounds = new Rect();
// The reference bounds used to calculate the normal/expanded target bounds
private Rect mNormalBounds = new Rect();
- private Rect mNormalMovementBounds = new Rect();
+ @VisibleForTesting Rect mNormalMovementBounds = new Rect();
private Rect mExpandedBounds = new Rect();
- private Rect mExpandedMovementBounds = new Rect();
+ @VisibleForTesting Rect mExpandedMovementBounds = new Rect();
private int mExpandedShortestEdgeSize;
// Used to workaround an issue where the WM rotation happens before we are notified, allowing
@@ -127,7 +131,7 @@ public class PipTouchHandler {
private final PipTouchState mTouchState;
private final FlingAnimationUtils mFlingAnimationUtils;
private final FloatingContentCoordinator mFloatingContentCoordinator;
- private final PipMotionHelper mMotionHelper;
+ private PipMotionHelper mMotionHelper;
private PipTouchGesture mGesture;
// Temp vars
@@ -240,14 +244,15 @@ public class PipTouchHandler {
mFloatingContentCoordinator.onContentRemoved(mMotionHelper);
}
+ mResizedBounds.setEmpty();
mPipResizeGestureHandler.onActivityUnpinned();
}
public void onPinnedStackAnimationEnded() {
// Always synchronize the motion helper bounds once PiP animations finish
mMotionHelper.synchronizePinnedStackBounds();
- mPipResizeGestureHandler.updateMiniSize(mMotionHelper.getBounds().width(),
- mMotionHelper.getBounds().height());
+ updateMovementBounds();
+ mResizedBounds.set(mMotionHelper.getBounds());
if (mShowPipMenuOnAnimationEnd) {
mMenuController.showMenu(MENU_STATE_CLOSE, mMotionHelper.getBounds(),
@@ -292,11 +297,13 @@ public class PipTouchHandler {
Size expandedSize = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio,
mExpandedShortestEdgeSize, displaySize.x, displaySize.y);
mExpandedBounds.set(0, 0, expandedSize.getWidth(), expandedSize.getHeight());
- mPipResizeGestureHandler.updateMaxSize(expandedSize.getWidth(), expandedSize.getHeight());
Rect expandedMovementBounds = new Rect();
mSnapAlgorithm.getMovementBounds(mExpandedBounds, insetBounds, expandedMovementBounds,
bottomOffset);
+ mPipResizeGestureHandler.updateMinSize(mNormalBounds.width(), mNormalBounds.height());
+ mPipResizeGestureHandler.updateMaxSize(mExpandedBounds.width(), mExpandedBounds.height());
+
// The extra offset does not really affect the movement bounds, but are applied based on the
// current state (ime showing, or shelf offset) when we need to actually shift
int extraOffset = Math.max(
@@ -332,7 +339,7 @@ public class PipTouchHandler {
mExpandedMovementBounds = expandedMovementBounds;
mDisplayRotation = displayRotation;
mInsetBounds.set(insetBounds);
- updateMovementBounds(mMenuState);
+ updateMovementBounds();
mMovementBoundsExtraOffsets = extraOffset;
// If we have a deferred resize, apply it now
@@ -392,7 +399,7 @@ public class PipTouchHandler {
case MotionEvent.ACTION_UP: {
// Update the movement bounds again if the state has changed since the user started
// dragging (ie. when the IME shows)
- updateMovementBounds(mMenuState);
+ updateMovementBounds();
if (mGesture.onUp(mTouchState)) {
break;
@@ -490,9 +497,11 @@ public class PipTouchHandler {
if (menuState == MENU_STATE_FULL && mMenuState != MENU_STATE_FULL) {
// Save the current snap fraction and if we do not drag or move the PiP, then
// we store back to this snap fraction. Otherwise, we'll reset the snap
- // fraction and snap to the closest edge
- Rect expandedBounds = new Rect(mExpandedBounds);
+ // fraction and snap to the closest edge.
+ // Also save the current resized bounds so when the menu disappears, we can restore it.
if (resize) {
+ mResizedBounds.set(mMotionHelper.getBounds());
+ Rect expandedBounds = new Rect(mExpandedBounds);
mSavedSnapFraction = mMotionHelper.animateToExpandedState(expandedBounds,
mMovementBounds, mExpandedMovementBounds);
}
@@ -520,9 +529,12 @@ public class PipTouchHandler {
}
if (mDeferResizeToNormalBoundsUntilRotation == -1) {
- Rect normalBounds = new Rect(mNormalBounds);
- mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction,
- mNormalMovementBounds, mMovementBounds, false /* immediate */);
+ Rect restoreBounds = new Rect(mResizedBounds);
+ Rect restoredMovementBounds = new Rect();
+ mSnapAlgorithm.getMovementBounds(restoreBounds, mInsetBounds,
+ restoredMovementBounds, mIsImeShowing ? mImeHeight : 0);
+ mMotionHelper.animateToUnexpandedState(restoreBounds, mSavedSnapFraction,
+ restoredMovementBounds, mMovementBounds, false /* immediate */);
mSavedSnapFraction = -1f;
}
} else {
@@ -533,7 +545,7 @@ public class PipTouchHandler {
}
}
mMenuState = menuState;
- updateMovementBounds(menuState);
+ updateMovementBounds();
// If pip menu has dismissed, we should register the A11y ActionReplacingConnection for pip
// as well, or it can't handle a11y focus and pip menu can't perform any action.
onRegistrationChanged(menuState == MENU_STATE_NONE);
@@ -549,6 +561,21 @@ public class PipTouchHandler {
return mMotionHelper;
}
+ @VisibleForTesting
+ PipResizeGestureHandler getPipResizeGestureHandler() {
+ return mPipResizeGestureHandler;
+ }
+
+ @VisibleForTesting
+ void setPipResizeGestureHandler(PipResizeGestureHandler pipResizeGestureHandler) {
+ mPipResizeGestureHandler = pipResizeGestureHandler;
+ }
+
+ @VisibleForTesting
+ void setPipMotionHelper(PipMotionHelper pipMotionHelper) {
+ mMotionHelper = pipMotionHelper;
+ }
+
/**
* @return the unexpanded bounds.
*/
@@ -709,14 +736,14 @@ public class PipTouchHandler {
* Updates the current movement bounds based on whether the menu is currently visible and
* resized.
*/
- private void updateMovementBounds(int menuState) {
- boolean isMenuExpanded = menuState == MENU_STATE_FULL;
- mMovementBounds = isMenuExpanded && willResizeMenu()
- ? mExpandedMovementBounds
- : mNormalMovementBounds;
- mPipBoundsHandler.setMinEdgeSize(
- isMenuExpanded ? mExpandedShortestEdgeSize : 0);
+ private void updateMovementBounds() {
+ mSnapAlgorithm.getMovementBounds(mMotionHelper.getBounds(), mInsetBounds,
+ mMovementBounds, mIsImeShowing ? mImeHeight : 0);
mMotionHelper.setCurrentMovementBounds(mMovementBounds);
+
+ boolean isMenuExpanded = mMenuState == MENU_STATE_FULL;
+ mPipBoundsHandler.setMinEdgeSize(
+ isMenuExpanded && willResizeMenu() ? mExpandedShortestEdgeSize : 0);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index 6206dd576ec4..050acd5a8728 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -51,6 +51,7 @@ import com.android.systemui.UiOffloadThread;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.pip.BasePipManager;
import com.android.systemui.pip.PipBoundsHandler;
+import com.android.systemui.pip.PipSurfaceTransactionHelper;
import com.android.systemui.pip.PipTaskOrganizer;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener;
@@ -229,7 +230,8 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
@Inject
public PipManager(Context context, BroadcastDispatcher broadcastDispatcher,
- PipBoundsHandler pipBoundsHandler) {
+ PipBoundsHandler pipBoundsHandler,
+ PipSurfaceTransactionHelper surfaceTransactionHelper) {
if (mInitialized) {
return;
}
@@ -239,7 +241,8 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
mPipBoundsHandler = pipBoundsHandler;
mResizeAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipResizeAnimationDuration);
- mPipTaskOrganizer = new PipTaskOrganizer(mContext, mPipBoundsHandler);
+ mPipTaskOrganizer = new PipTaskOrganizer(mContext, mPipBoundsHandler,
+ surfaceTransactionHelper);
mPipTaskOrganizer.registerPipTransitionCallback(this);
mActivityTaskManager = ActivityTaskManager.getService();
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
index 3cf0718aa067..ece1ce8bb4d0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
@@ -28,6 +28,7 @@ public interface QSHost {
void forceCollapsePanels();
void openPanels();
Context getContext();
+ Context getUserContext();
QSLogger getQSLogger();
Collection<QSTile> getTiles();
void addCallback(Callback callback);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
index 837256bf4052..d5e5b10d603c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
@@ -55,11 +55,13 @@ public class QSMediaPlayer extends MediaControlPanel {
* @param context
* @param parent
* @param manager
+ * @param foregroundExecutor
* @param backgroundExecutor
*/
public QSMediaPlayer(Context context, ViewGroup parent, NotificationMediaManager manager,
- Executor backgroundExecutor) {
- super(context, parent, manager, R.layout.qs_media_panel, QS_ACTION_IDS, backgroundExecutor);
+ Executor foregroundExecutor, Executor backgroundExecutor) {
+ super(context, parent, manager, R.layout.qs_media_panel, QS_ACTION_IDS, foregroundExecutor,
+ backgroundExecutor);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index bf72b33ada42..5ccf8c7e9212 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -53,6 +53,7 @@ import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSTile;
@@ -101,6 +102,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
private final ArrayList<QSMediaPlayer> mMediaPlayers = new ArrayList<>();
private final NotificationMediaManager mNotificationMediaManager;
private final LocalBluetoothManager mLocalBluetoothManager;
+ private final Executor mForegroundExecutor;
private final Executor mBackgroundExecutor;
private LocalMediaManager mLocalMediaManager;
private MediaDevice mDevice;
@@ -160,6 +162,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
BroadcastDispatcher broadcastDispatcher,
QSLogger qsLogger,
NotificationMediaManager notificationMediaManager,
+ @Main Executor foregroundExecutor,
@Background Executor backgroundExecutor,
@Nullable LocalBluetoothManager localBluetoothManager
) {
@@ -168,6 +171,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
mQSLogger = qsLogger;
mDumpManager = dumpManager;
mNotificationMediaManager = notificationMediaManager;
+ mForegroundExecutor = foregroundExecutor;
mBackgroundExecutor = backgroundExecutor;
mLocalBluetoothManager = localBluetoothManager;
@@ -270,7 +274,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
if (player == null) {
Log.d(TAG, "creating new player");
player = new QSMediaPlayer(mContext, this, mNotificationMediaManager,
- mBackgroundExecutor);
+ mForegroundExecutor, mBackgroundExecutor);
if (player.isPlaying()) {
mMediaCarousel.addView(player.getView(), 0, lp); // add in front
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index fab71918a3d1..9e8eb3a28781 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -45,7 +45,6 @@ import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.qs.external.TileLifecycleManager;
import com.android.systemui.qs.external.TileServices;
import com.android.systemui.qs.logging.QSLogger;
-import com.android.systemui.qs.tileimpl.QSFactoryImpl;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.phone.AutoTileManager;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -98,7 +97,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
@Inject
public QSTileHost(Context context,
StatusBarIconController iconController,
- QSFactoryImpl defaultFactory,
+ QSFactory defaultFactory,
@Main Handler mainHandler,
@Background Looper bgLooper,
PluginManager pluginManager,
@@ -120,7 +119,6 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
mServices = new TileServices(this, bgLooper, mBroadcastDispatcher);
mStatusBarOptional = statusBarOptional;
- defaultFactory.setHost(this);
mQsFactories.add(defaultFactory);
pluginManager.addPluginListener(this, QSFactory.class, true);
mDumpManager.registerDumpable(TAG, this);
@@ -211,10 +209,12 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
return mContext;
}
+ @Override
public Context getUserContext() {
return mUserContext;
}
+ @Override
public TileServices getTileServices() {
return mServices;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
index 4512afb3c178..0c5019491a59 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
@@ -48,12 +48,13 @@ public class QuickQSMediaPlayer extends MediaControlPanel {
* @param context
* @param parent
* @param manager
+ * @param foregroundExecutor
* @param backgroundExecutor
*/
public QuickQSMediaPlayer(Context context, ViewGroup parent, NotificationMediaManager manager,
- Executor backgroundExecutor) {
+ Executor foregroundExecutor, Executor backgroundExecutor) {
super(context, parent, manager, R.layout.qqs_media_panel, QQS_ACTION_IDS,
- backgroundExecutor);
+ foregroundExecutor, backgroundExecutor);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 6654b7ab0749..be01d75552de 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -32,6 +32,7 @@ import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.SignalState;
@@ -79,11 +80,12 @@ public class QuickQSPanel extends QSPanel {
BroadcastDispatcher broadcastDispatcher,
QSLogger qsLogger,
NotificationMediaManager notificationMediaManager,
+ @Main Executor foregroundExecutor,
@Background Executor backgroundExecutor,
@Nullable LocalBluetoothManager localBluetoothManager
) {
super(context, attrs, dumpManager, broadcastDispatcher, qsLogger, notificationMediaManager,
- backgroundExecutor, localBluetoothManager);
+ foregroundExecutor, backgroundExecutor, localBluetoothManager);
if (mFooter != null) {
removeView(mFooter.getView());
}
@@ -103,7 +105,7 @@ public class QuickQSPanel extends QSPanel {
int marginSize = (int) mContext.getResources().getDimension(R.dimen.qqs_media_spacing);
mMediaPlayer = new QuickQSMediaPlayer(mContext, mHorizontalLinearLayout,
- notificationMediaManager, backgroundExecutor);
+ notificationMediaManager, foregroundExecutor, backgroundExecutor);
LayoutParams lp2 = new LayoutParams(0, LayoutParams.MATCH_PARENT, 1);
lp2.setMarginEnd(marginSize);
lp2.setMarginStart(0);
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 3b27fb76bcc6..08c8f86c1125 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -46,7 +46,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Dependency;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile.State;
-import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.external.TileLifecycleManager.TileChangeListener;
import com.android.systemui.qs.tileimpl.QSTileImpl;
@@ -79,7 +79,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
private boolean mIsTokenGranted;
private boolean mIsShowingDialog;
- private CustomTile(QSTileHost host, String action, Context userContext) {
+ private CustomTile(QSHost host, String action, Context userContext) {
super(host);
mWindowManager = WindowManagerGlobal.getWindowManagerService();
mComponent = ComponentName.unflattenFromString(action);
@@ -392,7 +392,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
return ComponentName.unflattenFromString(action);
}
- public static CustomTile create(QSTileHost host, String spec, Context userContext) {
+ public static CustomTile create(QSHost host, String spec, Context userContext) {
if (spec == null || !spec.startsWith(PREFIX) || !spec.endsWith(")")) {
throw new IllegalArgumentException("Bad custom tile spec: " + spec);
}
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 1b8717b5049c..c182a58a28c4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -24,7 +24,7 @@ import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTileView;
-import com.android.systemui.qs.QSTileHost;
+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.BatterySaverTile;
@@ -51,6 +51,8 @@ import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;
+import dagger.Lazy;
+
@Singleton
public class QSFactoryImpl implements QSFactory {
@@ -77,10 +79,11 @@ public class QSFactoryImpl implements QSFactory {
private final Provider<UiModeNightTile> mUiModeNightTileProvider;
private final Provider<ScreenRecordTile> mScreenRecordTileProvider;
- private QSTileHost mHost;
+ private final Lazy<QSHost> mQsHostLazy;
@Inject
- public QSFactoryImpl(Provider<WifiTile> wifiTileProvider,
+ public QSFactoryImpl(Lazy<QSHost> qsHostLazy,
+ Provider<WifiTile> wifiTileProvider,
Provider<BluetoothTile> bluetoothTileProvider,
Provider<CellularTile> cellularTileProvider,
Provider<DndTile> dndTileProvider,
@@ -100,6 +103,7 @@ public class QSFactoryImpl implements QSFactory {
Provider<GarbageMonitor.MemoryTile> memoryTileProvider,
Provider<UiModeNightTile> uiModeNightTileProvider,
Provider<ScreenRecordTile> screenRecordTileProvider) {
+ mQsHostLazy = qsHostLazy;
mWifiTileProvider = wifiTileProvider;
mBluetoothTileProvider = bluetoothTileProvider;
mCellularTileProvider = cellularTileProvider;
@@ -122,10 +126,6 @@ public class QSFactoryImpl implements QSFactory {
mScreenRecordTileProvider = screenRecordTileProvider;
}
- public void setHost(QSTileHost host) {
- mHost = host;
- }
-
public QSTile createTile(String tileSpec) {
QSTileImpl tile = createTileInternal(tileSpec);
if (tile != null) {
@@ -179,7 +179,8 @@ public class QSFactoryImpl implements QSFactory {
// Custom tiles
if (tileSpec.startsWith(CustomTile.PREFIX)) {
- return CustomTile.create(mHost, tileSpec, mHost.getUserContext());
+ return CustomTile.create(mQsHostLazy.get(), tileSpec,
+ mQsHostLazy.get().getUserContext());
}
// Debug tiles.
@@ -196,7 +197,7 @@ public class QSFactoryImpl implements QSFactory {
@Override
public QSTileView createTileView(QSTile tile, boolean collapsedView) {
- Context context = new ContextThemeWrapper(mHost.getContext(), R.style.qs_theme);
+ Context context = new ContextThemeWrapper(mQsHostLazy.get().getContext(), R.style.qs_theme);
QSIconView icon = tile.createTileView(context);
if (collapsedView) {
return new QSTileBaseView(context, icon, collapsedView);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 7c770f43fb57..1780fb1848e7 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -347,6 +347,7 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
Insets visibleInsets, int taskId, Consumer<Uri> finisher) {
// TODO use taskId and visibleInsets
+ clearScreenshot("new screenshot requested");
takeScreenshot(screenshot, finisher, screenshotScreenBounds);
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index ea5ec0503067..d7eab3a6eac4 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -31,7 +31,6 @@ import android.graphics.Rect;
import android.os.Handler;
import android.os.RemoteException;
import android.provider.Settings;
-import android.util.Log;
import android.util.Slog;
import android.view.IWindowContainer;
import android.view.LayoutInflater;
@@ -73,7 +72,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
DisplayController.OnDisplaysChangedListener {
private static final String TAG = "Divider";
- static final boolean DEBUG = true;
+ static final boolean DEBUG = false;
static final int DEFAULT_APP_TRANSITION_DURATION = 336;
static final float ADJUSTED_NONFOCUS_DIM = 0.3f;
@@ -173,6 +172,9 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
@Nullable
private ValueAnimator mAnimation = null;
+ private boolean mPaused = true;
+ private boolean mPausedTargetAdjusted = false;
+
private boolean getSecondaryHasFocus(int displayId) {
try {
IWindowContainer imeSplit = ActivityTaskManager.getTaskOrganizerController()
@@ -185,6 +187,14 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
return false;
}
+ private void updateDimTargets() {
+ final boolean splitIsVisible = !mView.isHidden();
+ mTargetPrimaryDim = (mSecondaryHasFocus && mTargetShown && splitIsVisible)
+ ? ADJUSTED_NONFOCUS_DIM : 0.f;
+ mTargetSecondaryDim = (!mSecondaryHasFocus && mTargetShown && splitIsVisible)
+ ? ADJUSTED_NONFOCUS_DIM : 0.f;
+ }
+
@Override
public void onImeStartPositioning(int displayId, int hiddenTop, int shownTop,
boolean imeShouldShow, SurfaceControl.Transaction t) {
@@ -193,18 +203,31 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
}
final boolean splitIsVisible = !mView.isHidden();
mSecondaryHasFocus = getSecondaryHasFocus(displayId);
- mTargetAdjusted = splitIsVisible && imeShouldShow && mSecondaryHasFocus
+ final boolean targetAdjusted = splitIsVisible && imeShouldShow && mSecondaryHasFocus
&& !mSplitLayout.mDisplayLayout.isLandscape();
mHiddenTop = hiddenTop;
mShownTop = shownTop;
mTargetShown = imeShouldShow;
if (mLastAdjustTop < 0) {
mLastAdjustTop = imeShouldShow ? hiddenTop : shownTop;
+ } else {
+ // Check for an "interruption" of an existing animation. In this case, we need to
+ // fake-flip the last-known state direction so that the animation completes in the
+ // other direction.
+ if (mTargetAdjusted != targetAdjusted && targetAdjusted == mAdjusted) {
+ if (mLastAdjustTop != (imeShouldShow ? mShownTop : mHiddenTop)) {
+ mAdjusted = mTargetAdjusted;
+ }
+ }
}
- mTargetPrimaryDim = (mSecondaryHasFocus && mTargetShown && splitIsVisible)
- ? ADJUSTED_NONFOCUS_DIM : 0.f;
- mTargetSecondaryDim = (!mSecondaryHasFocus && mTargetShown && splitIsVisible)
- ? ADJUSTED_NONFOCUS_DIM : 0.f;
+ if (mPaused) {
+ mPausedTargetAdjusted = targetAdjusted;
+ if (DEBUG) Slog.d(TAG, " ime starting but paused " + dumpState());
+ return;
+ }
+ mTargetAdjusted = targetAdjusted;
+ updateDimTargets();
+ if (DEBUG) Slog.d(TAG, " ime starting. vis:" + splitIsVisible + " " + dumpState());
if (mAnimation != null || (mImeWasShown && imeShouldShow
&& mTargetAdjusted != mAdjusted)) {
// We need to animate adjustment independently of the IME position, so
@@ -259,7 +282,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
@Override
public void onImePositionChanged(int displayId, int imeTop,
SurfaceControl.Transaction t) {
- if (mAnimation != null || !inSplitMode()) {
+ if (mAnimation != null || !inSplitMode() || mPaused) {
// Not synchronized with IME anymore, so return.
return;
}
@@ -271,7 +294,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
@Override
public void onImeEndPositioning(int displayId, boolean cancelled,
SurfaceControl.Transaction t) {
- if (mAnimation != null || !inSplitMode()) {
+ if (mAnimation != null || !inSplitMode() || mPaused) {
// Not synchronized with IME anymore, so return.
return;
}
@@ -279,7 +302,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
}
private void onProgress(float progress, SurfaceControl.Transaction t) {
- if (mTargetAdjusted != mAdjusted) {
+ if (mTargetAdjusted != mAdjusted && !mPaused) {
final float fraction = mTargetAdjusted ? progress : 1.f - progress;
mLastAdjustTop = (int) (fraction * mShownTop + (1.f - fraction) * mHiddenTop);
mSplitLayout.updateAdjustedBounds(mLastAdjustTop, mHiddenTop, mShownTop);
@@ -342,6 +365,52 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
});
mAnimation.start();
}
+
+ private String dumpState() {
+ return "top:" + mHiddenTop + "->" + mShownTop
+ + " adj:" + mAdjusted + "->" + mTargetAdjusted + "(" + mLastAdjustTop + ")"
+ + " shw:" + mImeWasShown + "->" + mTargetShown
+ + " dims:" + mLastPrimaryDim + "," + mLastSecondaryDim
+ + "->" + mTargetPrimaryDim + "," + mTargetSecondaryDim
+ + " shf:" + mSecondaryHasFocus + " desync:" + (mAnimation != null)
+ + " paus:" + mPaused + "[" + mPausedTargetAdjusted + "]";
+ }
+
+ /** Completely aborts/resets adjustment state */
+ public void pause(int displayId) {
+ if (DEBUG) Slog.d(TAG, "ime pause posting " + dumpState());
+ mHandler.post(() -> {
+ if (DEBUG) Slog.d(TAG, "ime pause run posted " + dumpState());
+ if (mPaused) {
+ return;
+ }
+ mPaused = true;
+ mPausedTargetAdjusted = mTargetAdjusted;
+ mTargetAdjusted = false;
+ mTargetPrimaryDim = mTargetSecondaryDim = 0.f;
+ updateImeAdjustState();
+ startAsyncAnimation();
+ });
+ }
+
+ public void resume(int displayId) {
+ if (DEBUG) Slog.d(TAG, "ime resume posting " + dumpState());
+ mHandler.post(() -> {
+ if (DEBUG) Slog.d(TAG, "ime resume run posted " + dumpState());
+ if (!mPaused) {
+ return;
+ }
+ mPaused = false;
+ mTargetAdjusted = mPausedTargetAdjusted;
+ updateDimTargets();
+ if ((mTargetAdjusted != mAdjusted) && !mMinimized && mView != null) {
+ // End unminimize animations since they conflict with adjustment animations.
+ mView.finishAnimations();
+ }
+ updateImeAdjustState();
+ startAsyncAnimation();
+ });
+ }
}
private final DividerImeController mImePositionProcessor = new DividerImeController();
@@ -493,6 +562,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
}
void updateVisibility(final boolean visible) {
+ if (DEBUG) Slog.d(TAG, "Updating visibility " + mVisible + "->" + visible);
if (mVisible != visible) {
mVisible = visible;
mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
@@ -519,12 +589,21 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
/** Switch to minimized state if appropriate */
public void setMinimized(final boolean minimized) {
+ if (DEBUG) Slog.d(TAG, "posting ext setMinimized " + minimized + " vis:" + mVisible);
mHandler.post(() -> {
+ if (DEBUG) Slog.d(TAG, "run posted ext setMinimized " + minimized + " vis:" + mVisible);
+ if (!mVisible) {
+ return;
+ }
setHomeMinimized(minimized, mHomeStackResizable);
});
}
private void setHomeMinimized(final boolean minimized, boolean homeStackResizable) {
+ if (DEBUG) {
+ Slog.d(TAG, "setHomeMinimized min:" + mMinimized + "->" + minimized + " hrsz:"
+ + mHomeStackResizable + "->" + homeStackResizable + " split:" + inSplitMode());
+ }
WindowContainerTransaction wct = new WindowContainerTransaction();
// Update minimized state
if (mMinimized != minimized) {
@@ -544,7 +623,17 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
// Sync state to DividerView if it exists.
if (mView != null) {
+ final int displayId = mView.getDisplay() != null
+ ? mView.getDisplay().getDisplayId() : DEFAULT_DISPLAY;
+ // pause ime here (before updateMinimizedDockedStack)
+ if (mMinimized) {
+ mImePositionProcessor.pause(displayId);
+ }
mView.setMinimizedDockStack(minimized, getAnimDuration(), homeStackResizable);
+ if (!mMinimized) {
+ // afterwards so it can end any animations started in view
+ mImePositionProcessor.resume(displayId);
+ }
}
updateTouchable();
WindowManagerProxy.applyContainerTransaction(wct);
@@ -649,7 +738,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
if (!inSplitMode()) {
// Wasn't in split-mode yet, so enter now.
if (DEBUG) {
- Log.d(TAG, " entering split mode with minimized=true");
+ Slog.d(TAG, " entering split mode with minimized=true");
}
updateVisibility(true /* visible */);
}
@@ -660,7 +749,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
if (!inSplitMode()) {
// Wasn't in split-mode, so enter now.
if (DEBUG) {
- Log.d(TAG, " enter split mode unminimized ");
+ Slog.d(TAG, " enter split mode unminimized ");
}
updateVisibility(true /* visible */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 4114bb9d055d..131f4e1c9d59 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -34,6 +34,7 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
+import android.util.Slog;
import android.view.Choreographer;
import android.view.Display;
import android.view.InsetsState;
@@ -67,12 +68,15 @@ import com.android.systemui.R;
import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.statusbar.FlingAnimationUtils;
+import java.util.function.Consumer;
+
/**
* Docked stack divider.
*/
public class DividerView extends FrameLayout implements OnTouchListener,
OnComputeInternalInsetsListener {
private static final String TAG = "DividerView";
+ private static final boolean DEBUG = Divider.DEBUG;
public interface DividerCallbacks {
void onDraggingStart();
@@ -627,6 +631,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
cancelFlingAnimation();
updateDockSide();
}
+ if (DEBUG) Slog.d(TAG, "Getting fling " + position + "->" + snapTarget.position);
final boolean taskPositionSameAtEnd = snapTarget.flag == SnapTarget.FLAG_NONE;
ValueAnimator anim = ValueAnimator.ofInt(position, snapTarget.position);
anim.addUpdateListener(animation -> resizeStackDelayed((int) animation.getAnimatedValue(),
@@ -634,16 +639,21 @@ public class DividerView extends FrameLayout implements OnTouchListener,
? TASK_POSITION_SAME
: snapTarget.taskPosition,
snapTarget));
- Runnable endAction = () -> {
+ Consumer<Boolean> endAction = cancelled -> {
+ if (DEBUG) Slog.d(TAG, "End Fling " + cancelled + " min:" + mIsInMinimizeInteraction);
+ final boolean wasMinimizeInteraction = mIsInMinimizeInteraction;
+ // Reset minimized divider position after unminimized state animation finishes.
+ if (!cancelled && !mDockedStackMinimized && mIsInMinimizeInteraction) {
+ mIsInMinimizeInteraction = false;
+ }
boolean dismissed = commitSnapFlags(snapTarget);
mWindowManagerProxy.setResizing(false);
updateDockSide();
mCurrentAnimator = null;
mEntranceAnimationRunning = false;
mExitAnimationRunning = false;
- if (!dismissed) {
- WindowManagerProxy.applyResizeSplits((mIsInMinimizeInteraction
- ? mSnapTargetBeforeMinimized : snapTarget).position, mSplitLayout);
+ if (!dismissed && !wasMinimizeInteraction) {
+ WindowManagerProxy.applyResizeSplits(snapTarget.position, mSplitLayout);
}
if (mCallback != null) {
mCallback.onDraggingEnd();
@@ -667,12 +677,6 @@ public class DividerView extends FrameLayout implements OnTouchListener,
}
}
};
- Runnable notCancelledEndAction = () -> {
- // Reset minimized divider position after unminimized state animation finishes
- if (!mDockedStackMinimized && mIsInMinimizeInteraction) {
- mIsInMinimizeInteraction = false;
- }
- };
anim.addListener(new AnimatorListenerAdapter() {
private boolean mCancelled;
@@ -694,15 +698,11 @@ public class DividerView extends FrameLayout implements OnTouchListener,
delay = mSfChoreographer.getSurfaceFlingerOffsetMs();
}
if (delay == 0) {
- if (!mCancelled) {
- notCancelledEndAction.run();
- }
- endAction.run();
+ endAction.accept(mCancelled);
} else {
- if (!mCancelled) {
- mHandler.postDelayed(notCancelledEndAction, delay);
- }
- mHandler.postDelayed(endAction, delay);
+ final Boolean cancelled = mCancelled;
+ if (DEBUG) Slog.d(TAG, "Posting endFling " + cancelled + " d:" + delay + "ms");
+ mHandler.postDelayed(() -> endAction.accept(cancelled), delay);
}
}
});
@@ -899,6 +899,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
public void setMinimizedDockStack(boolean minimized, long animDuration,
boolean isHomeStackResizable) {
+ if (DEBUG) Slog.d(TAG, "setMinDock: " + mDockedStackMinimized + "->" + minimized);
mHomeStackResizable = isHomeStackResizable;
updateDockSide();
if (!isHomeStackResizable) {
@@ -946,6 +947,15 @@ public class DividerView extends FrameLayout implements OnTouchListener,
.start();
}
+ // Needed to end any currently playing animations when they might compete with other anims
+ // (specifically, IME adjust animation immediately after leaving minimized). Someday maybe
+ // these can be unified, but not today.
+ void finishAnimations() {
+ if (mCurrentAnimator != null) {
+ mCurrentAnimator.end();
+ }
+ }
+
public void setAdjustedForIme(boolean adjustedForIme, long animDuration) {
if (mAdjustedForIme == adjustedForIme) {
return;
@@ -1061,6 +1071,11 @@ public class DividerView extends FrameLayout implements OnTouchListener,
mDividerPositionX = dockedRect.right;
mDividerPositionY = dockedRect.bottom;
+ if (DEBUG) {
+ Slog.d(TAG, "Resizing split surfaces: " + dockedRect + " " + dockedTaskRect
+ + " " + otherRect + " " + otherTaskRect);
+ }
+
t.setPosition(mTiles.mPrimarySurface, dockedTaskRect.left, dockedTaskRect.top);
Rect crop = new Rect(dockedRect);
crop.offsetTo(-Math.min(dockedTaskRect.left - dockedRect.left, 0),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
index c523b7b7655e..866980458cad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
@@ -40,8 +40,10 @@ open class BlurUtils @Inject constructor(
) : Dumpable {
private val minBlurRadius = resources.getDimensionPixelSize(R.dimen.min_window_blur_radius)
private val maxBlurRadius = resources.getDimensionPixelSize(R.dimen.max_window_blur_radius)
- private val blurSysProp = SystemProperties
+ private val blurSupportedSysProp = SystemProperties
.getBoolean("ro.surface_flinger.supports_background_blur", false)
+ private val blurDisabledSysProp = SystemProperties
+ .getBoolean("persist.sys.sf.disable_blurs", false)
init {
dumpManager.registerDumpable(javaClass.name, this)
@@ -97,7 +99,7 @@ open class BlurUtils @Inject constructor(
* @return {@code true} when supported.
*/
open fun supportsBlursOnWindows(): Boolean {
- return blurSysProp && ActivityManager.isHighEndGfx()
+ return blurSupportedSysProp && !blurDisabledSysProp && ActivityManager.isHighEndGfx()
}
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
@@ -106,7 +108,8 @@ open class BlurUtils @Inject constructor(
it.increaseIndent()
it.println("minBlurRadius: $minBlurRadius")
it.println("maxBlurRadius: $maxBlurRadius")
- it.println("blurSysProp: $blurSysProp")
+ it.println("blurSupportedSysProp: $blurSupportedSysProp")
+ it.println("blurDisabledSysProp: $blurDisabledSysProp")
it.println("supportsBlursOnWindows: ${supportsBlursOnWindows()}")
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
index 4597b1656884..b33424c563a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
@@ -33,6 +33,7 @@ import com.android.keyguard.AlphaOptimizedLinearLayout;
import com.android.systemui.R;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry.OnSensitivityChangedListener;
import java.util.List;
@@ -159,20 +160,30 @@ public class HeadsUpStatusBarView extends AlphaOptimizedLinearLayout {
}
public void setEntry(NotificationEntry entry) {
- if (entry != null) {
- mShowingEntry = entry;
+ if (mShowingEntry != null) {
+ mShowingEntry.removeOnSensitivityChangedListener(mOnSensitivityChangedListener);
+ }
+ mShowingEntry = entry;
+
+ if (mShowingEntry != null) {
CharSequence text = entry.headsUpStatusBarText;
if (entry.isSensitive()) {
text = entry.headsUpStatusBarTextPublic;
}
mTextView.setText(text);
- mShowingEntry.setOnSensitiveChangedListener(() -> setEntry(entry));
- } else if (mShowingEntry != null){
- mShowingEntry.setOnSensitiveChangedListener(null);
- mShowingEntry = null;
+ mShowingEntry.addOnSensitivityChangedListener(mOnSensitivityChangedListener);
}
}
+ private final OnSensitivityChangedListener mOnSensitivityChangedListener = entry -> {
+ if (entry != mShowingEntry) {
+ throw new IllegalStateException("Got a sensitivity change for " + entry
+ + " but mShowingEntry is " + mShowingEntry);
+ }
+ // Update the text
+ setEntry(entry);
+ };
+
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index 047edd26c689..72d9d0ee8f8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -206,7 +206,8 @@ public class NotificationListener extends NotificationListenerWithPlugins {
false,
false,
false,
- null
+ null,
+ false
);
}
return ranking;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 87be73998fcc..d8fdf928f6ad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -70,6 +70,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -262,11 +263,11 @@ public class NotificationMediaManager implements Dumpable {
synchronized (mEntryManager) {
NotificationEntry entry = mEntryManager
.getActiveNotificationUnfiltered(mMediaNotificationKey);
- if (entry == null || entry.expandedIcon == null) {
+ if (entry == null || entry.getIcons().getShelfIcon() == null) {
return null;
}
- return entry.expandedIcon.getSourceIcon();
+ return entry.getIcons().getShelfIcon().getSourceIcon();
}
}
@@ -284,8 +285,7 @@ public class NotificationMediaManager implements Dumpable {
boolean metaDataChanged = false;
synchronized (mEntryManager) {
- Set<NotificationEntry> allNotifications =
- mEntryManager.getPendingAndActiveNotifications();
+ Collection<NotificationEntry> allNotifications = mEntryManager.getAllNotifs();
// Promote the media notification with a controller in 'playing' state, if any.
NotificationEntry mediaNotification = null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index eb8526d0ef91..8945f360f7b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -22,6 +22,7 @@ import android.animation.ValueAnimator
import android.app.WallpaperManager
import android.view.Choreographer
import android.view.View
+import androidx.annotation.VisibleForTesting
import androidx.dynamicanimation.animation.FloatPropertyCompat
import androidx.dynamicanimation.animation.SpringAnimation
import androidx.dynamicanimation.animation.SpringForce
@@ -29,8 +30,10 @@ import com.android.internal.util.IndentingPrintWriter
import com.android.systemui.Dumpable
import com.android.systemui.Interpolators
import com.android.systemui.dump.DumpManager
+import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK
+import com.android.systemui.statusbar.phone.NotificationShadeWindowController
import com.android.systemui.statusbar.phone.PanelExpansionListener
import com.android.systemui.statusbar.policy.KeyguardStateController
import java.io.FileDescriptor
@@ -44,17 +47,17 @@ import kotlin.math.max
*/
@Singleton
class NotificationShadeDepthController @Inject constructor(
- private val statusBarStateController: SysuiStatusBarStateController,
+ private val statusBarStateController: StatusBarStateController,
private val blurUtils: BlurUtils,
private val biometricUnlockController: BiometricUnlockController,
private val keyguardStateController: KeyguardStateController,
private val choreographer: Choreographer,
private val wallpaperManager: WallpaperManager,
+ private val notificationShadeWindowController: NotificationShadeWindowController,
dumpManager: DumpManager
) : PanelExpansionListener, Dumpable {
companion object {
private const val WAKE_UP_ANIMATION_ENABLED = true
- private const val SHADE_BLUR_ENABLED = true
}
lateinit var root: View
@@ -62,8 +65,9 @@ class NotificationShadeDepthController @Inject constructor(
private var keyguardAnimator: Animator? = null
private var notificationAnimator: Animator? = null
private var updateScheduled: Boolean = false
- private var shadeExpansion = 1.0f
- private val shadeSpring = SpringAnimation(this, object :
+ private var shadeExpansion = 0f
+ @VisibleForTesting
+ var shadeSpring = SpringAnimation(this, object :
FloatPropertyCompat<NotificationShadeDepthController>("shadeBlurRadius") {
override fun setValue(rect: NotificationShadeDepthController?, value: Float) {
shadeBlurRadius = value.toInt()
@@ -74,12 +78,25 @@ class NotificationShadeDepthController @Inject constructor(
}
})
private val zoomInterpolator = Interpolators.ACCELERATE_DECELERATE
+
+ /**
+ * Radius that we're animating to.
+ */
+ private var pendingShadeBlurRadius = -1
+
+ /**
+ * Shade blur radius on the current frame.
+ */
private var shadeBlurRadius = 0
set(value) {
if (field == value) return
field = value
scheduleUpdate()
}
+
+ /**
+ * Blur radius of the wake-up animation on this frame.
+ */
private var wakeAndUnlockBlurRadius = 0
set(value) {
if (field == value) return
@@ -100,6 +117,7 @@ class NotificationShadeDepthController @Inject constructor(
val rawZoom = max(blurUtils.ratioOfBlurRadius(blur), globalDialogVisibility)
wallpaperManager.setWallpaperZoomOut(root.windowToken,
zoomInterpolator.getInterpolation(rawZoom))
+ notificationShadeWindowController.setBackgroundBlurRadius(blur)
}
/**
@@ -139,6 +157,18 @@ class NotificationShadeDepthController @Inject constructor(
}
}
+ private val statusBarStateCallback = object : StatusBarStateController.StateListener {
+ override fun onStateChanged(newState: Int) {
+ updateShadeBlur()
+ }
+
+ override fun onDozingChanged(isDozing: Boolean) {
+ if (isDozing && shadeSpring.isRunning) {
+ shadeSpring.skipToEnd()
+ }
+ }
+ }
+
init {
dumpManager.registerDumpable(javaClass.name, this)
if (WAKE_UP_ANIMATION_ENABLED) {
@@ -147,24 +177,31 @@ class NotificationShadeDepthController @Inject constructor(
shadeSpring.spring = SpringForce(0.0f)
shadeSpring.spring.dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
shadeSpring.spring.stiffness = SpringForce.STIFFNESS_LOW
+ shadeSpring.addEndListener { _, _, _, _ -> pendingShadeBlurRadius = -1 }
+ statusBarStateController.addCallback(statusBarStateCallback)
}
/**
* Update blurs when pulling down the shade
*/
override fun onPanelExpansionChanged(expansion: Float, tracking: Boolean) {
- if (!SHADE_BLUR_ENABLED) {
+ if (expansion == shadeExpansion) {
return
}
+ shadeExpansion = expansion
+ updateShadeBlur()
+ }
+ private fun updateShadeBlur() {
var newBlur = 0
if (statusBarStateController.state == StatusBarState.SHADE) {
- newBlur = blurUtils.blurRadiusOfRatio(expansion)
+ newBlur = blurUtils.blurRadiusOfRatio(shadeExpansion)
}
- if (shadeBlurRadius == newBlur) {
+ if (pendingShadeBlurRadius == newBlur) {
return
}
+ pendingShadeBlurRadius = newBlur
shadeSpring.animateToFinalPosition(newBlur.toFloat())
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 1a8454cfa113..d7f2ae43cf9e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -329,7 +329,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
expandableRow.setAboveShelf(false);
}
if (notGoneIndex == 0) {
- StatusBarIconView icon = expandableRow.getEntry().expandedIcon;
+ StatusBarIconView icon = expandableRow.getEntry().getIcons().getShelfIcon();
NotificationIconContainer.IconState iconState = getIconState(icon);
// The icon state might be null in rare cases where the notification is actually
// added to the layout, but not to the shelf. An example are replied messages,
@@ -432,7 +432,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
// if the shelf is clipped, lets make sure we also clip the icon
maxTop = Math.max(maxTop, getTranslationY() + getClipTopAmount());
}
- StatusBarIconView icon = row.getEntry().expandedIcon;
+ StatusBarIconView icon = row.getEntry().getIcons().getShelfIcon();
float shelfIconPosition = getTranslationY() + icon.getTop() + icon.getTranslationY();
if (shelfIconPosition < maxTop && !mAmbientState.isFullyHidden()) {
int top = (int) (maxTop - shelfIconPosition);
@@ -444,7 +444,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
}
private void updateContinuousClipping(final ExpandableNotificationRow row) {
- StatusBarIconView icon = row.getEntry().expandedIcon;
+ StatusBarIconView icon = row.getEntry().getIcons().getShelfIcon();
boolean needsContinuousClipping = ViewState.isAnimatingY(icon) && !mAmbientState.isDozing();
boolean isContinuousClipping = icon.getTag(TAG_CONTINUOUS_CLIPPING) != null;
if (needsContinuousClipping && !isContinuousClipping) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 7f0479c39c95..c6d84ff79bde 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -29,6 +29,7 @@ import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -56,9 +57,9 @@ import com.android.systemui.util.leak.LeakDetector;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -105,6 +106,10 @@ public class NotificationEntryManager implements
*/
public static final int UNDEFINED_DISMISS_REASON = 0;
+ private final Set<NotificationEntry> mAllNotifications = new ArraySet<>();
+ private final Set<NotificationEntry> mReadOnlyAllNotifications =
+ Collections.unmodifiableSet(mAllNotifications);
+
/** Pending notifications are ones awaiting inflation */
@VisibleForTesting
protected final HashMap<String, NotificationEntry> mPendingNotifications = new HashMap<>();
@@ -468,6 +473,8 @@ public class NotificationEntryManager implements
entry.removeRow();
}
+ mAllNotifications.remove(entry);
+
// Let's remove the children if this was a summary
handleGroupSummaryRemoved(key);
removeVisibleNotification(key);
@@ -548,6 +555,7 @@ public class NotificationEntryManager implements
notification,
ranking,
mFgsFeatureController.isForegroundServiceDismissalEnabled());
+ mAllNotifications.add(entry);
mLeakDetector.trackInstance(entry);
@@ -709,15 +717,6 @@ public class NotificationEntryManager implements
}
/**
- * @return all notifications we're currently aware of (both pending and active notifications)
- */
- public Set<NotificationEntry> getPendingAndActiveNotifications() {
- Set<NotificationEntry> allNotifs = new HashSet<>(mPendingNotifications.values());
- allNotifs.addAll(mSortedAndFiltered);
- return allNotifs;
- }
-
- /**
* Use this method to retrieve a notification entry that has been prepared for presentation.
* Note that the notification may be filtered out and never shown to the user.
*
@@ -842,7 +841,7 @@ public class NotificationEntryManager implements
private void dumpEntry(PrintWriter pw, String indent, int i, NotificationEntry e) {
pw.print(indent);
- pw.println(" [" + i + "] key=" + e.getKey() + " icon=" + e.icon);
+ pw.println(" [" + i + "] key=" + e.getKey() + " icon=" + e.getIcons().getStatusBarIcon());
StatusBarNotification n = e.getSbn();
pw.print(indent);
pw.println(" pkg=" + n.getPackageName() + " id=" + n.getId() + " importance="
@@ -861,6 +860,15 @@ public class NotificationEntryManager implements
return mReadOnlyNotifications;
}
+ /**
+ * Returns a collections containing ALL notifications we know about, including ones that are
+ * hidden or for other users. See {@link CommonNotifCollection#getAllNotifs()}.
+ */
+ @Override
+ public Collection<NotificationEntry> getAllNotifs() {
+ return mReadOnlyAllNotifications;
+ }
+
/** @return A count of the active notifications */
public int getActiveNotificationsCount() {
return mReadOnlyNotifications.size();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
index fabe3a7bfee4..b357ada7bcf1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
@@ -45,7 +45,6 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpabl
private final ArrayList<Callback> mCallbacks = new ArrayList<>();
private final Handler mHandler;
- private NotificationPresenter mPresenter;
private boolean mPanelExpanded;
private boolean mScreenOn;
private boolean mReorderingAllowed;
@@ -80,7 +79,6 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpabl
}
public void setUpWithPresenter(NotificationPresenter presenter) {
- mPresenter = presenter;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java
index b960b42b3e2d..2c747bdcf7b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.collection;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
@@ -51,6 +52,7 @@ public class GroupEntry extends ListEntry {
return mSummary;
}
+ @NonNull
public List<NotificationEntry> getChildren() {
return mUnmodifiableChildren;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 749e5e2a769d..b90cfa8ae25e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -64,24 +64,34 @@ import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.notification.collection.coalescer.CoalescedEvent;
import com.android.systemui.statusbar.notification.collection.coalescer.GroupCoalescer;
import com.android.systemui.statusbar.notification.collection.coalescer.GroupCoalescer.BatchableNotificationHandler;
+import com.android.systemui.statusbar.notification.collection.notifcollection.CleanUpEntryEvent;
import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
+import com.android.systemui.statusbar.notification.collection.notifcollection.EntryAddedEvent;
+import com.android.systemui.statusbar.notification.collection.notifcollection.EntryRemovedEvent;
+import com.android.systemui.statusbar.notification.collection.notifcollection.EntryUpdatedEvent;
+import com.android.systemui.statusbar.notification.collection.notifcollection.InitEntryEvent;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionLogger;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifEvent;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
+import com.android.systemui.statusbar.notification.collection.notifcollection.RankingAppliedEvent;
+import com.android.systemui.statusbar.notification.collection.notifcollection.RankingUpdatedEvent;
import com.android.systemui.util.Assert;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Queue;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -124,6 +134,8 @@ public class NotifCollection implements Dumpable {
private final List<NotifLifetimeExtender> mLifetimeExtenders = new ArrayList<>();
private final List<NotifDismissInterceptor> mDismissInterceptors = new ArrayList<>();
+ private Queue<NotifEvent> mEventQueue = new ArrayDeque<>();
+
private boolean mAttached = false;
private boolean mAmDispatchingToOtherCode;
@@ -160,8 +172,8 @@ public class NotifCollection implements Dumpable {
mBuildListener = buildListener;
}
- /** @see NotifPipeline#getActiveNotifs() */
- Collection<NotificationEntry> getActiveNotifs() {
+ /** @see NotifPipeline#getAllNotifs() */
+ Collection<NotificationEntry> getAllNotifs() {
Assert.isMainThread();
return mReadOnlyNotificationSet;
}
@@ -242,7 +254,7 @@ public class NotifCollection implements Dumpable {
}
locallyDismissNotifications(entriesToLocallyDismiss);
- rebuildList();
+ dispatchEventsAndRebuildList();
}
/**
@@ -251,8 +263,7 @@ public class NotifCollection implements Dumpable {
public void dismissNotification(
NotificationEntry entry,
@NonNull DismissedByUserStats stats) {
- dismissNotifications(List.of(
- new Pair<NotificationEntry, DismissedByUserStats>(entry, stats)));
+ dismissNotifications(List.of(new Pair<>(entry, stats)));
}
/**
@@ -268,7 +279,7 @@ public class NotifCollection implements Dumpable {
// system process is dead if we're here.
}
- final List<NotificationEntry> entries = new ArrayList(getActiveNotifs());
+ final List<NotificationEntry> entries = new ArrayList<>(getAllNotifs());
for (int i = entries.size() - 1; i >= 0; i--) {
NotificationEntry entry = entries.get(i);
if (!shouldDismissOnClearAll(entry, userId)) {
@@ -283,7 +294,7 @@ public class NotifCollection implements Dumpable {
}
locallyDismissNotifications(entries);
- rebuildList();
+ dispatchEventsAndRebuildList();
}
/**
@@ -326,8 +337,9 @@ public class NotifCollection implements Dumpable {
private void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
Assert.isMainThread();
- postNotification(sbn, requireRanking(rankingMap, sbn.getKey()), rankingMap);
- rebuildList();
+ postNotification(sbn, requireRanking(rankingMap, sbn.getKey()));
+ applyRanking(rankingMap);
+ dispatchEventsAndRebuildList();
}
private void onNotificationGroupPosted(List<CoalescedEvent> batch) {
@@ -336,9 +348,9 @@ public class NotifCollection implements Dumpable {
mLogger.logNotifGroupPosted(batch.get(0).getSbn().getGroupKey(), batch.size());
for (CoalescedEvent event : batch) {
- postNotification(event.getSbn(), event.getRanking(), null);
+ postNotification(event.getSbn(), event.getRanking());
}
- rebuildList();
+ dispatchEventsAndRebuildList();
}
private void onNotificationRemoved(
@@ -354,55 +366,49 @@ public class NotifCollection implements Dumpable {
throw new IllegalStateException("No notification to remove with key " + sbn.getKey());
}
entry.mCancellationReason = reason;
- applyRanking(rankingMap);
tryRemoveNotification(entry);
- rebuildList();
+ applyRanking(rankingMap);
+ dispatchEventsAndRebuildList();
}
private void onNotificationRankingUpdate(RankingMap rankingMap) {
Assert.isMainThread();
+ mEventQueue.add(new RankingUpdatedEvent(rankingMap));
applyRanking(rankingMap);
- dispatchNotificationRankingUpdate(rankingMap);
- rebuildList();
+ dispatchEventsAndRebuildList();
}
private void postNotification(
StatusBarNotification sbn,
- Ranking ranking,
- @Nullable RankingMap rankingMap) {
+ Ranking ranking) {
NotificationEntry entry = mNotificationSet.get(sbn.getKey());
if (entry == null) {
// A new notification!
- mLogger.logNotifPosted(sbn.getKey());
-
entry = new NotificationEntry(sbn, ranking);
mNotificationSet.put(sbn.getKey(), entry);
- dispatchOnEntryInit(entry);
- if (rankingMap != null) {
- applyRanking(rankingMap);
- }
-
- dispatchOnEntryAdded(entry);
+ mLogger.logNotifPosted(sbn.getKey());
+ mEventQueue.add(new InitEntryEvent(entry));
+ mEventQueue.add(new EntryAddedEvent(entry));
} else {
// Update to an existing entry
- mLogger.logNotifUpdated(sbn.getKey());
// Notification is updated so it is essentially re-added and thus alive again, so we
// can reset its state.
+ // TODO: If a coalesced event ever gets here, it's possible to lose track of children,
+ // since their rankings might have been updated earlier (and thus we may no longer
+ // think a child is associated with this locally-dismissed entry).
cancelLocalDismissal(entry);
cancelLifetimeExtension(entry);
cancelDismissInterception(entry);
entry.mCancellationReason = REASON_NOT_CANCELED;
entry.setSbn(sbn);
- if (rankingMap != null) {
- applyRanking(rankingMap);
- }
- dispatchOnEntryUpdated(entry);
+ mLogger.logNotifUpdated(sbn.getKey());
+ mEventQueue.add(new EntryUpdatedEvent(entry));
}
}
@@ -432,8 +438,8 @@ public class NotifCollection implements Dumpable {
if (!isLifetimeExtended(entry)) {
mNotificationSet.remove(entry.getKey());
cancelDismissInterception(entry);
- dispatchOnEntryRemoved(entry, entry.mCancellationReason);
- dispatchOnEntryCleanUp(entry);
+ mEventQueue.add(new EntryRemovedEvent(entry, entry.mCancellationReason));
+ mEventQueue.add(new CleanUpEntryEvent(entry));
return true;
} else {
return false;
@@ -466,9 +472,16 @@ public class NotifCollection implements Dumpable {
}
}
}
+ mEventQueue.add(new RankingAppliedEvent());
}
- private void rebuildList() {
+ private void dispatchEventsAndRebuildList() {
+ mAmDispatchingToOtherCode = true;
+ while (!mEventQueue.isEmpty()) {
+ mEventQueue.remove().dispatchTo(mNotifCollectionListeners);
+ }
+ mAmDispatchingToOtherCode = false;
+
if (mBuildListener != null) {
mBuildListener.onBuildList(mReadOnlyNotificationSet);
}
@@ -491,7 +504,7 @@ public class NotifCollection implements Dumpable {
if (!isLifetimeExtended(entry)) {
if (tryRemoveNotification(entry)) {
- rebuildList();
+ dispatchEventsAndRebuildList();
}
}
}
@@ -660,57 +673,9 @@ public class NotifCollection implements Dumpable {
|| entry.getSbn().getUser().getIdentifier() == userId;
}
- private void dispatchOnEntryInit(NotificationEntry entry) {
- mAmDispatchingToOtherCode = true;
- for (NotifCollectionListener listener : mNotifCollectionListeners) {
- listener.onEntryInit(entry);
- }
- mAmDispatchingToOtherCode = false;
- }
-
- private void dispatchOnEntryAdded(NotificationEntry entry) {
- mAmDispatchingToOtherCode = true;
- for (NotifCollectionListener listener : mNotifCollectionListeners) {
- listener.onEntryAdded(entry);
- }
- mAmDispatchingToOtherCode = false;
- }
-
- private void dispatchOnEntryUpdated(NotificationEntry entry) {
- mAmDispatchingToOtherCode = true;
- for (NotifCollectionListener listener : mNotifCollectionListeners) {
- listener.onEntryUpdated(entry);
- }
- mAmDispatchingToOtherCode = false;
- }
-
- private void dispatchNotificationRankingUpdate(RankingMap map) {
- mAmDispatchingToOtherCode = true;
- for (NotifCollectionListener listener : mNotifCollectionListeners) {
- listener.onRankingUpdate(map);
- }
- mAmDispatchingToOtherCode = false;
- }
-
- private void dispatchOnEntryRemoved(NotificationEntry entry, @CancellationReason int reason) {
- mAmDispatchingToOtherCode = true;
- for (NotifCollectionListener listener : mNotifCollectionListeners) {
- listener.onEntryRemoved(entry, reason);
- }
- mAmDispatchingToOtherCode = false;
- }
-
- private void dispatchOnEntryCleanUp(NotificationEntry entry) {
- mAmDispatchingToOtherCode = true;
- for (NotifCollectionListener listener : mNotifCollectionListeners) {
- listener.onEntryCleanUp(entry);
- }
- mAmDispatchingToOtherCode = false;
- }
-
@Override
public void dump(@NonNull FileDescriptor fd, PrintWriter pw, @NonNull String[] args) {
- final List<NotificationEntry> entries = new ArrayList<>(getActiveNotifs());
+ final List<NotificationEntry> entries = new ArrayList<>(getAllNotifs());
pw.println("\t" + TAG + " unsorted/unfiltered notifications:");
if (entries.size() == 0) {
@@ -754,6 +719,7 @@ public class NotifCollection implements Dumpable {
private static final String TAG = "NotifCollection";
@IntDef(prefix = { "REASON_" }, value = {
+ REASON_NOT_CANCELED,
REASON_UNKNOWN,
REASON_CLICK,
REASON_CANCEL_ALL,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java
index 14903cd7bb27..17899e99275b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java
@@ -82,14 +82,14 @@ public class NotifPipeline implements CommonNotifCollection {
}
/**
- * Returns the list of "active" notifications, i.e. the notifications that are currently posted
+ * Returns the list of all known notifications, i.e. the notifications that are currently posted
* to the phone. In general, this tracks closely to the list maintained by NotificationManager,
* but it can diverge slightly due to lifetime extenders.
*
* The returned collection is read-only, unsorted, unfiltered, and ungrouped.
*/
- public Collection<NotificationEntry> getActiveNotifs() {
- return mNotifCollection.getActiveNotifs();
+ public Collection<NotificationEntry> getAllNotifs() {
+ return mNotifCollection.getAllNotifs();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewBarn.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewBarn.kt
new file mode 100644
index 000000000000..e7948cd1216e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewBarn.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection
+
+import android.view.textclassifier.Log
+import com.android.systemui.statusbar.notification.stack.NotificationListItem
+import java.lang.IllegalStateException
+
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/**
+ * The ViewBarn is just a map from [ListEntry] to an instance of [NotificationListItem] which is
+ * usually just an [ExpandableNotificationRow]
+ */
+@Singleton
+class NotifViewBarn @Inject constructor() {
+ private val DEBUG = false
+
+ private val rowMap = mutableMapOf<String, NotificationListItem>()
+
+ fun requireView(forEntry: ListEntry): NotificationListItem {
+ if (DEBUG) {
+ Log.d(TAG, "requireView: $forEntry.key")
+ }
+ val li = rowMap[forEntry.key]
+ if (li == null) {
+ throw IllegalStateException("No view has been registered for entry: $forEntry")
+ }
+
+ return li
+ }
+
+ fun registerViewForEntry(entry: ListEntry, view: NotificationListItem) {
+ if (DEBUG) {
+ Log.d(TAG, "registerViewForEntry: $entry.key")
+ }
+ rowMap[entry.key] = view
+ }
+
+ fun removeViewForEntry(entry: ListEntry) {
+ if (DEBUG) {
+ Log.d(TAG, "removeViewForEntry: $entry.key")
+ }
+ rowMap.remove(entry.key)
+ }
+}
+
+private const val TAG = "NotifViewBarn" \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewManager.kt
new file mode 100644
index 000000000000..0437877d8330
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewManager.kt
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection
+
+import android.annotation.MainThread
+import android.view.ViewGroup
+
+import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.statusbar.notification.VisualStabilityManager
+import com.android.systemui.statusbar.notification.collection.GroupEntry.ROOT_ENTRY
+import com.android.systemui.statusbar.notification.stack.NotificationListItem
+import com.android.systemui.util.Assert
+
+import java.io.FileDescriptor
+import java.io.PrintWriter
+import java.lang.IllegalStateException
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/**
+ * A consumer of a Notification tree built by [ShadeListBuilder] which will update the notification
+ * presenter with the minimum operations required to make the old tree match the new one
+ */
+@MainThread
+@Singleton
+class NotifViewManager @Inject constructor(
+ private val rowRegistry: NotifViewBarn,
+ private val stabilityManager: VisualStabilityManager,
+ private val featureFlags: FeatureFlags
+) {
+ var currentNotifs = listOf<ListEntry>()
+
+ private lateinit var listContainer: SimpleNotificationListContainer
+
+ fun attach(listBuilder: ShadeListBuilder) {
+ if (featureFlags.isNewNotifPipelineRenderingEnabled) {
+ listBuilder.setOnRenderListListener { entries: List<ListEntry> ->
+ this.onNotifTreeBuilt(entries)
+ }
+ }
+ }
+
+ fun setViewConsumer(consumer: SimpleNotificationListContainer) {
+ listContainer = consumer
+ }
+
+ /**
+ * Callback for when the tree is rebuilt
+ */
+ fun onNotifTreeBuilt(notifList: List<ListEntry>) {
+ Assert.isMainThread()
+
+ /*
+ * The assumption here is that anything from the old NotificationViewHierarchyManager that
+ * is responsible for filtering is done via the NotifFilter logic. This tree we get should
+ * be *the stuff to display* +/- redacted stuff
+ */
+
+ detachRows(notifList)
+ attachRows(notifList)
+
+ currentNotifs = notifList
+ }
+
+ private fun detachRows(entries: List<ListEntry>) {
+ // To properly detach rows, we are looking to remove any view in the consumer that is not
+ // present in the incoming list.
+ //
+ // Every listItem was top-level, so it's entry's parent was ROOT_ENTRY, but now
+ // there are two possibilities:
+ //
+ // 1. It is not present in the entry list
+ // 1a. It has moved to be a child in the entry list - transfer it
+ // 1b. It is gone completely - remove it
+ // 2. It is present in the entry list - diff the children
+ getListItems(listContainer)
+ .filter {
+ // Ignore things that are showing the blocking helper
+ !it.isBlockingHelperShowing
+ }
+ .forEach { listItem ->
+ val noLongerTopLevel = listItem.entry.parent != ROOT_ENTRY
+ val becameChild = noLongerTopLevel && listItem.entry.parent != null
+
+ val idx = entries.indexOf(listItem.entry)
+
+ if (noLongerTopLevel) {
+ // Summaries won't become children; remove the whole group
+ if (listItem.isSummaryWithChildren) {
+ listItem.removeAllChildren()
+ }
+
+ if (becameChild) {
+ // Top-level element is becoming a child, don't generate an animation
+ listContainer.setChildTransferInProgress(true)
+ }
+ listContainer.removeListItem(listItem)
+ listContainer.setChildTransferInProgress(false)
+ } else if (entries[idx] is GroupEntry) {
+ // A top-level entry exists. If it's a group, diff the children
+ val groupChildren = (entries[idx] as GroupEntry).children
+ listItem.notificationChildren?.forEach { listChild ->
+ if (!groupChildren.contains(listChild.entry)) {
+ listItem.removeChildNotification(listChild)
+
+ // TODO: the old code only calls this if the notif is gone from
+ // NEM.getActiveNotificationUnfiltered(). Do we care?
+ listContainer.notifyGroupChildRemoved(
+ listChild.view, listChild.view.parent as ViewGroup)
+ }
+ }
+ }
+ }
+ }
+
+ /** Convenience method for getting a sequence of [NotificationListItem]s */
+ private fun getListItems(container: SimpleNotificationListContainer):
+ Sequence<NotificationListItem> {
+ return (0 until container.getContainerChildCount()).asSequence()
+ .map { container.getContainerChildAt(it) }
+ .filterIsInstance<NotificationListItem>()
+ }
+
+ private fun attachRows(entries: List<ListEntry>) {
+
+ var orderChanged = false
+
+ // To attach rows we can use _this one weird trick_: if the intended view to add does not
+ // have a parent, then simply add it (and its children).
+ entries.forEach { entry ->
+ val listItem = rowRegistry.requireView(entry)
+
+ if (listItem.view.parent != null) {
+ listContainer.addListItem(listItem)
+ stabilityManager.notifyViewAddition(listItem.view)
+ }
+
+ if (entry is GroupEntry) {
+ for ((idx, childEntry) in entry.children.withIndex()) {
+ val childListItem = rowRegistry.requireView(childEntry)
+ // Child hasn't been added yet. add it!
+ if (!listItem.notificationChildren.contains(childListItem)) {
+ // TODO: old code here just Log.wtf()'d here. This might wreak havoc
+ if (childListItem.view.parent != null) {
+ throw IllegalStateException("trying to add a notification child that " +
+ "already has a parent. class: " +
+ "${childListItem.view.parent?.javaClass} " +
+ "\n child: ${childListItem.view}"
+ )
+ }
+
+ listItem.addChildNotification(childListItem, idx)
+ stabilityManager.notifyViewAddition(childListItem.view)
+ listContainer.notifyGroupChildAdded(childListItem.view)
+ }
+ }
+
+ // finally after removing and adding has been performed we can apply the order
+ orderChanged = orderChanged ||
+ listItem.applyChildOrder(
+ getChildListFromParent(entry),
+ stabilityManager,
+ null /*TODO: stability callback */
+ )
+ }
+ }
+
+ if (orderChanged) {
+ listContainer.generateChildOrderChangedEvent()
+ }
+ }
+
+ private fun getChildListFromParent(parent: ListEntry): List<NotificationListItem> {
+ if (parent is GroupEntry) {
+ return parent.children.map { child -> rowRegistry.requireView(child) }
+ .toList()
+ }
+
+ return emptyList()
+ }
+
+ fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) {
+ }
+}
+
+private const val TAG = "NotifViewDataSource" \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 3e9d8a436f38..7019b5b42cf4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -29,8 +29,6 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCRE
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
-import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
-import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
import static com.android.systemui.statusbar.notification.collection.NotifCollection.REASON_NOT_CANCELED;
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING;
@@ -44,10 +42,7 @@ import android.app.NotificationManager.Policy;
import android.app.Person;
import android.app.RemoteInputHistoryItem;
import android.content.Context;
-import android.content.pm.LauncherApps;
-import android.content.pm.LauncherApps.ShortcutQuery;
import android.content.pm.ShortcutInfo;
-import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Bundle;
import android.os.SystemClock;
@@ -55,25 +50,20 @@ import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
-import android.util.Log;
-import android.view.View;
-import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.statusbar.InflationTask;
-import com.android.systemui.statusbar.StatusBarIconView;
-import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
+import com.android.systemui.statusbar.notification.icon.IconPack;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
import com.android.systemui.statusbar.notification.row.NotificationGuts;
@@ -81,8 +71,6 @@ import com.android.systemui.statusbar.notification.row.NotificationRowContentBin
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -102,15 +90,11 @@ import java.util.Objects;
* clean this up in the future.
*/
public final class NotificationEntry extends ListEntry {
- private static final String TAG = "NotificationEntry";
private final String mKey;
private StatusBarNotification mSbn;
private Ranking mRanking;
- private StatusBarIcon mSmallIcon;
- private StatusBarIcon mPeopleAvatar;
-
/*
* Bookkeeping members
*/
@@ -142,10 +126,7 @@ public final class NotificationEntry extends ListEntry {
* TODO: Remove every member beneath this line if possible
*/
- public StatusBarIconView icon;
- public StatusBarIconView expandedIcon;
- public StatusBarIconView centeredIcon;
- public StatusBarIconView aodIcon;
+ private IconPack mIcons = IconPack.buildEmptyPack(null);
private boolean interruption;
public int targetSdk;
private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET;
@@ -191,7 +172,8 @@ public final class NotificationEntry extends ListEntry {
private boolean hasSentReply;
private boolean mSensitive = true;
- private Runnable mOnSensitiveChangedListener;
+ private List<OnSensitivityChangedListener> mOnSensitivityChangedListeners = new ArrayList<>();
+
private boolean mAutoHeadsUp;
private boolean mPulseSupressed;
private boolean mAllowFgsDismissal;
@@ -347,6 +329,15 @@ public final class NotificationEntry extends ListEntry {
* TODO: Remove as many of these as possible
*/
+ @NonNull
+ public IconPack getIcons() {
+ return mIcons;
+ }
+
+ public void setIcons(@NonNull IconPack icons) {
+ mIcons = icons;
+ }
+
public void setInterruption() {
interruption = true;
}
@@ -464,239 +455,6 @@ public final class NotificationEntry extends ListEntry {
|| SystemClock.elapsedRealtime() > initializationTime + INITIALIZATION_DELAY;
}
- /**
- * Create the icons for a notification
- * @param context the context to create the icons with
- * @param sbn the notification
- * @throws InflationException Exception if required icons are not valid or specified
- */
- public void createIcons(Context context, StatusBarNotification sbn)
- throws InflationException {
- StatusBarIcon ic = getIcon(context, sbn, false /* redact */);
-
- // Construct the icon.
- icon = new StatusBarIconView(context,
- sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn);
- icon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
-
- // Construct the expanded icon.
- expandedIcon = new StatusBarIconView(context,
- sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn);
- expandedIcon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
-
- // Construct the expanded icon.
- aodIcon = new StatusBarIconView(context,
- sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn);
- aodIcon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
- aodIcon.setIncreasedSize(true);
-
- try {
- setIcons(ic, Collections.singletonList(icon));
- if (isSensitive()) {
- ic = getIcon(context, sbn, true /* redact */);
- }
- setIcons(ic, Arrays.asList(expandedIcon, aodIcon));
- } catch (InflationException e) {
- icon = null;
- expandedIcon = null;
- centeredIcon = null;
- aodIcon = null;
- throw e;
- }
-
- expandedIcon.setVisibility(View.INVISIBLE);
- expandedIcon.setOnVisibilityChangedListener(
- newVisibility -> {
- if (row != null) {
- row.setIconsVisible(newVisibility != View.VISIBLE);
- }
- });
-
- // Construct the centered icon
- if (mSbn.getNotification().isMediaNotification()) {
- centeredIcon = new StatusBarIconView(context,
- sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn);
- centeredIcon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
- try {
- setIcons(ic, Collections.singletonList(centeredIcon));
- } catch (InflationException e) {
- centeredIcon = null;
- throw e;
- }
- }
- }
-
- /**
- * Determines if this icon should be tinted based on the sensitivity of the icon, its context
- * and the user's indicated sensitivity preference.
- *
- * @param ic The icon that should/should not be tinted.
- * @return
- */
- private boolean shouldTintIcon(StatusBarIconView ic) {
- boolean usedInSensitiveContext = (ic == expandedIcon || ic == aodIcon);
- return !isImportantConversation() || (usedInSensitiveContext && isSensitive());
- }
-
-
- private void setIcons(StatusBarIcon ic, List<StatusBarIconView> icons)
- throws InflationException {
- for (StatusBarIconView icon: icons) {
- if (icon == null) {
- continue;
- }
- icon.setTintIcons(shouldTintIcon(icon));
- if (!icon.set(ic)) {
- throw new InflationException("Couldn't create icon" + ic);
- }
- }
- }
-
- private StatusBarIcon getIcon(Context context, StatusBarNotification sbn, boolean redact)
- throws InflationException {
- Notification n = sbn.getNotification();
- final boolean showPeopleAvatar = isImportantConversation() && !redact;
-
- // If cached, return corresponding cached values
- if (showPeopleAvatar && mPeopleAvatar != null) {
- return mPeopleAvatar;
- } else if (!showPeopleAvatar && mSmallIcon != null) {
- return mSmallIcon;
- }
-
- Icon icon = showPeopleAvatar ? createPeopleAvatar(context) : n.getSmallIcon();
- if (icon == null) {
- throw new InflationException("No icon in notification from " + sbn.getPackageName());
- }
-
- StatusBarIcon ic = new StatusBarIcon(
- sbn.getUser(),
- sbn.getPackageName(),
- icon,
- n.iconLevel,
- n.number,
- StatusBarIconView.contentDescForNotification(context, n));
-
- // Cache if important conversation.
- if (isImportantConversation()) {
- if (showPeopleAvatar) {
- mPeopleAvatar = ic;
- } else {
- mSmallIcon = ic;
- }
- }
- return ic;
- }
-
- private Icon createPeopleAvatar(Context context) throws InflationException {
- // Attempt to extract form shortcut.
- String conversationId = getChannel().getConversationId();
- ShortcutQuery query = new ShortcutQuery()
- .setPackage(mSbn.getPackageName())
- .setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED)
- .setShortcutIds(Collections.singletonList(conversationId));
- List<ShortcutInfo> shortcuts = context.getSystemService(LauncherApps.class)
- .getShortcuts(query, mSbn.getUser());
- Icon ic = null;
- if (shortcuts != null && !shortcuts.isEmpty()) {
- ic = shortcuts.get(0).getIcon();
- }
-
- // Fall back to notification large icon if available
- if (ic == null) {
- ic = mSbn.getNotification().getLargeIcon();
- }
-
- // Fall back to extract from message
- if (ic == null) {
- Bundle extras = mSbn.getNotification().extras;
- List<Message> messages = Message.getMessagesFromBundleArray(
- extras.getParcelableArray(Notification.EXTRA_MESSAGES));
- Person user = extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON);
-
- for (int i = messages.size() - 1; i >= 0; i--) {
- Message message = messages.get(i);
- Person sender = message.getSenderPerson();
- if (sender != null && sender != user) {
- ic = message.getSenderPerson().getIcon();
- break;
- }
- }
- }
-
- // Revert to small icon if still not available
- if (ic == null) {
- ic = mSbn.getNotification().getSmallIcon();
- }
- if (ic == null) {
- throw new InflationException("No icon in notification from " + mSbn.getPackageName());
- }
- return ic;
- }
-
- private void updateSensitiveIconState() {
- try {
- StatusBarIcon ic = getIcon(getRow().getContext(), mSbn, isSensitive());
- setIcons(ic, Arrays.asList(expandedIcon, aodIcon));
- } catch (InflationException e) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Unable to update icon", e);
- }
- }
- }
-
- public void setIconTag(int key, Object tag) {
- if (icon != null) {
- icon.setTag(key, tag);
- expandedIcon.setTag(key, tag);
- }
-
- if (centeredIcon != null) {
- centeredIcon.setTag(key, tag);
- }
-
- if (aodIcon != null) {
- aodIcon.setTag(key, tag);
- }
- }
-
- /**
- * Update the notification icons.
- *
- * @param context the context to create the icons with.
- * @param sbn the notification to read the icon from.
- * @throws InflationException Exception if required icons are not valid or specified
- */
- public void updateIcons(Context context, StatusBarNotification sbn)
- throws InflationException {
- if (icon != null) {
- // Update the icon
- mSmallIcon = null;
- mPeopleAvatar = null;
-
- StatusBarIcon ic = getIcon(context, sbn, false /* redact */);
-
- icon.setNotification(sbn);
- expandedIcon.setNotification(sbn);
- aodIcon.setNotification(sbn);
- setIcons(ic, Arrays.asList(icon, expandedIcon));
-
- if (isSensitive()) {
- ic = getIcon(context, sbn, true /* redact */);
- }
- setIcons(ic, Collections.singletonList(aodIcon));
-
- if (centeredIcon != null) {
- centeredIcon.setNotification(sbn);
- setIcons(ic, Collections.singletonList(centeredIcon));
- }
- }
- }
-
- private boolean isImportantConversation() {
- return getChannel() != null && getChannel().isImportantConversation();
- }
-
public int getContrastedColor(Context context, boolean isLowPriority,
int backgroundColor) {
int rawColor = isLowPriority ? Notification.COLOR_DEFAULT :
@@ -1125,9 +883,8 @@ public final class NotificationEntry extends ListEntry {
getRow().setSensitive(sensitive, deviceSensitive);
if (sensitive != mSensitive) {
mSensitive = sensitive;
- updateSensitiveIconState();
- if (mOnSensitiveChangedListener != null) {
- mOnSensitiveChangedListener.run();
+ for (int i = 0; i < mOnSensitivityChangedListeners.size(); i++) {
+ mOnSensitivityChangedListeners.get(i).onSensitivityChanged(this);
}
}
}
@@ -1136,8 +893,14 @@ public final class NotificationEntry extends ListEntry {
return mSensitive;
}
- public void setOnSensitiveChangedListener(Runnable listener) {
- mOnSensitiveChangedListener = listener;
+ /** Add a listener to be notified when the entry's sensitivity changes. */
+ public void addOnSensitivityChangedListener(OnSensitivityChangedListener listener) {
+ mOnSensitivityChangedListeners.add(listener);
+ }
+
+ /** Remove a listener that was registered above. */
+ public void removeOnSensitivityChangedListener(OnSensitivityChangedListener listener) {
+ mOnSensitivityChangedListeners.remove(listener);
}
public boolean isPulseSuppressed() {
@@ -1167,6 +930,12 @@ public final class NotificationEntry extends ListEntry {
}
}
+ /** Listener interface for {@link #addOnSensitivityChangedListener} */
+ public interface OnSensitivityChangedListener {
+ /** Called when the sensitivity changes */
+ void onSensitivityChanged(@NonNull NotificationEntry entry);
+ }
+
/** @see #getDismissState() */
public enum DismissState {
/** User has not dismissed this notif or its parent */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 5b73b1aa7388..f7d6cef92b8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -319,7 +319,7 @@ public class ShadeListBuilder implements Dumpable {
logParentingChanges();
freeEmptyGroups();
- // Step 6: Dispatch the new list, first to any listeners and then to the view layer
+ // Step 8: Dispatch the new list, first to any listeners and then to the view layer
if (mIterationCount % 10 == 0) {
mLogger.logFinalList(mNotifList);
}
@@ -328,7 +328,7 @@ public class ShadeListBuilder implements Dumpable {
mOnRenderListListener.onRenderList(mReadOnlyNotifList);
}
- // Step 7: We're done!
+ // Step 9: We're done!
mLogger.logEndBuildList(mIterationCount);
mPipelineState.setState(STATE_IDLE);
mIterationCount++;
@@ -816,7 +816,7 @@ public class ShadeListBuilder implements Dumpable {
* @param entries A read-only view into the current notif list. Note that this list is
* backed by the live list and will change in response to new pipeline runs.
*/
- void onRenderList(List<ListEntry> entries);
+ void onRenderList(@NonNull List<ListEntry> entries);
}
private static final NotifSection sDefaultSection =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SimpleNotificationListContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SimpleNotificationListContainer.kt
new file mode 100644
index 000000000000..2dbe555af519
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SimpleNotificationListContainer.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection
+
+import android.view.View
+import android.view.ViewGroup
+import com.android.systemui.statusbar.notification.stack.NotificationListItem
+
+/**
+ * Minimal interface of what [NotifViewManager] needs from [NotificationListContainer]
+ */
+interface SimpleNotificationListContainer {
+ /** Called to signify that a top-level element is becoming a child in the shade */
+ fun setChildTransferInProgress(b: Boolean)
+ /** Used to generate a list of [NotificationListItem] */
+ fun getContainerChildAt(i: Int): View
+ /** Similar to above */
+ fun getContainerChildCount(): Int
+ /** Remove a [NotificationListItem] from the container */
+ fun removeListItem(li: NotificationListItem)
+ /** Add a [NotificationListItem] to the container */
+ fun addListItem(li: NotificationListItem)
+ /** Allows [NotifViewManager] to notify the container about a group child removal */
+ fun notifyGroupChildRemoved(row: View, parent: ViewGroup)
+ /** Allows [NotifViewManager] to notify the container about a group child addition */
+ fun notifyGroupChildAdded(row: View)
+ /** [NotifViewManager] calls this when the order of the children changes */
+ fun generateChildOrderChangedEvent()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
index 98c45ffd6afb..596235cfb4d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
@@ -260,11 +260,14 @@ public class GroupCoalescer implements Dumpable {
private void applyRanking(RankingMap rankingMap) {
for (CoalescedEvent event : mCoalescedEvents.values()) {
Ranking ranking = new Ranking();
- if (!rankingMap.getRanking(event.getKey(), ranking)) {
- throw new IllegalStateException(
- "Ranking map doesn't contain key: " + event.getKey());
+ if (rankingMap.getRanking(event.getKey(), ranking)) {
+ event.setRanking(ranking);
+ } else {
+ // TODO: (b/148791039) We should crash if we are ever handed a ranking with
+ // incomplete entries. Right now, there's a race condition in NotificationListener
+ // that means this might occur when SystemUI is starting up.
+ mLogger.logMissingRanking(event.getKey());
}
- event.setRanking(ranking);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt
index 6e8788db59d7..d4d5b64240c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt
@@ -57,6 +57,14 @@ class GroupCoalescerLogger @Inject constructor(
"Modification of notif $str1 triggered TIMEOUT emit of batched group $str2"
})
}
+
+ fun logMissingRanking(forKey: String) {
+ buffer.log(TAG, LogLevel.WARNING, {
+ str1 = forKey
+ }, {
+ "RankingMap is missing an entry for coalesced notification $str1"
+ })
+ }
}
private const val TAG = "GroupCoalescer" \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
index 370de838b903..92426e54ec91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
@@ -126,7 +126,7 @@ public class BubbleCoordinator implements Coordinator {
mInterceptedDismissalEntries.remove(entry.getKey());
mOnEndDismissInterception.onEndDismissInterception(mDismissInterceptor, entry,
createDismissedByUserStats(entry));
- } else if (mNotifPipeline.getActiveNotifs().contains(entry)) {
+ } else if (mNotifPipeline.getAllNotifs().contains(entry)) {
// Bubbles are hiding the notifications from the shade, but the bubble was
// deleted; therefore, the notification should be cancelled as if it were a user
// dismissal (this won't re-enter handleInterceptDimissal because Bubbles
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java
index 854444fc8bb7..b5b756d6ed9b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java
@@ -240,7 +240,7 @@ public class ForegroundCoordinator implements Coordinator {
}
private NotificationEntry findNotificationEntryWithKey(String key) {
- for (NotificationEntry entry : mNotifPipeline.getActiveNotifs()) {
+ for (NotificationEntry entry : mNotifPipeline.getAllNotifs()) {
if (entry.getKey().equals(key)) {
return entry;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
new file mode 100644
index 000000000000..573c129f199a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator;
+
+import static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY;
+
+import android.annotation.Nullable;
+
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+
+import java.util.Objects;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Coordinates heads up notification (HUN) interactions with the notification pipeline based on
+ * the HUN state reported by the {@link HeadsUpManager}. In this class we only consider one
+ * notification, in particular the {@link HeadsUpManager#getTopEntry()}, to be HeadsUpping at a
+ * time even though other notifications may be queued to heads up next.
+ *
+ * The current HUN, but not HUNs that are queued to heads up, will be:
+ * - Lifetime extended until it's no longer heads upping.
+ * - Promoted out of its group if it's a child of a group.
+ * - In the HeadsUpCoordinatorSection. Ordering is configured in {@link NotifCoordinators}.
+ * - Removed from HeadsUpManager if it's removed from the NotificationCollection.
+ *
+ * Note: The inflation callback in {@link PreparationCoordinator} handles showing HUNs.
+ */
+@Singleton
+public class HeadsUpCoordinator implements Coordinator {
+ private static final String TAG = "HeadsUpCoordinator";
+
+ private final HeadsUpManager mHeadsUpManager;
+ private final NotificationRemoteInputManager mRemoteInputManager;
+
+ // tracks the current HeadUpNotification reported by HeadsUpManager
+ private @Nullable NotificationEntry mCurrentHun;
+
+ private NotifLifetimeExtender.OnEndLifetimeExtensionCallback mEndLifetimeExtension;
+ private NotificationEntry mNotifExtendingLifetime; // notif we've extended the lifetime for
+
+ @Inject
+ public HeadsUpCoordinator(
+ HeadsUpManager headsUpManager,
+ NotificationRemoteInputManager remoteInputManager) {
+ mHeadsUpManager = headsUpManager;
+ mRemoteInputManager = remoteInputManager;
+ }
+
+ @Override
+ public void attach(NotifPipeline pipeline) {
+ mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
+ pipeline.addCollectionListener(mNotifCollectionListener);
+ pipeline.addPromoter(mNotifPromoter);
+ pipeline.addNotificationLifetimeExtender(mLifetimeExtender);
+ }
+
+ @Override
+ public NotifSection getSection() {
+ return mNotifSection;
+ }
+
+ private final NotifCollectionListener mNotifCollectionListener = new NotifCollectionListener() {
+ /**
+ * Stop alerting HUNs that are removed from the notification collection
+ */
+ @Override
+ public void onEntryRemoved(NotificationEntry entry, int reason) {
+ final String entryKey = entry.getKey();
+ if (mHeadsUpManager.isAlerting(entryKey)) {
+ boolean removeImmediatelyForRemoteInput =
+ mRemoteInputManager.getController().isSpinning(entryKey)
+ && !FORCE_REMOTE_INPUT_HISTORY;
+ mHeadsUpManager.removeNotification(entry.getKey(), removeImmediatelyForRemoteInput);
+ }
+ }
+ };
+
+ private final NotifLifetimeExtender mLifetimeExtender = new NotifLifetimeExtender() {
+ @Override
+ public String getName() {
+ return TAG;
+ }
+
+ @Override
+ public void setCallback(OnEndLifetimeExtensionCallback callback) {
+ mEndLifetimeExtension = callback;
+ }
+
+ @Override
+ public boolean shouldExtendLifetime(NotificationEntry entry, int reason) {
+ boolean isShowingHun = isCurrentlyShowingHun(entry);
+ if (isShowingHun) {
+ mNotifExtendingLifetime = entry;
+ }
+ return isShowingHun;
+ }
+
+ @Override
+ public void cancelLifetimeExtension(NotificationEntry entry) {
+ if (Objects.equals(mNotifExtendingLifetime, entry)) {
+ mNotifExtendingLifetime = null;
+ }
+ }
+ };
+
+ private final NotifPromoter mNotifPromoter = new NotifPromoter(TAG) {
+ @Override
+ public boolean shouldPromoteToTopLevel(NotificationEntry entry) {
+ return isCurrentlyShowingHun(entry);
+ }
+ };
+
+ private final NotifSection mNotifSection = new NotifSection(TAG) {
+ @Override
+ public boolean isInSection(ListEntry entry) {
+ return isCurrentlyShowingHun(entry);
+ }
+ };
+
+ private final OnHeadsUpChangedListener mOnHeadsUpChangedListener =
+ new OnHeadsUpChangedListener() {
+ @Override
+ public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
+ NotificationEntry newHUN = mHeadsUpManager.getTopEntry();
+ if (!Objects.equals(mCurrentHun, newHUN)) {
+ endNotifLifetimeExtension();
+ mCurrentHun = newHUN;
+ mNotifPromoter.invalidateList();
+ mNotifSection.invalidateList();
+ }
+ }
+ };
+
+ private boolean isCurrentlyShowingHun(ListEntry entry) {
+ return mCurrentHun == entry.getRepresentativeEntry();
+ }
+
+ private void endNotifLifetimeExtension() {
+ if (mNotifExtendingLifetime != null) {
+ mEndLifetimeExtension.onEndLifetimeExtension(
+ mLifetimeExtender,
+ mNotifExtendingLifetime);
+ mNotifExtendingLifetime = null;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
index 7a22d75505e5..98104f84f30e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
@@ -49,6 +49,7 @@ public class NotifCoordinators implements Dumpable {
public NotifCoordinators(
DumpManager dumpManager,
FeatureFlags featureFlags,
+ HeadsUpCoordinator headsUpCoordinator,
KeyguardCoordinator keyguardCoordinator,
RankingCoordinator rankingCoordinator,
ForegroundCoordinator foregroundCoordinator,
@@ -56,7 +57,6 @@ public class NotifCoordinators implements Dumpable {
BubbleCoordinator bubbleCoordinator,
PreparationCoordinator preparationCoordinator) {
dumpManager.registerDumpable(TAG, this);
-
mCoordinators.add(new HideLocallyDismissedNotifsCoordinator());
mCoordinators.add(keyguardCoordinator);
mCoordinators.add(rankingCoordinator);
@@ -64,9 +64,10 @@ public class NotifCoordinators implements Dumpable {
mCoordinators.add(deviceProvisionedCoordinator);
mCoordinators.add(bubbleCoordinator);
if (featureFlags.isNewNotifPipelineRenderingEnabled()) {
+ mCoordinators.add(headsUpCoordinator);
mCoordinators.add(preparationCoordinator);
}
- // TODO: add new Coordinators here! (b/145134683, b/112656837)
+ // TODO: add new Coordinators here! (b/112656837)
// TODO: add the sections in a particular ORDER (HeadsUp < People < Alerting)
for (Coordinator c : mCoordinators) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
index ebecf181b808..742615c7fd0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
@@ -26,13 +26,16 @@ import com.android.systemui.statusbar.notification.collection.GroupEntry;
import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotifViewBarn;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.ShadeListBuilder;
import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -57,21 +60,31 @@ public class PreparationCoordinator implements Coordinator {
private final PreparationCoordinatorLogger mLogger;
private final NotifInflater mNotifInflater;
private final NotifInflationErrorManager mNotifErrorManager;
+ private final NotifViewBarn mViewBarn;
private final Map<NotificationEntry, Integer> mInflationStates = new ArrayMap<>();
private final IStatusBarService mStatusBarService;
+ private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
+ private final HeadsUpManager mHeadsUpManager;
@Inject
public PreparationCoordinator(
PreparationCoordinatorLogger logger,
NotifInflaterImpl notifInflater,
NotifInflationErrorManager errorManager,
- IStatusBarService service) {
+ NotifViewBarn viewBarn,
+ IStatusBarService service,
+ NotificationInterruptStateProvider notificationInterruptStateProvider,
+ HeadsUpManager headsUpManager
+ ) {
mLogger = logger;
mNotifInflater = notifInflater;
mNotifInflater.setInflationCallback(mInflationCallback);
mNotifErrorManager = errorManager;
mNotifErrorManager.addInflationErrorListener(mInflationErrorListener);
+ mViewBarn = viewBarn;
mStatusBarService = service;
+ mNotificationInterruptStateProvider = notificationInterruptStateProvider;
+ mHeadsUpManager = headsUpManager;
}
@Override
@@ -109,6 +122,7 @@ public class PreparationCoordinator implements Coordinator {
@Override
public void onEntryCleanUp(NotificationEntry entry) {
mInflationStates.remove(entry);
+ mViewBarn.removeViewForEntry(entry);
}
};
@@ -142,7 +156,13 @@ public class PreparationCoordinator implements Coordinator {
@Override
public void onInflationFinished(NotificationEntry entry) {
mLogger.logNotifInflated(entry.getKey());
+ mViewBarn.registerViewForEntry(entry, entry.getRow());
mInflationStates.put(entry, STATE_INFLATED);
+
+ // TODO: should eventually be moved to HeadsUpCoordinator
+ if (mNotificationInterruptStateProvider.shouldHeadsUp(entry)) {
+ mHeadsUpManager.showNotification(entry);
+ }
mNotifInflatingFilter.invalidateList();
}
};
@@ -151,6 +171,7 @@ public class PreparationCoordinator implements Coordinator {
new NotifInflationErrorManager.NotifInflationErrorListener() {
@Override
public void onNotifInflationError(NotificationEntry entry, Exception e) {
+ mViewBarn.removeViewForEntry(entry);
mInflationStates.put(entry, STATE_ERROR);
try {
final StatusBarNotification sbn = entry.getSbn();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index 4beeedecfdf5..723728488826 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.notification.collection.inflation;
-import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
import android.annotation.Nullable;
@@ -29,8 +28,6 @@ import android.util.Log;
import android.view.ViewGroup;
import com.android.internal.util.NotificationMessagingUtil;
-import com.android.systemui.R;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -38,25 +35,22 @@ import com.android.systemui.statusbar.NotificationUiAdjustment;
import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.NotificationClicker;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.icon.IconManager;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
import com.android.systemui.statusbar.notification.row.NotifBindPipeline;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder;
import com.android.systemui.statusbar.notification.row.RowContentBindParams;
import com.android.systemui.statusbar.notification.row.RowContentBindStage;
import com.android.systemui.statusbar.notification.row.RowInflaterTask;
import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotificationRowComponent;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.StatusBar;
import java.util.Objects;
import javax.inject.Inject;
-import javax.inject.Named;
import javax.inject.Provider;
import javax.inject.Singleton;
@@ -66,23 +60,23 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
private static final String TAG = "NotificationViewManager";
- private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
-
private final Context mContext;
- private final NotifBindPipeline mNotifBindPipeline;
- private final RowContentBindStage mRowContentBindStage;
private final NotificationMessagingUtil mMessagingUtil;
private final NotificationRemoteInputManager mNotificationRemoteInputManager;
private final NotificationLockscreenUserManager mNotificationLockscreenUserManager;
+ private final NotifBindPipeline mNotifBindPipeline;
+ private final RowContentBindStage mRowContentBindStage;
+ private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
+ private final Provider<RowInflaterTask> mRowInflaterTaskProvider;
+ private final ExpandableNotificationRowComponent.Builder
+ mExpandableNotificationRowComponentBuilder;
+ private final IconManager mIconManager;
private NotificationPresenter mPresenter;
private NotificationListContainer mListContainer;
private NotificationRowContentBinder.InflationCallback mInflationCallback;
private BindRowCallback mBindRowCallback;
private NotificationClicker mNotificationClicker;
- private final Provider<RowInflaterTask> mRowInflaterTaskProvider;
- private final ExpandableNotificationRowComponent.Builder
- mExpandableNotificationRowComponentBuilder;
@Inject
public NotificationRowBinderImpl(
@@ -92,14 +86,10 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
NotificationLockscreenUserManager notificationLockscreenUserManager,
NotifBindPipeline notifBindPipeline,
RowContentBindStage rowContentBindStage,
- @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress,
- KeyguardBypassController keyguardBypassController,
- StatusBarStateController statusBarStateController,
- NotificationGroupManager notificationGroupManager,
- NotificationGutsManager notificationGutsManager,
NotificationInterruptStateProvider notificationInterruptionStateProvider,
Provider<RowInflaterTask> rowInflaterTaskProvider,
- ExpandableNotificationRowComponent.Builder expandableNotificationRowComponentBuilder) {
+ ExpandableNotificationRowComponent.Builder expandableNotificationRowComponentBuilder,
+ IconManager iconManager) {
mContext = context;
mNotifBindPipeline = notifBindPipeline;
mRowContentBindStage = rowContentBindStage;
@@ -109,6 +99,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
mNotificationInterruptStateProvider = notificationInterruptionStateProvider;
mRowInflaterTaskProvider = rowInflaterTaskProvider;
mExpandableNotificationRowComponentBuilder = expandableNotificationRowComponentBuilder;
+ mIconManager = iconManager;
}
/**
@@ -120,6 +111,8 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
mPresenter = presenter;
mListContainer = listContainer;
mBindRowCallback = bindRowCallback;
+
+ mIconManager.attach();
}
public void setInflationCallback(NotificationRowContentBinder.InflationCallback callback) {
@@ -142,12 +135,12 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
final StatusBarNotification sbn = entry.getSbn();
if (entry.rowExists()) {
- entry.updateIcons(mContext, sbn);
+ mIconManager.updateIcons(entry);
entry.reset();
updateNotification(entry, pmUser, sbn, entry.getRow());
entry.getRowController().setOnDismissRunnable(onDismissRunnable);
} else {
- entry.createIcons(mContext, sbn);
+ mIconManager.createIcons(entry);
mRowInflaterTaskProvider.get().inflate(mContext, parent, entry,
row -> {
// Setup the controller for the view.
@@ -227,8 +220,8 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
row.setLegacy(entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD
&& entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
- // TODO: should updates to the entry be happening somewhere else?
- entry.setIconTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
+ // TODO: should this be happening somewhere else?
+ mIconManager.updateIconTags(entry, entry.targetSdk);
row.setOnActivatedListener(mPresenter);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/FakePipelineConsumer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/FakePipelineConsumer.java
deleted file mode 100644
index 15f312dbdc00..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/FakePipelineConsumer.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.collection.init;
-
-import com.android.systemui.Dumpable;
-import com.android.systemui.statusbar.notification.collection.GroupEntry;
-import com.android.systemui.statusbar.notification.collection.ListEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.ShadeListBuilder;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Temporary class that tracks the result of the list builder and dumps it to text when requested.
- *
- * Eventually, this will be something that hands off the result of the pipeline to the View layer.
- */
-public class FakePipelineConsumer implements Dumpable {
- private List<ListEntry> mEntries = Collections.emptyList();
-
- /** Attach the consumer to the pipeline. */
- public void attach(ShadeListBuilder listBuilder) {
- listBuilder.setOnRenderListListener(this::onBuildComplete);
- }
-
- private void onBuildComplete(List<ListEntry> entries) {
- mEntries = entries;
- }
-
- @Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println();
- pw.println("Active notif tree:");
- for (int i = 0; i < mEntries.size(); i++) {
- ListEntry entry = mEntries.get(i);
- if (entry instanceof GroupEntry) {
- GroupEntry ge = (GroupEntry) entry;
- pw.println(dumpGroup(ge, "", i));
-
- pw.println(dumpEntry(ge.getSummary(), INDENT, -1));
- for (int j = 0; j < ge.getChildren().size(); j++) {
- pw.println(dumpEntry(ge.getChildren().get(j), INDENT, j));
- }
- } else {
- pw.println(dumpEntry(entry.getRepresentativeEntry(), "", i));
- }
- }
- }
-
- private String dumpGroup(GroupEntry entry, String indent, int index) {
- return String.format(
- "%s[%d] %s (group)",
- indent,
- index,
- entry.getKey());
- }
-
- private String dumpEntry(NotificationEntry entry, String indent, int index) {
- return String.format(
- "%s[%s] %s (channel=%s)",
- indent,
- index == -1 ? "*" : Integer.toString(index),
- entry.getKey(),
- entry.getChannel() != null ? entry.getChannel().getId() : "");
- }
-
- private static final String INDENT = " ";
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
index 258f6d018057..f150257df252 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
@@ -25,10 +25,12 @@ import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotifViewManager;
import com.android.systemui.statusbar.notification.collection.ShadeListBuilder;
import com.android.systemui.statusbar.notification.collection.coalescer.GroupCoalescer;
import com.android.systemui.statusbar.notification.collection.coordinator.NotifCoordinators;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -50,7 +52,7 @@ public class NotifPipelineInitializer implements Dumpable {
private final DumpManager mDumpManager;
private final FeatureFlags mFeatureFlags;
- private final FakePipelineConsumer mFakePipelineConsumer = new FakePipelineConsumer();
+ private final NotifViewManager mNotifViewManager;
@Inject
public NotifPipelineInitializer(
@@ -61,7 +63,8 @@ public class NotifPipelineInitializer implements Dumpable {
NotifCoordinators notifCoordinators,
NotifInflaterImpl notifInflater,
DumpManager dumpManager,
- FeatureFlags featureFlags) {
+ FeatureFlags featureFlags,
+ NotifViewManager notifViewManager) {
mPipelineWrapper = pipelineWrapper;
mGroupCoalescer = groupCoalescer;
mNotifCollection = notifCollection;
@@ -70,12 +73,14 @@ public class NotifPipelineInitializer implements Dumpable {
mDumpManager = dumpManager;
mNotifInflater = notifInflater;
mFeatureFlags = featureFlags;
+ mNotifViewManager = notifViewManager;
}
/** Hooks the new pipeline up to NotificationManager */
public void initialize(
NotificationListener notificationService,
- NotificationRowBinderImpl rowBinder) {
+ NotificationRowBinderImpl rowBinder,
+ NotificationListContainer listContainer) {
mDumpManager.registerDumpable("NotifPipeline", this);
@@ -88,7 +93,8 @@ public class NotifPipelineInitializer implements Dumpable {
mNotifPluggableCoordinators.attach(mPipelineWrapper);
// Wire up pipeline
- mFakePipelineConsumer.attach(mListBuilder);
+ mNotifViewManager.setViewConsumer(listContainer);
+ mNotifViewManager.attach(mListBuilder);
mListBuilder.attach(mNotifCollection);
mNotifCollection.attach(mGroupCoalescer);
mGroupCoalescer.attach(notificationService);
@@ -98,7 +104,7 @@ public class NotifPipelineInitializer implements Dumpable {
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- mFakePipelineConsumer.dump(fd, pw, args);
+ mNotifViewManager.dump(fd, pw, args);
mNotifPluggableCoordinators.dump(fd, pw, args);
mGroupCoalescer.dump(fd, pw, args);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollection.java
index 171816fd28da..b4c2bb8b6d3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollection.java
@@ -20,6 +20,8 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import java.util.Collection;
+
/**
* A notification collection that manages the list of {@link NotificationEntry}s that will be
* rendered.
@@ -34,4 +36,13 @@ public interface CommonNotifCollection {
* or deleted.
*/
void addCollectionListener(NotifCollectionListener listener);
+
+ /**
+ * Returns the list of all known notifications, i.e. the notifications that are currently posted
+ * to the phone. In general, this tracks closely to the list maintained by NotificationManager,
+ * but it can diverge slightly due to lifetime extenders.
+ *
+ * The returned collection is read-only, unsorted, unfiltered, and ungrouped.
+ */
+ Collection<NotificationEntry> getAllNotifs();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java
index b2c53dae16cd..0c0cded32db2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java
@@ -70,8 +70,24 @@ public interface NotifCollectionListener {
}
/**
- * Called whenever the RankingMap is updated by system server. By the time this listener is
- * called, the Rankings of all entries will have been updated.
+ * Called whenever a ranking update is applied. During a ranking update, all active,
+ * non-lifetime-extended notification entries will have their ranking object updated.
+ *
+ * Ranking updates occur whenever a notification is added, updated, or removed, or when a
+ * standalone ranking is sent from the server.
+ */
+ default void onRankingApplied() {
+ }
+
+ /**
+ * Called whenever system server sends a standalone ranking update (i.e. one that isn't
+ * associated with a notification being added or removed).
+ *
+ * In general it is unsafe to depend on this method as rankings can change for other reasons.
+ * Instead, listen for {@link #onRankingApplied()}, which is called whenever ANY ranking update
+ * is applied, regardless of source.
+ *
+ * @deprecated Use {@link #onRankingApplied()} instead.
*/
default void onRankingUpdate(NotificationListenerService.RankingMap rankingMap) {
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifEvent.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifEvent.kt
new file mode 100644
index 000000000000..2ef0368061ba
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifEvent.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.notifcollection
+
+import android.service.notification.NotificationListenerService.RankingMap
+import com.android.systemui.statusbar.notification.collection.NotifCollection
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+
+/**
+ * Set of classes that represent the various events that [NotifCollection] can dispatch to
+ * [NotifCollectionListener]s.
+ *
+ * These events build up in a queue and are periodically emitted in chunks by the collection.
+ */
+
+sealed class NotifEvent {
+ fun dispatchTo(listeners: List<NotifCollectionListener>) {
+ for (i in listeners.indices) {
+ dispatchToListener(listeners[i])
+ }
+ }
+
+ abstract fun dispatchToListener(listener: NotifCollectionListener)
+}
+
+data class InitEntryEvent(
+ val entry: NotificationEntry
+) : NotifEvent() {
+ override fun dispatchToListener(listener: NotifCollectionListener) {
+ listener.onEntryInit(entry)
+ }
+}
+
+data class EntryAddedEvent(
+ val entry: NotificationEntry
+) : NotifEvent() {
+ override fun dispatchToListener(listener: NotifCollectionListener) {
+ listener.onEntryAdded(entry)
+ }
+}
+
+data class EntryUpdatedEvent(
+ val entry: NotificationEntry
+) : NotifEvent() {
+ override fun dispatchToListener(listener: NotifCollectionListener) {
+ listener.onEntryUpdated(entry)
+ }
+}
+
+data class EntryRemovedEvent(
+ val entry: NotificationEntry,
+ val reason: Int
+) : NotifEvent() {
+ override fun dispatchToListener(listener: NotifCollectionListener) {
+ listener.onEntryRemoved(entry, reason)
+ }
+}
+
+data class CleanUpEntryEvent(
+ val entry: NotificationEntry
+) : NotifEvent() {
+ override fun dispatchToListener(listener: NotifCollectionListener) {
+ listener.onEntryCleanUp(entry)
+ }
+}
+
+data class RankingUpdatedEvent(
+ val rankingMap: RankingMap
+) : NotifEvent() {
+ override fun dispatchToListener(listener: NotifCollectionListener) {
+ listener.onRankingUpdate(rankingMap)
+ }
+}
+
+class RankingAppliedEvent() : NotifEvent() {
+ override fun dispatchToListener(listener: NotifCollectionListener) {
+ listener.onRankingApplied()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt
new file mode 100644
index 000000000000..afc123faba79
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.icon
+
+import android.app.Notification
+import android.content.Context
+import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import javax.inject.Inject
+
+/**
+ * Testable wrapper around Context.
+ */
+class IconBuilder @Inject constructor(
+ private val context: Context
+) {
+ fun createIconView(entry: NotificationEntry): StatusBarIconView {
+ return StatusBarIconView(
+ context,
+ "${entry.sbn.packageName}/0x${Integer.toHexString(entry.sbn.id)}",
+ entry.sbn)
+ }
+
+ fun getIconContentDescription(n: Notification): CharSequence {
+ return StatusBarIconView.contentDescForNotification(context, n)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
new file mode 100644
index 000000000000..bb0fcafdb354
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.icon
+
+import android.app.Notification
+import android.app.Person
+import android.content.pm.LauncherApps
+import android.graphics.drawable.Icon
+import android.os.Build
+import android.os.Bundle
+import android.util.Log
+import android.view.View
+import android.widget.ImageView
+import com.android.internal.statusbar.StatusBarIcon
+import com.android.systemui.R
+import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.notification.InflationException
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import javax.inject.Inject
+
+/**
+ * Inflates and updates icons associated with notifications
+ *
+ * Notifications are represented by icons in a few different places -- in the status bar, in the
+ * notification shelf, in AOD, etc. This class is in charge of inflating the views that hold these
+ * icons and keeping the icon assets themselves up to date as notifications change.
+ *
+ * TODO: Much of this code was copied whole-sale in order to get it out of NotificationEntry.
+ * Long-term, it should probably live somewhere in the content inflation pipeline.
+ */
+class IconManager @Inject constructor(
+ private val notifCollection: CommonNotifCollection,
+ private val launcherApps: LauncherApps,
+ private val iconBuilder: IconBuilder
+) {
+ fun attach() {
+ notifCollection.addCollectionListener(entryListener)
+ }
+
+ private val entryListener = object : NotifCollectionListener {
+ override fun onEntryInit(entry: NotificationEntry) {
+ entry.addOnSensitivityChangedListener(sensitivityListener)
+ }
+
+ override fun onEntryCleanUp(entry: NotificationEntry) {
+ entry.removeOnSensitivityChangedListener(sensitivityListener)
+ }
+
+ override fun onRankingApplied() {
+ // When the sensitivity changes OR when the isImportantConversation status changes,
+ // we need to update the icons
+ for (entry in notifCollection.allNotifs) {
+ val isImportant = isImportantConversation(entry)
+ if (entry.icons.areIconsAvailable &&
+ isImportant != entry.icons.isImportantConversation) {
+ updateIconsSafe(entry)
+ }
+ entry.icons.isImportantConversation = isImportant
+ }
+ }
+ }
+
+ private val sensitivityListener = NotificationEntry.OnSensitivityChangedListener {
+ entry -> updateIconsSafe(entry)
+ }
+
+ /**
+ * Inflate icon views for each icon variant and assign appropriate icons to them. Stores the
+ * result in [NotificationEntry.getIcons].
+ *
+ * @throws InflationException Exception if required icons are not valid or specified
+ */
+ @Throws(InflationException::class)
+ fun createIcons(entry: NotificationEntry) {
+ // Construct the status bar icon view.
+ val sbIcon = iconBuilder.createIconView(entry)
+ sbIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
+
+ // Construct the shelf icon view.
+ val shelfIcon = iconBuilder.createIconView(entry)
+ shelfIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
+
+ shelfIcon.visibility = View.INVISIBLE
+ // TODO: This doesn't belong here
+ shelfIcon.setOnVisibilityChangedListener { newVisibility: Int ->
+ if (entry.row != null) {
+ entry.row.setIconsVisible(newVisibility != View.VISIBLE)
+ }
+ }
+
+ // Construct the aod icon view.
+ val aodIcon = iconBuilder.createIconView(entry)
+ aodIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
+ aodIcon.setIncreasedSize(true)
+
+ // Construct the centered icon view.
+ val centeredIcon = if (entry.sbn.notification.isMediaNotification) {
+ iconBuilder.createIconView(entry).apply {
+ scaleType = ImageView.ScaleType.CENTER_INSIDE
+ }
+ } else {
+ null
+ }
+
+ // Set the icon views' icons
+ val (normalIconDescriptor, sensitiveIconDescriptor) = getIconDescriptors(entry)
+
+ try {
+ setIcon(entry, normalIconDescriptor, sbIcon)
+ setIcon(entry, sensitiveIconDescriptor, shelfIcon)
+ setIcon(entry, sensitiveIconDescriptor, aodIcon)
+ if (centeredIcon != null) {
+ setIcon(entry, normalIconDescriptor, centeredIcon)
+ }
+ entry.icons = IconPack.buildPack(sbIcon, shelfIcon, aodIcon, centeredIcon, entry.icons)
+ } catch (e: InflationException) {
+ entry.icons = IconPack.buildEmptyPack(entry.icons)
+ throw e
+ }
+ }
+
+ /**
+ * Update the notification icons.
+ *
+ * @param entry the notification to read the icon from.
+ * @throws InflationException Exception if required icons are not valid or specified
+ */
+ @Throws(InflationException::class)
+ fun updateIcons(entry: NotificationEntry) {
+ if (!entry.icons.areIconsAvailable) {
+ return
+ }
+ entry.icons.smallIconDescriptor = null
+ entry.icons.peopleAvatarDescriptor = null
+
+ val (normalIconDescriptor, sensitiveIconDescriptor) = getIconDescriptors(entry)
+
+ entry.icons.statusBarIcon?.let {
+ it.notification = entry.sbn
+ setIcon(entry, normalIconDescriptor, it)
+ }
+
+ entry.icons.shelfIcon?.let {
+ it.notification = entry.sbn
+ setIcon(entry, normalIconDescriptor, it)
+ }
+
+ entry.icons.aodIcon?.let {
+ it.notification = entry.sbn
+ setIcon(entry, sensitiveIconDescriptor, it)
+ }
+
+ entry.icons.centeredIcon?.let {
+ it.notification = entry.sbn
+ setIcon(entry, sensitiveIconDescriptor, it)
+ }
+ }
+
+ /**
+ * Updates tags on the icon views to match the posting app's target SDK level
+ *
+ * Note that this method MUST be called after both [createIcons] and [updateIcons].
+ */
+ fun updateIconTags(entry: NotificationEntry, targetSdk: Int) {
+ setTagOnIconViews(
+ entry.icons,
+ R.id.icon_is_pre_L,
+ targetSdk < Build.VERSION_CODES.LOLLIPOP)
+ }
+
+ private fun updateIconsSafe(entry: NotificationEntry) {
+ try {
+ updateIcons(entry)
+ } catch (e: InflationException) {
+ // TODO This should mark the entire row as involved in an inflation error
+ Log.e(TAG, "Unable to update icon", e)
+ }
+ }
+
+ @Throws(InflationException::class)
+ private fun getIconDescriptors(
+ entry: NotificationEntry
+ ): Pair<StatusBarIcon, StatusBarIcon> {
+ val iconDescriptor = getIconDescriptor(entry, false /* redact */)
+ val sensitiveDescriptor = if (entry.isSensitive) {
+ getIconDescriptor(entry, true /* redact */)
+ } else {
+ iconDescriptor
+ }
+ return Pair(iconDescriptor, sensitiveDescriptor)
+ }
+
+ @Throws(InflationException::class)
+ private fun getIconDescriptor(
+ entry: NotificationEntry,
+ redact: Boolean
+ ): StatusBarIcon {
+ val n = entry.sbn.notification
+ val showPeopleAvatar = isImportantConversation(entry) && !redact
+
+ val peopleAvatarDescriptor = entry.icons.peopleAvatarDescriptor
+ val smallIconDescriptor = entry.icons.smallIconDescriptor
+
+ // If cached, return corresponding cached values
+ if (showPeopleAvatar && peopleAvatarDescriptor != null) {
+ return peopleAvatarDescriptor
+ } else if (!showPeopleAvatar && smallIconDescriptor != null) {
+ return smallIconDescriptor
+ }
+
+ val icon =
+ (if (showPeopleAvatar) {
+ createPeopleAvatar(entry)
+ } else {
+ n.smallIcon
+ }) ?: throw InflationException(
+ "No icon in notification from " + entry.sbn.packageName)
+
+ val ic = StatusBarIcon(
+ entry.sbn.user,
+ entry.sbn.packageName,
+ icon,
+ n.iconLevel,
+ n.number,
+ iconBuilder.getIconContentDescription(n))
+
+ // Cache if important conversation.
+ if (isImportantConversation(entry)) {
+ if (showPeopleAvatar) {
+ entry.icons.peopleAvatarDescriptor = ic
+ } else {
+ entry.icons.smallIconDescriptor = ic
+ }
+ }
+
+ return ic
+ }
+
+ @Throws(InflationException::class)
+ private fun setIcon(
+ entry: NotificationEntry,
+ iconDescriptor: StatusBarIcon,
+ iconView: StatusBarIconView
+ ) {
+ iconView.setTintIcons(shouldTintIconView(entry, iconView))
+ if (!iconView.set(iconDescriptor)) {
+ throw InflationException("Couldn't create icon $iconDescriptor")
+ }
+ }
+
+ @Throws(InflationException::class)
+ private fun createPeopleAvatar(entry: NotificationEntry): Icon? {
+ // Attempt to extract form shortcut.
+ val conversationId = entry.ranking.channel.conversationId
+ val query = LauncherApps.ShortcutQuery()
+ .setPackage(entry.sbn.packageName)
+ .setQueryFlags(
+ LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC
+ or LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED)
+ .setShortcutIds(listOf(conversationId))
+ val shortcuts = launcherApps.getShortcuts(query, entry.sbn.user)
+ var ic: Icon? = null
+ if (shortcuts != null && shortcuts.isNotEmpty()) {
+ ic = shortcuts[0].icon
+ }
+
+ // Fall back to notification large icon if available
+ if (ic == null) {
+ ic = entry.sbn.notification.getLargeIcon()
+ }
+
+ // Fall back to extract from message
+ if (ic == null) {
+ val extras: Bundle = entry.sbn.notification.extras
+ val messages = Notification.MessagingStyle.Message.getMessagesFromBundleArray(
+ extras.getParcelableArray(Notification.EXTRA_MESSAGES))
+ val user = extras.getParcelable<Person>(Notification.EXTRA_MESSAGING_PERSON)
+ for (i in messages.indices.reversed()) {
+ val message = messages[i]
+ val sender = message.senderPerson
+ if (sender != null && sender !== user) {
+ ic = message.senderPerson!!.icon
+ break
+ }
+ }
+ }
+
+ // Revert to small icon if still not available
+ if (ic == null) {
+ ic = entry.sbn.notification.smallIcon
+ }
+ if (ic == null) {
+ throw InflationException("No icon in notification from " + entry.sbn.packageName)
+ }
+ return ic
+ }
+
+ /**
+ * Determines if this icon should be tinted based on the sensitivity of the icon, its context
+ * and the user's indicated sensitivity preference.
+ *
+ * @param iconView The icon that should/should not be tinted.
+ */
+ private fun shouldTintIconView(entry: NotificationEntry, iconView: StatusBarIconView): Boolean {
+ val usedInSensitiveContext =
+ iconView === entry.icons.shelfIcon || iconView === entry.icons.aodIcon
+ return !isImportantConversation(entry) || usedInSensitiveContext && entry.isSensitive
+ }
+
+ private fun isImportantConversation(entry: NotificationEntry): Boolean {
+ return entry.ranking.channel != null && entry.ranking.channel.isImportantConversation
+ }
+
+ private fun setTagOnIconViews(icons: IconPack, key: Int, tag: Any) {
+ icons.statusBarIcon?.setTag(key, tag)
+ icons.shelfIcon?.setTag(key, tag)
+ icons.aodIcon?.setTag(key, tag)
+ icons.centeredIcon?.setTag(key, tag)
+ }
+}
+
+private const val TAG = "IconManager" \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java
new file mode 100644
index 000000000000..054e381f096a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.icon;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.statusbar.StatusBarIcon;
+import com.android.systemui.statusbar.StatusBarIconView;
+
+/**
+ * Data class for storing icons associated with a notification
+ */
+public final class IconPack {
+
+ private final boolean mAreIconsAvailable;
+ @Nullable private final StatusBarIconView mStatusBarIcon;
+ @Nullable private final StatusBarIconView mShelfIcon;
+ @Nullable private final StatusBarIconView mAodIcon;
+ @Nullable private final StatusBarIconView mCenteredIcon;
+
+ @Nullable private StatusBarIcon mSmallIconDescriptor;
+ @Nullable private StatusBarIcon mPeopleAvatarDescriptor;
+
+ private boolean mIsImportantConversation;
+
+ /**
+ * Builds an empty instance of IconPack that doesn't have any icons (because either they
+ * haven't been inflated yet or there was an error while inflating them).
+ */
+ public static IconPack buildEmptyPack(@Nullable IconPack fromSource) {
+ return new IconPack(false, null, null, null, null, fromSource);
+ }
+
+ /**
+ * Builds an instance of an IconPack that contains successfully-inflated icons
+ */
+ public static IconPack buildPack(
+ @NonNull StatusBarIconView statusBarIcon,
+ @NonNull StatusBarIconView shelfIcon,
+ @NonNull StatusBarIconView aodIcon,
+ @Nullable StatusBarIconView centeredIcon,
+ @Nullable IconPack source) {
+ return new IconPack(true, statusBarIcon, shelfIcon, aodIcon, centeredIcon, source);
+ }
+
+ private IconPack(
+ boolean areIconsAvailable,
+ @Nullable StatusBarIconView statusBarIcon,
+ @Nullable StatusBarIconView shelfIcon,
+ @Nullable StatusBarIconView aodIcon,
+ @Nullable StatusBarIconView centeredIcon,
+ @Nullable IconPack source) {
+ mAreIconsAvailable = areIconsAvailable;
+ mStatusBarIcon = statusBarIcon;
+ mShelfIcon = shelfIcon;
+ mCenteredIcon = centeredIcon;
+ mAodIcon = aodIcon;
+ if (source != null) {
+ mIsImportantConversation = source.mIsImportantConversation;
+ }
+ }
+
+ /** The version of the notification icon that appears in the status bar. */
+ @Nullable
+ public StatusBarIconView getStatusBarIcon() {
+ return mStatusBarIcon;
+ }
+
+ /**
+ * The version of the icon that appears in the "shelf" at the bottom of the notification shade.
+ * In general, this icon also appears somewhere on the notification and is "sucked" into the
+ * shelf as the scrolls beyond it.
+ */
+ @Nullable
+ public StatusBarIconView getShelfIcon() {
+ return mShelfIcon;
+ }
+
+ @Nullable
+ public StatusBarIconView getCenteredIcon() {
+ return mCenteredIcon;
+ }
+
+ /** The version of the icon that's shown when pulsing (in AOD). */
+ @Nullable
+ public StatusBarIconView getAodIcon() {
+ return mAodIcon;
+ }
+
+ @Nullable
+ StatusBarIcon getSmallIconDescriptor() {
+ return mSmallIconDescriptor;
+ }
+
+ void setSmallIconDescriptor(@Nullable StatusBarIcon smallIconDescriptor) {
+ mSmallIconDescriptor = smallIconDescriptor;
+ }
+
+ @Nullable
+ StatusBarIcon getPeopleAvatarDescriptor() {
+ return mPeopleAvatarDescriptor;
+ }
+
+ void setPeopleAvatarDescriptor(@Nullable StatusBarIcon peopleAvatarDescriptor) {
+ mPeopleAvatarDescriptor = peopleAvatarDescriptor;
+ }
+
+ boolean isImportantConversation() {
+ return mIsImportantConversation;
+ }
+
+ void setImportantConversation(boolean importantConversation) {
+ mIsImportantConversation = importantConversation;
+ }
+
+ public boolean getAreIconsAvailable() {
+ return mAreIconsAvailable;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index 8a6d5c70d6f1..7a7178c3464a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -94,7 +94,10 @@ class NotificationsControllerImpl @Inject constructor(
notifBindPipelineInitializer.initialize()
if (featureFlags.isNewNotifPipelineEnabled) {
- newNotifPipeline.get().initialize(notificationListener, notificationRowBinder)
+ newNotifPipeline.get().initialize(
+ notificationListener,
+ notificationRowBinder,
+ listContainer)
}
if (featureFlags.isNewNotifPipelineRenderingEnabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index ea1bdd6a0ef6..b9dd97482d88 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -271,6 +271,10 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
}
}
+ public boolean isActive() {
+ return mActivated;
+ }
+
private void startActivateAnimation(final boolean reverse) {
if (!isAttachedToWindow()) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
index 2643ec975023..2f0e433b3927 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
@@ -111,7 +111,7 @@ public class ActivatableNotificationViewController {
if (mNeedsDimming && ev.getActionMasked() == MotionEvent.ACTION_DOWN
&& mView.disallowSingleClick(ev)
&& !mAccessibilityManager.isTouchExplorationEnabled()) {
- if (!mView.isActivated()) {
+ if (!mView.isActive()) {
return true;
} else if (!mDoubleTapHelper.isWithinDoubleTapSlop(ev)) {
mBlockNextTouch = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/DungeonRow.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/DungeonRow.kt
index 373457d4e336..dbfa27f1f68e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/DungeonRow.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/DungeonRow.kt
@@ -37,7 +37,7 @@ class DungeonRow(context: Context, attrs: AttributeSet) : LinearLayout(context,
}
(findViewById(R.id.icon) as StatusBarIconView).apply {
- set(entry?.icon?.statusBarIcon)
+ set(entry?.icons?.statusBarIcon?.statusBarIcon)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index f61fe9830939..5f2b256153ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -94,6 +94,7 @@ import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
+import com.android.systemui.statusbar.notification.stack.NotificationListItem;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.SwipeableView;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -115,7 +116,8 @@ import java.util.function.Consumer;
* the group summary (which contains 1 or more child notifications).
*/
public class ExpandableNotificationRow extends ActivatableNotificationView
- implements PluginListener<NotificationMenuRowPlugin>, SwipeableView {
+ implements PluginListener<NotificationMenuRowPlugin>, SwipeableView,
+ NotificationListItem {
private static final boolean DEBUG = false;
private static final int DEFAULT_DIVIDER_ALPHA = 0x29;
@@ -579,7 +581,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
@VisibleForTesting
void updateShelfIconColor() {
- StatusBarIconView expandedIcon = mEntry.expandedIcon;
+ StatusBarIconView expandedIcon = mEntry.getIcons().getShelfIcon();
boolean isPreL = Boolean.TRUE.equals(expandedIcon.getTag(R.id.icon_is_pre_L));
boolean colorize = !isPreL || NotificationUtils.isGrayscale(expandedIcon,
ContrastColorUtil.getInstance(mContext));
@@ -666,6 +668,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
layout.setHeights(minHeight, headsUpHeight, mNotificationMaxHeight);
}
+ @NonNull
public NotificationEntry getEntry() {
return mEntry;
}
@@ -767,6 +770,17 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
row.setIsChildInGroup(true, this);
}
+ /**
+ * Same as {@link #addChildNotification(ExpandableNotificationRow, int)}, but takes a
+ * {@link NotificationListItem} instead
+ *
+ * @param childItem item
+ * @param childIndex index
+ */
+ public void addChildNotification(NotificationListItem childItem, int childIndex) {
+ addChildNotification((ExpandableNotificationRow) childItem.getView(), childIndex);
+ }
+
public void removeChildNotification(ExpandableNotificationRow row) {
if (mChildrenContainer != null) {
mChildrenContainer.removeNotification(row);
@@ -777,6 +791,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
@Override
+ public void removeChildNotification(NotificationListItem child) {
+ removeChildNotification((ExpandableNotificationRow) child.getView());
+ }
+
+ @Override
public boolean isChildInGroup() {
return mNotificationParent != null;
}
@@ -879,7 +898,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
* @param callback the callback to invoked in case it is not allowed
* @return whether the list order has changed
*/
- public boolean applyChildOrder(List<ExpandableNotificationRow> childOrder,
+ public boolean applyChildOrder(List<? extends NotificationListItem> childOrder,
VisualStabilityManager visualStabilityManager,
VisualStabilityManager.Callback callback) {
return mChildrenContainer != null && mChildrenContainer.applyChildOrder(childOrder,
@@ -1274,6 +1293,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
onChildrenCountChanged();
}
+ @Override
+ public View getView() {
+ return this;
+ }
+
public void setForceUnlocked(boolean forceUnlocked) {
mForceUnlocked = forceUnlocked;
if (mIsSummaryWithChildren) {
@@ -1290,7 +1314,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
setLongPressListener(null);
mGroupParentWhenDismissed = mNotificationParent;
mChildAfterViewWhenDismissed = null;
- mEntry.icon.setDismissed();
+ mEntry.getIcons().getStatusBarIcon().setDismissed();
if (isChildInGroup()) {
List<ExpandableNotificationRow> notificationChildren =
mNotificationParent.getNotificationChildren();
@@ -1832,7 +1856,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mTranslateableViews.get(i).setTranslationX(0);
}
invalidateOutline();
- getEntry().expandedIcon.setScrollX(0);
+ getEntry().getIcons().getShelfIcon().setScrollX(0);
}
if (mMenuRow != null) {
@@ -1912,7 +1936,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
// In order to keep the shelf in sync with this swiping, we're simply translating
// it's icon by the same amount. The translation is already being used for the normal
// positioning, so we can use the scrollX instead.
- getEntry().expandedIcon.setScrollX((int) -translationX);
+ getEntry().getIcons().getShelfIcon().setScrollX((int) -translationX);
}
if (mMenuRow != null && mMenuRow.getMenuView() != null) {
@@ -2111,7 +2135,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
@Override
public StatusBarIconView getShelfIcon() {
- return getEntry().expandedIcon;
+ return getEntry().getIcons().getShelfIcon();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
index 1e2571b5c801..162786cc1fbd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
@@ -40,6 +40,7 @@ class NotificationConversationTemplateViewWrapper constructor(
private var conversationIcon: View? = null
private var conversationBadge: View? = null
private var expandButton: View? = null
+ private lateinit var expandButtonContainer: View
private var messagingLinearLayout: MessagingLinearLayout? = null
init {
@@ -56,6 +57,8 @@ class NotificationConversationTemplateViewWrapper constructor(
com.android.internal.R.id.conversation_icon_badge)
expandButton = conversationLayout.requireViewById(
com.android.internal.R.id.expand_button)
+ expandButtonContainer = conversationLayout.requireViewById(
+ com.android.internal.R.id.expand_button_container)
}
override fun onContentUpdated(row: ExpandableNotificationRow) {
@@ -90,6 +93,14 @@ class NotificationConversationTemplateViewWrapper constructor(
conversationLayout.updateExpandability(expandable, onClickListener)
}
+ override fun disallowSingleClick(x: Float, y: Float): Boolean {
+ if (expandButtonContainer.visibility == View.VISIBLE
+ && isOnView(expandButtonContainer, x, y)) {
+ return true
+ }
+ return super.disallowSingleClick(x, y)
+ }
+
override fun getMinLayoutHeight(): Int {
if (mActionsContainer != null && mActionsContainer.visibility != View.GONE) {
return minHeightWithActions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
index d41f5af6c524..2d99ab1e999f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
@@ -58,7 +58,6 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp
private TextView mText;
protected View mActionsContainer;
private ImageView mReplyAction;
- private Rect mTmpRect = new Rect();
private int mContentHeight;
private int mMinHeightHint;
@@ -271,18 +270,6 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp
return super.disallowSingleClick(x, y);
}
- private boolean isOnView(View view, float x, float y) {
- View searchView = (View) view.getParent();
- while (searchView != null && !(searchView instanceof ExpandableNotificationRow)) {
- searchView.getHitRect(mTmpRect);
- x -= mTmpRect.left;
- y -= mTmpRect.top;
- searchView = (View) searchView.getParent();
- }
- view.getHitRect(mTmpRect);
- return mTmpRect.contains((int) x,(int) y);
- }
-
@Override
public void onContentUpdated(ExpandableNotificationRow row) {
// Reinspect the notification. Before the super call, because the super call also updates
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index c834e4b376ed..46d7d9374290 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -24,6 +24,7 @@ import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
+import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
@@ -49,6 +50,7 @@ public abstract class NotificationViewWrapper implements TransformableView {
protected final View mView;
protected final ExpandableNotificationRow mRow;
+ private final Rect mTmpRect = new Rect();
protected int mBackgroundColor = 0;
@@ -305,6 +307,26 @@ public abstract class NotificationViewWrapper implements TransformableView {
return false;
}
+ /**
+ * Is a given x and y coordinate on a view.
+ *
+ * @param view the view to be checked
+ * @param x the x coordinate, relative to the ExpandableNotificationRow
+ * @param y the y coordinate, relative to the ExpandableNotificationRow
+ * @return {@code true} if it is on the view
+ */
+ protected boolean isOnView(View view, float x, float y) {
+ View searchView = (View) view.getParent();
+ while (searchView != null && !(searchView instanceof ExpandableNotificationRow)) {
+ searchView.getHitRect(mTmpRect);
+ x -= mTmpRect.left;
+ y -= mTmpRect.top;
+ searchView = (View) searchView.getParent();
+ }
+ view.getHitRect(mTmpRect);
+ return mTmpRect.contains((int) x,(int) y);
+ }
+
public int getMinLayoutHeight() {
return 0;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index d7c88e3a3f60..2c17764799a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -412,7 +412,7 @@ public class NotificationChildrenContainer extends ViewGroup {
* @param callback
* @return whether the list order has changed
*/
- public boolean applyChildOrder(List<ExpandableNotificationRow> childOrder,
+ public boolean applyChildOrder(List<? extends NotificationListItem> childOrder,
VisualStabilityManager visualStabilityManager,
VisualStabilityManager.Callback callback) {
if (childOrder == null) {
@@ -421,7 +421,7 @@ public class NotificationChildrenContainer extends ViewGroup {
boolean result = false;
for (int i = 0; i < mChildren.size() && i < childOrder.size(); i++) {
ExpandableNotificationRow child = mChildren.get(i);
- ExpandableNotificationRow desiredChild = childOrder.get(i);
+ ExpandableNotificationRow desiredChild = (ExpandableNotificationRow) childOrder.get(i);
if (child != desiredChild) {
if (visualStabilityManager.canReorderNotification(desiredChild)) {
mChildren.remove(desiredChild);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
index 15cc72c2d7a1..c4a720cb659f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
@@ -18,12 +18,14 @@ package com.android.systemui.statusbar.notification.stack;
import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
+import android.annotation.NonNull;
import android.view.View;
import android.view.ViewGroup;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.SimpleNotificationListContainer;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -33,7 +35,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableView;
* notification views added and removed from it, and will manage displaying them to the user.
*/
public interface NotificationListContainer extends ExpandableView.OnHeightChangedListener,
- VisibilityLocationProvider {
+ VisibilityLocationProvider, SimpleNotificationListContainer {
/**
* Called when a child is being transferred.
@@ -186,4 +188,10 @@ public interface NotificationListContainer extends ExpandableView.OnHeightChange
}
default void setWillExpand(boolean willExpand) {};
+
+ /**
+ * Remove a list item from the container
+ * @param v the item to remove
+ */
+ void removeListItem(@NonNull NotificationListItem v);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListItem.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListItem.java
new file mode 100644
index 000000000000..8991abe52ce1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListItem.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.stack;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.view.View;
+
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+import java.util.List;
+
+/**
+* A NotificationListItem is a child view of the notification list that can yield a
+* NotificationEntry when asked. I.e., it's an ExpandableNotificationRow but doesn't require us
+* to strictly rely on ExpandableNotificationRow as our consumed type
+ */
+public interface NotificationListItem {
+ /** @return entry for this item */
+ @NonNull
+ NotificationEntry getEntry();
+
+ /** @return true if the blocking helper is showing */
+ boolean isBlockingHelperShowing();
+
+ /** @return true if this list item is a summary with children */
+ boolean isSummaryWithChildren();
+
+ // This generic is kind of ugly - we should change this once the old VHM is gone
+ /** @return list of the children of this item */
+ List<? extends NotificationListItem> getNotificationChildren();
+
+ /** remove all children from this list item */
+ void removeAllChildren();
+
+ /** remove particular child */
+ void removeChildNotification(NotificationListItem child);
+
+ /** add an item as a child */
+ void addChildNotification(NotificationListItem child, int childIndex);
+
+ /** Update the order of the children with the new list */
+ boolean applyChildOrder(
+ List<? extends NotificationListItem> childOrderList,
+ VisualStabilityManager vsm,
+ @Nullable VisualStabilityManager.Callback callback);
+
+ /** return the associated view for this list item */
+ @NonNull
+ View getView();
+}
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 cfcbd885754d..4d4a2ded08ca 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
@@ -3323,11 +3323,21 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
}
@Override
+ public void notifyGroupChildRemoved(View child, ViewGroup parent) {
+ notifyGroupChildRemoved((ExpandableView) child, parent);
+ }
+
+ @Override
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void notifyGroupChildAdded(ExpandableView row) {
onViewAddedInternal(row);
}
+ @Override
+ public void notifyGroupChildAdded(View view) {
+ notifyGroupChildAdded((ExpandableView) view);
+ }
+
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void setAnimationsEnabled(boolean animationsEnabled) {
mAnimationsEnabled = animationsEnabled;
@@ -5137,11 +5147,22 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
@Override
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+ public void removeListItem(NotificationListItem v) {
+ removeContainerView(v.getView());
+ }
+
+ @Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void addContainerView(View v) {
Assert.isMainThread();
addView(v);
}
+ @Override
+ public void addListItem(NotificationListItem v) {
+ addContainerView(v.getView());
+ }
+
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void runAfterAnimationFinished(Runnable runnable) {
mAnimationFinishedRunnables.add(runnable);
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 0996ff27e9a3..14442e346db4 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
@@ -453,9 +453,10 @@ public class StackStateAnimator {
needsAnimation = false;
}
NotificationEntry entry = row.getEntry();
- StatusBarIconView icon = entry.icon;
- if (entry.centeredIcon != null && entry.centeredIcon.getParent() != null) {
- icon = entry.centeredIcon;
+ StatusBarIconView icon = entry.getIcons().getStatusBarIcon();
+ final StatusBarIconView centeredIcon = entry.getIcons().getCenteredIcon();
+ if (centeredIcon != null && centeredIcon.getParent() != null) {
+ icon = centeredIcon;
}
if (icon.getParent() != null) {
icon.getLocationOnScreen(mTmpLocation);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
index 745843deeddb..adca10ff7677 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -44,6 +44,7 @@ import android.view.ViewConfiguration;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
+import com.android.internal.policy.GestureNavigationSettingsObserver;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.bubbles.BubbleController;
@@ -99,8 +100,10 @@ public class EdgeBackGestureHandler implements DisplayListener,
private final Region mExcludeRegion = new Region();
private final Region mUnrestrictedExcludeRegion = new Region();
- // The edge width where touch down is allowed
- private int mEdgeWidth;
+ // The left side edge width where touch down is allowed
+ private int mEdgeWidthLeft;
+ // The right side edge width where touch down is allowed
+ private int mEdgeWidthRight;
// The bottom gesture area height
private int mBottomGestureHeight;
// The slop to distinguish between horizontal and vertical motion
@@ -127,6 +130,8 @@ public class EdgeBackGestureHandler implements DisplayListener,
private int mRightInset;
private int mSysUiFlags;
+ private final GestureNavigationSettingsObserver mGestureNavigationSettingsObserver;
+
private final NavigationEdgeBackPlugin.BackCallback mBackCallback =
new NavigationEdgeBackPlugin.BackCallback() {
@Override
@@ -174,13 +179,17 @@ public class EdgeBackGestureHandler implements DisplayListener,
mLongPressTimeout = Math.min(MAX_LONG_PRESS_TIMEOUT,
ViewConfiguration.getLongPressTimeout());
+ mGestureNavigationSettingsObserver = new GestureNavigationSettingsObserver(
+ mContext.getMainThreadHandler(), mContext, () -> updateCurrentUserResources(res));
+
updateCurrentUserResources(res);
sysUiFlagContainer.addCallback(sysUiFlags -> mSysUiFlags = sysUiFlags);
}
public void updateCurrentUserResources(Resources res) {
- mEdgeWidth = res.getDimensionPixelSize(
- com.android.internal.R.dimen.config_backGestureInset);
+ mEdgeWidthLeft = mGestureNavigationSettingsObserver.getLeftSensitivity(res);
+ mEdgeWidthRight = mGestureNavigationSettingsObserver.getRightSensitivity(res);
+
mBottomGestureHeight = res.getDimensionPixelSize(
com.android.internal.R.dimen.navigation_bar_gesture_height);
}
@@ -236,6 +245,7 @@ public class EdgeBackGestureHandler implements DisplayListener,
}
if (!mIsEnabled) {
+ mGestureNavigationSettingsObserver.unregister();
mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this);
mPluginManager.removePluginListener(this);
@@ -248,6 +258,7 @@ public class EdgeBackGestureHandler implements DisplayListener,
}
} else {
+ mGestureNavigationSettingsObserver.register();
updateDisplaySize();
mContext.getSystemService(DisplayManager.class).registerDisplayListener(this,
mContext.getMainThreadHandler());
@@ -321,7 +332,8 @@ public class EdgeBackGestureHandler implements DisplayListener,
private boolean isWithinTouchRegion(int x, int y) {
// Disallow if too far from the edge
- if (x > mEdgeWidth + mLeftInset && x < (mDisplaySize.x - mEdgeWidth - mRightInset)) {
+ if (x > mEdgeWidthLeft + mLeftInset
+ && x < (mDisplaySize.x - mEdgeWidthRight - mRightInset)) {
return false;
}
@@ -364,7 +376,7 @@ public class EdgeBackGestureHandler implements DisplayListener,
if (action == MotionEvent.ACTION_DOWN) {
// Verify if this is in within the touch region and we aren't in immersive mode, and
// either the bouncer is showing or the notification panel is hidden
- mIsOnLeftEdge = ev.getX() <= mEdgeWidth + mLeftInset;
+ mIsOnLeftEdge = ev.getX() <= mEdgeWidthLeft + mLeftInset;
mInRejectedExclusion = false;
mAllowGesture = !QuickStepContract.isBackGestureDisabled(mSysUiFlags)
&& isWithinTouchRegion((int) ev.getX(), (int) ev.getY());
@@ -461,7 +473,8 @@ public class EdgeBackGestureHandler implements DisplayListener,
pw.println(" mExcludeRegion=" + mExcludeRegion);
pw.println(" mUnrestrictedExcludeRegion=" + mUnrestrictedExcludeRegion);
pw.println(" mIsAttached=" + mIsAttached);
- pw.println(" mEdgeWidth=" + mEdgeWidth);
+ pw.println(" mEdgeWidthLeft=" + mEdgeWidthLeft);
+ pw.println(" mEdgeWidthRight=" + mEdgeWidthRight);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index c39ee3a90230..51c02c9f93ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -269,7 +269,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
}
updateIsolatedIconLocation(false /* requireUpdate */);
mNotificationIconAreaController.showIconIsolated(newEntry == null ? null
- : newEntry.icon, animateIsolation);
+ : newEntry.getIcons().getStatusBarIcon(), animateIsolation);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index c282cb8a5cb5..0b747f94e935 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -114,8 +114,9 @@ public class HeadsUpTouchHelper implements Gefingerpoken {
mInitialTouchY = y;
int startHeight = (int) (mPickedChild.getActualHeight()
+ mPickedChild.getTranslationY());
- mPanel.setPanelScrimMinFraction((float) startHeight
- / mPanel.getMaxPanelHeight());
+ float maxPanelHeight = mPanel.getMaxPanelHeight();
+ mPanel.setPanelScrimMinFraction(maxPanelHeight > 0f
+ ? (float) startHeight / maxPanelHeight : 0f);
mPanel.startExpandMotion(x, y, true /* startTracking */, startHeight);
mPanel.startExpandingFromPeek();
// This call needs to be after the expansion start otherwise we will get a
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index b119f0b1f1e2..31266db9e144 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -551,14 +551,15 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
public void setWindowState(
int displayId, @WindowType int window, @WindowVisibleState int state) {
if (displayId == mDisplayId
- && mNavigationBarView != null
&& window == StatusBarManager.WINDOW_NAVIGATION_BAR
&& mNavigationBarWindowState != state) {
mNavigationBarWindowState = state;
+ updateSystemUiStateFlags(-1);
if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state));
- updateSystemUiStateFlags(-1);
- mNavigationBarView.setWindowVisible(isNavBarWindowVisible());
+ if (mNavigationBarView != null) {
+ mNavigationBarView.setWindowVisible(isNavBarWindowVisible());
+ }
}
}
@@ -1219,6 +1220,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
@Override
public void onViewDetachedFromWindow(View v) {
FragmentHostManager.removeAndDestroy(v);
+ navigationBarView.removeOnAttachStateChangeListener(this);
}
});
context.getSystemService(WindowManager.class).addView(navigationBarView, lp);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
index 43ac4cf732aa..d24ccf343a3a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
@@ -28,6 +28,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.om.IOverlayManager;
+import android.content.om.OverlayInfo;
import android.content.pm.PackageManager;
import android.content.res.ApkAssets;
import android.os.PatternMatcher;
@@ -172,6 +173,9 @@ public class NavigationModeController implements Dumpable {
public void updateCurrentInteractionMode(boolean notify) {
mCurrentUserContext = getCurrentUserContext();
int mode = getCurrentInteractionMode(mCurrentUserContext);
+ if (mode == NAV_BAR_MODE_GESTURAL) {
+ switchToDefaultGestureNavOverlayIfNecessary();
+ }
mUiBgExecutor.execute(() -> {
Settings.Secure.putString(mCurrentUserContext.getContentResolver(),
Secure.NAVIGATION_MODE, String.valueOf(mode));
@@ -277,6 +281,34 @@ public class NavigationModeController implements Dumpable {
}
}
+ private void switchToDefaultGestureNavOverlayIfNecessary() {
+ final int userId = mCurrentUserContext.getUserId();
+ try {
+ final IOverlayManager om = mOverlayManager;
+ final OverlayInfo info = om.getOverlayInfo(NAV_BAR_MODE_GESTURAL_OVERLAY, userId);
+ if (info != null && !info.isEnabled()) {
+ // Enable the default gesture nav overlay, and move the back gesture inset scale to
+ // Settings.Secure for left and right sensitivity.
+ final int curInset = mCurrentUserContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.config_backGestureInset);
+ om.setEnabledExclusiveInCategory(NAV_BAR_MODE_GESTURAL_OVERLAY, userId);
+ final int defInset = mCurrentUserContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.config_backGestureInset);
+
+ final float scale = defInset == 0 ? 1.0f : ((float) curInset) / defInset;
+ Settings.Secure.putFloat(mCurrentUserContext.getContentResolver(),
+ Secure.BACK_GESTURE_INSET_SCALE_LEFT, scale);
+ Settings.Secure.putFloat(mCurrentUserContext.getContentResolver(),
+ Secure.BACK_GESTURE_INSET_SCALE_RIGHT, scale);
+ if (DEBUG) {
+ Log.v(TAG, "Moved back sensitivity for user " + userId + " to scale " + scale);
+ }
+ }
+ } catch (SecurityException | IllegalStateException | RemoteException e) {
+ Log.e(TAG, "Failed to switch to default gesture nav overlay for user " + userId);
+ }
+ }
+
public void setModeOverlay(String overlayPkg, int userId) {
mUiBgExecutor.execute(() -> {
try {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
deleted file mode 100644
index 5d8044f37c38..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import android.annotation.IntDef;
-import android.content.ComponentCallbacks;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.database.ContentObserver;
-import android.graphics.Point;
-import android.net.Uri;
-import android.os.Handler;
-import android.provider.Settings;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Coordinates with the prototype settings plugin app that uses Settings.Global to allow different
- * prototypes to run in the system. The class will handle communication changes from the settings
- * app and call back to listeners.
- */
-public class NavigationPrototypeController extends ContentObserver implements ComponentCallbacks {
- private static final String HIDE_BACK_BUTTON_SETTING = "quickstepcontroller_hideback";
- private static final String HIDE_HOME_BUTTON_SETTING = "quickstepcontroller_hidehome";
- private static final String PROTOTYPE_ENABLED = "prototype_enabled";
-
- public static final String EDGE_SENSITIVITY_WIDTH_SETTING =
- "quickstepcontroller_edge_width_sensitivity";
- private final String GESTURE_MATCH_SETTING = "quickstepcontroller_gesture_match_map";
- public static final String NAV_COLOR_ADAPT_ENABLE_SETTING = "navbar_color_adapt_enable";
- public static final String SHOW_HOME_HANDLE_SETTING = "quickstepcontroller_showhandle";
- public static final String ENABLE_ASSISTANT_GESTURE = "ENABLE_ASSISTANT_GESTURE";
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({ACTION_DEFAULT, ACTION_QUICKSTEP, ACTION_QUICKSCRUB, ACTION_BACK,
- ACTION_QUICKSWITCH, ACTION_NOTHING, ACTION_ASSISTANT})
- @interface GestureAction {}
- static final int ACTION_DEFAULT = 0;
- static final int ACTION_QUICKSTEP = 1;
- static final int ACTION_QUICKSCRUB = 2;
- static final int ACTION_BACK = 3;
- static final int ACTION_QUICKSWITCH = 4;
- static final int ACTION_NOTHING = 5;
- static final int ACTION_ASSISTANT = 6;
-
- private OnPrototypeChangedListener mListener;
-
- /**
- * Each index corresponds to a different action set in QuickStepController
- * {@see updateSwipeLTRBackSetting}
- */
- private int[] mActionMap = new int[6];
-
- private final Context mContext;
-
- public NavigationPrototypeController(Context context) {
- super(new Handler());
- mContext = context;
- updateSwipeLTRBackSetting();
- }
-
- public void setOnPrototypeChangedListener(OnPrototypeChangedListener listener) {
- mListener = listener;
- }
-
- /**
- * Observe all the settings to react to from prototype settings
- */
- public void register() {
- registerObserver(HIDE_BACK_BUTTON_SETTING);
- registerObserver(HIDE_HOME_BUTTON_SETTING);
- registerObserver(GESTURE_MATCH_SETTING);
- registerObserver(NAV_COLOR_ADAPT_ENABLE_SETTING);
- registerObserver(SHOW_HOME_HANDLE_SETTING);
- registerObserver(ENABLE_ASSISTANT_GESTURE);
- mContext.registerComponentCallbacks(this);
- }
-
- /**
- * Disable observing settings to react to from prototype settings
- */
- public void unregister() {
- mContext.getContentResolver().unregisterContentObserver(this);
- mContext.unregisterComponentCallbacks(this);
- }
-
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- super.onChange(selfChange, uri);
- if (!selfChange && mListener != null) {
- final String path = uri.getPath();
- if (path.endsWith(GESTURE_MATCH_SETTING)) {
- // Get the settings gesture map corresponding to each action
- // {@see updateSwipeLTRBackSetting}
- updateSwipeLTRBackSetting();
- mListener.onGestureRemap(mActionMap);
- } else if (path.endsWith(HIDE_BACK_BUTTON_SETTING)) {
- mListener.onBackButtonVisibilityChanged(
- !getGlobalBool(HIDE_BACK_BUTTON_SETTING, false));
- } else if (path.endsWith(HIDE_HOME_BUTTON_SETTING)) {
- mListener.onHomeButtonVisibilityChanged(!hideHomeButton());
- } else if (path.endsWith(NAV_COLOR_ADAPT_ENABLE_SETTING)) {
- mListener.onColorAdaptChanged(mContext.getDisplayId() == DEFAULT_DISPLAY);
- } else if (path.endsWith(SHOW_HOME_HANDLE_SETTING)) {
- mListener.onHomeHandleVisiblilityChanged(showHomeHandle());
- } else if (path.endsWith(ENABLE_ASSISTANT_GESTURE)) {
- mListener.onAssistantGestureEnabled(isAssistantGestureEnabled());
- }
- }
- }
-
- /**
- * @return the width for edge swipe
- */
- public int getEdgeSensitivityWidth() {
- return mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.config_backGestureInset);
- }
-
- /**
- * @return full screen height
- */
- public int getEdgeSensitivityHeight() {
- final Point size = new Point();
- mContext.getDisplay().getRealSize(size);
- return size.y;
- }
-
- public boolean isEnabled() {
- return getGlobalBool(PROTOTYPE_ENABLED, false);
- }
-
- /**
- * Retrieve the action map to apply to the quick step controller
- * @return an action map
- */
- int[] getGestureActionMap() {
- return mActionMap;
- }
-
- /**
- * @return if home button should be invisible
- */
- boolean hideHomeButton() {
- return getGlobalBool(HIDE_HOME_BUTTON_SETTING, false /* default */);
- }
-
- boolean showHomeHandle() {
- return getGlobalBool(SHOW_HOME_HANDLE_SETTING, false /* default */);
- }
-
- boolean isAssistantGestureEnabled() {
- return getGlobalBool(ENABLE_ASSISTANT_GESTURE, false /* default */);
- }
-
-
- /**
- * Since Settings.Global cannot pass arrays, use a string to represent each character as a
- * gesture map to actions corresponding to {@see GestureAction}. The number is represented as:
- * Number: [up] [down] [left] [right]
- */
- private void updateSwipeLTRBackSetting() {
- String value = Settings.Global.getString(mContext.getContentResolver(),
- GESTURE_MATCH_SETTING);
- if (value != null) {
- for (int i = 0; i < mActionMap.length; ++i) {
- mActionMap[i] = Character.getNumericValue(value.charAt(i));
- }
- }
- }
-
- private boolean getGlobalBool(String name, boolean defaultVal) {
- return Settings.Global.getInt(mContext.getContentResolver(), name, defaultVal ? 1 : 0) == 1;
- }
-
- private int getGlobalInt(String name, int defaultVal) {
- return Settings.Global.getInt(mContext.getContentResolver(), name, defaultVal);
- }
-
- private void registerObserver(String name) {
- mContext.getContentResolver()
- .registerContentObserver(Settings.Global.getUriFor(name), false, this);
- }
-
- private static int convertDpToPixel(float dp) {
- return (int) (dp * Resources.getSystem().getDisplayMetrics().density);
- }
-
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- if (mListener != null) {
- mListener.onEdgeSensitivityChanged(getEdgeSensitivityWidth(),
- getEdgeSensitivityHeight());
- }
- }
-
- @Override
- public void onLowMemory() {
- }
-
- public interface OnPrototypeChangedListener {
- void onGestureRemap(@GestureAction int[] actions);
- void onBackButtonVisibilityChanged(boolean visible);
- void onHomeButtonVisibilityChanged(boolean visible);
- void onHomeHandleVisiblilityChanged(boolean visible);
- void onColorAdaptChanged(boolean enabled);
- void onEdgeSensitivityChanged(int width, int height);
- void onAssistantGestureEnabled(boolean enabled);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index 77337e95bb95..ccf670708e44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -398,8 +398,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State
NotificationGroup group = mGroupMap.get(groupKey);
//TODO: see if this can become an Entry
return group == null ? null
- : group.summary == null ? null
- : group.summary;
+ : group.summary;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index b09ccffdc4de..f58cce58af74 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -253,8 +253,8 @@ public class NotificationIconAreaController implements DarkReceiver,
boolean hidePulsing, boolean onlyShowCenteredIcon) {
final boolean isCenteredNotificationIcon = mCenteredIconView != null
- && entry.centeredIcon != null
- && Objects.equals(entry.centeredIcon, mCenteredIconView);
+ && entry.getIcons().getCenteredIcon() != null
+ && Objects.equals(entry.getIcons().getCenteredIcon(), mCenteredIconView);
if (onlyShowCenteredIcon) {
return isCenteredNotificationIcon;
}
@@ -307,7 +307,7 @@ public class NotificationIconAreaController implements DarkReceiver,
}
private void updateShelfIcons() {
- updateIconsForLayout(entry -> entry.expandedIcon, mShelfIcons,
+ updateIconsForLayout(entry -> entry.getIcons().getShelfIcon(), mShelfIcons,
true /* showAmbient */,
true /* showLowPriority */,
false /* hideDismissed */,
@@ -319,7 +319,7 @@ public class NotificationIconAreaController implements DarkReceiver,
}
public void updateStatusBarIcons() {
- updateIconsForLayout(entry -> entry.icon, mNotificationIcons,
+ updateIconsForLayout(entry -> entry.getIcons().getStatusBarIcon(), mNotificationIcons,
false /* showAmbient */,
mShowLowPriority,
true /* hideDismissed */,
@@ -331,7 +331,7 @@ public class NotificationIconAreaController implements DarkReceiver,
}
private void updateCenterIcon() {
- updateIconsForLayout(entry -> entry.centeredIcon, mCenteredIcon,
+ updateIconsForLayout(entry -> entry.getIcons().getCenteredIcon(), mCenteredIcon,
false /* showAmbient */,
true /* showLowPriority */,
false /* hideDismissed */,
@@ -343,7 +343,7 @@ public class NotificationIconAreaController implements DarkReceiver,
}
public void updateAodNotificationIcons() {
- updateIconsForLayout(entry -> entry.aodIcon, mAodIcons,
+ updateIconsForLayout(entry -> entry.getIcons().getAodIcon(), mAodIcons,
false /* showAmbient */,
true /* showLowPriority */,
true /* hideDismissed */,
@@ -517,7 +517,7 @@ public class NotificationIconAreaController implements DarkReceiver,
* Shows the icon view given in the center.
*/
public void showIconCentered(NotificationEntry entry) {
- StatusBarIconView icon = entry == null ? null : entry.centeredIcon;
+ StatusBarIconView icon = entry == null ? null : entry.getIcons().getCenteredIcon();
if (!Objects.equals(mCenteredIconView, icon)) {
mCenteredIconView = icon;
updateNotificationIcons();
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 c61d7bbf67c5..f9726d2d77f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -1844,7 +1844,14 @@ public class NotificationPanelViewController extends PanelViewController {
} else {
maxHeight = calculatePanelHeightShade();
}
- maxHeight = Math.max(maxHeight, min);
+ maxHeight = Math.max(min, maxHeight);
+ if (maxHeight == 0) {
+ Log.wtf(TAG, "maxPanelHeight is 0. getOverExpansionAmount(): "
+ + getOverExpansionAmount() + ", calculatePanelHeightQsExpanded: "
+ + calculatePanelHeightQsExpanded() + ", calculatePanelHeightShade: "
+ + calculatePanelHeightShade() + ", mStatusBarMinHeight = "
+ + mStatusBarMinHeight + ", mQsMinExpansionHeight = " + mQsMinExpansionHeight);
+ }
return maxHeight;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
index 75f5023ce48a..e1e679f06eef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
@@ -309,7 +309,8 @@ public class NotificationShadeWindowController implements Callback, Dumpable,
return !state.mForceCollapsed && (state.isKeyguardShowingAndNotOccluded()
|| state.mPanelVisible || state.mKeyguardFadingAway || state.mBouncerShowing
|| state.mHeadsUpShowing || state.mBubblesShowing
- || state.mScrimsVisibility != ScrimController.TRANSPARENT);
+ || state.mScrimsVisibility != ScrimController.TRANSPARENT)
+ || state.mBackgroundBlurRadius > 0;
}
private void applyFitsSystemWindows(State state) {
@@ -478,6 +479,19 @@ public class NotificationShadeWindowController implements Callback, Dumpable,
apply(mCurrentState);
}
+ /**
+ * Current blur level, controller by
+ * {@link com.android.systemui.statusbar.NotificationShadeDepthController}.
+ * @param backgroundBlurRadius Radius in pixels.
+ */
+ public void setBackgroundBlurRadius(int backgroundBlurRadius) {
+ if (mCurrentState.mBackgroundBlurRadius == backgroundBlurRadius) {
+ return;
+ }
+ mCurrentState.mBackgroundBlurRadius = backgroundBlurRadius;
+ apply(mCurrentState);
+ }
+
public void setHeadsUpShowing(boolean showing) {
mCurrentState.mHeadsUpShowing = showing;
apply(mCurrentState);
@@ -665,6 +679,7 @@ public class NotificationShadeWindowController implements Callback, Dumpable,
boolean mForcePluginOpen;
boolean mDozing;
int mScrimsVisibility;
+ int mBackgroundBlurRadius;
private boolean isKeyguardShowingAndNotOccluded() {
return mKeyguardShowing && !mKeyguardOccluded;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index 8d8c8da62b4c..c1065189f716 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.phone;
+import static java.lang.Float.isNaN;
+
import android.content.Context;
import android.os.Bundle;
import android.os.Parcelable;
@@ -161,6 +163,9 @@ public abstract class PanelBar extends FrameLayout {
* fraction as the panel also might be expanded if the fraction is 0
*/
public void panelExpansionChanged(float frac, boolean expanded) {
+ if (isNaN(frac)) {
+ throw new IllegalArgumentException("frac cannot be NaN");
+ }
boolean fullyClosed = true;
boolean fullyOpened = false;
if (SPEW) LOG("panelExpansionChanged: start state=%d", mState);
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 1359f74d0b3a..1c1e7c4eaa4a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -18,6 +18,8 @@ package com.android.systemui.statusbar.phone;
import static com.android.systemui.ScreenDecorations.DisplayCutoutView.boundsFromDirection;
+import static java.lang.Float.isNaN;
+
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
@@ -40,6 +42,7 @@ import com.android.systemui.R;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.util.leak.RotationUtils;
import java.util.Objects;
@@ -47,7 +50,6 @@ public class PhoneStatusBarView extends PanelBar {
private static final String TAG = "PhoneStatusBarView";
private static final boolean DEBUG = StatusBar.DEBUG;
private static final boolean DEBUG_GESTURES = false;
- private static final int NO_VALUE = Integer.MIN_VALUE;
private final CommandQueue mCommandQueue;
StatusBar mBar;
@@ -64,13 +66,14 @@ public class PhoneStatusBarView extends PanelBar {
}
};
private DarkReceiver mBattery;
- private int mLastOrientation;
+ private int mRotationOrientation;
@Nullable
private View mCenterIconSpace;
@Nullable
private View mCutoutSpace;
@Nullable
private DisplayCutout mDisplayCutout;
+ private int mStatusBarHeight;
/**
* Draw this many pixels into the left/right side of the cutout to optimally use the space
@@ -107,7 +110,7 @@ public class PhoneStatusBarView extends PanelBar {
super.onAttachedToWindow();
// Always have Battery meters in the status bar observe the dark/light modes.
Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mBattery);
- if (updateOrientationAndCutout(getResources().getConfiguration().orientation)) {
+ if (updateOrientationAndCutout()) {
updateLayoutForCutout();
}
}
@@ -124,7 +127,7 @@ public class PhoneStatusBarView extends PanelBar {
super.onConfigurationChanged(newConfig);
// May trigger cutout space layout-ing
- if (updateOrientationAndCutout(newConfig.orientation)) {
+ if (updateOrientationAndCutout()) {
updateLayoutForCutout();
requestLayout();
}
@@ -132,7 +135,7 @@ public class PhoneStatusBarView extends PanelBar {
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- if (updateOrientationAndCutout(mLastOrientation)) {
+ if (updateOrientationAndCutout()) {
updateLayoutForCutout();
requestLayout();
}
@@ -140,17 +143,14 @@ public class PhoneStatusBarView extends PanelBar {
}
/**
- *
- * @param newOrientation may pass NO_VALUE for no change
* @return boolean indicating if we need to update the cutout location / margins
*/
- private boolean updateOrientationAndCutout(int newOrientation) {
+ private boolean updateOrientationAndCutout() {
boolean changed = false;
- if (newOrientation != NO_VALUE) {
- if (mLastOrientation != newOrientation) {
- changed = true;
- mLastOrientation = newOrientation;
- }
+ int newRotation = RotationUtils.getExactRotation(mContext);
+ if (newRotation != mRotationOrientation) {
+ changed = true;
+ mRotationOrientation = newRotation;
}
if (!Objects.equals(getRootWindowInsets().getDisplayCutout(), mDisplayCutout)) {
@@ -256,6 +256,9 @@ public class PhoneStatusBarView extends PanelBar {
@Override
public void panelScrimMinFractionChanged(float minFraction) {
+ if (isNaN(minFraction)) {
+ throw new IllegalArgumentException("minFraction cannot be NaN");
+ }
if (mMinFraction != minFraction) {
mMinFraction = minFraction;
updateScrimFraction();
@@ -293,17 +296,16 @@ public class PhoneStatusBarView extends PanelBar {
final int waterfallTopInset =
mDisplayCutout == null ? 0 : mDisplayCutout.getWaterfallInsets().top;
ViewGroup.LayoutParams layoutParams = getLayoutParams();
- layoutParams.height =
- getResources().getDimensionPixelSize(R.dimen.status_bar_height) - waterfallTopInset;
+ mStatusBarHeight = getResources().getDimensionPixelSize(R.dimen.status_bar_height);
+ layoutParams.height = mStatusBarHeight - waterfallTopInset;
setLayoutParams(layoutParams);
}
private void updateLayoutForCutout() {
updateStatusBarHeight();
- Pair<Integer, Integer> cornerCutoutMargins =
- StatusBarWindowView.cornerCutoutMargins(mDisplayCutout, getDisplay());
- updateCutoutLocation(cornerCutoutMargins);
- updateSafeInsets(cornerCutoutMargins);
+ updateCutoutLocation(StatusBarWindowView.cornerCutoutMargins(mDisplayCutout, getDisplay()));
+ updateSafeInsets(StatusBarWindowView.statusBarCornerCutoutMargins(mDisplayCutout,
+ getDisplay(), mRotationOrientation, mStatusBarHeight));
}
private void updateCutoutLocation(Pair<Integer, Integer> cornerCutoutMargins) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index d1ff32d9304e..c5901398a54b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -47,6 +47,7 @@ import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dock.DockManager;
+import com.android.systemui.statusbar.BlurUtils;
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.notification.stack.ViewState;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -107,21 +108,21 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
/**
* Default alpha value for most scrims.
*/
- public static final float SCRIM_ALPHA = 0.2f;
+ protected static final float KEYGUARD_SCRIM_ALPHA = 0.2f;
/**
* Scrim opacity when the phone is about to wake-up.
*/
public static final float WAKE_SENSOR_SCRIM_ALPHA = 0.6f;
/**
- * A scrim varies its opacity based on a busyness factor, for example
- * how many notifications are currently visible.
+ * The default scrim under the shade and dialogs.
+ * This should not be lower than 0.54, otherwise we won't pass GAR.
*/
public static final float BUSY_SCRIM_ALPHA = 0.75f;
/**
- * The most common scrim, the one under the keyguard.
+ * Same as above, but when blur is supported.
*/
- protected static final float SCRIM_BEHIND_ALPHA_KEYGUARD = SCRIM_ALPHA;
+ public static final float BLUR_SCRIM_ALPHA = 0.54f;
static final int TAG_KEY_ANIM = R.id.scrim;
private static final int TAG_START_ALPHA = R.id.scrim_alpha_start;
@@ -146,7 +147,8 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
private GradientColors mColors;
private boolean mNeedsDrawableColorUpdate;
- private float mScrimBehindAlphaKeyguard = SCRIM_BEHIND_ALPHA_KEYGUARD;
+ private float mScrimBehindAlphaKeyguard = KEYGUARD_SCRIM_ALPHA;
+ private final float mDefaultScrimAlpha;
// Assuming the shade is expanded during initialization
private float mExpansionFraction = 1f;
@@ -192,9 +194,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
AlarmManager alarmManager, KeyguardStateController keyguardStateController,
DelayedWakeLock.Builder delayedWakeLockBuilder, Handler handler,
KeyguardUpdateMonitor keyguardUpdateMonitor, SysuiColorExtractor sysuiColorExtractor,
- DockManager dockManager) {
+ DockManager dockManager, BlurUtils blurUtils) {
mScrimStateListener = lightBarController::setScrimState;
+ mDefaultScrimAlpha = blurUtils.supportsBlursOnWindows()
+ ? BLUR_SCRIM_ALPHA : BUSY_SCRIM_ALPHA;
mKeyguardStateController = keyguardStateController;
mDarkenWhileDragging = !mKeyguardStateController.canDismissLockScreen();
@@ -236,6 +240,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
states[i].init(mScrimInFront, mScrimBehind, mScrimForBubble, mDozeParameters,
mDockManager);
states[i].setScrimBehindAlphaKeyguard(mScrimBehindAlphaKeyguard);
+ states[i].setDefaultScrimAlpha(mDefaultScrimAlpha);
}
mScrimBehind.setDefaultFocusHighlightEnabled(false);
@@ -483,7 +488,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
// Darken scrim as you pull down the shade when unlocked
float behindFraction = getInterpolatedFraction();
behindFraction = (float) Math.pow(behindFraction, 0.8f);
- mBehindAlpha = behindFraction * BUSY_SCRIM_ALPHA;
+ mBehindAlpha = behindFraction * mDefaultScrimAlpha;
mInFrontAlpha = 0;
} else if (mState == ScrimState.KEYGUARD || mState == ScrimState.PULSING) {
// Either darken of make the scrim transparent when you
@@ -491,7 +496,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
float interpolatedFract = getInterpolatedFraction();
float alphaBehind = mState.getBehindAlpha();
if (mDarkenWhileDragging) {
- mBehindAlpha = MathUtils.lerp(BUSY_SCRIM_ALPHA, alphaBehind,
+ mBehindAlpha = MathUtils.lerp(mDefaultScrimAlpha, alphaBehind,
interpolatedFract);
mInFrontAlpha = mState.getFrontAlpha();
} else {
@@ -967,7 +972,8 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
pw.print(" mTracking=");
pw.println(mTracking);
-
+ pw.print(" mDefaultScrimAlpha=");
+ pw.println(mDefaultScrimAlpha);
pw.print(" mExpansionFraction=");
pw.println(mExpansionFraction);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index c23fd0a3d5d6..ade642c5ea9a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -92,7 +92,7 @@ public enum ScrimState {
BOUNCER {
@Override
public void prepare(ScrimState previousState) {
- mBehindAlpha = ScrimController.BUSY_SCRIM_ALPHA;
+ mBehindAlpha = mDefaultScrimAlpha;
mFrontAlpha = 0f;
mBubbleAlpha = 0f;
}
@@ -106,7 +106,7 @@ public enum ScrimState {
public void prepare(ScrimState previousState) {
mBehindAlpha = 0;
mBubbleAlpha = 0f;
- mFrontAlpha = ScrimController.BUSY_SCRIM_ALPHA;
+ mFrontAlpha = mDefaultScrimAlpha;
}
},
@@ -233,9 +233,9 @@ public enum ScrimState {
mBehindTint = Color.TRANSPARENT;
mBubbleTint = Color.TRANSPARENT;
- mFrontAlpha = ScrimController.TRANSPARENT;
- mBehindAlpha = ScrimController.BUSY_SCRIM_ALPHA;
- mBubbleAlpha = ScrimController.BUSY_SCRIM_ALPHA;
+ mFrontAlpha = 0f;
+ mBehindAlpha = mDefaultScrimAlpha;
+ mBubbleAlpha = mDefaultScrimAlpha;
mAnimationDuration = ScrimController.ANIMATION_DURATION;
mBlankScreen = false;
@@ -255,6 +255,7 @@ public enum ScrimState {
float mBubbleAlpha;
float mScrimBehindAlphaKeyguard;
+ float mDefaultScrimAlpha;
ScrimView mScrimInFront;
ScrimView mScrimBehind;
ScrimView mScrimForBubble;
@@ -341,6 +342,10 @@ public enum ScrimState {
mScrimBehindAlphaKeyguard = scrimBehindAlphaKeyguard;
}
+ public void setDefaultScrimAlpha(float defaultScrimAlpha) {
+ mDefaultScrimAlpha = defaultScrimAlpha;
+ }
+
public void setWallpaperSupportsAmbientMode(boolean wallpaperSupportsAmbientMode) {
mWallpaperSupportsAmbientMode = wallpaperSupportsAmbientMode;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 22bf513272f4..760a6d6a71c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -33,6 +33,8 @@ import android.view.View;
import android.view.WindowInsets;
import android.widget.FrameLayout;
+import com.android.systemui.util.leak.RotationUtils;
+
/**
* Status bar view.
*/
@@ -52,17 +54,13 @@ public class StatusBarWindowView extends FrameLayout {
@Override
public WindowInsets onApplyWindowInsets(WindowInsets windowInsets) {
final Insets insets = windowInsets.getInsetsIgnoringVisibility(systemBars());
- mLeftInset = 0;
- mRightInset = 0;
+ mLeftInset = insets.left;
+ mRightInset = insets.right;
mTopInset = 0;
DisplayCutout displayCutout = getRootWindowInsets().getDisplayCutout();
if (displayCutout != null) {
mTopInset = displayCutout.getWaterfallInsets().top;
- mLeftInset = displayCutout.getSafeInsetLeft();
- mRightInset = displayCutout.getSafeInsetRight();
}
- mLeftInset = Math.max(insets.left, mLeftInset);
- mRightInset = Math.max(insets.right, mRightInset);
applyMargins();
return windowInsets;
}
@@ -100,44 +98,33 @@ public class StatusBarWindowView extends FrameLayout {
return new Pair<>(roundedCornerContentPadding, roundedCornerContentPadding);
}
- // compute the padding needed for corner cutout.
- final int leftMargin = cutout.getSafeInsetLeft();
- final int rightMargin = cutout.getSafeInsetRight();
+ // padding needed for corner cutout.
int leftCornerCutoutPadding = 0;
int rightCornerCutoutPadding = 0;
if (cornerCutoutPadding != null) {
- if (cornerCutoutPadding.first > leftMargin) {
- leftCornerCutoutPadding = cornerCutoutPadding.first - leftMargin;
- }
- if (cornerCutoutPadding.second > rightMargin) {
- rightCornerCutoutPadding = cornerCutoutPadding.second - rightMargin;
- }
- }
-
- // compute the padding needed for rounded corner
- int leftRoundedCornerPadding = 0;
- int rightRoundedCornerPadding = 0;
- if (roundedCornerContentPadding > leftMargin) {
- leftRoundedCornerPadding = roundedCornerContentPadding - leftMargin;
- }
- if (roundedCornerContentPadding > rightMargin) {
- rightRoundedCornerPadding = roundedCornerContentPadding - rightMargin;
+ leftCornerCutoutPadding = cornerCutoutPadding.first;
+ rightCornerCutoutPadding = cornerCutoutPadding.second;
}
return new Pair<>(
- Math.max(leftCornerCutoutPadding, leftRoundedCornerPadding),
- Math.max(rightCornerCutoutPadding, rightRoundedCornerPadding));
+ Math.max(leftCornerCutoutPadding, roundedCornerContentPadding),
+ Math.max(rightCornerCutoutPadding, roundedCornerContentPadding));
}
+
/**
- * Compute the corner cutout margins
- *
- * @param cutout
- * @param display
- * @return
+ * Compute the corner cutout margins in portrait mode
*/
public static Pair<Integer, Integer> cornerCutoutMargins(DisplayCutout cutout,
Display display) {
+ return statusBarCornerCutoutMargins(cutout, display, RotationUtils.ROTATION_NONE, 0);
+ }
+
+ /**
+ * Compute the corner cutout margins in the given orientation (exactRotation)
+ */
+ public static Pair<Integer, Integer> statusBarCornerCutoutMargins(DisplayCutout cutout,
+ Display display, int exactRotation, int statusBarHeight) {
if (cutout == null) {
return null;
}
@@ -145,14 +132,33 @@ public class StatusBarWindowView extends FrameLayout {
display.getRealSize(size);
Rect bounds = new Rect();
- boundsFromDirection(cutout, Gravity.TOP, bounds);
+ switch (exactRotation) {
+ case RotationUtils.ROTATION_LANDSCAPE:
+ boundsFromDirection(cutout, Gravity.LEFT, bounds);
+ break;
+ case RotationUtils.ROTATION_SEASCAPE:
+ boundsFromDirection(cutout, Gravity.RIGHT, bounds);
+ break;
+ case RotationUtils.ROTATION_NONE:
+ boundsFromDirection(cutout, Gravity.TOP, bounds);
+ break;
+ case RotationUtils.ROTATION_UPSIDE_DOWN:
+ // we assume the cutout is always on top in portrait mode
+ return null;
+ }
+
+ if (statusBarHeight >= 0 && bounds.top > statusBarHeight) {
+ return null;
+ }
if (bounds.left <= 0) {
return new Pair<>(bounds.right, 0);
}
+
if (bounds.right >= size.x) {
return new Pair<>(0, size.x - bounds.left);
}
+
return null;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index 24b96855377f..a81189eaeaf8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -53,6 +53,16 @@ public interface BatteryController extends DemoMode, Dumpable,
boolean isAodPowerSave();
/**
+ * Returns {@code true} if reverse is supported.
+ */
+ default boolean isReverseSupported() { return false; }
+
+ /**
+ * Returns {@code true} if reverse is on.
+ */
+ default boolean isReverseOn() { return false; }
+
+ /**
* Set reverse state.
* @param isReverse true if turn on reverse, false otherwise
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index 35954d88391a..496bf68e67a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -99,7 +99,6 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
- filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING);
filter.addAction(ACTION_LEVEL_TEST);
mBroadcastDispatcher.registerReceiver(this, filter);
}
@@ -155,8 +154,6 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
fireBatteryLevelChanged();
} else if (action.equals(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)) {
updatePowerSave();
- } else if (action.equals(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING)) {
- setPowerSave(intent.getBooleanExtra(PowerManager.EXTRA_POWER_SAVE_MODE, false));
} else if (action.equals(ACTION_LEVEL_TEST)) {
mTestmode = true;
mMainHandler.post(new Runnable() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
index 7d532a88caac..38c165b661b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -65,7 +65,6 @@ public class HotspotControllerImpl implements HotspotController, WifiManager.Sof
new TetheringManager.TetheringEventCallback() {
@Override
public void onTetheringSupported(boolean supported) {
- super.onTetheringSupported(supported);
if (mIsTetheringSupported != supported) {
mIsTetheringSupported = supported;
fireHotspotAvailabilityChanged();
@@ -75,7 +74,6 @@ public class HotspotControllerImpl implements HotspotController, WifiManager.Sof
@Override
public void onTetherableInterfaceRegexpsChanged(
TetheringManager.TetheringInterfaceRegexps reg) {
- super.onTetherableInterfaceRegexpsChanged(reg);
final boolean newValue = reg.getTetherableWifiRegexs().size() != 0;
if (mHasTetherableWifiRegexs != newValue) {
mHasTetherableWifiRegexs = newValue;
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
index 2f1b160ffd4b..54118390325c 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
@@ -49,6 +49,8 @@ import javax.inject.Singleton;
public class DisplayImeController implements DisplayController.OnDisplaysChangedListener {
private static final String TAG = "DisplayImeController";
+ private static final boolean DEBUG = false;
+
public static final int ANIMATION_DURATION_SHOW_MS = 275;
public static final int ANIMATION_DURATION_HIDE_MS = 340;
public static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
@@ -209,6 +211,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
if ((types & WindowInsets.Type.ime()) == 0) {
return;
}
+ if (DEBUG) Slog.d(TAG, "Got showInsets for ime");
startAnimation(true /* show */);
}
@@ -217,6 +220,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
if ((types & WindowInsets.Type.ime()) == 0) {
return;
}
+ if (DEBUG) Slog.d(TAG, "Got hideInsets for ime");
startAnimation(false /* show */);
}
@@ -241,6 +245,11 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
return;
}
mHandler.post(() -> {
+ if (DEBUG) {
+ Slog.d(TAG, "Run startAnim show:" + show + " was:"
+ + (mAnimationDirection == DIRECTION_SHOW ? "SHOW"
+ : (mAnimationDirection == DIRECTION_HIDE ? "HIDE" : "NONE")));
+ }
if ((mAnimationDirection == DIRECTION_SHOW && show)
|| (mAnimationDirection == DIRECTION_HIDE && !show)) {
return;
@@ -289,6 +298,11 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
public void onAnimationStart(Animator animation) {
SurfaceControl.Transaction t = mTransactionPool.acquire();
t.setPosition(mImeSourceControl.getLeash(), x, startY);
+ if (DEBUG) {
+ Slog.d(TAG, "onAnimationStart d:" + mDisplayId + " top:"
+ + imeTop(imeSource, hiddenY) + "->" + imeTop(imeSource, shownY)
+ + " showing:" + (mAnimationDirection == DIRECTION_SHOW));
+ }
dispatchStartPositioning(mDisplayId, imeTop(imeSource, hiddenY),
imeTop(imeSource, shownY), mAnimationDirection == DIRECTION_SHOW,
t);
@@ -304,6 +318,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
@Override
public void onAnimationEnd(Animator animation) {
+ if (DEBUG) Slog.d(TAG, "onAnimationEnd " + mCancelled);
SurfaceControl.Transaction t = mTransactionPool.acquire();
if (!mCancelled) {
t.setPosition(mImeSourceControl.getLeash(), x, endY);
diff --git a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java b/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java
index ccb86999674c..381ccdb50386 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java
@@ -196,7 +196,7 @@ public class SystemWindows {
final Display display = mDisplayController.getDisplay(mDisplayId);
SurfaceControlViewHost viewRoot = new SurfaceControlViewHost(mContext, display, wwm);
attrs.flags |= FLAG_HARDWARE_ACCELERATED;
- viewRoot.addView(view, attrs);
+ viewRoot.setView(view, attrs);
mViewRoots.put(view, viewRoot);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 1b34b3d85c09..b6537bf8c615 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -23,6 +23,8 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CO
import static com.android.systemui.ScreenDecorations.rectsToRegion;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@@ -42,6 +44,7 @@ import static org.mockito.Mockito.when;
import android.content.res.Configuration;
import android.graphics.Insets;
import android.graphics.Rect;
+import android.graphics.drawable.VectorDrawable;
import android.hardware.display.DisplayManager;
import android.os.Handler;
import android.testing.AndroidTestingRunner;
@@ -142,6 +145,8 @@ public class ScreenDecorationsTest extends SysuiTestCase {
com.android.internal.R.dimen.rounded_corner_radius_bottom, 0);
mContext.getOrCreateTestableResources()
.addOverride(dimen.rounded_corner_content_padding, 0);
+ mContext.getOrCreateTestableResources()
+ .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
// no cutout
doReturn(null).when(mScreenDecorations).getCutout();
@@ -161,12 +166,70 @@ public class ScreenDecorationsTest extends SysuiTestCase {
com.android.internal.R.dimen.rounded_corner_radius, 20);
mContext.getOrCreateTestableResources()
.addOverride(dimen.rounded_corner_content_padding, 20);
+ mContext.getOrCreateTestableResources()
+ .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
+
+ // no cutout
+ doReturn(null).when(mScreenDecorations).getCutout();
+
+ mScreenDecorations.start();
+
+ // Top and bottom windows are created for rounded corners.
+ verify(mWindowManager, times(1))
+ .addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]), any());
+ verify(mWindowManager, times(1))
+ .addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]), any());
+
+ // Left and right window should be null.
+ assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]);
+ assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]);
+
+ // One tunable.
+ verify(mTunerService, times(1)).addTunable(any(), any());
+ }
+
+ @Test
+ public void testRoundingRadius_NoCutout() {
+ final int testRadius = 1;
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius, testRadius);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius_top, testRadius);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius_bottom, testRadius);
+ mContext.getOrCreateTestableResources()
+ .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
// no cutout
doReturn(null).when(mScreenDecorations).getCutout();
mScreenDecorations.start();
+ // Size of corner view should same as rounded_corner_radius{_top|_bottom}
+ assertThat(mScreenDecorations.mRoundedDefault).isEqualTo(testRadius);
+ assertThat(mScreenDecorations.mRoundedDefaultTop).isEqualTo(testRadius);
+ assertThat(mScreenDecorations.mRoundedDefaultBottom).isEqualTo(testRadius);
+ }
+
+ @Test
+ public void testRoundingMultipleRadius_NoCutout() {
+ final VectorDrawable d = (VectorDrawable) mContext.getDrawable(R.drawable.rounded);
+ final int multipleRadiusSize = Math.max(d.getIntrinsicWidth(), d.getIntrinsicHeight());
+
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius, 9999);
+ mContext.getOrCreateTestableResources()
+ .addOverride(dimen.rounded_corner_content_padding, 9999);
+ mContext.getOrCreateTestableResources()
+ .addOverride(R.bool.config_roundedCornerMultipleRadius, true);
+ // no cutout
+ doReturn(null).when(mScreenDecorations).getCutout();
+
+ mScreenDecorations.start();
// Top and bottom windows are created for rounded corners.
verify(mWindowManager, times(1))
.addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]), any());
@@ -179,6 +242,11 @@ public class ScreenDecorationsTest extends SysuiTestCase {
// One tunable.
verify(mTunerService, times(1)).addTunable(any(), any());
+
+ // Size of corner view should exactly match max(width, height) of R.drawable.rounded
+ assertThat(mScreenDecorations.mRoundedDefault).isEqualTo(multipleRadiusSize);
+ assertThat(mScreenDecorations.mRoundedDefaultTop).isEqualTo(multipleRadiusSize);
+ assertThat(mScreenDecorations.mRoundedDefaultBottom).isEqualTo(multipleRadiusSize);
}
@Test
@@ -193,6 +261,8 @@ public class ScreenDecorationsTest extends SysuiTestCase {
com.android.internal.R.dimen.rounded_corner_radius_bottom, 0);
mContext.getOrCreateTestableResources()
.addOverride(dimen.rounded_corner_content_padding, 0);
+ mContext.getOrCreateTestableResources()
+ .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
// top cutout
doReturn(new DisplayCutout(
@@ -227,6 +297,8 @@ public class ScreenDecorationsTest extends SysuiTestCase {
com.android.internal.R.dimen.rounded_corner_radius_bottom, 0);
mContext.getOrCreateTestableResources()
.addOverride(dimen.rounded_corner_content_padding, 0);
+ mContext.getOrCreateTestableResources()
+ .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
// left cutout
doReturn(new DisplayCutout(
@@ -257,6 +329,8 @@ public class ScreenDecorationsTest extends SysuiTestCase {
com.android.internal.R.dimen.rounded_corner_radius, 20);
mContext.getOrCreateTestableResources()
.addOverride(dimen.rounded_corner_content_padding, 20);
+ mContext.getOrCreateTestableResources()
+ .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
// top cutout
doReturn(new DisplayCutout(
@@ -288,6 +362,8 @@ public class ScreenDecorationsTest extends SysuiTestCase {
com.android.internal.R.dimen.rounded_corner_radius, 20);
mContext.getOrCreateTestableResources()
.addOverride(dimen.rounded_corner_content_padding, 20);
+ mContext.getOrCreateTestableResources()
+ .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
// left cutout
doReturn(new DisplayCutout(
@@ -319,6 +395,8 @@ public class ScreenDecorationsTest extends SysuiTestCase {
com.android.internal.R.dimen.rounded_corner_radius, 20);
mContext.getOrCreateTestableResources()
.addOverride(dimen.rounded_corner_content_padding, 20);
+ mContext.getOrCreateTestableResources()
+ .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
// top and left cutout
doReturn(new DisplayCutout(
@@ -355,6 +433,8 @@ public class ScreenDecorationsTest extends SysuiTestCase {
com.android.internal.R.dimen.rounded_corner_radius_bottom, 0);
mContext.getOrCreateTestableResources()
.addOverride(dimen.rounded_corner_content_padding, 0);
+ mContext.getOrCreateTestableResources()
+ .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
// Set to short edge cutout(top).
doReturn(new DisplayCutout(
@@ -402,6 +482,8 @@ public class ScreenDecorationsTest extends SysuiTestCase {
com.android.internal.R.dimen.rounded_corner_radius_bottom, 0);
mContext.getOrCreateTestableResources()
.addOverride(dimen.rounded_corner_content_padding, 0);
+ mContext.getOrCreateTestableResources()
+ .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
// top cutout
doReturn(new DisplayCutout(
@@ -440,6 +522,8 @@ public class ScreenDecorationsTest extends SysuiTestCase {
com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
mContext.getOrCreateTestableResources().addOverride(
com.android.internal.R.dimen.rounded_corner_radius, 20);
+ mContext.getOrCreateTestableResources()
+ .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
mScreenDecorations.start();
assertEquals(mScreenDecorations.mRoundedDefault, 20);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index a36f2c754243..bb2eea9dd134 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -31,6 +31,7 @@ import android.util.Log;
import androidx.test.InstrumentationRegistry;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.FalsingManager;
@@ -80,6 +81,9 @@ public abstract class SysuiTestCase {
// None of them actually need it.
mDependency.injectTestDependency(FalsingManager.class, new FalsingManagerFake());
mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
+
+ // TODO: b/151614195 investigate root cause of needing this mock dependency
+ mDependency.injectMockDependency(LocalBluetoothManager.class);
}
@After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
index f40fc94763ab..866dfdc9c7e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
@@ -268,13 +268,18 @@ public class BubbleDataTest extends SysuiTestCase {
sendUpdatedEntryAtTime(mEntryB2, 5000);
mBubbleData.setListener(mListener);
- // Test
sendUpdatedEntryAtTime(mEntryC1, 6000);
verifyUpdateReceived();
-
- // Verify
assertBubbleRemoved(mBubbleA1, BubbleController.DISMISS_AGED);
- assertThat(mBubbleData.getOverflowBubbles()).isEqualTo(ImmutableList.of(mBubbleA1));
+ assertOverflowChangedTo(ImmutableList.of(mBubbleA1));
+
+ Bubble bubbleA1 = mBubbleData.getOrCreateBubble(mEntryA1);
+ bubbleA1.markUpdatedAt(7000L);
+ mBubbleData.notificationEntryUpdated(bubbleA1, false /* suppressFlyout*/,
+ true /* showInShade */);
+ verifyUpdateReceived();
+ assertBubbleRemoved(mBubbleA2, BubbleController.DISMISS_AGED);
+ assertOverflowChangedTo(ImmutableList.of(mBubbleA2));
}
/**
@@ -931,6 +936,11 @@ public class BubbleDataTest extends SysuiTestCase {
assertThat(update.expanded).named("expanded").isEqualTo(expected);
}
+ private void assertOverflowChangedTo(ImmutableList<Bubble> bubbles) {
+ BubbleData.Update update = mUpdateCaptor.getValue();
+ assertThat(update.overflowBubbles).isEqualTo(bubbles);
+ }
+
private NotificationEntry createBubbleEntry(int userId, String notifKey, String packageName) {
return createBubbleEntry(userId, notifKey, packageName, 1000);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java
index e3187cb9a6c5..b1ac022dbe9c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java
@@ -43,6 +43,7 @@ import org.mockito.Mock;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.function.IntSupplier;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -59,7 +60,13 @@ public class StackAnimationControllerTest extends PhysicsAnimationLayoutTestCase
@Before
public void setUp() throws Exception {
super.setUp();
- mStackController = spy(new TestableStackController(mFloatingContentCoordinator));
+ mStackController = spy(new TestableStackController(
+ mFloatingContentCoordinator, new IntSupplier() {
+ @Override
+ public int getAsInt() {
+ return mLayout.getChildCount();
+ }
+ }));
mLayout.setActiveController(mStackController);
addOneMoreThanBubbleLimitBubbles();
mStackOffset = mLayout.getResources().getDimensionPixelSize(R.dimen.bubble_stack_offset);
@@ -295,8 +302,9 @@ public class StackAnimationControllerTest extends PhysicsAnimationLayoutTestCase
*/
private class TestableStackController extends StackAnimationController {
TestableStackController(
- FloatingContentCoordinator floatingContentCoordinator) {
- super(floatingContentCoordinator);
+ FloatingContentCoordinator floatingContentCoordinator,
+ IntSupplier bubbleCountSupplier) {
+ super(floatingContentCoordinator, bubbleCountSupplier);
}
@Override
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 8320b058208c..6871aad0ea98 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -42,6 +42,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.statusbar.phone.NavigationModeController;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.util.DeviceConfigProxy;
@@ -71,6 +72,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
private @Mock DumpManager mDumpManager;
private @Mock PowerManager mPowerManager;
private @Mock TrustManager mTrustManager;
+ private @Mock NavigationModeController mNavigationModeController;
private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake();
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
@@ -88,7 +90,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
mContext, mFalsingManager, mLockPatternUtils, mBroadcastDispatcher,
mNotificationShadeWindowController, () -> mStatusBarKeyguardViewManager,
mDismissCallbackRegistry, mUpdateMonitor, mDumpManager, mUiBgExecutor,
- mPowerManager, mTrustManager, mDeviceConfig);
+ mPowerManager, mTrustManager, mDeviceConfig, mNavigationModeController);
mViewMediator.start();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java
index a2ab78483b68..b863f143056f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java
@@ -58,7 +58,8 @@ public class PipAnimationControllerTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
- mPipAnimationController = new PipAnimationController(mContext);
+ mPipAnimationController = new PipAnimationController(
+ mContext, new PipSurfaceTransactionHelper(mContext));
MockitoAnnotations.initMocks(this);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
index b12db2b44e3d..0bf0f04d2d43 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
@@ -24,6 +24,7 @@ import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableResources;
+import android.util.Size;
import android.view.DisplayInfo;
import android.view.Gravity;
@@ -51,6 +52,7 @@ public class PipBoundsHandlerTest extends SysuiTestCase {
private static final float MIN_ASPECT_RATIO = 0.5f;
private static final float MAX_ASPECT_RATIO = 2f;
private static final Rect EMPTY_CURRENT_BOUNDS = null;
+ private static final Size EMPTY_MINIMAL_SIZE = null;
private PipBoundsHandler mPipBoundsHandler;
private DisplayInfo mDefaultDisplayInfo;
@@ -119,7 +121,7 @@ public class PipBoundsHandlerTest extends SysuiTestCase {
};
for (float aspectRatio : aspectRatios) {
final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
- aspectRatio, EMPTY_CURRENT_BOUNDS);
+ aspectRatio, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
final float actualAspectRatio =
destinationBounds.width() / (destinationBounds.height() * 1f);
assertEquals("Destination bounds matches the given aspect ratio",
@@ -135,7 +137,7 @@ public class PipBoundsHandlerTest extends SysuiTestCase {
};
for (float aspectRatio : invalidAspectRatios) {
final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
- aspectRatio, EMPTY_CURRENT_BOUNDS);
+ aspectRatio, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
final float actualAspectRatio =
destinationBounds.width() / (destinationBounds.height() * 1f);
assertEquals("Destination bounds fallbacks to default aspect ratio",
@@ -151,7 +153,7 @@ public class PipBoundsHandlerTest extends SysuiTestCase {
currentBounds.right = (int) (currentBounds.height() * aspectRatio) + currentBounds.left;
final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
- aspectRatio, currentBounds);
+ aspectRatio, currentBounds, EMPTY_MINIMAL_SIZE);
final float actualAspectRatio =
destinationBounds.width() / (destinationBounds.height() * 1f);
@@ -160,14 +162,58 @@ public class PipBoundsHandlerTest extends SysuiTestCase {
}
@Test
+ public void getDestinationBounds_withMinSize_returnMinBounds() {
+ final float[] aspectRatios = new float[] {
+ (MIN_ASPECT_RATIO + DEFAULT_ASPECT_RATIO) / 2,
+ DEFAULT_ASPECT_RATIO,
+ (MAX_ASPECT_RATIO + DEFAULT_ASPECT_RATIO) / 2
+ };
+ final Size[] minimalSizes = new Size[] {
+ new Size((int) (100 * aspectRatios[0]), 100),
+ new Size((int) (100 * aspectRatios[1]), 100),
+ new Size((int) (100 * aspectRatios[2]), 100)
+ };
+ for (int i = 0; i < aspectRatios.length; i++) {
+ final float aspectRatio = aspectRatios[i];
+ final Size minimalSize = minimalSizes[i];
+ final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
+ aspectRatio, EMPTY_CURRENT_BOUNDS, minimalSize);
+ assertTrue("Destination bounds is no smaller than minimal requirement",
+ (destinationBounds.width() == minimalSize.getWidth()
+ && destinationBounds.height() >= minimalSize.getHeight())
+ || (destinationBounds.height() == minimalSize.getHeight()
+ && destinationBounds.width() >= minimalSize.getWidth()));
+ final float actualAspectRatio =
+ destinationBounds.width() / (destinationBounds.height() * 1f);
+ assertEquals("Destination bounds matches the given aspect ratio",
+ aspectRatio, actualAspectRatio, ASPECT_RATIO_ERROR_MARGIN);
+ }
+ }
+
+ @Test
+ public void getDestinationBounds_withCurrentBounds_ignoreMinBounds() {
+ final float aspectRatio = (DEFAULT_ASPECT_RATIO + MAX_ASPECT_RATIO) / 2;
+ final Rect currentBounds = new Rect(0, 0, 0, 100);
+ currentBounds.right = (int) (currentBounds.height() * aspectRatio) + currentBounds.left;
+ final Size minSize = new Size(currentBounds.width() / 2, currentBounds.height() / 2);
+
+ final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
+ aspectRatio, currentBounds, minSize);
+
+ assertTrue("Destination bounds ignores minimal size",
+ destinationBounds.width() > minSize.getWidth()
+ && destinationBounds.height() > minSize.getHeight());
+ }
+
+ @Test
public void setShelfHeight_offsetBounds() {
final int shelfHeight = 100;
final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(
- DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
mPipBoundsHandler.setShelfHeight(true, shelfHeight);
final Rect newPosition = mPipBoundsHandler.getDestinationBounds(
- DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
oldPosition.offset(0, -shelfHeight);
assertBoundsWithMargin("offsetBounds by shelf", oldPosition, newPosition);
@@ -177,11 +223,11 @@ public class PipBoundsHandlerTest extends SysuiTestCase {
public void onImeVisibilityChanged_offsetBounds() {
final int imeHeight = 100;
final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(
- DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
mPipBoundsHandler.onImeVisibilityChanged(true, imeHeight);
final Rect newPosition = mPipBoundsHandler.getDestinationBounds(
- DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
oldPosition.offset(0, -imeHeight);
assertBoundsWithMargin("offsetBounds by IME", oldPosition, newPosition);
@@ -191,13 +237,13 @@ public class PipBoundsHandlerTest extends SysuiTestCase {
public void onSaveReentryBounds_restoreLastPosition() {
final ComponentName componentName = new ComponentName(mContext, "component1");
final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(
- DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
oldPosition.offset(0, -100);
mPipBoundsHandler.onSaveReentryBounds(componentName, oldPosition);
final Rect newPosition = mPipBoundsHandler.getDestinationBounds(
- DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
assertBoundsWithMargin("restoreLastPosition", oldPosition, newPosition);
}
@@ -206,14 +252,14 @@ public class PipBoundsHandlerTest extends SysuiTestCase {
public void onResetReentryBounds_useDefaultBounds() {
final ComponentName componentName = new ComponentName(mContext, "component1");
final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(
- DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
final Rect newBounds = new Rect(defaultBounds);
newBounds.offset(0, -100);
mPipBoundsHandler.onSaveReentryBounds(componentName, newBounds);
mPipBoundsHandler.onResetReentryBounds(componentName);
final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(
- DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
assertBoundsWithMargin("useDefaultBounds", defaultBounds, actualBounds);
}
@@ -222,14 +268,14 @@ public class PipBoundsHandlerTest extends SysuiTestCase {
public void onResetReentryBounds_componentMismatch_restoreLastPosition() {
final ComponentName componentName = new ComponentName(mContext, "component1");
final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(
- DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
final Rect newBounds = new Rect(defaultBounds);
newBounds.offset(0, -100);
mPipBoundsHandler.onSaveReentryBounds(componentName, newBounds);
mPipBoundsHandler.onResetReentryBounds(new ComponentName(mContext, "component2"));
final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(
- DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
assertBoundsWithMargin("restoreLastPosition", newBounds, actualBounds);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java
new file mode 100644
index 000000000000..4d7e6aec8aa9
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.pip.phone;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.IActivityManager;
+import android.app.IActivityTaskManager;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.util.Size;
+import android.view.DisplayInfo;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.pip.PipBoundsHandler;
+import com.android.systemui.pip.PipSnapAlgorithm;
+import com.android.systemui.pip.PipTaskOrganizer;
+import com.android.systemui.shared.system.InputConsumerController;
+import com.android.systemui.util.DeviceConfigProxy;
+import com.android.systemui.util.FloatingContentCoordinator;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests against {@link PipTouchHandler}, including but not limited to:
+ * - Update movement bounds based on new bounds
+ * - Update movement bounds based on IME/shelf
+ * - Update movement bounds to PipResizeHandler
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class PipTouchHandlerTest extends SysuiTestCase {
+ private static final int ROUNDING_ERROR_MARGIN = 10;
+ private static final float DEFAULT_ASPECT_RATIO = 1f;
+ private static final Rect EMPTY_CURRENT_BOUNDS = null;
+
+ private PipTouchHandler mPipTouchHandler;
+ private DisplayInfo mDefaultDisplayInfo;
+
+ @Mock
+ private IActivityManager mActivityManager;
+
+ @Mock
+ private IActivityTaskManager mIActivityTaskManager;
+
+ @Mock
+ private PipMenuActivityController mPipMenuActivityController;
+
+ @Mock
+ private InputConsumerController mInputConsumerController;
+
+ @Mock
+ private PipBoundsHandler mPipBoundsHandler;
+
+ @Mock
+ private PipTaskOrganizer mPipTaskOrganizer;
+
+ @Mock
+ private FloatingContentCoordinator mFloatingContentCoordinator;
+
+ @Mock
+ private DeviceConfigProxy mDeviceConfigProxy;
+
+
+ private PipSnapAlgorithm mPipSnapAlgorithm;
+ private PipMotionHelper mMotionHelper;
+ private PipResizeGestureHandler mPipResizeGestureHandler;
+
+ Rect mInsetBounds;
+ Rect mMinBounds;
+ Rect mCurBounds;
+ boolean mFromImeAdjustment;
+ boolean mFromShelfAdjustment;
+ int mDisplayRotation;
+
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mPipSnapAlgorithm = new PipSnapAlgorithm(mContext);
+ mPipTouchHandler = new PipTouchHandler(mContext, mActivityManager, mIActivityTaskManager,
+ mPipMenuActivityController, mInputConsumerController, mPipBoundsHandler,
+ mPipTaskOrganizer, mFloatingContentCoordinator, mDeviceConfigProxy,
+ mPipSnapAlgorithm);
+ mMotionHelper = Mockito.spy(mPipTouchHandler.getMotionHelper());
+ mPipResizeGestureHandler = Mockito.spy(mPipTouchHandler.getPipResizeGestureHandler());
+ mPipTouchHandler.setPipMotionHelper(mMotionHelper);
+ mPipTouchHandler.setPipResizeGestureHandler(mPipResizeGestureHandler);
+
+ // Assume a display of 1000 x 1000
+ // inset of 10
+ mInsetBounds = new Rect(10, 10, 990, 990);
+ // minBounds of 100x100 bottom right corner
+ mMinBounds = new Rect(890, 890, 990, 990);
+ mCurBounds = new Rect();
+ mFromImeAdjustment = false;
+ mFromShelfAdjustment = false;
+ mDisplayRotation = 0;
+ }
+
+ @Test
+ public void updateMovementBounds_minBounds() {
+ Rect expectedMinMovementBounds = new Rect();
+ mPipSnapAlgorithm.getMovementBounds(mMinBounds, mInsetBounds, expectedMinMovementBounds, 0);
+
+ mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mMinBounds, mCurBounds,
+ mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation);
+
+ assertEquals(expectedMinMovementBounds, mPipTouchHandler.mNormalMovementBounds);
+ verify(mPipResizeGestureHandler, times(1))
+ .updateMinSize(mMinBounds.width(), mMinBounds.height());
+ }
+
+ @Test
+ public void updateMovementBounds_maxBounds() {
+ Point displaySize = new Point();
+ mContext.getDisplay().getRealSize(displaySize);
+ Size maxSize = mPipSnapAlgorithm.getSizeForAspectRatio(1,
+ mContext.getResources().getDimensionPixelSize(
+ R.dimen.pip_expanded_shortest_edge_size), displaySize.x, displaySize.y);
+ Rect maxBounds = new Rect(0, 0, maxSize.getWidth(), maxSize.getHeight());
+ Rect expectedMaxMovementBounds = new Rect();
+ mPipSnapAlgorithm.getMovementBounds(maxBounds, mInsetBounds, expectedMaxMovementBounds, 0);
+
+ mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mMinBounds, mCurBounds,
+ mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation);
+
+ assertEquals(expectedMaxMovementBounds, mPipTouchHandler.mExpandedMovementBounds);
+ verify(mPipResizeGestureHandler, times(1))
+ .updateMaxSize(maxBounds.width(), maxBounds.height());
+ }
+
+ @Test
+ public void updateMovementBounds_withImeAdjustment_movesPip() {
+ mFromImeAdjustment = true;
+ mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mMinBounds, mCurBounds,
+ mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation);
+
+ verify(mMotionHelper, times(1)).animateToOffset(any(), anyInt());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
index ac304210d416..dbbbaac66554 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
@@ -85,6 +85,8 @@ public class QSPanelTest extends SysuiTestCase {
@Mock
private NotificationMediaManager mNotificationMediaManager;
@Mock
+ private Executor mForegroundExecutor;
+ @Mock
private Executor mBackgroundExecutor;
@Mock
private LocalBluetoothManager mLocalBluetoothManager;
@@ -97,7 +99,7 @@ public class QSPanelTest extends SysuiTestCase {
mTestableLooper.runWithLooper(() -> {
mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
mQsPanel = new QSPanel(mContext, null, mDumpManager, mBroadcastDispatcher,
- mQSLogger, mNotificationMediaManager, mBackgroundExecutor,
+ mQSLogger, mNotificationMediaManager, mForegroundExecutor, mBackgroundExecutor,
mLocalBluetoothManager);
// Provides a parent with non-zero size for QSPanel
mParentView = new FrameLayout(mContext);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index 73f3ddd29bcf..95c3e5aee870 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -86,7 +86,7 @@ public class QSTileHostTest extends SysuiTestCase {
@Mock
private StatusBarIconController mIconController;
@Mock
- private QSFactoryImpl mDefaultFactory;
+ private QSFactory mDefaultFactory;
@Mock
private PluginManager mPluginManager;
@Mock
@@ -295,7 +295,7 @@ public class QSTileHostTest extends SysuiTestCase {
private static class TestQSTileHost extends QSTileHost {
TestQSTileHost(Context context, StatusBarIconController iconController,
- QSFactoryImpl defaultFactory, Handler mainHandler, Looper bgLooper,
+ QSFactory defaultFactory, Handler mainHandler, Looper bgLooper,
PluginManager pluginManager, TunerService tunerService,
Provider<AutoTileManager> autoTiles, DumpManager dumpManager,
BroadcastDispatcher broadcastDispatcher, StatusBar statusBar, QSLogger qsLogger) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
index 58be50e1e66d..953198c42d66 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
@@ -30,7 +30,7 @@ import android.view.IWindowManager
import androidx.test.runner.AndroidJUnit4
import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.qs.QSTile
-import com.android.systemui.qs.QSTileHost
+import com.android.systemui.qs.QSHost
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import org.junit.Assert.assertEquals
@@ -56,7 +56,7 @@ class CustomTileTest : SysuiTestCase() {
val TILE_SPEC = CustomTile.toSpec(componentName)
}
- @Mock private lateinit var mTileHost: QSTileHost
+ @Mock private lateinit var mTileHost: QSHost
@Mock private lateinit var mTileService: IQSTileService
@Mock private lateinit var mTileServices: TileServices
@Mock private lateinit var mTileServiceManager: TileServiceManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
new file mode 100644
index 000000000000..f061f34072d0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar
+
+import android.app.WallpaperManager
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.Choreographer
+import android.view.View
+import android.view.ViewRootImpl
+import androidx.dynamicanimation.animation.SpringAnimation
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.phone.BiometricUnlockController
+import com.android.systemui.statusbar.phone.NotificationShadeWindowController
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.Mockito.*
+import org.mockito.junit.MockitoJUnit
+
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+@SmallTest
+class NotificationShadeDepthControllerTest : SysuiTestCase() {
+
+ @Mock private lateinit var statusBarStateController: StatusBarStateController
+ @Mock private lateinit var blurUtils: BlurUtils
+ @Mock private lateinit var biometricUnlockController: BiometricUnlockController
+ @Mock private lateinit var keyguardStateController: KeyguardStateController
+ @Mock private lateinit var choreographer: Choreographer
+ @Mock private lateinit var wallpaperManager: WallpaperManager
+ @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
+ @Mock private lateinit var dumpManager: DumpManager
+ @Mock private lateinit var root: View
+ @Mock private lateinit var viewRootImpl: ViewRootImpl
+ @Mock private lateinit var shadeSpring: SpringAnimation
+ @JvmField @Rule val mockitoRule = MockitoJUnit.rule()
+
+ private lateinit var statusBarStateListener: StatusBarStateController.StateListener
+ private var statusBarState = StatusBarState.SHADE
+ private val maxBlur = 150
+ private lateinit var notificationShadeDepthController: NotificationShadeDepthController
+
+ @Before
+ fun setup() {
+ `when`(root.viewRootImpl).thenReturn(viewRootImpl)
+ `when`(statusBarStateController.state).then { statusBarState }
+ `when`(blurUtils.blurRadiusOfRatio(anyFloat())).then { answer ->
+ (answer.arguments[0] as Float * maxBlur).toInt()
+ }
+ notificationShadeDepthController = NotificationShadeDepthController(
+ statusBarStateController, blurUtils, biometricUnlockController,
+ keyguardStateController, choreographer, wallpaperManager,
+ notificationShadeWindowController, dumpManager)
+ notificationShadeDepthController.shadeSpring = shadeSpring
+ notificationShadeDepthController.root = root
+
+ val captor = ArgumentCaptor.forClass(StatusBarStateController.StateListener::class.java)
+ verify(statusBarStateController).addCallback(captor.capture())
+ statusBarStateListener = captor.value
+ }
+
+ @Test
+ fun setupListeners() {
+ verify(dumpManager).registerDumpable(anyString(), safeEq(notificationShadeDepthController))
+ }
+
+ @Test
+ fun onPanelExpansionChanged_apliesBlur_ifShade() {
+ notificationShadeDepthController.onPanelExpansionChanged(1f /* expansion */,
+ false /* tracking */)
+ verify(shadeSpring).animateToFinalPosition(eq(maxBlur.toFloat()))
+ }
+
+ @Test
+ fun onStateChanged_reevalutesBlurs_ifSameRadiusAndNewState() {
+ onPanelExpansionChanged_apliesBlur_ifShade()
+ clearInvocations(shadeSpring)
+
+ statusBarState = StatusBarState.KEYGUARD
+ statusBarStateListener.onStateChanged(statusBarState)
+ verify(shadeSpring).animateToFinalPosition(eq(0f))
+ }
+
+ @Test
+ fun updateGlobalDialogVisibility_schedulesUpdate() {
+ notificationShadeDepthController.updateGlobalDialogVisibility(0.5f, root)
+ verify(choreographer).postFrameCallback(any())
+ }
+
+ private fun <T : Any> safeEq(value: T): T {
+ return eq(value) ?: value
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index cc5f149eabd3..83877f2e72f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -52,6 +52,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.notification.stack.NotificationListItem;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -285,9 +286,15 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
public void notifyGroupChildAdded(ExpandableView row) {}
@Override
+ public void notifyGroupChildAdded(View v) {}
+
+ @Override
public void notifyGroupChildRemoved(ExpandableView row, ViewGroup childrenContainer) {}
@Override
+ public void notifyGroupChildRemoved(View v, ViewGroup childrenContainer) {}
+
+ @Override
public void generateAddAnimation(ExpandableView child, boolean fromMoreCard) {}
@Override
@@ -313,12 +320,22 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
}
@Override
+ public void removeListItem(NotificationListItem li) {
+ removeContainerView(li.getView());
+ }
+
+ @Override
public void addContainerView(View v) {
mLayout.addView(v);
mRows.add(v);
}
@Override
+ public void addListItem(NotificationListItem li) {
+ addContainerView(li.getView());
+ }
+
+ @Override
public void setMaxDisplayedNotifications(int maxNotifications) {
if (mMakeReentrantCallDuringSetMaxDisplayedNotifications) {
mViewHierarchyManager.onDynamicPrivacyChanged();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
index fe8b89f381d6..a58000d09594 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
@@ -55,6 +55,7 @@ public class RankingBuilder {
private boolean mIsVisuallyInterruptive = false;
private boolean mIsConversation = false;
private ShortcutInfo mShortcutInfo = null;
+ private boolean mIsBubble = false;
public RankingBuilder() {
}
@@ -82,6 +83,7 @@ public class RankingBuilder {
mIsVisuallyInterruptive = ranking.visuallyInterruptive();
mIsConversation = ranking.isConversation();
mShortcutInfo = ranking.getShortcutInfo();
+ mIsBubble = ranking.isBubble();
}
public Ranking build() {
@@ -108,7 +110,8 @@ public class RankingBuilder {
mCanBubble,
mIsVisuallyInterruptive,
mIsConversation,
- mShortcutInfo);
+ mShortcutInfo,
+ mIsBubble);
return ranking;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 312bb7f08e72..972357e960ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -143,7 +143,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
IMPORTANCE_DEFAULT,
null, null,
null, null, null, true, sentiment, false, -1, false, null, null, false, false,
- false, null);
+ false, null, false);
return true;
}).when(mRankingMap).getRanking(eq(key), any(Ranking.class));
}
@@ -162,7 +162,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
null, null,
null, null, null, true,
Ranking.USER_SENTIMENT_NEUTRAL, false, -1,
- false, smartActions, null, false, false, false, null);
+ false, smartActions, null, false, false, false, null, false);
return true;
}).when(mRankingMap).getRanking(eq(key), any(Ranking.class));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index b7184be7d567..82de4a3b490c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -34,16 +34,17 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static java.util.Collections.singletonList;
import static java.util.Objects.requireNonNull;
import android.annotation.Nullable;
@@ -83,13 +84,13 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -123,6 +124,8 @@ public class NotifCollectionTest extends SysuiTestCase {
private NotifCollection mCollection;
private BatchableNotificationHandler mNotifHandler;
+ private InOrder mListenerInOrder;
+
private NoManSimulator mNoMan;
@Before
@@ -133,6 +136,8 @@ public class NotifCollectionTest extends SysuiTestCase {
when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(true);
when(mFeatureFlags.isNewNotifPipelineEnabled()).thenReturn(true);
+ mListenerInOrder = inOrder(mCollectionListener);
+
mCollection = new NotifCollection(
mStatusBarService,
mock(DumpManager.class),
@@ -159,10 +164,12 @@ public class NotifCollectionTest extends SysuiTestCase {
.setRank(4747));
// THEN the listener is notified
- verify(mCollectionListener).onEntryInit(mEntryCaptor.capture());
- NotificationEntry entry = mEntryCaptor.getValue();
+ final NotificationEntry entry = mCollectionListener.getEntry(notif1.key);
+
+ mListenerInOrder.verify(mCollectionListener).onEntryInit(entry);
+ mListenerInOrder.verify(mCollectionListener).onEntryAdded(entry);
+ mListenerInOrder.verify(mCollectionListener).onRankingApplied();
- verify(mCollectionListener).onEntryAdded(entry);
assertEquals(notif1.key, entry.getKey());
assertEquals(notif1.sbn, entry.getSbn());
assertEquals(notif1.ranking, entry.getRanking());
@@ -215,12 +222,11 @@ public class NotifCollectionTest extends SysuiTestCase {
assertEquals(entry2.getRanking(), capturedUpdate.getRanking());
// THEN onBuildList is called only once
- verify(mBuildListener).onBuildList(mBuildListCaptor.capture());
- assertEquals(new ArraySet<>(Arrays.asList(
- capturedAdds.get(0),
- capturedAdds.get(1),
- capturedUpdate
- )), new ArraySet<>(mBuildListCaptor.getValue()));
+ verifyBuiltList(
+ List.of(
+ capturedAdds.get(0),
+ capturedAdds.get(1),
+ capturedUpdate));
}
@Test
@@ -234,9 +240,11 @@ public class NotifCollectionTest extends SysuiTestCase {
.setRank(89));
// THEN the listener is notified
- verify(mCollectionListener).onEntryUpdated(mEntryCaptor.capture());
+ final NotificationEntry entry = mCollectionListener.getEntry(notif2.key);
+
+ mListenerInOrder.verify(mCollectionListener).onEntryUpdated(entry);
+ mListenerInOrder.verify(mCollectionListener).onRankingApplied();
- NotificationEntry entry = mEntryCaptor.getValue();
assertEquals(notif2.key, entry.getKey());
assertEquals(notif2.sbn, entry.getSbn());
assertEquals(notif2.ranking, entry.getRanking());
@@ -256,8 +264,10 @@ public class NotifCollectionTest extends SysuiTestCase {
mNoMan.retractNotif(notif.sbn, REASON_APP_CANCEL);
// THEN the listener is notified
- verify(mCollectionListener).onEntryRemoved(entry, REASON_APP_CANCEL);
- verify(mCollectionListener).onEntryCleanUp(entry);
+ mListenerInOrder.verify(mCollectionListener).onEntryRemoved(entry, REASON_APP_CANCEL);
+ mListenerInOrder.verify(mCollectionListener).onEntryCleanUp(entry);
+ mListenerInOrder.verify(mCollectionListener).onRankingApplied();
+
assertEquals(notif.sbn, entry.getSbn());
assertEquals(notif.ranking, entry.getRanking());
}
@@ -415,8 +425,8 @@ public class NotifCollectionTest extends SysuiTestCase {
// THEN the dismissed entry still appears in the notification set
assertEquals(
- new ArraySet<>(Collections.singletonList(entry1)),
- new ArraySet<>(mCollection.getActiveNotifs()));
+ new ArraySet<>(singletonList(entry1)),
+ new ArraySet<>(mCollection.getAllNotifs()));
}
@Test
@@ -444,7 +454,7 @@ public class NotifCollectionTest extends SysuiTestCase {
mNoMan.retractNotif(notif2.sbn, REASON_CANCEL);
assertEquals(
new ArraySet<>(List.of(entry1, entry2, entry3)),
- new ArraySet<>(mCollection.getActiveNotifs()));
+ new ArraySet<>(mCollection.getAllNotifs()));
// WHEN the summary is dismissed by the user
mCollection.dismissNotification(entry1, defaultStats(entry1));
@@ -452,7 +462,7 @@ public class NotifCollectionTest extends SysuiTestCase {
// THEN the summary is removed, but both children stick around
assertEquals(
new ArraySet<>(List.of(entry2, entry3)),
- new ArraySet<>(mCollection.getActiveNotifs()));
+ new ArraySet<>(mCollection.getAllNotifs()));
assertEquals(NOT_DISMISSED, entry2.getDismissState());
assertEquals(NOT_DISMISSED, entry3.getDismissState());
}
@@ -561,7 +571,7 @@ public class NotifCollectionTest extends SysuiTestCase {
}
@Test
- public void testEndDismissInterceptionUpdatesDismissInterceptors() throws RemoteException {
+ public void testEndDismissInterceptionUpdatesDismissInterceptors() {
// GIVEN a collection with notifications with multiple dismiss interceptors
mInterceptor1.shouldInterceptDismissal = true;
mInterceptor2.shouldInterceptDismissal = true;
@@ -592,7 +602,7 @@ public class NotifCollectionTest extends SysuiTestCase {
@Test(expected = IllegalStateException.class)
- public void testEndingDismissalOfNonInterceptedThrows() throws RemoteException {
+ public void testEndingDismissalOfNonInterceptedThrows() {
// GIVEN a collection with notifications with a dismiss interceptor that hasn't been called
mInterceptor1.shouldInterceptDismissal = false;
mCollection.addNotificationDismissInterceptor(mInterceptor1);
@@ -820,7 +830,7 @@ public class NotifCollectionTest extends SysuiTestCase {
verify(mExtender3).shouldExtendLifetime(entry2, REASON_CLICK);
// THEN the entry is not removed
- assertTrue(mCollection.getActiveNotifs().contains(entry2));
+ assertTrue(mCollection.getAllNotifs().contains(entry2));
// THEN the entry properly records all extenders that returned true
assertEquals(Arrays.asList(mExtender1, mExtender2), entry2.mLifetimeExtenders);
@@ -841,7 +851,7 @@ public class NotifCollectionTest extends SysuiTestCase {
// GIVEN a notification gets lifetime-extended by one of them
mNoMan.retractNotif(notif2.sbn, REASON_APP_CANCEL);
- assertTrue(mCollection.getActiveNotifs().contains(entry2));
+ assertTrue(mCollection.getAllNotifs().contains(entry2));
clearInvocations(mExtender1, mExtender2, mExtender3);
// WHEN the last active extender expires (but new ones become active)
@@ -856,7 +866,7 @@ public class NotifCollectionTest extends SysuiTestCase {
verify(mExtender3).shouldExtendLifetime(entry2, REASON_APP_CANCEL);
// THEN the entry is not removed
- assertTrue(mCollection.getActiveNotifs().contains(entry2));
+ assertTrue(mCollection.getAllNotifs().contains(entry2));
// THEN the entry properly records all extenders that returned true
assertEquals(Arrays.asList(mExtender1, mExtender3), entry2.mLifetimeExtenders);
@@ -878,7 +888,7 @@ public class NotifCollectionTest extends SysuiTestCase {
// GIVEN a notification gets lifetime-extended by a couple of them
mNoMan.retractNotif(notif2.sbn, REASON_APP_CANCEL);
- assertTrue(mCollection.getActiveNotifs().contains(entry2));
+ assertTrue(mCollection.getAllNotifs().contains(entry2));
clearInvocations(mExtender1, mExtender2, mExtender3);
// WHEN one (but not all) of the extenders expires
@@ -886,7 +896,7 @@ public class NotifCollectionTest extends SysuiTestCase {
mExtender2.callback.onEndLifetimeExtension(mExtender2, entry2);
// THEN the entry is not removed
- assertTrue(mCollection.getActiveNotifs().contains(entry2));
+ assertTrue(mCollection.getAllNotifs().contains(entry2));
// THEN we don't re-query the extenders
verify(mExtender1, never()).shouldExtendLifetime(entry2, REASON_APP_CANCEL);
@@ -894,7 +904,7 @@ public class NotifCollectionTest extends SysuiTestCase {
verify(mExtender3, never()).shouldExtendLifetime(entry2, REASON_APP_CANCEL);
// THEN the entry properly records all extenders that returned true
- assertEquals(Arrays.asList(mExtender1), entry2.mLifetimeExtenders);
+ assertEquals(singletonList(mExtender1), entry2.mLifetimeExtenders);
}
@Test
@@ -913,7 +923,7 @@ public class NotifCollectionTest extends SysuiTestCase {
// GIVEN a notification gets lifetime-extended by a couple of them
mNoMan.retractNotif(notif2.sbn, REASON_UNKNOWN);
- assertTrue(mCollection.getActiveNotifs().contains(entry2));
+ assertTrue(mCollection.getAllNotifs().contains(entry2));
clearInvocations(mExtender1, mExtender2, mExtender3);
// WHEN all of the active extenders expire
@@ -923,7 +933,7 @@ public class NotifCollectionTest extends SysuiTestCase {
mExtender1.callback.onEndLifetimeExtension(mExtender1, entry2);
// THEN the entry removed
- assertFalse(mCollection.getActiveNotifs().contains(entry2));
+ assertFalse(mCollection.getAllNotifs().contains(entry2));
verify(mCollectionListener).onEntryRemoved(entry2, REASON_UNKNOWN);
}
@@ -943,7 +953,7 @@ public class NotifCollectionTest extends SysuiTestCase {
// GIVEN a notification gets lifetime-extended by a couple of them
mNoMan.retractNotif(notif2.sbn, REASON_UNKNOWN);
- assertTrue(mCollection.getActiveNotifs().contains(entry2));
+ assertTrue(mCollection.getAllNotifs().contains(entry2));
clearInvocations(mExtender1, mExtender2, mExtender3);
// WHEN the notification is reposted
@@ -954,7 +964,7 @@ public class NotifCollectionTest extends SysuiTestCase {
verify(mExtender2).cancelLifetimeExtension(entry2);
// THEN the notification is still present
- assertTrue(mCollection.getActiveNotifs().contains(entry2));
+ assertTrue(mCollection.getAllNotifs().contains(entry2));
}
@Test(expected = IllegalStateException.class)
@@ -973,7 +983,7 @@ public class NotifCollectionTest extends SysuiTestCase {
// GIVEN a notification gets lifetime-extended by a couple of them
mNoMan.retractNotif(notif2.sbn, REASON_UNKNOWN);
- assertTrue(mCollection.getActiveNotifs().contains(entry2));
+ assertTrue(mCollection.getAllNotifs().contains(entry2));
clearInvocations(mExtender1, mExtender2, mExtender3);
// WHEN a lifetime extender makes a reentrant call during cancelLifetimeExtension()
@@ -1002,7 +1012,7 @@ public class NotifCollectionTest extends SysuiTestCase {
// GIVEN a notification gets lifetime-extended by a couple of them
mNoMan.retractNotif(notif2.sbn, REASON_UNKNOWN);
- assertTrue(mCollection.getActiveNotifs().contains(entry2));
+ assertTrue(mCollection.getAllNotifs().contains(entry2));
clearInvocations(mExtender1, mExtender2, mExtender3);
// WHEN the notification is reposted
@@ -1055,11 +1065,11 @@ public class NotifCollectionTest extends SysuiTestCase {
// WHEN both notifications are manually dismissed together
mCollection.dismissNotifications(
- List.of(new Pair(entry1, defaultStats(entry1)),
- new Pair(entry2, defaultStats(entry2))));
+ List.of(new Pair<>(entry1, defaultStats(entry1)),
+ new Pair<>(entry2, defaultStats(entry2))));
// THEN build list is only called one time
- verify(mBuildListener).onBuildList(any(Collection.class));
+ verifyBuiltList(List.of(entry1, entry2));
}
@Test
@@ -1074,8 +1084,8 @@ public class NotifCollectionTest extends SysuiTestCase {
DismissedByUserStats stats1 = defaultStats(entry1);
DismissedByUserStats stats2 = defaultStats(entry2);
mCollection.dismissNotifications(
- List.of(new Pair(entry1, defaultStats(entry1)),
- new Pair(entry2, defaultStats(entry2))));
+ List.of(new Pair<>(entry1, defaultStats(entry1)),
+ new Pair<>(entry2, defaultStats(entry2))));
// THEN we send the dismissals to system server
verify(mStatusBarService).onNotificationClear(
@@ -1109,8 +1119,8 @@ public class NotifCollectionTest extends SysuiTestCase {
// WHEN both notifications are manually dismissed together
mCollection.dismissNotifications(
- List.of(new Pair(entry1, defaultStats(entry1)),
- new Pair(entry2, defaultStats(entry2))));
+ List.of(new Pair<>(entry1, defaultStats(entry1)),
+ new Pair<>(entry2, defaultStats(entry2))));
// THEN the entries are marked as dismissed
assertEquals(DISMISSED, entry1.getDismissState());
@@ -1134,8 +1144,8 @@ public class NotifCollectionTest extends SysuiTestCase {
// WHEN both notifications are manually dismissed together
mCollection.dismissNotifications(
- List.of(new Pair(entry1, defaultStats(entry1)),
- new Pair(entry2, defaultStats(entry2))));
+ List.of(new Pair<>(entry1, defaultStats(entry1)),
+ new Pair<>(entry2, defaultStats(entry2))));
// THEN all interceptors get checked
verify(mInterceptor1).shouldInterceptDismissal(entry1);
@@ -1162,7 +1172,7 @@ public class NotifCollectionTest extends SysuiTestCase {
mCollection.dismissAllNotifications(entry1.getSbn().getUser().getIdentifier());
// THEN build list is only called one time
- verify(mBuildListener).onBuildList(any(Collection.class));
+ verifyBuiltList(List.of(entry1, entry2));
}
@Test
@@ -1271,13 +1281,18 @@ public class NotifCollectionTest extends SysuiTestCase {
NotificationVisibility.obtain(entry.getKey(), 7, 2, true));
}
- public CollectionEvent postNotif(NotificationEntryBuilder builder) {
+ private CollectionEvent postNotif(NotificationEntryBuilder builder) {
clearInvocations(mCollectionListener);
NotifEvent rawEvent = mNoMan.postNotif(builder);
verify(mCollectionListener).onEntryAdded(mEntryCaptor.capture());
return new CollectionEvent(rawEvent, requireNonNull(mEntryCaptor.getValue()));
}
+ private void verifyBuiltList(Collection<NotificationEntry> list) {
+ verify(mBuildListener).onBuildList(mBuildListCaptor.capture());
+ assertEquals(new ArraySet<>(list), new ArraySet<>(mBuildListCaptor.getValue()));
+ }
+
private static class RecordingCollectionListener implements NotifCollectionListener {
private final Map<String, NotificationEntry> mLastSeenEntries = new ArrayMap<>();
@@ -1303,6 +1318,14 @@ public class NotifCollectionTest extends SysuiTestCase {
public void onEntryCleanUp(NotificationEntry entry) {
}
+ @Override
+ public void onRankingApplied() {
+ }
+
+ @Override
+ public void onRankingUpdate(RankingMap rankingMap) {
+ }
+
public NotificationEntry getEntry(String key) {
if (!mLastSeenEntries.containsKey(key)) {
throw new RuntimeException("Key not found: " + key);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinatorTest.java
index 67b1aad94f51..407e1e671f86 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinatorTest.java
@@ -223,7 +223,7 @@ public class ForegroundCoordinatorTest extends SysuiTestCase {
.setPkg(TEST_PKG)
.setId(2)
.build();
- when(mNotifPipeline.getActiveNotifs()).thenReturn(List.of(entry1, entry2, entry2Other));
+ when(mNotifPipeline.getAllNotifs()).thenReturn(List.of(entry1, entry2, entry2Other));
// GIVEN that entry2 is currently associated with a foreground service
when(mForegroundServiceController.getStandardLayoutKey(0, TEST_PKG))
@@ -253,7 +253,7 @@ public class ForegroundCoordinatorTest extends SysuiTestCase {
.setPkg(TEST_PKG)
.setId(2)
.build();
- when(mNotifPipeline.getActiveNotifs()).thenReturn(List.of(entry));
+ when(mNotifPipeline.getAllNotifs()).thenReturn(List.of(entry));
when(mForegroundServiceController.getStandardLayoutKey(0, TEST_PKG))
.thenReturn(entry.getKey());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java
new file mode 100644
index 000000000000..0c109c498dd7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.RemoteInputController;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+
+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)
+@TestableLooper.RunWithLooper
+public class HeadsUpCoordinatorTest extends SysuiTestCase {
+
+ private HeadsUpCoordinator mCoordinator;
+
+ // captured listeners and pluggables:
+ private NotifCollectionListener mCollectionListener;
+ private NotifPromoter mNotifPromoter;
+ private NotifLifetimeExtender mNotifLifetimeExtender;
+ private OnHeadsUpChangedListener mOnHeadsUpChangedListener;
+ private NotifSection mNotifSection;
+
+ @Mock private NotifPipeline mNotifPipeline;
+ @Mock private HeadsUpManager mHeadsUpManager;
+ @Mock private NotificationRemoteInputManager mRemoteInputManager;
+ @Mock private RemoteInputController mRemoteInputController;
+ @Mock private NotifLifetimeExtender.OnEndLifetimeExtensionCallback mEndLifetimeExtension;
+
+ private NotificationEntry mEntry;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
+
+ mCoordinator = new HeadsUpCoordinator(
+ mHeadsUpManager,
+ mRemoteInputManager
+ );
+
+ mCoordinator.attach(mNotifPipeline);
+
+ // capture arguments:
+ ArgumentCaptor<NotifCollectionListener> notifCollectionCaptor =
+ ArgumentCaptor.forClass(NotifCollectionListener.class);
+ ArgumentCaptor<NotifPromoter> notifPromoterCaptor =
+ ArgumentCaptor.forClass(NotifPromoter.class);
+ ArgumentCaptor<NotifLifetimeExtender> notifLifetimeExtenderCaptor =
+ ArgumentCaptor.forClass(NotifLifetimeExtender.class);
+ ArgumentCaptor<OnHeadsUpChangedListener> headsUpChangedListenerCaptor =
+ ArgumentCaptor.forClass(OnHeadsUpChangedListener.class);
+
+ verify(mNotifPipeline).addCollectionListener(notifCollectionCaptor.capture());
+ verify(mNotifPipeline).addPromoter(notifPromoterCaptor.capture());
+ verify(mNotifPipeline).addNotificationLifetimeExtender(
+ notifLifetimeExtenderCaptor.capture());
+ verify(mHeadsUpManager).addListener(headsUpChangedListenerCaptor.capture());
+
+ mCollectionListener = notifCollectionCaptor.getValue();
+ mNotifPromoter = notifPromoterCaptor.getValue();
+ mNotifLifetimeExtender = notifLifetimeExtenderCaptor.getValue();
+ mOnHeadsUpChangedListener = headsUpChangedListenerCaptor.getValue();
+
+ mNotifSection = mCoordinator.getSection();
+ mNotifLifetimeExtender.setCallback(mEndLifetimeExtension);
+ mEntry = new NotificationEntryBuilder().build();
+ }
+
+ @Test
+ public void testPromotesCurrentHUN() {
+ // GIVEN the current HUN is set to mEntry
+ setCurrentHUN(mEntry);
+
+ // THEN only promote the current HUN, mEntry
+ assertTrue(mNotifPromoter.shouldPromoteToTopLevel(mEntry));
+ assertFalse(mNotifPromoter.shouldPromoteToTopLevel(new NotificationEntryBuilder().build()));
+ }
+
+ @Test
+ public void testIncludeInSectionCurrentHUN() {
+ // GIVEN the current HUN is set to mEntry
+ setCurrentHUN(mEntry);
+
+ // THEN only section the current HUN, mEntry
+ assertTrue(mNotifSection.isInSection(mEntry));
+ assertFalse(mNotifSection.isInSection(new NotificationEntryBuilder().build()));
+ }
+
+ @Test
+ public void testLifetimeExtendsCurrentHUN() {
+ // GIVEN there is a HUN, mEntry
+ setCurrentHUN(mEntry);
+
+ // THEN only the current HUN, mEntry, should be lifetimeExtended
+ assertTrue(mNotifLifetimeExtender.shouldExtendLifetime(mEntry, /* cancellationReason */ 0));
+ assertFalse(mNotifLifetimeExtender.shouldExtendLifetime(
+ new NotificationEntryBuilder().build(), /* cancellationReason */ 0));
+ }
+
+ @Test
+ public void testLifetimeExtensionEndsOnNewHUN() {
+ // GIVEN there was a HUN that was lifetime extended
+ setCurrentHUN(mEntry);
+ assertTrue(mNotifLifetimeExtender.shouldExtendLifetime(
+ mEntry, /* cancellation reason */ 0));
+
+ // WHEN there's a new HUN
+ NotificationEntry newHUN = new NotificationEntryBuilder().build();
+ setCurrentHUN(newHUN);
+
+ // THEN the old entry's lifetime extension should be cancelled
+ verify(mEndLifetimeExtension).onEndLifetimeExtension(mNotifLifetimeExtender, mEntry);
+ }
+
+ @Test
+ public void testLifetimeExtensionEndsOnNoHUNs() {
+ // GIVEN there was a HUN that was lifetime extended
+ setCurrentHUN(mEntry);
+ assertTrue(mNotifLifetimeExtender.shouldExtendLifetime(
+ mEntry, /* cancellation reason */ 0));
+
+ // WHEN there's no longer a HUN
+ setCurrentHUN(null);
+
+ // THEN the old entry's lifetime extension should be cancelled
+ verify(mEndLifetimeExtension).onEndLifetimeExtension(mNotifLifetimeExtender, mEntry);
+ }
+
+ @Test
+ public void testOnEntryRemovedRemovesHeadsUpNotification() {
+ // GIVEN the current HUN is mEntry
+ setCurrentHUN(mEntry);
+
+ // WHEN mEntry is removed from the notification collection
+ mCollectionListener.onEntryRemoved(mEntry, /* cancellation reason */ 0);
+ when(mRemoteInputController.isSpinning(any())).thenReturn(false);
+
+ // THEN heads up manager should remove the entry
+ verify(mHeadsUpManager).removeNotification(mEntry.getKey(), false);
+ }
+
+ private void setCurrentHUN(NotificationEntry entry) {
+ when(mHeadsUpManager.getTopEntry()).thenReturn(entry);
+ when(mHeadsUpManager.isAlerting(any())).thenReturn(false);
+ if (entry != null) {
+ when(mHeadsUpManager.isAlerting(entry.getKey())).thenReturn(true);
+ }
+ mOnHeadsUpChangedListener.onHeadsUpStateChanged(entry, entry != null);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
index 792b4d5c707c..8143cf5a4673 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
@@ -20,8 +20,10 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.os.RemoteException;
import android.testing.AndroidTestingRunner;
@@ -33,12 +35,15 @@ import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotifViewBarn;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import org.junit.Before;
import org.junit.Test;
@@ -73,6 +78,8 @@ public class PreparationCoordinatorTest extends SysuiTestCase {
@Mock private NotifPipeline mNotifPipeline;
@Mock private IStatusBarService mService;
@Mock private NotifInflaterImpl mNotifInflater;
+ @Mock private NotificationInterruptStateProvider mNotificationInterruptStateProvider;
+ @Mock private HeadsUpManager mHeadsUpManager;
@Before
public void setUp() {
@@ -86,7 +93,10 @@ public class PreparationCoordinatorTest extends SysuiTestCase {
mock(PreparationCoordinatorLogger.class),
mNotifInflater,
mErrorManager,
- mService);
+ mock(NotifViewBarn.class),
+ mService,
+ mNotificationInterruptStateProvider,
+ mHeadsUpManager);
ArgumentCaptor<NotifFilter> filterCaptor = ArgumentCaptor.forClass(NotifFilter.class);
mCoordinator.attach(mNotifPipeline);
@@ -170,4 +180,24 @@ public class PreparationCoordinatorTest extends SysuiTestCase {
// THEN it isn't filtered from shade list
assertFalse(mUninflatedFilter.shouldFilterOut(mEntry, 0));
}
+
+ @Test
+ public void testShowHUNOnInflationFinished() {
+ // WHEN a notification should HUN and its inflation is finished
+ when(mNotificationInterruptStateProvider.shouldHeadsUp(mEntry)).thenReturn(true);
+ mCallback.onInflationFinished(mEntry);
+
+ // THEN we tell the HeadsUpManager to show the notification
+ verify(mHeadsUpManager).showNotification(mEntry);
+ }
+
+ @Test
+ public void testNoHUNOnInflationFinished() {
+ // WHEN a notification shouldn't HUN and its inflation is finished
+ when(mNotificationInterruptStateProvider.shouldHeadsUp(mEntry)).thenReturn(false);
+ mCallback.onInflationFinished(mEntry);
+
+ // THEN we never tell the HeadsUpManager to show the notification
+ verify(mHeadsUpManager, never()).showNotification(mEntry);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index e960185ebe3a..c356e0d16512 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -329,10 +329,10 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
@Test
public void testIconScrollXAfterTranslationAndReset() throws Exception {
mGroupRow.setTranslation(50);
- assertEquals(50, -mGroupRow.getEntry().expandedIcon.getScrollX());
+ assertEquals(50, -mGroupRow.getEntry().getIcons().getShelfIcon().getScrollX());
mGroupRow.resetTranslation();
- assertEquals(0, mGroupRow.getEntry().expandedIcon.getScrollX());
+ assertEquals(0, mGroupRow.getEntry().getIcons().getShelfIcon().getScrollX());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
index a21a047d9a70..2e787ce3e2b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
@@ -28,6 +28,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Notification;
+import android.content.pm.LauncherApps;
import android.os.Handler;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.Ranking;
@@ -61,6 +62,8 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
+import com.android.systemui.statusbar.notification.icon.IconBuilder;
+import com.android.systemui.statusbar.notification.icon.IconManager;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
@@ -245,14 +248,13 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {
mLockscreenUserManager,
pipeline,
stage,
- true, /* allowLongPress */
- mock(KeyguardBypassController.class),
- mock(StatusBarStateController.class),
- mGroupManager,
- mGutsManager,
mNotificationInterruptionStateProvider,
RowInflaterTask::new,
- mExpandableNotificationRowComponentBuilder);
+ mExpandableNotificationRowComponentBuilder,
+ new IconManager(
+ mEntryManager,
+ mock(LauncherApps.class),
+ new IconBuilder(mContext)));
mEntryManager.setUpWithPresenter(mPresenter);
mEntryManager.addNotificationEntryListener(mEntryListener);
@@ -284,7 +286,8 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {
false,
false,
false,
- null);
+ null,
+ false);
mRankingMap = new NotificationListenerService.RankingMap(new Ranking[] {ranking});
TestableLooper.get(this).processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 5a89fc44f970..dc3374bd511b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -34,6 +34,7 @@ import android.app.NotificationChannel;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.LauncherApps;
import android.graphics.drawable.Icon;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
@@ -53,6 +54,8 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.icon.IconBuilder;
+import com.android.systemui.statusbar.notification.icon.IconManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.ExpansionLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.OnExpandClickListener;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
@@ -93,6 +96,7 @@ public class NotificationTestHelper {
private final NotifBindPipeline mBindPipeline;
private final NotifCollectionListener mBindPipelineEntryListener;
private final RowContentBindStage mBindStage;
+ private final IconManager mIconManager;
private StatusBarStateController mStatusBarStateController;
public NotificationTestHelper(Context context, TestableDependency dependency) {
@@ -106,6 +110,10 @@ public class NotificationTestHelper {
mock(KeyguardBypassController.class), mock(NotificationGroupManager.class),
mock(ConfigurationControllerImpl.class));
mGroupManager.setHeadsUpManager(mHeadsUpManager);
+ mIconManager = new IconManager(
+ mock(CommonNotifCollection.class),
+ mock(LauncherApps.class),
+ new IconBuilder(mContext));
NotificationContentInflater contentBinder = new NotificationContentInflater(
mock(NotifRemoteViewCache.class),
@@ -377,7 +385,7 @@ public class NotificationTestHelper {
.build();
entry.setRow(row);
- entry.createIcons(mContext, entry.getSbn());
+ mIconManager.createIcons(entry);
row.setEntry(entry);
mBindPipelineEntryListener.onEntryInit(entry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java
index 83e89bdce2d5..5320ecd46d62 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java
@@ -20,6 +20,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.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -27,6 +28,7 @@ import static org.mockito.Mockito.when;
import android.app.IActivityManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
+import android.view.View;
import android.view.WindowManager;
import androidx.test.filters.SmallTest;
@@ -110,4 +112,13 @@ public class NotificationShadeWindowControllerTest extends SysuiTestCase {
public void testSetForcePluginOpen_beforeStatusBarInitialization() {
mNotificationShadeWindowController.setForcePluginOpen(true);
}
+
+ @Test
+ public void setBackgroundBlurRadius_expandedWithBlurs() {
+ mNotificationShadeWindowController.setBackgroundBlurRadius(10);
+ verify(mNotificationShadeWindowView).setVisibility(eq(View.VISIBLE));
+
+ mNotificationShadeWindowController.setBackgroundBlurRadius(0);
+ verify(mNotificationShadeWindowView).setVisibility(eq(View.INVISIBLE));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 408dfc0e1dee..23f263746fb3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -50,6 +50,7 @@ import com.android.systemui.DejankUtils;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dock.DockManager;
+import com.android.systemui.statusbar.BlurUtils;
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.wakelock.DelayedWakeLock;
@@ -101,6 +102,8 @@ public class ScrimControllerTest extends SysuiTestCase {
private SysuiColorExtractor mSysuiColorExtractor;
@Mock
private DockManager mDockManager;
+ @Mock
+ private BlurUtils mBlurUtils;
private static class AnimatorListener implements Animator.AnimatorListener {
@@ -215,7 +218,7 @@ public class ScrimControllerTest extends SysuiTestCase {
mScrimController = new ScrimController(mLightBarController,
mDozeParamenters, mAlarmManager, mKeyguardStateController, mDelayedWakeLockBuilder,
new FakeHandler(mLooper.getLooper()), mKeyguardUpdateMonitor, mSysuiColorExtractor,
- mDockManager);
+ mDockManager, mBlurUtils);
mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
mScrimController.attachViews(mScrimBehind, mScrimInFront, mScrimForBubble);
mScrimController.setAnimatorListener(mAnimatorListener);
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
index 8b8b9e57a500..48be0d96425c 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
@@ -64,16 +64,26 @@ public final class TetheredClient implements Parcelable {
dest.writeInt(mTetheringType);
}
+ /**
+ * Get the MAC address used to identify the client.
+ */
@NonNull
public MacAddress getMacAddress() {
return mMacAddress;
}
+ /**
+ * Get information on the list of addresses that are associated with the client.
+ */
@NonNull
public List<AddressInfo> getAddresses() {
return new ArrayList<>(mAddresses);
}
+ /**
+ * Get the type of tethering used by the client.
+ * @return one of the {@code TetheringManager#TETHERING_*} constants.
+ */
public int getTetheringType() {
return mTetheringType;
}
@@ -115,45 +125,47 @@ public final class TetheredClient implements Parcelable {
private final LinkAddress mAddress;
@Nullable
private final String mHostname;
- // TODO: use LinkAddress expiration time once it is supported
- private final long mExpirationTime;
/** @hide */
public AddressInfo(@NonNull LinkAddress address, @Nullable String hostname) {
- this(address, hostname, 0);
- }
-
- /** @hide */
- public AddressInfo(@NonNull LinkAddress address, String hostname, long expirationTime) {
this.mAddress = address;
this.mHostname = hostname;
- this.mExpirationTime = expirationTime;
}
private AddressInfo(Parcel in) {
- this(in.readParcelable(null), in.readString(), in.readLong());
+ this(in.readParcelable(null), in.readString());
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeParcelable(mAddress, flags);
dest.writeString(mHostname);
- dest.writeLong(mExpirationTime);
}
+ /**
+ * Get the link address (including prefix length and lifetime) used by the client.
+ *
+ * This may be an IPv4 or IPv6 address.
+ */
@NonNull
public LinkAddress getAddress() {
return mAddress;
}
+ /**
+ * Get the hostname that was advertised by the client when obtaining its address, if any.
+ */
@Nullable
public String getHostname() {
return mHostname;
}
- /** @hide TODO: use expiration time in LinkAddress */
+ /**
+ * Get the expiration time of the address assigned to the client.
+ * @hide
+ */
public long getExpirationTime() {
- return mExpirationTime;
+ return mAddress.getExpirationTime();
}
@Override
@@ -163,7 +175,7 @@ public final class TetheredClient implements Parcelable {
@Override
public int hashCode() {
- return Objects.hash(mAddress, mHostname, mExpirationTime);
+ return Objects.hash(mAddress, mHostname);
}
@Override
@@ -173,8 +185,7 @@ public final class TetheredClient implements Parcelable {
// Use .equals() for addresses as all changes, including address expiry changes,
// should be included.
return other.mAddress.equals(mAddress)
- && Objects.equals(mHostname, other.mHostname)
- && mExpirationTime == other.mExpirationTime;
+ && Objects.equals(mHostname, other.mHostname);
}
@NonNull
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
index a18f5da60cad..fd6f171487a9 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
@@ -32,7 +32,7 @@ import android.os.ResultReceiver;
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
-public class TetheringConstants {
+public final class TetheringConstants {
/** An explicit private class to avoid exposing constructor.*/
private TetheringConstants() { }
diff --git a/packages/Tethering/res/values/config.xml b/packages/Tethering/res/values/config.xml
index 4af5c5371952..04d6215dce96 100644
--- a/packages/Tethering/res/values/config.xml
+++ b/packages/Tethering/res/values/config.xml
@@ -157,4 +157,49 @@
<!-- ComponentName of the service used to run no ui tether provisioning. -->
<string translatable="false" name="config_wifi_tether_enable">com.android.settings/.wifi.tether.TetherService</string>
+
+ <!-- Enable tethering notification -->
+ <!-- Icons for showing tether enable notification.
+ Each item should have two elements and be separated with ";".
+
+ The first element is downstream types which is one of tethering. This element has to be
+ made by WIFI, USB, BT, and OR'd with the others. Use "|" to combine multiple downstream
+ types and use "," to separate each combinations. Such as
+
+ USB|BT,WIFI|USB|BT
+
+ The second element is icon for the item. This element has to be composed by
+ <package name>:drawable/<resource name>. Such as
+
+ 1. com.android.networkstack.tethering:drawable/stat_sys_tether_general
+ 2. android:drawable/xxx
+
+ So the entire string of each item would be
+
+ USB|BT,WIFI|USB|BT;com.android.networkstack.tethering:drawable/stat_sys_tether_general
+
+ NOTE: One config can be separated into two or more for readability. Such as
+
+ WIFI|USB,WIFI|BT,USB|BT,WIFI|USB|BT;android:drawable/xxx
+
+ can be separated into
+
+ WIFI|USB;android:drawable/xxx
+ WIFI|BT;android:drawable/xxx
+ USB|BT;android:drawable/xxx
+ WIFI|USB|BT;android:drawable/xxx
+
+ Notification will not show if the downstream type isn't listed in array.
+ Empty array means disable notifications. -->
+ <!-- In AOSP, hotspot is configured to no notification by default. Because status bar has showed
+ an icon on the right side already -->
+ <string-array translatable="false" name="tethering_notification_icons">
+ <item>USB;com.android.networkstack.tethering:drawable/stat_sys_tether_usb</item>
+ <item>BT;com.android.networkstack.tethering:drawable/stat_sys_tether_bluetooth</item>
+ <item>WIFI|USB,WIFI|BT,USB|BT,WIFI|USB|BT;com.android.networkstack.tethering:drawable/stat_sys_tether_general</item>
+ </string-array>
+ <!-- String for tether enable notification title. -->
+ <string name="tethering_notification_title">@string/tethered_notification_title</string>
+ <!-- String for tether enable notification message. -->
+ <string name="tethering_notification_message">@string/tethered_notification_message</string>
</resources>
diff --git a/packages/Tethering/res/values/overlayable.xml b/packages/Tethering/res/values/overlayable.xml
index fe025c7ac993..bbba3f30a292 100644
--- a/packages/Tethering/res/values/overlayable.xml
+++ b/packages/Tethering/res/values/overlayable.xml
@@ -16,6 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<overlayable name="TetheringConfig">
<policy type="product|system|vendor">
+ <!-- Params from config.xml that can be overlaid -->
<item type="array" name="config_tether_usb_regexs"/>
<item type="array" name="config_tether_ncm_regexs" />
<item type="array" name="config_tether_wifi_regexs"/>
@@ -31,6 +32,45 @@
<item type="string" name="config_mobile_hotspot_provision_response"/>
<item type="integer" name="config_mobile_hotspot_provision_check_period"/>
<item type="string" name="config_wifi_tether_enable"/>
+ <!-- Configuration values for TetheringNotificationUpdater -->
+ <!-- Icons for showing tether enable notification.
+ Each item should have two elements and be separated with ";".
+
+ The first element is downstream types which is one of tethering. This element has to be
+ made by WIFI, USB, BT, and OR'd with the others. Use "|" to combine multiple downstream
+ types and use "," to separate each combinations. Such as
+
+ USB|BT,WIFI|USB|BT
+
+ The second element is icon for the item. This element has to be composed by
+ <package name>:drawable/<resource name>. Such as
+
+ 1. com.android.networkstack.tethering:drawable/stat_sys_tether_general
+ 2. android:drawable/xxx
+
+ So the entire string of each item would be
+
+ USB|BT,WIFI|USB|BT;com.android.networkstack.tethering:drawable/stat_sys_tether_general
+
+ NOTE: One config can be separated into two or more for readability. Such as
+
+ WIFI|USB,WIFI|BT,USB|BT,WIFI|USB|BT;android:drawable/xxx
+
+ can be separated into
+
+ WIFI|USB;android:drawable/xxx
+ WIFI|BT;android:drawable/xxx
+ USB|BT;android:drawable/xxx
+ WIFI|USB|BT;android:drawable/xxx
+
+ Notification will not show if the downstream type isn't listed in array.
+ Empty array means disable notifications. -->
+ <item type="array" name="tethering_notification_icons"/>
+ <!-- String for tether enable notification title. -->
+ <item type="string" name="tethering_notification_title"/>
+ <!-- String for tether enable notification message. -->
+ <item type="string" name="tethering_notification_message"/>
+ <!-- Params from config.xml that can be overlaid -->
</policy>
</overlayable>
</resources>
diff --git a/packages/Tethering/res/values/strings.xml b/packages/Tethering/res/values/strings.xml
index 792bce9fc334..ba98a66ff7e0 100644
--- a/packages/Tethering/res/values/strings.xml
+++ b/packages/Tethering/res/values/strings.xml
@@ -15,19 +15,21 @@
-->
<resources>
<!-- Shown when the device is tethered -->
- <!-- Strings for tethered notification title [CHAR LIMIT=200] -->
+ <!-- String for tethered notification title [CHAR LIMIT=200] -->
<string name="tethered_notification_title">Tethering or hotspot active</string>
- <!-- Strings for tethered notification message [CHAR LIMIT=200] -->
+ <!-- String for tethered notification message [CHAR LIMIT=200] -->
<string name="tethered_notification_message">Tap to set up.</string>
<!-- This notification is shown when tethering has been disabled on a user's device.
The device is managed by the user's employer. Tethering can't be turned on unless the
IT administrator allows it. The noun "admin" is another reference for "IT administrator." -->
- <!-- Strings for tether disabling notification title [CHAR LIMIT=200] -->
+ <!-- String for tether disabling notification title [CHAR LIMIT=200] -->
<string name="disable_tether_notification_title">Tethering is disabled</string>
- <!-- Strings for tether disabling notification message [CHAR LIMIT=200] -->
+ <!-- String for tether disabling notification message [CHAR LIMIT=200] -->
<string name="disable_tether_notification_message">Contact your admin for details</string>
- <!-- Strings for tether notification channel name [CHAR LIMIT=200] -->
+ <!-- This string should be consistent with the "Hotspot & tethering" text in the "Network and
+ Internet" settings page. That is currently the tether_settings_title_all string. -->
+ <!-- String for tether notification channel name [CHAR LIMIT=200] -->
<string name="notification_channel_tethering_status">Hotspot &amp; tethering status</string>
</resources> \ No newline at end of file
diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java
index 38f8609e217f..6c0c432d46dc 100644
--- a/packages/Tethering/src/android/net/ip/IpServer.java
+++ b/packages/Tethering/src/android/net/ip/IpServer.java
@@ -24,6 +24,7 @@ import static android.net.util.NetworkConstants.FF;
import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
import static android.net.util.NetworkConstants.asByte;
import static android.net.util.TetheringMessageBase.BASE_IPSERVER;
+import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
import android.net.INetd;
import android.net.INetworkStackStatusCallback;
@@ -448,7 +449,9 @@ public class IpServer extends StateMachine {
final ArrayList<TetheredClient> leases = new ArrayList<>();
for (DhcpLeaseParcelable lease : leaseParcelables) {
final LinkAddress address = new LinkAddress(
- intToInet4AddressHTH(lease.netAddr), lease.prefixLength);
+ intToInet4AddressHTH(lease.netAddr), lease.prefixLength,
+ 0 /* flags */, RT_SCOPE_UNIVERSE /* as per RFC6724#3.2 */,
+ lease.expTime /* deprecationTime */, lease.expTime /* expirationTime */);
final MacAddress macAddress;
try {
@@ -460,7 +463,7 @@ public class IpServer extends StateMachine {
}
final TetheredClient.AddressInfo addressInfo = new TetheredClient.AddressInfo(
- address, lease.hostname, lease.expTime);
+ address, lease.hostname);
leases.add(new TetheredClient(
macAddress,
Collections.singletonList(addressInfo),
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
index a402ffa47355..15cdb6ad7a8e 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
@@ -39,8 +39,7 @@ import android.net.RouteInfo;
import android.net.netlink.ConntrackMessage;
import android.net.netlink.NetlinkConstants;
import android.net.netlink.NetlinkSocket;
-import android.net.netstats.provider.AbstractNetworkStatsProvider;
-import android.net.netstats.provider.NetworkStatsProviderCallback;
+import android.net.netstats.provider.NetworkStatsProvider;
import android.net.util.SharedLog;
import android.os.Handler;
import android.provider.Settings;
@@ -89,8 +88,8 @@ public class OffloadController {
private final Handler mHandler;
private final OffloadHardwareInterface mHwInterface;
private final ContentResolver mContentResolver;
- private final @NonNull OffloadTetheringStatsProvider mStatsProvider;
- private final @Nullable NetworkStatsProviderCallback mStatsProviderCb;
+ @Nullable
+ private final OffloadTetheringStatsProvider mStatsProvider;
private final SharedLog mLog;
private final HashMap<String, LinkProperties> mDownstreams;
private boolean mConfigInitialized;
@@ -124,19 +123,18 @@ public class OffloadController {
mHandler = h;
mHwInterface = hwi;
mContentResolver = contentResolver;
- mStatsProvider = new OffloadTetheringStatsProvider();
mLog = log.forSubComponent(TAG);
mDownstreams = new HashMap<>();
mExemptPrefixes = new HashSet<>();
mLastLocalPrefixStrs = new HashSet<>();
- NetworkStatsProviderCallback providerCallback = null;
+ OffloadTetheringStatsProvider provider = new OffloadTetheringStatsProvider();
try {
- providerCallback = nsm.registerNetworkStatsProvider(
- getClass().getSimpleName(), mStatsProvider);
+ nsm.registerNetworkStatsProvider(getClass().getSimpleName(), provider);
} catch (RuntimeException e) {
Log.wtf(TAG, "Cannot register offload stats provider: " + e);
+ provider = null;
}
- mStatsProviderCb = providerCallback;
+ mStatsProvider = provider;
}
/** Start hardware offload. */
@@ -185,7 +183,7 @@ public class OffloadController {
// and we need to synchronize stats and limits between
// software and hardware forwarding.
updateStatsForAllUpstreams();
- mStatsProvider.pushTetherStats();
+ if (mStatsProvider != null) mStatsProvider.pushTetherStats();
}
@Override
@@ -198,7 +196,7 @@ public class OffloadController {
// limits set take into account any software tethering
// traffic that has been happening in the meantime.
updateStatsForAllUpstreams();
- mStatsProvider.pushTetherStats();
+ if (mStatsProvider != null) mStatsProvider.pushTetherStats();
// [2] (Re)Push all state.
computeAndPushLocalPrefixes(UpdateType.FORCE);
pushAllDownstreamState();
@@ -217,10 +215,12 @@ public class OffloadController {
// TODO: rev the HAL so that it provides an interface name.
updateStatsForCurrentUpstream();
- mStatsProvider.pushTetherStats();
- // Push stats to service does not cause the service react to it immediately.
- // Inform the service about limit reached.
- if (mStatsProviderCb != null) mStatsProviderCb.onLimitReached();
+ if (mStatsProvider != null) {
+ mStatsProvider.pushTetherStats();
+ // Push stats to service does not cause the service react to it
+ // immediately. Inform the service about limit reached.
+ mStatsProvider.notifyLimitReached();
+ }
}
@Override
@@ -263,13 +263,17 @@ public class OffloadController {
}
@VisibleForTesting
- class OffloadTetheringStatsProvider extends AbstractNetworkStatsProvider {
+ class OffloadTetheringStatsProvider extends NetworkStatsProvider {
// These stats must only ever be touched on the handler thread.
@NonNull
private NetworkStats mIfaceStats = new NetworkStats(0L, 0);
@NonNull
private NetworkStats mUidStats = new NetworkStats(0L, 0);
+ /**
+ * A helper function that collect tether stats from local hashmap. Note that this does not
+ * invoke binder call.
+ */
@VisibleForTesting
@NonNull
NetworkStats getTetherStats(@NonNull StatsType how) {
@@ -280,14 +284,14 @@ public class OffloadController {
final ForwardedStats value = kv.getValue();
final Entry entry = new Entry(kv.getKey(), uid, SET_DEFAULT, TAG_NONE, METERED_NO,
ROAMING_NO, DEFAULT_NETWORK_NO, value.rxBytes, 0L, value.txBytes, 0L, 0L);
- stats = stats.addValues(entry);
+ stats = stats.addEntry(entry);
}
return stats;
}
@Override
- public void setLimit(String iface, long quotaBytes) {
+ public void onSetLimit(String iface, long quotaBytes) {
// Listen for all iface is necessary since upstream might be changed after limit
// is set.
mHandler.post(() -> {
@@ -315,13 +319,12 @@ public class OffloadController {
*/
public void pushTetherStats() {
// TODO: remove the accumulated stats and report the diff from HAL directly.
- if (null == mStatsProviderCb) return;
final NetworkStats ifaceDiff =
getTetherStats(StatsType.STATS_PER_IFACE).subtract(mIfaceStats);
final NetworkStats uidDiff =
getTetherStats(StatsType.STATS_PER_UID).subtract(mUidStats);
try {
- mStatsProviderCb.onStatsUpdated(0 /* token */, ifaceDiff, uidDiff);
+ notifyStatsUpdated(0 /* token */, ifaceDiff, uidDiff);
mIfaceStats = mIfaceStats.add(ifaceDiff);
mUidStats = mUidStats.add(uidDiff);
} catch (RuntimeException e) {
@@ -330,7 +333,7 @@ public class OffloadController {
}
@Override
- public void requestStatsUpdate(int token) {
+ public void onRequestStatsUpdate(int token) {
// Do not attempt to update stats by querying the offload HAL
// synchronously from a different thread than the Handler thread. http://b/64771555.
mHandler.post(() -> {
@@ -340,7 +343,7 @@ public class OffloadController {
}
@Override
- public void setAlert(long quotaBytes) {
+ public void onSetAlert(long quotaBytes) {
// TODO: Ask offload HAL to notify alert without stopping traffic.
}
}
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
index f89da849ea91..3d8dbab7d4fb 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
@@ -59,10 +59,8 @@ import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
+import static com.android.server.connectivity.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE;
+
import android.app.usage.NetworkStatsManager;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothPan;
@@ -72,7 +70,6 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.res.Resources;
import android.hardware.usb.UsbManager;
import android.net.ConnectivityManager;
import android.net.EthernetManager;
@@ -128,7 +125,6 @@ import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.MessageUtils;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
-import com.android.networkstack.tethering.R;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -224,14 +220,13 @@ public class Tethering {
private final ActiveDataSubIdListener mActiveDataSubIdListener;
private final ConnectedClientsTracker mConnectedClientsTracker;
private final TetheringThreadExecutor mExecutor;
+ private final TetheringNotificationUpdater mNotificationUpdater;
private int mActiveDataSubId = INVALID_SUBSCRIPTION_ID;
// All the usage of mTetheringEventCallback should run in the same thread.
private ITetheringEventCallback mTetheringEventCallback = null;
private volatile TetheringConfiguration mConfig;
private InterfaceSet mCurrentUpstreamIfaceSet;
- private Notification.Builder mTetheredNotificationBuilder;
- private int mLastNotificationId;
private boolean mRndisEnabled; // track the RNDIS function enabled state
// True iff. WiFi tethering should be started when soft AP is ready.
@@ -255,6 +250,7 @@ public class Tethering {
mContext = mDeps.getContext();
mNetd = mDeps.getINetd(mContext);
mLooper = mDeps.getTetheringLooper();
+ mNotificationUpdater = mDeps.getNotificationUpdater(mContext);
mPublicSync = new Object();
@@ -738,13 +734,10 @@ public class Tethering {
final ArrayList<String> erroredList = new ArrayList<>();
final ArrayList<Integer> lastErrorList = new ArrayList<>();
- boolean wifiTethered = false;
- boolean usbTethered = false;
- boolean bluetoothTethered = false;
-
final TetheringConfiguration cfg = mConfig;
mTetherStatesParcel = new TetherStatesParcel();
+ int downstreamTypesMask = DOWNSTREAM_NONE;
synchronized (mPublicSync) {
for (int i = 0; i < mTetherStates.size(); i++) {
TetherState tetherState = mTetherStates.valueAt(i);
@@ -758,11 +751,11 @@ public class Tethering {
localOnlyList.add(iface);
} else if (tetherState.lastState == IpServer.STATE_TETHERED) {
if (cfg.isUsb(iface)) {
- usbTethered = true;
+ downstreamTypesMask |= (1 << TETHERING_USB);
} else if (cfg.isWifi(iface)) {
- wifiTethered = true;
+ downstreamTypesMask |= (1 << TETHERING_WIFI);
} else if (cfg.isBluetooth(iface)) {
- bluetoothTethered = true;
+ downstreamTypesMask |= (1 << TETHERING_BLUETOOTH);
}
tetherList.add(iface);
}
@@ -796,98 +789,7 @@ public class Tethering {
"error", TextUtils.join(",", erroredList)));
}
- if (usbTethered) {
- if (wifiTethered || bluetoothTethered) {
- showTetheredNotification(R.drawable.stat_sys_tether_general);
- } else {
- showTetheredNotification(R.drawable.stat_sys_tether_usb);
- }
- } else if (wifiTethered) {
- if (bluetoothTethered) {
- showTetheredNotification(R.drawable.stat_sys_tether_general);
- } else {
- /* We now have a status bar icon for WifiTethering, so drop the notification */
- clearTetheredNotification();
- }
- } else if (bluetoothTethered) {
- showTetheredNotification(R.drawable.stat_sys_tether_bluetooth);
- } else {
- clearTetheredNotification();
- }
- }
-
- private void showTetheredNotification(int id) {
- showTetheredNotification(id, true);
- }
-
- @VisibleForTesting
- protected void showTetheredNotification(int id, boolean tetheringOn) {
- NotificationManager notificationManager =
- (NotificationManager) mContext.createContextAsUser(UserHandle.ALL, 0)
- .getSystemService(Context.NOTIFICATION_SERVICE);
- if (notificationManager == null) {
- return;
- }
- final NotificationChannel channel = new NotificationChannel(
- "TETHERING_STATUS",
- mContext.getResources().getString(R.string.notification_channel_tethering_status),
- NotificationManager.IMPORTANCE_LOW);
- notificationManager.createNotificationChannel(channel);
-
- if (mLastNotificationId != 0) {
- if (mLastNotificationId == id) {
- return;
- }
- notificationManager.cancel(null, mLastNotificationId);
- mLastNotificationId = 0;
- }
-
- Intent intent = new Intent();
- intent.setClassName("com.android.settings", "com.android.settings.TetherSettings");
- intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
-
- PendingIntent pi = PendingIntent.getActivity(
- mContext.createContextAsUser(UserHandle.CURRENT, 0), 0, intent, 0, null);
-
- Resources r = mContext.getResources();
- final CharSequence title;
- final CharSequence message;
-
- if (tetheringOn) {
- title = r.getText(R.string.tethered_notification_title);
- message = r.getText(R.string.tethered_notification_message);
- } else {
- title = r.getText(R.string.disable_tether_notification_title);
- message = r.getText(R.string.disable_tether_notification_message);
- }
-
- if (mTetheredNotificationBuilder == null) {
- mTetheredNotificationBuilder = new Notification.Builder(mContext, channel.getId());
- mTetheredNotificationBuilder.setWhen(0)
- .setOngoing(true)
- .setColor(mContext.getColor(
- android.R.color.system_notification_accent_color))
- .setVisibility(Notification.VISIBILITY_PUBLIC)
- .setCategory(Notification.CATEGORY_STATUS);
- }
- mTetheredNotificationBuilder.setSmallIcon(id)
- .setContentTitle(title)
- .setContentText(message)
- .setContentIntent(pi);
- mLastNotificationId = id;
-
- notificationManager.notify(null, mLastNotificationId, mTetheredNotificationBuilder.build());
- }
-
- @VisibleForTesting
- protected void clearTetheredNotification() {
- NotificationManager notificationManager =
- (NotificationManager) mContext.createContextAsUser(UserHandle.ALL, 0)
- .getSystemService(Context.NOTIFICATION_SERVICE);
- if (notificationManager != null && mLastNotificationId != 0) {
- notificationManager.cancel(null, mLastNotificationId);
- mLastNotificationId = 0;
- }
+ mNotificationUpdater.onDownstreamChanged(downstreamTypesMask);
}
private class StateReceiver extends BroadcastReceiver {
@@ -1081,12 +983,10 @@ public class Tethering {
return;
}
- mWrapper.clearTetheredNotification();
+ // TODO: Add user restrictions notification.
final boolean isTetheringActiveOnDevice = (mWrapper.getTetheredIfaces().length != 0);
if (newlyDisallowed && isTetheringActiveOnDevice) {
- mWrapper.showTetheredNotification(
- R.drawable.stat_sys_tether_general, false);
mWrapper.untetherAll();
// TODO(b/148139325): send tetheringSupported on restriction change
}
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java
index e019c3aca26a..0330dad6a1ae 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -26,6 +26,8 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
+import androidx.annotation.NonNull;
+
import com.android.internal.util.StateMachine;
import java.util.ArrayList;
@@ -102,6 +104,13 @@ public abstract class TetheringDependencies {
}
/**
+ * Get a reference to the TetheringNotificationUpdater to be used by tethering.
+ */
+ public TetheringNotificationUpdater getNotificationUpdater(@NonNull final Context ctx) {
+ return new TetheringNotificationUpdater(ctx);
+ }
+
+ /**
* Get tethering thread looper.
*/
public abstract Looper getTetheringLooper();
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java
new file mode 100644
index 000000000000..b97f75268a3b
--- /dev/null
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java
@@ -0,0 +1,198 @@
+/*
+ * 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.connectivity.tethering;
+
+import static android.net.TetheringManager.TETHERING_BLUETOOTH;
+import static android.net.TetheringManager.TETHERING_USB;
+import static android.net.TetheringManager.TETHERING_WIFI;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.SparseArray;
+
+import androidx.annotation.ArrayRes;
+import androidx.annotation.DrawableRes;
+import androidx.annotation.IntRange;
+import androidx.annotation.NonNull;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.networkstack.tethering.R;
+
+/**
+ * A class to display tethering-related notifications.
+ *
+ * <p>This class is not thread safe, it is intended to be used only from the tethering handler
+ * thread. However the constructor is an exception, as it is called on another thread ;
+ * therefore for thread safety all members of this class MUST either be final or initialized
+ * to their default value (0, false or null).
+ *
+ * @hide
+ */
+public class TetheringNotificationUpdater {
+ private static final String TAG = TetheringNotificationUpdater.class.getSimpleName();
+ private static final String CHANNEL_ID = "TETHERING_STATUS";
+ private static final boolean NOTIFY_DONE = true;
+ private static final boolean NO_NOTIFY = false;
+ // Id to update and cancel tethering notification. Must be unique within the tethering app.
+ private static final int NOTIFY_ID = 20191115;
+ @VisibleForTesting
+ static final int NO_ICON_ID = 0;
+ @VisibleForTesting
+ static final int DOWNSTREAM_NONE = 0;
+ private final Context mContext;
+ private final NotificationManager mNotificationManager;
+ private final NotificationChannel mChannel;
+ // Downstream type is one of ConnectivityManager.TETHERING_* constants, 0 1 or 2.
+ // This value has to be made 1 2 and 4, and OR'd with the others.
+ // WARNING : the constructor is called on a different thread. Thread safety therefore
+ // relies on this value being initialized to 0, and not any other value. If you need
+ // to change this, you will need to change the thread where the constructor is invoked,
+ // or to introduce synchronization.
+ private int mDownstreamTypesMask = DOWNSTREAM_NONE;
+
+ public TetheringNotificationUpdater(@NonNull final Context context) {
+ mContext = context;
+ mNotificationManager = (NotificationManager) context.createContextAsUser(UserHandle.ALL, 0)
+ .getSystemService(Context.NOTIFICATION_SERVICE);
+ mChannel = new NotificationChannel(
+ CHANNEL_ID,
+ context.getResources().getString(R.string.notification_channel_tethering_status),
+ NotificationManager.IMPORTANCE_LOW);
+ mNotificationManager.createNotificationChannel(mChannel);
+ }
+
+ /** Called when downstream has changed */
+ public void onDownstreamChanged(@IntRange(from = 0, to = 7) final int downstreamTypesMask) {
+ if (mDownstreamTypesMask == downstreamTypesMask) return;
+ mDownstreamTypesMask = downstreamTypesMask;
+ updateNotification();
+ }
+
+ private void updateNotification() {
+ final boolean tetheringInactive = mDownstreamTypesMask <= DOWNSTREAM_NONE;
+
+ if (tetheringInactive || setupNotification() == NO_NOTIFY) {
+ clearNotification();
+ }
+ }
+
+ private void clearNotification() {
+ mNotificationManager.cancel(null /* tag */, NOTIFY_ID);
+ }
+
+ /**
+ * Returns the downstream types mask which convert from given string.
+ *
+ * @param types This string has to be made by "WIFI", "USB", "BT", and OR'd with the others.
+ *
+ * @return downstream types mask value.
+ */
+ @IntRange(from = 0, to = 7)
+ private int getDownstreamTypesMask(@NonNull final String types) {
+ int downstreamTypesMask = DOWNSTREAM_NONE;
+ final String[] downstreams = types.split("\\|");
+ for (String downstream : downstreams) {
+ if ("USB".equals(downstream.trim())) {
+ downstreamTypesMask |= (1 << TETHERING_USB);
+ } else if ("WIFI".equals(downstream.trim())) {
+ downstreamTypesMask |= (1 << TETHERING_WIFI);
+ } else if ("BT".equals(downstream.trim())) {
+ downstreamTypesMask |= (1 << TETHERING_BLUETOOTH);
+ }
+ }
+ return downstreamTypesMask;
+ }
+
+ /**
+ * Returns the icons {@link android.util.SparseArray} which get from given string-array resource
+ * id.
+ *
+ * @param id String-array resource id
+ *
+ * @return {@link android.util.SparseArray} with downstream types and icon id info.
+ */
+ @NonNull
+ private SparseArray<Integer> getIcons(@ArrayRes int id) {
+ final Resources res = mContext.getResources();
+ final String[] array = res.getStringArray(id);
+ final SparseArray<Integer> icons = new SparseArray<>();
+ for (String config : array) {
+ if (TextUtils.isEmpty(config)) continue;
+
+ final String[] elements = config.split(";");
+ if (elements.length != 2) {
+ Log.wtf(TAG,
+ "Unexpected format in Tethering notification configuration : " + config);
+ continue;
+ }
+
+ final String[] types = elements[0].split(",");
+ for (String type : types) {
+ int mask = getDownstreamTypesMask(type);
+ if (mask == DOWNSTREAM_NONE) continue;
+ icons.put(mask, res.getIdentifier(
+ elements[1].trim(), null /* defType */, null /* defPackage */));
+ }
+ }
+ return icons;
+ }
+
+ private boolean setupNotification() {
+ final Resources res = mContext.getResources();
+ final SparseArray<Integer> downstreamIcons = getIcons(R.array.tethering_notification_icons);
+
+ final int iconId = downstreamIcons.get(mDownstreamTypesMask, NO_ICON_ID);
+ if (iconId == NO_ICON_ID) return NO_NOTIFY;
+
+ final String title = res.getString(R.string.tethering_notification_title);
+ final String message = res.getString(R.string.tethering_notification_message);
+
+ showNotification(iconId, title, message);
+ return NOTIFY_DONE;
+ }
+
+ private void showNotification(@DrawableRes final int iconId, @NonNull final String title,
+ @NonNull final String message) {
+ final Intent intent = new Intent(Settings.ACTION_TETHER_SETTINGS);
+ final PendingIntent pi = PendingIntent.getActivity(
+ mContext.createContextAsUser(UserHandle.CURRENT, 0),
+ 0 /* requestCode */, intent, 0 /* flags */, null /* options */);
+ final Notification notification =
+ new Notification.Builder(mContext, mChannel.getId())
+ .setSmallIcon(iconId)
+ .setContentTitle(title)
+ .setContentText(message)
+ .setOngoing(true)
+ .setColor(mContext.getColor(
+ android.R.color.system_notification_accent_color))
+ .setVisibility(Notification.VISIBILITY_PUBLIC)
+ .setCategory(Notification.CATEGORY_STATUS)
+ .setContentIntent(pi)
+ .build();
+
+ mNotificationManager.notify(null /* tag */, NOTIFY_ID, notification);
+ }
+}
diff --git a/packages/Tethering/tests/unit/src/android/net/TetheredClientTest.kt b/packages/Tethering/tests/unit/src/android/net/TetheredClientTest.kt
index d85389aeb625..a20a0dfd9c89 100644
--- a/packages/Tethering/tests/unit/src/android/net/TetheredClientTest.kt
+++ b/packages/Tethering/tests/unit/src/android/net/TetheredClientTest.kt
@@ -20,6 +20,7 @@ import android.net.InetAddresses.parseNumericAddress
import android.net.TetheredClient.AddressInfo
import android.net.TetheringManager.TETHERING_BLUETOOTH
import android.net.TetheringManager.TETHERING_USB
+import android.system.OsConstants.RT_SCOPE_UNIVERSE
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
import com.android.testutils.assertParcelSane
@@ -30,11 +31,19 @@ import kotlin.test.assertNotEquals
private val TEST_MACADDR = MacAddress.fromBytes(byteArrayOf(12, 23, 34, 45, 56, 67))
private val TEST_OTHER_MACADDR = MacAddress.fromBytes(byteArrayOf(23, 34, 45, 56, 67, 78))
-private val TEST_ADDR1 = LinkAddress(parseNumericAddress("192.168.113.3"), 24)
-private val TEST_ADDR2 = LinkAddress(parseNumericAddress("fe80::1:2:3"), 64)
+private val TEST_ADDR1 = makeLinkAddress("192.168.113.3", prefixLength = 24, expTime = 123L)
+private val TEST_ADDR2 = makeLinkAddress("fe80::1:2:3", prefixLength = 64, expTime = 456L)
private val TEST_ADDRINFO1 = AddressInfo(TEST_ADDR1, "test_hostname")
private val TEST_ADDRINFO2 = AddressInfo(TEST_ADDR2, null)
+private fun makeLinkAddress(addr: String, prefixLength: Int, expTime: Long) = LinkAddress(
+ parseNumericAddress(addr),
+ prefixLength,
+ 0 /* flags */,
+ RT_SCOPE_UNIVERSE,
+ expTime /* deprecationTime */,
+ expTime /* expirationTime */)
+
@RunWith(AndroidJUnit4::class)
@SmallTest
class TetheredClientTest {
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/ConnectedClientsTrackerTest.kt b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/ConnectedClientsTrackerTest.kt
index 56f3e21cbffe..1cdc3bbb9933 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/ConnectedClientsTrackerTest.kt
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/ConnectedClientsTrackerTest.kt
@@ -46,23 +46,28 @@ class ConnectedClientsTrackerTest {
private val client1Addr = MacAddress.fromString("01:23:45:67:89:0A")
private val client1 = TetheredClient(client1Addr, listOf(
- AddressInfo(LinkAddress("192.168.43.44/32"), null /* hostname */, clock.time + 20)),
+ makeAddrInfo("192.168.43.44/32", null /* hostname */, clock.time + 20)),
TETHERING_WIFI)
private val wifiClient1 = makeWifiClient(client1Addr)
private val client2Addr = MacAddress.fromString("02:34:56:78:90:AB")
- private val client2Exp30AddrInfo = AddressInfo(
- LinkAddress("192.168.43.45/32"), "my_hostname", clock.time + 30)
+ private val client2Exp30AddrInfo = makeAddrInfo(
+ "192.168.43.45/32", "my_hostname", clock.time + 30)
private val client2 = TetheredClient(client2Addr, listOf(
client2Exp30AddrInfo,
- AddressInfo(LinkAddress("2001:db8:12::34/72"), "other_hostname", clock.time + 10)),
+ makeAddrInfo("2001:db8:12::34/72", "other_hostname", clock.time + 10)),
TETHERING_WIFI)
private val wifiClient2 = makeWifiClient(client2Addr)
private val client3Addr = MacAddress.fromString("03:45:67:89:0A:BC")
private val client3 = TetheredClient(client3Addr,
- listOf(AddressInfo(LinkAddress("2001:db8:34::34/72"), "other_other_hostname",
- clock.time + 10)),
+ listOf(makeAddrInfo("2001:db8:34::34/72", "other_other_hostname", clock.time + 10)),
TETHERING_USB)
+ private fun makeAddrInfo(addr: String, hostname: String?, expTime: Long) =
+ LinkAddress(addr).let {
+ AddressInfo(LinkAddress(it.address, it.prefixLength, it.flags, it.scope,
+ expTime /* deprecationTime */, expTime /* expirationTime */), hostname)
+ }
+
@Test
fun testUpdateConnectedClients() {
doReturn(emptyList<TetheredClient>()).`when`(server1).allLeases
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java
index 7e62e5aca993..fe840864fb99 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java
@@ -33,6 +33,8 @@ import static com.android.testutils.MiscAssertsKt.assertContainsAll;
import static com.android.testutils.MiscAssertsKt.assertThrows;
import static com.android.testutils.NetworkStatsUtilsKt.orderInsensitiveEquals;
+import static junit.framework.Assert.assertNotNull;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -61,8 +63,7 @@ import android.net.LinkProperties;
import android.net.NetworkStats;
import android.net.NetworkStats.Entry;
import android.net.RouteInfo;
-import android.net.netstats.provider.AbstractNetworkStatsProvider;
-import android.net.netstats.provider.NetworkStatsProviderCallback;
+import android.net.netstats.provider.INetworkStatsProviderCallback;
import android.net.util.SharedLog;
import android.os.Handler;
import android.os.Looper;
@@ -108,12 +109,10 @@ public class OffloadControllerTest {
@Mock private ApplicationInfo mApplicationInfo;
@Mock private Context mContext;
@Mock private NetworkStatsManager mStatsManager;
- @Mock private NetworkStatsProviderCallback mTetherStatsProviderCb;
+ @Mock private INetworkStatsProviderCallback mTetherStatsProviderCb;
+ private OffloadController.OffloadTetheringStatsProvider mTetherStatsProvider;
private final ArgumentCaptor<ArrayList> mStringArrayCaptor =
ArgumentCaptor.forClass(ArrayList.class);
- private final ArgumentCaptor<OffloadController.OffloadTetheringStatsProvider>
- mTetherStatsProviderCaptor =
- ArgumentCaptor.forClass(OffloadController.OffloadTetheringStatsProvider.class);
private final ArgumentCaptor<OffloadHardwareInterface.ControlCallback> mControlCallbackCaptor =
ArgumentCaptor.forClass(OffloadHardwareInterface.ControlCallback.class);
private MockContentResolver mContentResolver;
@@ -126,8 +125,6 @@ public class OffloadControllerTest {
mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
when(mContext.getContentResolver()).thenReturn(mContentResolver);
FakeSettingsProvider.clearSettingsProvider();
- when(mStatsManager.registerNetworkStatsProvider(anyString(), any()))
- .thenReturn(mTetherStatsProviderCb);
}
@After public void tearDown() throws Exception {
@@ -154,8 +151,14 @@ public class OffloadControllerTest {
private OffloadController makeOffloadController() throws Exception {
OffloadController offload = new OffloadController(new Handler(Looper.getMainLooper()),
mHardware, mContentResolver, mStatsManager, new SharedLog("test"));
+ final ArgumentCaptor<OffloadController.OffloadTetheringStatsProvider>
+ tetherStatsProviderCaptor =
+ ArgumentCaptor.forClass(OffloadController.OffloadTetheringStatsProvider.class);
verify(mStatsManager).registerNetworkStatsProvider(anyString(),
- mTetherStatsProviderCaptor.capture());
+ tetherStatsProviderCaptor.capture());
+ mTetherStatsProvider = tetherStatsProviderCaptor.getValue();
+ assertNotNull(mTetherStatsProvider);
+ mTetherStatsProvider.setProviderCallbackBinder(mTetherStatsProviderCb);
return offload;
}
@@ -413,9 +416,6 @@ public class OffloadControllerTest {
final OffloadController offload = makeOffloadController();
offload.start();
- final OffloadController.OffloadTetheringStatsProvider provider =
- mTetherStatsProviderCaptor.getValue();
-
final String ethernetIface = "eth1";
final String mobileIface = "rmnet_data0";
@@ -443,15 +443,15 @@ public class OffloadControllerTest {
inOrder.verify(mHardware, times(1)).getForwardedStats(eq(mobileIface));
// Verify that the fetched stats are stored.
- final NetworkStats ifaceStats = provider.getTetherStats(STATS_PER_IFACE);
- final NetworkStats uidStats = provider.getTetherStats(STATS_PER_UID);
+ final NetworkStats ifaceStats = mTetherStatsProvider.getTetherStats(STATS_PER_IFACE);
+ final NetworkStats uidStats = mTetherStatsProvider.getTetherStats(STATS_PER_UID);
final NetworkStats expectedIfaceStats = new NetworkStats(0L, 2)
- .addValues(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999))
- .addValues(buildTestEntry(STATS_PER_IFACE, ethernetIface, 12345, 54321));
+ .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999))
+ .addEntry(buildTestEntry(STATS_PER_IFACE, ethernetIface, 12345, 54321));
final NetworkStats expectedUidStats = new NetworkStats(0L, 2)
- .addValues(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999))
- .addValues(buildTestEntry(STATS_PER_UID, ethernetIface, 12345, 54321));
+ .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999))
+ .addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 12345, 54321));
assertTrue(orderInsensitiveEquals(expectedIfaceStats, ifaceStats));
assertTrue(orderInsensitiveEquals(expectedUidStats, uidStats));
@@ -462,13 +462,12 @@ public class OffloadControllerTest {
NetworkStats.class);
// Force pushing stats update to verify the stats reported.
- provider.pushTetherStats();
- verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(),
- ifaceStatsCaptor.capture(), uidStatsCaptor.capture());
+ mTetherStatsProvider.pushTetherStats();
+ verify(mTetherStatsProviderCb, times(1))
+ .notifyStatsUpdated(anyInt(), ifaceStatsCaptor.capture(), uidStatsCaptor.capture());
assertTrue(orderInsensitiveEquals(expectedIfaceStats, ifaceStatsCaptor.getValue()));
assertTrue(orderInsensitiveEquals(expectedUidStats, uidStatsCaptor.getValue()));
-
when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(
new ForwardedStats(100000, 100000));
offload.setUpstreamLinkProperties(null);
@@ -483,31 +482,31 @@ public class OffloadControllerTest {
inOrder.verifyNoMoreInteractions();
// Verify that the stored stats is accumulated.
- final NetworkStats ifaceStatsAccu = provider.getTetherStats(STATS_PER_IFACE);
- final NetworkStats uidStatsAccu = provider.getTetherStats(STATS_PER_UID);
+ final NetworkStats ifaceStatsAccu = mTetherStatsProvider.getTetherStats(STATS_PER_IFACE);
+ final NetworkStats uidStatsAccu = mTetherStatsProvider.getTetherStats(STATS_PER_UID);
final NetworkStats expectedIfaceStatsAccu = new NetworkStats(0L, 2)
- .addValues(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999))
- .addValues(buildTestEntry(STATS_PER_IFACE, ethernetIface, 112345, 154321));
+ .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999))
+ .addEntry(buildTestEntry(STATS_PER_IFACE, ethernetIface, 112345, 154321));
final NetworkStats expectedUidStatsAccu = new NetworkStats(0L, 2)
- .addValues(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999))
- .addValues(buildTestEntry(STATS_PER_UID, ethernetIface, 112345, 154321));
+ .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999))
+ .addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 112345, 154321));
assertTrue(orderInsensitiveEquals(expectedIfaceStatsAccu, ifaceStatsAccu));
assertTrue(orderInsensitiveEquals(expectedUidStatsAccu, uidStatsAccu));
// Verify that only diff of stats is reported.
reset(mTetherStatsProviderCb);
- provider.pushTetherStats();
+ mTetherStatsProvider.pushTetherStats();
final NetworkStats expectedIfaceStatsDiff = new NetworkStats(0L, 2)
- .addValues(buildTestEntry(STATS_PER_IFACE, mobileIface, 0, 0))
- .addValues(buildTestEntry(STATS_PER_IFACE, ethernetIface, 100000, 100000));
+ .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 0, 0))
+ .addEntry(buildTestEntry(STATS_PER_IFACE, ethernetIface, 100000, 100000));
final NetworkStats expectedUidStatsDiff = new NetworkStats(0L, 2)
- .addValues(buildTestEntry(STATS_PER_UID, mobileIface, 0, 0))
- .addValues(buildTestEntry(STATS_PER_UID, ethernetIface, 100000, 100000));
- verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(),
- ifaceStatsCaptor.capture(), uidStatsCaptor.capture());
+ .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 0, 0))
+ .addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 100000, 100000));
+ verify(mTetherStatsProviderCb, times(1))
+ .notifyStatsUpdated(anyInt(), ifaceStatsCaptor.capture(), uidStatsCaptor.capture());
assertTrue(orderInsensitiveEquals(expectedIfaceStatsDiff, ifaceStatsCaptor.getValue()));
assertTrue(orderInsensitiveEquals(expectedUidStatsDiff, uidStatsCaptor.getValue()));
}
@@ -529,19 +528,18 @@ public class OffloadControllerTest {
lp.setInterfaceName(ethernetIface);
offload.setUpstreamLinkProperties(lp);
- AbstractNetworkStatsProvider provider = mTetherStatsProviderCaptor.getValue();
final InOrder inOrder = inOrder(mHardware);
when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(true);
when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(true);
// Applying an interface quota to the current upstream immediately sends it to the hardware.
- provider.setLimit(ethernetIface, ethernetLimit);
+ mTetherStatsProvider.onSetLimit(ethernetIface, ethernetLimit);
waitForIdle();
inOrder.verify(mHardware).setDataLimit(ethernetIface, ethernetLimit);
inOrder.verifyNoMoreInteractions();
// Applying an interface quota to another upstream does not take any immediate action.
- provider.setLimit(mobileIface, mobileLimit);
+ mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit);
waitForIdle();
inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong());
@@ -554,7 +552,7 @@ public class OffloadControllerTest {
// Setting a limit of ITetheringStatsProvider.QUOTA_UNLIMITED causes the limit to be set
// to Long.MAX_VALUE.
- provider.setLimit(mobileIface, ITetheringStatsProvider.QUOTA_UNLIMITED);
+ mTetherStatsProvider.onSetLimit(mobileIface, ITetheringStatsProvider.QUOTA_UNLIMITED);
waitForIdle();
inOrder.verify(mHardware).setDataLimit(mobileIface, Long.MAX_VALUE);
@@ -562,7 +560,7 @@ public class OffloadControllerTest {
when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(false);
lp.setInterfaceName(ethernetIface);
offload.setUpstreamLinkProperties(lp);
- provider.setLimit(mobileIface, mobileLimit);
+ mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit);
waitForIdle();
inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong());
@@ -571,7 +569,7 @@ public class OffloadControllerTest {
when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(false);
lp.setInterfaceName(mobileIface);
offload.setUpstreamLinkProperties(lp);
- provider.setLimit(mobileIface, mobileLimit);
+ mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit);
waitForIdle();
inOrder.verify(mHardware).getForwardedStats(ethernetIface);
inOrder.verify(mHardware).stopOffloadControl();
@@ -587,7 +585,7 @@ public class OffloadControllerTest {
OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue();
callback.onStoppedLimitReached();
- verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), any(), any());
+ verify(mTetherStatsProviderCb, times(1)).notifyStatsUpdated(anyInt(), any(), any());
}
@Test
@@ -691,7 +689,7 @@ public class OffloadControllerTest {
verify(mHardware, times(1)).getForwardedStats(eq(RMNET0));
verify(mHardware, times(1)).getForwardedStats(eq(WLAN0));
// TODO: verify the exact stats reported.
- verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), any(), any());
+ verify(mTetherStatsProviderCb, times(1)).notifyStatsUpdated(anyInt(), any(), any());
verifyNoMoreInteractions(mTetherStatsProviderCb);
verifyNoMoreInteractions(mHardware);
}
@@ -756,7 +754,7 @@ public class OffloadControllerTest {
// Verify forwarded stats behaviour.
verify(mHardware, times(1)).getForwardedStats(eq(RMNET0));
verify(mHardware, times(1)).getForwardedStats(eq(WLAN0));
- verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), any(), any());
+ verify(mTetherStatsProviderCb, times(1)).notifyStatsUpdated(anyInt(), any(), any());
verifyNoMoreInteractions(mTetherStatsProviderCb);
// TODO: verify local prefixes and downstreams are also pushed to the HAL.
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
index af7ad662b8a5..820c852a271a 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
@@ -46,6 +46,8 @@ import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+import static com.android.server.connectivity.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE;
+
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -53,7 +55,6 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.notNull;
-import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.any;
@@ -188,6 +189,7 @@ public class TetheringTest {
@Mock private NetworkRequest mNetworkRequest;
@Mock private ConnectivityManager mCm;
@Mock private EthernetManager mEm;
+ @Mock private TetheringNotificationUpdater mNotificationUpdater;
private final MockIpServerDependencies mIpServerDependencies =
spy(new MockIpServerDependencies());
@@ -207,6 +209,7 @@ public class TetheringTest {
private PhoneStateListener mPhoneStateListener;
private InterfaceConfigurationParcel mInterfaceConfiguration;
+
private class TestContext extends BroadcastInterceptingContext {
TestContext(Context base) {
super(base);
@@ -249,11 +252,6 @@ public class TetheringTest {
if (TelephonyManager.class.equals(serviceClass)) return Context.TELEPHONY_SERVICE;
return super.getSystemServiceName(serviceClass);
}
-
- @Override
- public Context createContextAsUser(UserHandle user, int flags) {
- return mContext;
- }
}
public class MockIpServerDependencies extends IpServer.Dependencies {
@@ -315,12 +313,10 @@ public class TetheringTest {
public class MockTetheringDependencies extends TetheringDependencies {
StateMachine mUpstreamNetworkMonitorMasterSM;
ArrayList<IpServer> mIpv6CoordinatorNotifyList;
- int mIsTetheringSupportedCalls;
public void reset() {
mUpstreamNetworkMonitorMasterSM = null;
mIpv6CoordinatorNotifyList = null;
- mIsTetheringSupportedCalls = 0;
}
@Override
@@ -354,7 +350,6 @@ public class TetheringTest {
@Override
public boolean isTetheringSupported() {
- mIsTetheringSupportedCalls++;
return true;
}
@@ -384,6 +379,11 @@ public class TetheringTest {
// TODO: add test for bluetooth tethering.
return null;
}
+
+ @Override
+ public TetheringNotificationUpdater getNotificationUpdater(Context ctx) {
+ return mNotificationUpdater;
+ }
}
private static UpstreamNetworkState buildMobileUpstreamState(boolean withIPv4,
@@ -472,7 +472,6 @@ public class TetheringTest {
when(mOffloadHardwareInterface.getForwardedStats(any())).thenReturn(mForwardedStats);
mServiceContext = new TestContext(mContext);
- when(mContext.getSystemService(Context.NOTIFICATION_SERVICE)).thenReturn(null);
mContentResolver = new MockContentResolver(mServiceContext);
mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
mIntents = new Vector<>();
@@ -605,7 +604,8 @@ public class TetheringTest {
// it creates a IpServer and sends out a broadcast indicating that the
// interface is "available".
if (emulateInterfaceStatusChanged) {
- assertEquals(1, mTetheringDependencies.mIsTetheringSupportedCalls);
+ // There is 1 IpServer state change event: STATE_AVAILABLE
+ verify(mNotificationUpdater, times(1)).onDownstreamChanged(DOWNSTREAM_NONE);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
verify(mWifiManager).updateInterfaceIpState(
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
@@ -689,9 +689,8 @@ public class TetheringTest {
verifyNoMoreInteractions(mWifiManager);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY);
verify(mUpstreamNetworkMonitor, times(1)).startObserveAllNetworks();
- // This will be called twice, one is on entering IpServer.STATE_AVAILABLE,
- // and another one is on IpServer.STATE_TETHERED/IpServer.STATE_LOCAL_ONLY.
- assertEquals(2, mTetheringDependencies.mIsTetheringSupportedCalls);
+ // There are 2 IpServer state change events: STATE_AVAILABLE -> STATE_LOCAL_ONLY
+ verify(mNotificationUpdater, times(2)).onDownstreamChanged(DOWNSTREAM_NONE);
// Emulate externally-visible WifiManager effects, when hotspot mode
// is being torn down.
@@ -917,7 +916,8 @@ public class TetheringTest {
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED);
mLooper.dispatchAll();
- assertEquals(1, mTetheringDependencies.mIsTetheringSupportedCalls);
+ // There is 1 IpServer state change event: STATE_AVAILABLE
+ verify(mNotificationUpdater, times(1)).onDownstreamChanged(DOWNSTREAM_NONE);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
verify(mWifiManager).updateInterfaceIpState(
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
@@ -961,9 +961,9 @@ public class TetheringTest {
// In tethering mode, in the default configuration, an explicit request
// for a mobile network is also made.
verify(mUpstreamNetworkMonitor, times(1)).registerMobileNetworkRequest();
- // This will be called twice, one is on entering IpServer.STATE_AVAILABLE,
- // and another one is on IpServer.STATE_TETHERED/IpServer.STATE_LOCAL_ONLY.
- assertEquals(2, mTetheringDependencies.mIsTetheringSupportedCalls);
+ // There are 2 IpServer state change events: STATE_AVAILABLE -> STATE_TETHERED
+ verify(mNotificationUpdater, times(1)).onDownstreamChanged(DOWNSTREAM_NONE);
+ verify(mNotificationUpdater, times(1)).onDownstreamChanged(eq(1 << TETHERING_WIFI));
/////
// We do not currently emulate any upstream being found.
@@ -1034,9 +1034,10 @@ public class TetheringTest {
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
verify(mWifiManager).updateInterfaceIpState(
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED);
- // There are 3 state change event:
- // AVAILABLE -> STATE_TETHERED -> STATE_AVAILABLE.
- assertEquals(3, mTetheringDependencies.mIsTetheringSupportedCalls);
+ // There are 3 IpServer state change event:
+ // STATE_AVAILABLE -> STATE_TETHERED -> STATE_AVAILABLE.
+ verify(mNotificationUpdater, times(2)).onDownstreamChanged(DOWNSTREAM_NONE);
+ verify(mNotificationUpdater, times(1)).onDownstreamChanged(eq(1 << TETHERING_WIFI));
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
// This is called, but will throw.
verify(mNetd, times(1)).ipfwdEnableForwarding(TETHERING_NAME);
@@ -1071,9 +1072,6 @@ public class TetheringTest {
ural.onUserRestrictionsChanged();
verify(mockTethering, times(expectedInteractionsWithShowNotification))
- .showTetheredNotification(anyInt(), eq(false));
-
- verify(mockTethering, times(expectedInteractionsWithShowNotification))
.untetherAll();
}
@@ -1429,9 +1427,8 @@ public class TetheringTest {
verifyNoMoreInteractions(mNetd);
verifyTetheringBroadcast(TEST_P2P_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY);
verify(mUpstreamNetworkMonitor, times(1)).startObserveAllNetworks();
- // This will be called twice, one is on entering IpServer.STATE_AVAILABLE,
- // and another one is on IpServer.STATE_TETHERED/IpServer.STATE_LOCAL_ONLY.
- assertEquals(2, mTetheringDependencies.mIsTetheringSupportedCalls);
+ // There are 2 IpServer state change events: STATE_AVAILABLE -> STATE_LOCAL_ONLY
+ verify(mNotificationUpdater, times(2)).onDownstreamChanged(DOWNSTREAM_NONE);
assertEquals(TETHER_ERROR_NO_ERROR, mTethering.getLastTetherError(TEST_P2P_IFNAME));
diff --git a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/config.xml
index 6f692c8021c0..b9c5f1ded3d5 100644
--- a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/config.xml
+++ b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/config.xml
@@ -16,6 +16,9 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string translatable="false" name="config_mainBuiltInDisplayCutout"></string>
+ <string translatable="false" name="config_mainBuiltInDisplayCutoutRectApproximation"></string>
+
<!-- Height of the status bar in portrait. The height should be
Max((status bar content height + waterfall top size), top cutout size) -->
<dimen name="status_bar_height_portrait">28dp</dimen>
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index 5d699c01d138..5d97d213928f 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -1232,6 +1232,7 @@ public class AccessibilityWindowManager {
*/
public boolean computePartialInteractiveRegionForWindowLocked(int windowId,
@NonNull Region outRegion) {
+ windowId = resolveParentWindowIdLocked(windowId);
final DisplayWindowsObserver observer = getDisplayWindowObserverByWindowIdLocked(windowId);
if (observer != null) {
return observer.computePartialInteractiveRegionForWindowLocked(windowId, outRegion);
@@ -1436,6 +1437,7 @@ public class AccessibilityWindowManager {
*/
@Nullable
public WindowInfo findWindowInfoByIdLocked(int windowId) {
+ windowId = resolveParentWindowIdLocked(windowId);
final DisplayWindowsObserver observer = getDisplayWindowObserverByWindowIdLocked(windowId);
if (observer != null) {
return observer.findWindowInfoByIdLocked(windowId);
diff --git a/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java b/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
index 3376f2bd74f5..5de817101177 100644
--- a/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
+++ b/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
@@ -62,7 +62,7 @@ import java.util.function.Consumer;
* side flow.
*
* <p>
- * This class is thread safe.
+ * This class should hold the same lock as {@link Session} as they call into each other.
*/
final class InlineSuggestionSession {
@@ -105,12 +105,12 @@ final class InlineSuggestionSession {
private boolean mImeInputViewStarted = false;
InlineSuggestionSession(InputMethodManagerInternal inputMethodManagerInternal,
- int userId, ComponentName componentName, Handler handler) {
+ int userId, ComponentName componentName, Handler handler, Object lock) {
mInputMethodManagerInternal = inputMethodManagerInternal;
mUserId = userId;
mComponentName = componentName;
mHandler = handler;
- mLock = new Object();
+ mLock = lock;
mImeStatusListener = new ImeStatusListener() {
@Override
public void onInputMethodStartInputView(AutofillId imeFieldId) {
@@ -261,29 +261,27 @@ final class InlineSuggestionSession {
mHandler = handler;
mTimeoutCallback = () -> {
Log.w(TAG, "Timed out waiting for IME callback InlineSuggestionsRequest.");
- synchronized (mLock) {
- completeIfNotLocked(null);
- }
+ completeIfNot(null);
};
mHandler.postDelayed(mTimeoutCallback, INLINE_REQUEST_TIMEOUT_MS);
}
- private void completeIfNotLocked(@Nullable ImeResponse response) {
- if (mResponse.isDone()) {
- return;
+ private void completeIfNot(@Nullable ImeResponse response) {
+ synchronized (mLock) {
+ if (mResponse.isDone()) {
+ return;
+ }
+ mResponse.complete(response);
+ mRequestConsumer.accept(response == null ? null : response.mRequest);
+ mHandler.removeCallbacks(mTimeoutCallback);
}
- mResponse.complete(response);
- mRequestConsumer.accept(response == null ? null : response.mRequest);
- mHandler.removeCallbacks(mTimeoutCallback);
}
@BinderThread
@Override
public void onInlineSuggestionsUnsupported() throws RemoteException {
if (sDebug) Log.d(TAG, "onInlineSuggestionsUnsupported() called.");
- synchronized (mLock) {
- completeIfNotLocked(null);
- }
+ completeIfNot(null);
}
@BinderThread
@@ -302,13 +300,9 @@ final class InlineSuggestionSession {
mImeStatusListener.onInputMethodFinishInputView(imeFieldId);
}
if (request != null && callback != null) {
- synchronized (mLock) {
- completeIfNotLocked(new ImeResponse(request, callback));
- }
+ completeIfNot(new ImeResponse(request, callback));
} else {
- synchronized (mLock) {
- completeIfNotLocked(null);
- }
+ completeIfNot(null);
}
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 5064663871e3..de3111867ab8 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -756,7 +756,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
setClientLocked(client);
mInlineSuggestionSession = new InlineSuggestionSession(inputMethodManagerInternal, userId,
- componentName, handler);
+ componentName, handler, mLock);
mMetricsLogger.write(newLogMaker(MetricsEvent.AUTOFILL_SESSION_STARTED)
.addTaggedData(MetricsEvent.FIELD_AUTOFILL_FLAGS, flags));
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 942d5633e381..9e19ad2aecf4 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -98,9 +98,9 @@ java_library_static {
"android.hardware.power-V1.0-java",
"android.hardware.tv.cec-V1.0-java",
"android.hardware.vibrator-java",
+ "android.net.ipsec.ike.stubs.module_libs_api",
"app-compat-annotations",
"framework-tethering-stubs-module_libs_api",
- "ike-stubs",
],
required: [
@@ -128,7 +128,6 @@ java_library_static {
"android.hidl.manager-V1.2-java",
"dnsresolver_aidl_interface-V2-java",
"netd_event_listener_interface-java",
- "ike-stubs",
"overlayable_policy_aidl-java",
],
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index dadcd4e03f89..9fddafbe023b 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -991,4 +991,9 @@ public abstract class PackageManagerInternal {
* @param enabled true if visibility blocks should be logged
*/
public abstract void setVisibilityLogging(String packageName, boolean enabled);
+
+ /**
+ * Returns if a package name is a valid system package.
+ */
+ public abstract boolean isSystemPackage(@NonNull String packageName);
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index ecf1f134a3de..deae459e390d 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2083,9 +2083,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
private void enforceNetworkFactoryPermission() {
- mContext.enforceCallingOrSelfPermission(
+ enforceAnyPermissionOf(
android.Manifest.permission.NETWORK_FACTORY,
- "ConnectivityService");
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
}
private boolean checkSettingsPermission() {
@@ -7817,7 +7817,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
getMatchingPermissionedCallbacks(nai);
for (final IConnectivityDiagnosticsCallback cb : results) {
try {
- cb.onConnectivityReport(report);
+ cb.onConnectivityReportAvailable(report);
} catch (RemoteException ex) {
loge("Error invoking onConnectivityReport", ex);
}
@@ -7892,8 +7892,15 @@ public class ConnectivityService extends IConnectivityManager.Stub
return true;
}
- if (!mLocationPermissionChecker.checkLocationPermission(
- callbackPackageName, null /* featureId */, callbackUid, null /* message */)) {
+ // LocationPermissionChecker#checkLocationPermission can throw SecurityException if the uid
+ // and package name don't match. Throwing on the CS thread is not acceptable, so wrap the
+ // call in a try-catch.
+ try {
+ if (!mLocationPermissionChecker.checkLocationPermission(
+ callbackPackageName, null /* featureId */, callbackUid, null /* message */)) {
+ return false;
+ }
+ } catch (SecurityException e) {
return false;
}
diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java
index 318a03074492..191a9bc7651d 100644
--- a/services/core/java/com/android/server/DynamicSystemService.java
+++ b/services/core/java/com/android/server/DynamicSystemService.java
@@ -22,13 +22,9 @@ import android.gsi.AvbPublicKey;
import android.gsi.GsiProgress;
import android.gsi.IGsiService;
import android.gsi.IGsiServiceCallback;
-import android.gsi.IGsid;
import android.os.Environment;
-import android.os.IBinder;
-import android.os.IBinder.DeathRecipient;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.image.IDynamicSystemService;
@@ -42,9 +38,8 @@ import java.io.File;
* DynamicSystemService implements IDynamicSystemService. It provides permission check before
* passing requests to gsid
*/
-public class DynamicSystemService extends IDynamicSystemService.Stub implements DeathRecipient {
+public class DynamicSystemService extends IDynamicSystemService.Stub {
private static final String TAG = "DynamicSystemService";
- private static final String NO_SERVICE_ERROR = "no gsiservice";
private static final int GSID_ROUGH_TIMEOUT_MS = 8192;
private static final String PATH_DEFAULT = "/data/gsi/";
private Context mContext;
@@ -55,57 +50,12 @@ public class DynamicSystemService extends IDynamicSystemService.Stub implements
mContext = context;
}
- private static IGsiService connect(DeathRecipient recipient) throws RemoteException {
- IBinder binder = ServiceManager.getService("gsiservice");
- if (binder == null) {
- return null;
- }
- /**
- * The init will restart gsiservice if it crashed and the proxy object will need to be
- * re-initialized in this case.
- */
- binder.linkToDeath(recipient, 0);
-
- IGsid gsid = IGsid.Stub.asInterface(binder);
- return gsid.getClient();
- }
-
- /** implements DeathRecipient */
- @Override
- public void binderDied() {
- Slog.w(TAG, "gsiservice died; reconnecting");
- synchronized (this) {
- mGsiService = null;
- }
- }
-
private IGsiService getGsiService() throws RemoteException {
checkPermission();
-
- if (!"running".equals(SystemProperties.get("init.svc.gsid"))) {
- SystemProperties.set("ctl.start", "gsid");
+ if (mGsiService != null) {
+ return mGsiService;
}
-
- for (int sleepMs = 64; sleepMs <= (GSID_ROUGH_TIMEOUT_MS << 1); sleepMs <<= 1) {
- synchronized (this) {
- if (mGsiService == null) {
- mGsiService = connect(this);
- }
- if (mGsiService != null) {
- return mGsiService;
- }
- }
-
- try {
- Slog.d(TAG, "GsiService is not ready, wait for " + sleepMs + "ms");
- Thread.sleep(sleepMs);
- } catch (InterruptedException e) {
- Slog.e(TAG, "Interrupted when waiting for GSID");
- return null;
- }
- }
-
- throw new RemoteException(NO_SERVICE_ERROR);
+ return IGsiService.Stub.asInterface(waitForService("gsiservice"));
}
private void checkPermission() {
@@ -133,6 +83,7 @@ public class DynamicSystemService extends IDynamicSystemService.Stub implements
@Override
public boolean startInstallation(String dsuSlot) throws RemoteException {
IGsiService service = getGsiService();
+ mGsiService = service;
// priority from high to low: sysprop -> sdcard -> /data
String path = SystemProperties.get("os.aot.path");
if (path.isEmpty()) {
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index b464422e9e3d..52a1b5a62941 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -354,6 +354,10 @@ public class PackageWatchdog {
*/
public void onPackageFailure(List<VersionedPackage> packages,
@FailureReasons int failureReason) {
+ if (packages == null) {
+ Slog.w(TAG, "Could not resolve a list of failing packages");
+ return;
+ }
mLongTaskHandler.post(() -> {
synchronized (mLock) {
if (mAllObservers.isEmpty()) {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index b7d050a25484..2c220d3f8861 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -154,7 +154,6 @@ import com.android.internal.util.HexDump;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.internal.widget.LockPatternUtils;
-import com.android.server.SystemService.TargetUser;
import com.android.server.pm.Installer;
import com.android.server.storage.AppFuseBridge;
import com.android.server.storage.StorageSessionController;
@@ -233,6 +232,8 @@ class StorageManagerService extends IStorageManager.Stub
private static final String FUSE_ENABLED = "fuse_enabled";
private static final boolean DEFAULT_FUSE_ENABLED = true;
+ private final Set<Integer> mFuseMountedUser = new ArraySet<>();
+
public static class Lifecycle extends SystemService {
private StorageManagerService mStorageManagerService;
@@ -1497,6 +1498,9 @@ class StorageManagerService extends IStorageManager.Stub
@GuardedBy("mLock")
private void onVolumeStateChangedLocked(VolumeInfo vol, int oldState, int newState) {
+ if (vol.type == VolumeInfo.TYPE_EMULATED && newState != VolumeInfo.STATE_MOUNTED) {
+ mFuseMountedUser.remove(vol.getMountUserId());
+ }
// Remember that we saw this volume so we're ready to accept user
// metadata, or so we can annoy them when a private volume is ejected
if (!TextUtils.isEmpty(vol.fsUuid)) {
@@ -2075,39 +2079,86 @@ class StorageManagerService extends IStorageManager.Stub
mount(vol);
}
+ private void remountAppStorageDirs(Map<Integer, String> pidPkgMap, int userId) {
+ for (Entry<Integer, String> entry : pidPkgMap.entrySet()) {
+ final int pid = entry.getKey();
+ final String packageName = entry.getValue();
+ Slog.i(TAG, "Remounting storage for pid: " + pid);
+ final String[] sharedPackages =
+ mPmInternal.getSharedUserPackagesForPackage(packageName, userId);
+ final int uid = mPmInternal.getPackageUidInternal(packageName, 0, userId);
+ final String[] packages =
+ sharedPackages.length != 0 ? sharedPackages : new String[]{packageName};
+ try {
+ mVold.remountAppStorageDirs(uid, pid, packages);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+ }
+
private void mount(VolumeInfo vol) {
try {
// TODO(b/135341433): Remove paranoid logging when FUSE is stable
Slog.i(TAG, "Mounting volume " + vol);
mVold.mount(vol.id, vol.mountFlags, vol.mountUserId, new IVoldMountCallback.Stub() {
- @Override
- public boolean onVolumeChecking(FileDescriptor fd, String path,
- String internalPath) {
- vol.path = path;
- vol.internalPath = internalPath;
- ParcelFileDescriptor pfd = new ParcelFileDescriptor(fd);
+ @Override
+ public boolean onVolumeChecking(FileDescriptor fd, String path,
+ String internalPath) {
+ vol.path = path;
+ vol.internalPath = internalPath;
+ ParcelFileDescriptor pfd = new ParcelFileDescriptor(fd);
+ try {
+ mStorageSessionController.onVolumeMount(pfd, vol);
+ return true;
+ } catch (ExternalStorageServiceException e) {
+ Slog.e(TAG, "Failed to mount volume " + vol, e);
+
+ int nextResetSeconds = REMOTE_TIMEOUT_SECONDS * 2;
+ Slog.i(TAG, "Scheduling reset in " + nextResetSeconds + "s");
+ mHandler.removeMessages(H_RESET);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(H_RESET),
+ TimeUnit.SECONDS.toMillis(nextResetSeconds));
+ return false;
+ } finally {
try {
- mStorageSessionController.onVolumeMount(pfd, vol);
- return true;
- } catch (ExternalStorageServiceException e) {
- Slog.e(TAG, "Failed to mount volume " + vol, e);
-
- int nextResetSeconds = REMOTE_TIMEOUT_SECONDS * 2;
- Slog.i(TAG, "Scheduling reset in " + nextResetSeconds + "s");
- mHandler.removeMessages(H_RESET);
- mHandler.sendMessageDelayed(mHandler.obtainMessage(H_RESET),
- TimeUnit.SECONDS.toMillis(nextResetSeconds));
- return false;
- } finally {
- try {
- pfd.close();
- } catch (Exception e) {
- Slog.e(TAG, "Failed to close FUSE device fd", e);
- }
+ pfd.close();
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to close FUSE device fd", e);
}
}
- });
+ }
+ });
Slog.i(TAG, "Mounted volume " + vol);
+ if (vol.type == VolumeInfo.TYPE_EMULATED) {
+ final int userId = vol.getMountUserId();
+ mFuseMountedUser.add(userId);
+ // Async remount app storage so it won't block the main thread.
+ new Thread(() -> {
+ Map<Integer, String> pidPkgMap = null;
+ // getProcessesWithPendingBindMounts() could fail when a new app process is
+ // starting and it's not planning to mount storage dirs in zygote, but it's
+ // rare, so we retry 5 times and hope we can get the result successfully.
+ for (int i = 0; i < 5; i++) {
+ try {
+ pidPkgMap = LocalServices.getService(ActivityManagerInternal.class)
+ .getProcessesWithPendingBindMounts(vol.getMountUserId());
+ break;
+ } catch (IllegalStateException e) {
+ Slog.i(TAG, "Some processes are starting, retry");
+ // Wait 100ms and retry so hope the pending process is started.
+ SystemClock.sleep(100);
+ }
+ }
+ if (pidPkgMap != null) {
+ remountAppStorageDirs(pidPkgMap, userId);
+ } else {
+ Slog.wtf(TAG, "Not able to getStorageNotOptimizedProcesses() after"
+ + " 5 retries");
+ }
+
+ }).start();
+ }
} catch (Exception e) {
Slog.wtf(TAG, e);
}
@@ -4309,7 +4360,8 @@ class StorageManagerService extends IStorageManager.Stub
pw.println();
pw.println("mObbPathToStateMap:");
pw.increaseIndent();
- final Iterator<Entry<String, ObbState>> maps = mObbPathToStateMap.entrySet().iterator();
+ final Iterator<Entry<String, ObbState>> maps =
+ mObbPathToStateMap.entrySet().iterator();
while (maps.hasNext()) {
final Entry<String, ObbState> e = maps.next();
pw.print(e.getKey());
@@ -4350,45 +4402,41 @@ class StorageManagerService extends IStorageManager.Stub
}
/**
- * Check if fuse is running in target user, if it's running then setup its obb directories.
- * TODO: System server should store a list of active pids that obb is not mounted and use it.
+ * Check if fuse is running in target user, if it's running then setup its storage dirs.
+ * Return true if storage dirs are mounted.
*/
@Override
- public void prepareObbDirs(int userId, Set<String> packageList, String processName) {
- String fuseRunningUsersList = SystemProperties.get("vold.fuse_running_users", "");
- String[] fuseRunningUsers = fuseRunningUsersList.split(",");
- boolean fuseReady = false;
- String targetUserId = String.valueOf(userId);
- for (String user : fuseRunningUsers) {
- if (targetUserId.equals(user)) {
- fuseReady = true;
- }
+ public boolean prepareStorageDirs(int userId, Set<String> packageList,
+ String processName) {
+ if (!mFuseMountedUser.contains(userId)) {
+ Slog.w(TAG, "User " + userId + " is not unlocked yet so skip mounting obb");
+ return false;
}
- if (fuseReady) {
- try {
- final IVold vold = IVold.Stub.asInterface(
- ServiceManager.getServiceOrThrow("vold"));
- for (String pkg : packageList) {
- final String packageObbDir =
- String.format("/storage/emulated/%d/Android/obb/%s/", userId, pkg);
- final String packageDataDir =
- String.format("/storage/emulated/%d/Android/data/%s/",
- userId, pkg);
-
- // Create package obb and data dir if it doesn't exist.
- File file = new File(packageObbDir);
- if (!file.exists()) {
- vold.setupAppDir(packageObbDir, mPmInternal.getPackage(pkg).getUid());
- }
- file = new File(packageDataDir);
- if (!file.exists()) {
- vold.setupAppDir(packageDataDir, mPmInternal.getPackage(pkg).getUid());
- }
+ try {
+ final IVold vold = IVold.Stub.asInterface(
+ ServiceManager.getServiceOrThrow("vold"));
+ for (String pkg : packageList) {
+ final String packageObbDir =
+ String.format("/storage/emulated/%d/Android/obb/%s/", userId, pkg);
+ final String packageDataDir =
+ String.format("/storage/emulated/%d/Android/data/%s/",
+ userId, pkg);
+
+ // Create package obb and data dir if it doesn't exist.
+ File file = new File(packageObbDir);
+ if (!file.exists()) {
+ vold.setupAppDir(packageObbDir, mPmInternal.getPackage(pkg).getUid());
+ }
+ file = new File(packageDataDir);
+ if (!file.exists()) {
+ vold.setupAppDir(packageDataDir, mPmInternal.getPackage(pkg).getUid());
}
- } catch (ServiceManager.ServiceNotFoundException | RemoteException e) {
- Slog.e(TAG, "Unable to create obb and data directories for " + processName, e);
}
+ } catch (ServiceManager.ServiceNotFoundException | RemoteException e) {
+ Slog.e(TAG, "Unable to create obb and data directories for " + processName,e);
+ return false;
}
+ return true;
}
@Override
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index 35a980286d8f..f772a4a5a300 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -53,6 +53,7 @@ import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
@@ -230,6 +231,7 @@ class TestNetworkService extends ITestNetworkManager.Stub {
@Nullable LinkProperties lp,
boolean isMetered,
int callingUid,
+ @NonNull int[] administratorUids,
@NonNull IBinder binder)
throws RemoteException, SocketException {
Objects.requireNonNull(looper, "missing Looper");
@@ -248,6 +250,7 @@ class TestNetworkService extends ITestNetworkManager.Stub {
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
nc.setNetworkSpecifier(new StringNetworkSpecifier(iface));
+ nc.setAdministratorUids(intArrayToList(administratorUids));
if (!isMetered) {
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
}
@@ -290,6 +293,14 @@ class TestNetworkService extends ITestNetworkManager.Stub {
return new TestNetworkAgent(looper, context, ni, nc, lp, callingUid, binder);
}
+ private List<Integer> intArrayToList(@NonNull int[] array) {
+ final List<Integer> list = new ArrayList<>(array.length);
+ for (final int i : array) {
+ list.add(i);
+ }
+ return list;
+ }
+
/**
* Sets up a Network with extremely limited privileges, guarded by the MANAGE_TEST_NETWORKS
* permission.
@@ -301,6 +312,7 @@ class TestNetworkService extends ITestNetworkManager.Stub {
@NonNull String iface,
@Nullable LinkProperties lp,
boolean isMetered,
+ @NonNull int[] administratorUids,
@NonNull IBinder binder) {
enforceTestNetworkPermissions(mContext);
@@ -335,6 +347,7 @@ class TestNetworkService extends ITestNetworkManager.Stub {
lp,
isMetered,
callingUid,
+ administratorUids,
binder);
mTestNetworkTracker.put(agent.getNetwork().netId, agent);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index cfc8b459b8a5..0ebb5bba539d 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -137,6 +137,10 @@ public final class ActiveServices {
private static final boolean SHOW_DUNGEON_NOTIFICATION = false;
+ //TODO: remove this when development is done.
+ private static final int DEBUG_FGS_ALLOW_WHILE_IN_USE = 0;
+ private static final int DEBUG_FGS_ENFORCE_TYPE = 1;
+
// How long we wait for a service to finish executing.
static final int SERVICE_TIMEOUT = 20*1000;
@@ -1701,7 +1705,7 @@ public final class ActiveServices {
if (acceptances > 0 || rejections > 0) {
FrameworkStatsLog.write(
FrameworkStatsLog.FOREGROUND_SERVICE_APP_OP_SESSION_ENDED,
- mProcessRecord.uid, opToEnum(op),
+ mProcessRecord.uid, AppOpsManager.opToLoggingId(op),
modeToEnum(mAppOpModes.get(op)),
acceptances, rejections
);
@@ -1725,22 +1729,6 @@ public final class ActiveServices {
}
}
- /** Maps AppOp op value to atoms.proto enum. */
- private static int opToEnum(int op) {
- switch (op) {
- case AppOpsManager.OP_COARSE_LOCATION: return FrameworkStatsLog
- .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_COARSE_LOCATION;
- case AppOpsManager.OP_FINE_LOCATION: return FrameworkStatsLog
- .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_FINE_LOCATION;
- case AppOpsManager.OP_CAMERA: return FrameworkStatsLog
- .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_CAMERA;
- case AppOpsManager.OP_RECORD_AUDIO: return FrameworkStatsLog
- .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_RECORD_AUDIO;
- default: return FrameworkStatsLog
- .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_NONE;
- }
- }
-
private void cancelForegroundNotificationLocked(ServiceRecord r) {
if (r.foregroundId != 0) {
// First check to see if this app has any other active foreground services
@@ -4931,10 +4919,20 @@ public final class ActiveServices {
if (!r.isForeground) {
continue;
}
- if (!r.mAllowWhileInUsePermissionInFgs
- && r.mInfoDenyWhileInUsePermissionInFgs != null) {
- final String msg = r.mInfoDenyWhileInUsePermissionInFgs
- + " affected while-in-use permission:"
+ if (mode == DEBUG_FGS_ALLOW_WHILE_IN_USE) {
+ if (!r.mAllowWhileInUsePermissionInFgs
+ && r.mInfoDenyWhileInUsePermissionInFgs != null) {
+ final String msg = r.mInfoDenyWhileInUsePermissionInFgs
+ + " affected while-in-use permission:"
+ + AppOpsManager.opToPublicName(op);
+ Slog.wtf(TAG, msg);
+ }
+ } else if (mode == DEBUG_FGS_ENFORCE_TYPE) {
+ final String msg =
+ "FGS Missing foregroundServiceType in manifest file [callingPackage: "
+ + r.mRecentCallingPackage
+ + "; intent:" + r.intent.getIntent()
+ + "] affected while-in-use permission:"
+ AppOpsManager.opToPublicName(op);
Slog.wtf(TAG, msg);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f64272bf08d1..ebca1f7e97a4 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2179,9 +2179,17 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) {
+ Process.enableFreezer(false);
+ }
+
if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext,
"meminfo", pw)) return;
PriorityDump.dump(mPriorityDumper, fd, pw, args);
+
+ if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) {
+ Process.enableFreezer(true);
+ }
}
}
@@ -2193,9 +2201,17 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) {
+ Process.enableFreezer(false);
+ }
+
if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext,
"gfxinfo", pw)) return;
mActivityManagerService.dumpGraphicsHardwareUsage(fd, pw, args);
+
+ if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) {
+ Process.enableFreezer(true);
+ }
}
}
@@ -2207,9 +2223,17 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) {
+ Process.enableFreezer(false);
+ }
+
if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext,
"dbinfo", pw)) return;
mActivityManagerService.dumpDbInfo(fd, pw, args);
+
+ if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) {
+ Process.enableFreezer(true);
+ }
}
}
@@ -17538,8 +17562,11 @@ public class ActivityManagerService extends IActivityManager.Stub
final void setProcessTrackerStateLocked(ProcessRecord proc, int memFactor, long now) {
if (proc.thread != null && proc.baseProcessTracker != null) {
- proc.baseProcessTracker.setState(
- proc.getReportedProcState(), memFactor, now, proc.pkgList.mPkgList);
+ final int procState = proc.getReportedProcState();
+ if (procState != PROCESS_STATE_NONEXISTENT) {
+ proc.baseProcessTracker.setState(
+ procState, memFactor, now, proc.pkgList.mPkgList);
+ }
}
}
@@ -18714,6 +18741,11 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
+ public Map<Integer, String> getProcessesWithPendingBindMounts(int userId) {
+ return mProcessList.getProcessesWithPendingBindMounts(userId);
+ }
+
+ @Override
public boolean isSystemReady() {
// no need to synchronize(this) just to read & return the value
return mSystemReady;
@@ -19810,6 +19842,7 @@ public class ActivityManagerService extends IActivityManager.Stub
void setPermissions(@Nullable String[] permissions) {
mPermissions = permissions;
+ PackageManager.invalidatePackageInfoCache();
}
@Override
diff --git a/services/core/java/com/android/server/am/BugReportHandlerUtil.java b/services/core/java/com/android/server/am/BugReportHandlerUtil.java
index ba89fce0b3f8..03f4a54086a8 100644
--- a/services/core/java/com/android/server/am/BugReportHandlerUtil.java
+++ b/services/core/java/com/android/server/am/BugReportHandlerUtil.java
@@ -16,15 +16,20 @@
package com.android.server.am;
+import static android.app.AppOpsManager.OP_NONE;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import android.app.Activity;
import android.app.BroadcastOptions;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Binder;
+import android.os.BugreportManager;
+import android.os.BugreportParams;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
@@ -110,9 +115,17 @@ public final class BugReportHandlerUtil {
options.setBackgroundActivityStartsAllowed(true);
final long identity = Binder.clearCallingIdentity();
try {
- context.sendBroadcastAsUser(intent, UserHandle.of(handlerUser),
+ // Handler app's BroadcastReceiver should call setResultCode(Activity.RESULT_OK) to
+ // let ResultBroadcastReceiver know the handler app is available.
+ context.sendOrderedBroadcastAsUser(intent,
+ UserHandle.of(handlerUser),
android.Manifest.permission.DUMP,
- options.toBundle());
+ OP_NONE, options.toBundle(),
+ new ResultBroadcastReceiver(),
+ /* scheduler= */ null,
+ Activity.RESULT_CANCELED,
+ /* initialData= */ null,
+ /* initialExtras= */ null);
} catch (RuntimeException e) {
Slog.e(TAG, "Error while trying to launch bugreport handler app.", e);
return false;
@@ -176,4 +189,19 @@ public final class BugReportHandlerUtil {
Binder.restoreCallingIdentity(identity);
}
}
+
+ private static class ResultBroadcastReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (getResultCode() == Activity.RESULT_OK) {
+ return;
+ }
+
+ Slog.w(TAG, "Request bug report because handler app seems to be not available.");
+ BugreportManager bugreportManager = context.getSystemService(BugreportManager.class);
+ bugreportManager.requestBugreport(
+ new BugreportParams(BugreportParams.BUGREPORT_MODE_INTERACTIVE),
+ /* shareTitle= */null, /* shareDescription= */ null);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index 48ceba976502..8527ae971352 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -115,6 +115,10 @@ final class CoreSettingsObserver extends ContentObserver {
DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.ENABLE_CURSOR_DRAG_FROM_ANYWHERE,
WidgetFlags.KEY_ENABLE_CURSOR_DRAG_FROM_ANYWHERE, boolean.class,
WidgetFlags.ENABLE_CURSOR_DRAG_FROM_ANYWHERE_DEFAULT));
+ sDeviceConfigEntries.add(new DeviceConfigEntry<Integer>(
+ DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.FINGER_TO_CURSOR_DISTANCE,
+ WidgetFlags.KEY_FINGER_TO_CURSOR_DISTANCE, int.class,
+ WidgetFlags.FINGER_TO_CURSOR_DISTANCE_DEFAULT));
sDeviceConfigEntries.add(new DeviceConfigEntry<Boolean>(
DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.ENABLE_INSERTION_HANDLE_GESTURES,
WidgetFlags.KEY_ENABLE_INSERTION_HANDLE_GESTURES, boolean.class,
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index b584ea5b7a25..2b1534b1c96f 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -75,7 +75,6 @@ import android.app.ActivityManager;
import android.app.ApplicationExitInfo;
import android.app.usage.UsageEvents;
import android.compat.annotation.ChangeId;
-import android.compat.annotation.Disabled;
import android.compat.annotation.EnabledAfter;
import android.content.Context;
import android.content.pm.ServiceInfo;
@@ -149,12 +148,13 @@ public final class OomAdjuster {
* capabilities.
*/
@ChangeId
- //TODO: change to @EnabledAfter when enforcing the feature.
- @Disabled
+ @EnabledAfter(targetSdkVersion=android.os.Build.VERSION_CODES.Q)
static final long CAMERA_MICROPHONE_CAPABILITY_CHANGE_ID = 136219221L;
//TODO: remove this when development is done.
private static final int TEMP_PROCESS_CAPABILITY_FOREGROUND_LOCATION = 1 << 31;
+ private static final int TEMP_PROCESS_CAPABILITY_FOREGROUND_CAMERA = 1 << 30;
+ private static final int TEMP_PROCESS_CAPABILITY_FOREGROUND_MICROPHONE = 1 << 29;
/**
* For some direct access we need to power manager.
@@ -1513,10 +1513,12 @@ public final class OomAdjuster {
if (enabled) {
capabilityFromFGS |=
(fgsType & FOREGROUND_SERVICE_TYPE_CAMERA)
- != 0 ? PROCESS_CAPABILITY_FOREGROUND_CAMERA : 0;
+ != 0 ? PROCESS_CAPABILITY_FOREGROUND_CAMERA
+ : TEMP_PROCESS_CAPABILITY_FOREGROUND_CAMERA;
capabilityFromFGS |=
(fgsType & FOREGROUND_SERVICE_TYPE_MICROPHONE)
- != 0 ? PROCESS_CAPABILITY_FOREGROUND_MICROPHONE : 0;
+ != 0 ? PROCESS_CAPABILITY_FOREGROUND_MICROPHONE
+ : TEMP_PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
} else {
capabilityFromFGS |= PROCESS_CAPABILITY_FOREGROUND_CAMERA
| PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 0e9970ec7766..57bd42bfd05e 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -99,6 +99,7 @@ import android.provider.DeviceConfig;
import android.system.Os;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.EventLog;
import android.util.LongSparseArray;
import android.util.Pair;
@@ -136,8 +137,11 @@ import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
+import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* Activity manager code dealing with processes.
@@ -154,7 +158,8 @@ public final class ProcessList {
"persist.sys.vold_app_data_isolation_enabled";
// A device config to control the minimum target SDK to enable app data isolation
- static final String ANDROID_APP_DATA_ISOLATION_MIN_SDK = "android_app_data_isolation_min_sdk";
+ static final String ANDROID_APP_DATA_ISOLATION_MIN_SDK =
+ "android_app_data_isolation_min_sdk";
// The minimum time we allow between crashes, for us to consider this
// application to be bad and stop and its services and reject broadcasts.
@@ -799,6 +804,31 @@ public final class ProcessList {
}
}
+ /**
+ * Get a map of pid and package name that process of that pid Android/data and Android/obb
+ * directory is not mounted to lowerfs to speed up access.
+ */
+ Map<Integer, String> getProcessesWithPendingBindMounts(int userId) {
+ final Map<Integer, String> pidPackageMap = new HashMap<>();
+ synchronized (mService) {
+ for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+ final ProcessRecord record = mLruProcesses.get(i);
+ if (record.userId != userId || !record.bindMountPending) {
+ continue;
+ }
+ final int pid = record.pid;
+ // It can happen when app process is starting, but zygote work is not done yet so
+ // system does not this pid record yet.
+ if (pid == 0) {
+ throw new IllegalStateException("Pending process is not started yet,"
+ + "retry later");
+ }
+ pidPackageMap.put(pid, record.info.packageName);
+ }
+ return pidPackageMap;
+ }
+ }
+
private void updateOomLevels(int displayWidth, int displayHeight, boolean write) {
// Scale buckets from avail memory: at 300MB we use the lowest values to
// 700MB or more for the top values.
@@ -1680,6 +1710,7 @@ public final class ProcessList {
if (app.pid > 0 && app.pid != ActivityManagerService.MY_PID) {
checkSlow(startTime, "startProcess: removing from pids map");
mService.removePidLocked(app);
+ app.bindMountPending = false;
mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
checkSlow(startTime, "startProcess: done removing from pids map");
app.setPid(0);
@@ -2162,8 +2193,10 @@ public final class ProcessList {
}
final Map<String, Pair<String, Long>> pkgDataInfoMap;
+ boolean bindMountAppStorageDirs = false;
if (shouldIsolateAppData(app)) {
+ bindMountAppStorageDirs = mVoldAppDataIsolationEnabled;
// Get all packages belongs to the same shared uid. sharedPackages is empty array
// if it doesn't have shared uid.
final PackageManagerInternal pmInt = mService.getPackageManagerInternalLocked();
@@ -2172,11 +2205,17 @@ public final class ProcessList {
pkgDataInfoMap = getPackageAppDataInfoMap(pmInt, sharedPackages.length == 0
? new String[]{app.info.packageName} : sharedPackages, uid);
+ int userId = UserHandle.getUserId(uid);
if (mVoldAppDataIsolationEnabled) {
StorageManagerInternal storageManagerInternal = LocalServices.getService(
- StorageManagerInternal.class);
- storageManagerInternal.prepareObbDirs(UserHandle.getUserId(uid),
- pkgDataInfoMap.keySet(), app.processName);
+ StorageManagerInternal.class);
+ if (!storageManagerInternal.prepareStorageDirs(userId, pkgDataInfoMap.keySet(),
+ app.processName)) {
+ // Cannot prepare Android/app and Android/obb directory,
+ // so we won't mount it in zygote.
+ app.bindMountPending = true;
+ bindMountAppStorageDirs = false;
+ }
}
} else {
pkgDataInfoMap = null;
@@ -2197,7 +2236,7 @@ public final class ProcessList {
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, null, app.info.packageName,
/*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, isTopApp,
- app.mDisabledCompatChanges, pkgDataInfoMap,
+ app.mDisabledCompatChanges, pkgDataInfoMap, bindMountAppStorageDirs,
new String[]{PROC_START_SEQ_IDENT + app.startSeq});
} else {
startResult = Process.start(entryPoint,
@@ -2205,6 +2244,7 @@ public final class ProcessList {
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, invokeWith, app.info.packageName, zygotePolicyFlags,
isTopApp, app.mDisabledCompatChanges, pkgDataInfoMap,
+ bindMountAppStorageDirs,
new String[]{PROC_START_SEQ_IDENT + app.startSeq});
}
checkSlow(startTime, "startProcess: returned from zygote!");
@@ -2663,6 +2703,7 @@ public final class ProcessList {
int pid = app.pid;
if (pid > 0) {
mService.removePidLocked(app);
+ app.bindMountPending = false;
mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
mService.mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
if (app.isolated) {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index a78caaced36c..e7f66bb484e0 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -251,6 +251,7 @@ class ProcessRecord implements WindowProcessListener {
int adjSourceProcState; // Debugging: proc state of adjSource's process.
Object adjTarget; // Debugging: target component impacting oom_adj.
Runnable crashHandler; // Optional local handler to be invoked in the process crash.
+ boolean bindMountPending; // True if Android/obb and Android/data need to be bind mount .
// Cache of last retrieve memory info and uptime, to throttle how frequently
// apps can requyest it.
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 310664e10bf3..2e2241f5deea 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -41,6 +41,7 @@ import static android.app.AppOpsManager.OP_PLAY_AUDIO;
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
import static android.app.AppOpsManager.OpEventProxyInfo;
import static android.app.AppOpsManager.RestrictionBypass;
+import static android.app.AppOpsManager.SAMPLING_STRATEGY_BOOT_TIME_SAMPLING;
import static android.app.AppOpsManager.SAMPLING_STRATEGY_RARELY_USED;
import static android.app.AppOpsManager.SAMPLING_STRATEGY_UNIFORM;
import static android.app.AppOpsManager.UID_STATE_BACKGROUND;
@@ -77,10 +78,10 @@ import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
import android.app.AppOpsManager;
+import android.app.AppOpsManager.AttributedOpEntry;
import android.app.AppOpsManager.HistoricalOps;
import android.app.AppOpsManager.Mode;
import android.app.AppOpsManager.OpEntry;
-import android.app.AppOpsManager.AttributedOpEntry;
import android.app.AppOpsManager.OpFlags;
import android.app.AppOpsManagerInternal;
import android.app.AppOpsManagerInternal.CheckOpsDelegate;
@@ -244,9 +245,15 @@ public class AppOpsService extends IAppOpsService.Stub {
private static final int MAX_UNFORWARED_OPS = 10;
private static final int MAX_UNUSED_POOLED_OBJECTS = 3;
+ private static final int RARELY_USED_PACKAGES_INITIALIZATION_DELAY_MILLIS = 300000;
//TODO: remove this when development is done.
private static final int TEMP_PROCESS_CAPABILITY_FOREGROUND_LOCATION = 1 << 31;
+ private static final int TEMP_PROCESS_CAPABILITY_FOREGROUND_CAMERA = 1 << 30;
+ private static final int TEMP_PROCESS_CAPABILITY_FOREGROUND_MICROPHONE = 1 << 29;
+ private static final int DEBUG_FGS_ALLOW_WHILE_IN_USE = 0;
+ private static final int DEBUG_FGS_ENFORCE_TYPE = 1;
+
final Context mContext;
final AtomicFile mFile;
@@ -549,7 +556,7 @@ public class AppOpsService extends IAppOpsService.Stub {
// The FGS has the location capability, but due to FGS BG start
// restriction it lost the capability, use temp location capability
// to mark this case.
- maybeShowWhileInUseDebugToast(op, mode);
+ maybeShowWhileInUseDebugToast(op, DEBUG_FGS_ALLOW_WHILE_IN_USE);
return MODE_IGNORED;
} else {
return MODE_IGNORED;
@@ -557,15 +564,25 @@ public class AppOpsService extends IAppOpsService.Stub {
case OP_CAMERA:
if ((capability & PROCESS_CAPABILITY_FOREGROUND_CAMERA) != 0) {
return MODE_ALLOWED;
+ } else if ((capability & TEMP_PROCESS_CAPABILITY_FOREGROUND_CAMERA)
+ != 0) {
+ // CHANGE TO MODE_IGNORED when enforce this feature.
+ maybeShowWhileInUseDebugToast(op, DEBUG_FGS_ENFORCE_TYPE);
+ return MODE_ALLOWED;
} else {
- maybeShowWhileInUseDebugToast(op, mode);
+ maybeShowWhileInUseDebugToast(op, DEBUG_FGS_ALLOW_WHILE_IN_USE);
return MODE_IGNORED;
}
case OP_RECORD_AUDIO:
if ((capability & PROCESS_CAPABILITY_FOREGROUND_MICROPHONE) != 0) {
return MODE_ALLOWED;
+ } else if ((capability & TEMP_PROCESS_CAPABILITY_FOREGROUND_MICROPHONE)
+ != 0) {
+ // CHANGE TO MODE_IGNORED when enforce this feature.
+ maybeShowWhileInUseDebugToast(op, DEBUG_FGS_ENFORCE_TYPE);
+ return MODE_ALLOWED;
} else {
- maybeShowWhileInUseDebugToast(op, mode);
+ maybeShowWhileInUseDebugToast(op, DEBUG_FGS_ALLOW_WHILE_IN_USE);
return MODE_IGNORED;
}
default:
@@ -580,15 +597,24 @@ public class AppOpsService extends IAppOpsService.Stub {
case OP_CAMERA:
if ((capability & PROCESS_CAPABILITY_FOREGROUND_CAMERA) != 0) {
return MODE_ALLOWED;
+ } else if ((capability & TEMP_PROCESS_CAPABILITY_FOREGROUND_CAMERA) != 0) {
+ // CHANGE TO MODE_IGNORED when enforce this feature.
+ maybeShowWhileInUseDebugToast(op, DEBUG_FGS_ENFORCE_TYPE);
+ return MODE_ALLOWED;
} else {
- maybeShowWhileInUseDebugToast(op, mode);
+ maybeShowWhileInUseDebugToast(op, DEBUG_FGS_ALLOW_WHILE_IN_USE);
return MODE_IGNORED;
}
case OP_RECORD_AUDIO:
if ((capability & PROCESS_CAPABILITY_FOREGROUND_MICROPHONE) != 0) {
return MODE_ALLOWED;
+ } else if ((capability & TEMP_PROCESS_CAPABILITY_FOREGROUND_MICROPHONE)
+ != 0) {
+ // CHANGE TO MODE_IGNORED when enforce this feature.
+ maybeShowWhileInUseDebugToast(op, DEBUG_FGS_ENFORCE_TYPE);
+ return MODE_ALLOWED;
} else {
- maybeShowWhileInUseDebugToast(op, mode);
+ maybeShowWhileInUseDebugToast(op, DEBUG_FGS_ALLOW_WHILE_IN_USE);
return MODE_IGNORED;
}
default:
@@ -1657,17 +1683,14 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}, packageAddedFilter);
- List<String> packageNames = getPackageNamesForSampling();
- synchronized (this) {
- resamplePackageAndAppOpLocked(packageNames);
- }
-
- AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
+ mHandler.postDelayed(new Runnable() {
@Override
public void run() {
+ List<String> packageNames = getPackageNamesForSampling();
+ resamplePackageAndAppOpLocked(packageNames);
initializeRarelyUsedPackagesList(new ArraySet<>(packageNames));
}
- });
+ }, RARELY_USED_PACKAGES_INITIALIZATION_DELAY_MILLIS);
PackageManagerInternal packageManagerInternal = LocalServices.getService(
PackageManagerInternal.class);
@@ -3374,18 +3397,22 @@ public class AppOpsService extends IAppOpsService.Stub {
synchronized (this) {
Op op = getOpLocked(code, uid, resolvedPackageName, attributionTag, bypass, true);
if (op == null) {
+ Slog.e(TAG, "Operation not found: uid=" + uid + " pkg=" + packageName + "("
+ + attributionTag + ") op=" + AppOpsManager.opToName(code));
return;
}
final AttributedOp attributedOp = op.mAttributions.get(attributionTag);
if (attributedOp == null) {
+ Slog.e(TAG, "Attribution not found: uid=" + uid + " pkg=" + packageName + "("
+ + attributionTag + ") op=" + AppOpsManager.opToName(code));
return;
}
- try {
+ if (attributedOp.isRunning()) {
attributedOp.finished(clientId);
- } catch (IllegalStateException e) {
- Slog.e(TAG, "Operation not started: uid=" + uid + " pkg="
- + packageName + " op=" + AppOpsManager.opToName(code), e);
+ } else {
+ Slog.e(TAG, "Operation not started: uid=" + uid + " pkg=" + packageName + "("
+ + attributionTag + ") op=" + AppOpsManager.opToName(code));
}
}
}
@@ -5614,7 +5641,7 @@ public class AppOpsService extends IAppOpsService.Stub {
int uid = Binder.getCallingUid();
Objects.requireNonNull(packageName);
synchronized (this) {
- switchPackageIfRarelyUsedLocked(packageName);
+ switchPackageIfBootTimeOrRarelyUsedLocked(packageName);
if (!packageName.equals(mSampledPackage)) {
return new MessageSamplingConfig(OP_NONE, 0,
Instant.now().plus(1, ChronoUnit.HOURS).toEpochMilli());
@@ -5644,7 +5671,7 @@ public class AppOpsService extends IAppOpsService.Stub {
private void reportRuntimeAppOpAccessMessageAsyncLocked(int uid,
@NonNull String packageName, int opCode, @Nullable String attributionTag,
@NonNull String message) {
- switchPackageIfRarelyUsedLocked(packageName);
+ switchPackageIfBootTimeOrRarelyUsedLocked(packageName);
if (!Objects.equals(mSampledPackage, packageName)) {
return;
}
@@ -5695,11 +5722,16 @@ public class AppOpsService extends IAppOpsService.Stub {
/**
* Checks if package is in the list of rarely used package and starts watching the new package
- * to collect incoming message.
+ * to collect incoming message or if collection is happening in first minutes since boot.
* @param packageName
*/
- private void switchPackageIfRarelyUsedLocked(@NonNull String packageName) {
- if (mRarelyUsedPackages.contains(packageName)) {
+ private void switchPackageIfBootTimeOrRarelyUsedLocked(@NonNull String packageName) {
+ if (mSampledPackage == null) {
+ if (ThreadLocalRandom.current().nextFloat() < 0.1f) {
+ mSamplingStrategy = SAMPLING_STRATEGY_BOOT_TIME_SAMPLING;
+ resampleAppOpForPackageLocked(packageName);
+ }
+ } else if (mRarelyUsedPackages.contains(packageName)) {
mRarelyUsedPackages.remove(packageName);
if (ThreadLocalRandom.current().nextFloat() < 0.5f) {
mSamplingStrategy = SAMPLING_STRATEGY_RARELY_USED;
@@ -5761,6 +5793,10 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
synchronized (this) {
+ int numPkgs = mRarelyUsedPackages.size();
+ for (int i = 0; i < numPkgs; i++) {
+ candidates.add(mRarelyUsedPackages.valueAt(i));
+ }
mRarelyUsedPackages = candidates;
}
}
diff --git a/services/core/java/com/android/server/hdmi/TEST_MAPPING b/services/core/java/com/android/server/hdmi/TEST_MAPPING
new file mode 100644
index 000000000000..7245ec4b6256
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/TEST_MAPPING
@@ -0,0 +1,34 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.hdmi"
+ },
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.hdmi"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index 6da0de13f623..44ab43828c2d 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -50,6 +50,7 @@ import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
@@ -69,8 +70,11 @@ import com.android.server.pm.parsing.pkg.ParsedPackage;
import java.io.ByteArrayInputStream;
import java.io.File;
+import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateEncodingException;
@@ -85,6 +89,8 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
/** Implementation of {@link AppIntegrityManagerService}. */
public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
@@ -239,6 +245,11 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
return new ParceledListSlice<>(rules);
}
+ @Override
+ public List<String> getWhitelistedRuleProviders() throws RemoteException {
+ return getAllowedRuleProviders();
+ }
+
private void handleIntegrityVerification(Intent intent) {
int verificationId = intent.getIntExtra(EXTRA_VERIFICATION_ID, -1);
@@ -467,8 +478,23 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
if (installationPath == null) {
throw new IllegalArgumentException("Installation path is null, package not found");
}
- SourceStampVerificationResult sourceStampVerificationResult =
- SourceStampVerifier.verify(installationPath.getAbsolutePath());
+
+ SourceStampVerificationResult sourceStampVerificationResult;
+ if (installationPath.isDirectory()) {
+ try (Stream<Path> filesList = Files.list(installationPath.toPath())) {
+ List<String> apkFiles =
+ filesList
+ .map(path -> path.toAbsolutePath().toString())
+ .collect(Collectors.toList());
+ sourceStampVerificationResult = SourceStampVerifier.verify(apkFiles);
+ } catch (IOException e) {
+ throw new IllegalArgumentException("Could not read APK directory");
+ }
+ } else {
+ sourceStampVerificationResult =
+ SourceStampVerifier.verify(installationPath.getAbsolutePath());
+ }
+
appInstallMetadata.setIsStampPresent(sourceStampVerificationResult.isPresent());
appInstallMetadata.setIsStampVerified(sourceStampVerificationResult.isVerified());
// A verified stamp is set to be trusted.
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index c1c37603b9c3..1b4ec8a8fa32 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -530,6 +530,11 @@ public class LockSettingsService extends ILockSettings.Stub {
return Settings.Global.getInt(contentResolver, keyName, defaultValue);
}
+ public int settingsSecureGetInt(ContentResolver contentResolver, String keyName,
+ int defaultValue, int userId) {
+ return Settings.Secure.getIntForUser(contentResolver, keyName, defaultValue, userId);
+ }
+
public @NonNull ManagedProfilePasswordCache getManagedProfilePasswordCache() {
try {
java.security.KeyStore ks = java.security.KeyStore.getInstance("AndroidKeyStore");
@@ -1010,6 +1015,13 @@ public class LockSettingsService extends ILockSettings.Stub {
}
}
+ private void enforceFrpResolved() {
+ if (mInjector.settingsSecureGetInt(mContext.getContentResolver(),
+ Settings.Secure.SECURE_FRP_MODE, 0, UserHandle.USER_SYSTEM) == 1) {
+ throw new SecurityException("Cannot change credential while FRP is not resolved yet");
+ }
+ }
+
private final void checkWritePermission(int userId) {
mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsWrite");
}
@@ -1572,6 +1584,7 @@ public class LockSettingsService extends ILockSettings.Stub {
"This operation requires secure lock screen feature");
}
checkWritePermission(userId);
+ enforceFrpResolved();
// When changing credential for profiles with unified challenge, some callers
// will pass in empty credential while others will pass in the credential of
diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
index 265b9ad10561..28f838044907 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
@@ -85,7 +85,9 @@ class BluetoothRouteProvider {
mListener = listener;
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
buildBluetoothRoutes();
+ }
+ public void start() {
mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP);
mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.HEARING_AID);
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index aad72034296d..c7d14e0afe7b 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -103,32 +103,29 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
publishProviderState();
boolean sessionInfoChanged;
- synchronized (mLock) {
- sessionInfoChanged = updateSessionInfosIfNeededLocked();
- }
+ sessionInfoChanged = updateSessionInfosIfNeeded();
if (sessionInfoChanged) {
notifySessionInfoUpdated();
}
});
-
- mHandler.post(() -> notifyProviderState());
-
- //TODO: clean up this
- // This is required because it is not instantiated in the main thread and
- // BluetoothRoutesUpdatedListener can be called before here
- synchronized (mLock) {
- updateSessionInfosIfNeededLocked();
- }
+ updateSessionInfosIfNeeded();
mContext.registerReceiver(new VolumeChangeReceiver(),
new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION));
+
+ mHandler.post(() -> {
+ mBtRouteProvider.start();
+ notifyProviderState();
+ });
}
@Override
public void requestCreateSession(long requestId, String packageName, String routeId,
Bundle sessionHints) {
- // Handle it as an internal transfer.
+
transferToRoute(requestId, SYSTEM_SESSION_ID, routeId);
+ mCallback.onSessionCreated(this, requestId, mSessionInfos.get(0));
+ //TODO: We should call after the session info is changed.
}
@Override
@@ -218,36 +215,38 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
/**
* Updates the mSessionInfo. Returns true if the session info is changed.
*/
- boolean updateSessionInfosIfNeededLocked() {
- // Prevent to execute this method before mBtRouteProvider is created.
- if (mBtRouteProvider == null) return false;
- RoutingSessionInfo oldSessionInfo = mSessionInfos.isEmpty() ? null : mSessionInfos.get(0);
-
- RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
- SYSTEM_SESSION_ID, "" /* clientPackageName */)
- .setSystemSession(true);
-
- MediaRoute2Info selectedRoute = mBtRouteProvider.getSelectedRoute();
- if (selectedRoute == null) {
- selectedRoute = mDefaultRoute;
- } else {
- builder.addTransferableRoute(mDefaultRoute.getId());
- }
- mSelectedRouteId = selectedRoute.getId();
- builder.addSelectedRoute(mSelectedRouteId);
-
- for (MediaRoute2Info route : mBtRouteProvider.getTransferableRoutes()) {
- builder.addTransferableRoute(route.getId());
- }
+ boolean updateSessionInfosIfNeeded() {
+ synchronized (mLock) {
+ // Prevent to execute this method before mBtRouteProvider is created.
+ if (mBtRouteProvider == null) return false;
+ RoutingSessionInfo oldSessionInfo = mSessionInfos.isEmpty() ? null : mSessionInfos.get(
+ 0);
+
+ RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
+ SYSTEM_SESSION_ID, "" /* clientPackageName */)
+ .setSystemSession(true);
+
+ MediaRoute2Info selectedRoute = mBtRouteProvider.getSelectedRoute();
+ if (selectedRoute == null) {
+ selectedRoute = mDefaultRoute;
+ } else {
+ builder.addTransferableRoute(mDefaultRoute.getId());
+ }
+ mSelectedRouteId = selectedRoute.getId();
+ builder.addSelectedRoute(mSelectedRouteId);
+ for (MediaRoute2Info route : mBtRouteProvider.getTransferableRoutes()) {
+ builder.addTransferableRoute(route.getId());
+ }
- RoutingSessionInfo newSessionInfo = builder.setProviderId(mUniqueId).build();
- if (Objects.equals(oldSessionInfo, newSessionInfo)) {
- return false;
- } else {
- mSessionInfos.clear();
- mSessionInfos.add(newSessionInfo);
- return true;
+ RoutingSessionInfo newSessionInfo = builder.setProviderId(mUniqueId).build();
+ if (Objects.equals(oldSessionInfo, newSessionInfo)) {
+ return false;
+ } else {
+ mSessionInfos.clear();
+ mSessionInfos.add(newSessionInfo);
+ return true;
+ }
}
}
@@ -261,6 +260,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
synchronized (mLock) {
sessionInfo = mSessionInfos.get(0);
}
+
mCallback.onSessionUpdated(this, sessionInfo);
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index 563dcf7e1156..48f1ddb023fd 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -21,7 +21,7 @@ import static com.android.server.net.NetworkPolicyManagerService.isUidNetworking
import android.annotation.NonNull;
import android.net.Network;
import android.net.NetworkTemplate;
-import android.net.netstats.provider.AbstractNetworkStatsProvider;
+import android.net.netstats.provider.NetworkStatsProvider;
import android.telephony.SubscriptionPlan;
import java.util.Set;
@@ -130,8 +130,8 @@ public abstract class NetworkPolicyManagerInternal {
Set<String> packageNames, int userId);
/**
- * Notifies that the specified {@link AbstractNetworkStatsProvider} has reached its quota
- * which was set through {@link AbstractNetworkStatsProvider#setLimit(String, long)}.
+ * Notifies that the specified {@link NetworkStatsProvider} has reached its quota
+ * which was set through {@link NetworkStatsProvider#onSetLimit(String, long)}.
*
* @param tag the human readable identifier of the custom network stats provider.
*/
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 0b1c91f4e2f5..caf29146e9db 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -75,7 +75,7 @@ import static android.net.NetworkTemplate.MATCH_MOBILE;
import static android.net.NetworkTemplate.MATCH_WIFI;
import static android.net.NetworkTemplate.buildTemplateMobileAll;
import static android.net.TrafficStats.MB_IN_BYTES;
-import static android.net.netstats.provider.AbstractNetworkStatsProvider.QUOTA_UNLIMITED;
+import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
import static android.os.Trace.TRACE_TAG_NETWORK;
import static android.provider.Settings.Global.NETPOLICY_OVERRIDE_ENABLED;
import static android.provider.Settings.Global.NETPOLICY_QUOTA_ENABLED;
diff --git a/services/core/java/com/android/server/net/NetworkStatsFactory.java b/services/core/java/com/android/server/net/NetworkStatsFactory.java
index 22b01bee6c6a..75ffe35674d1 100644
--- a/services/core/java/com/android/server/net/NetworkStatsFactory.java
+++ b/services/core/java/com/android/server/net/NetworkStatsFactory.java
@@ -229,7 +229,7 @@ public class NetworkStatsFactory {
entry.txPackets += reader.nextLong();
}
- stats.addEntry(entry);
+ stats.insertEntry(entry);
reader.finishLine();
}
} catch (NullPointerException|NumberFormatException e) {
@@ -279,7 +279,7 @@ public class NetworkStatsFactory {
entry.txBytes = reader.nextLong();
entry.txPackets = reader.nextLong();
- stats.addEntry(entry);
+ stats.insertEntry(entry);
reader.finishLine();
}
} catch (NullPointerException|NumberFormatException e) {
@@ -439,7 +439,7 @@ public class NetworkStatsFactory {
if ((limitIfaces == null || ArrayUtils.contains(limitIfaces, entry.iface))
&& (limitUid == UID_ALL || limitUid == entry.uid)
&& (limitTag == TAG_ALL || limitTag == entry.tag)) {
- stats.addEntry(entry);
+ stats.insertEntry(entry);
}
reader.finishLine();
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 4af31b036940..10136b3b8b64 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -17,12 +17,14 @@
package com.android.server.net;
import static android.Manifest.permission.ACCESS_NETWORK_STATE;
+import static android.Manifest.permission.NETWORK_STATS_PROVIDER;
import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
import static android.Manifest.permission.UPDATE_DEVICE_STATS;
import static android.content.Intent.ACTION_SHUTDOWN;
import static android.content.Intent.ACTION_UID_REMOVED;
import static android.content.Intent.ACTION_USER_REMOVED;
import static android.content.Intent.EXTRA_UID;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED;
import static android.net.ConnectivityManager.isNetworkTypeMobile;
import static android.net.NetworkStack.checkNetworkStackPermission;
@@ -101,7 +103,7 @@ import android.net.NetworkTemplate;
import android.net.TrafficStats;
import android.net.netstats.provider.INetworkStatsProvider;
import android.net.netstats.provider.INetworkStatsProviderCallback;
-import android.net.netstats.provider.NetworkStatsProviderCallback;
+import android.net.netstats.provider.NetworkStatsProvider;
import android.os.BestClock;
import android.os.Binder;
import android.os.DropBoxManager;
@@ -556,7 +558,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
} catch (RemoteException e) {
// ignored; service lives in system_server
}
- invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.setAlert(mGlobalAlertBytes));
+ invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.onSetAlert(mGlobalAlertBytes));
}
@Override
@@ -757,7 +759,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
final NetworkStatsHistory.Entry entry = history.getValues(start, end, now, null);
final NetworkStats stats = new NetworkStats(end - start, 1);
- stats.addEntry(new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE,
+ stats.insertEntry(new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE,
METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, entry.rxBytes, entry.rxPackets,
entry.txBytes, entry.txPackets, entry.operations));
return stats;
@@ -1374,7 +1376,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
Trace.traceBegin(TRACE_TAG_NETWORK, "provider.requestStatsUpdate");
final int registeredCallbackCount = mStatsProviderCbList.getRegisteredCallbackCount();
mStatsProviderSem.drainPermits();
- invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.requestStatsUpdate(0 /* unused */));
+ invokeForAllStatsProviderCallbacks(
+ (cb) -> cb.mProvider.onRequestStatsUpdate(0 /* unused */));
try {
mStatsProviderSem.tryAcquire(registeredCallbackCount,
MAX_STATS_PROVIDER_POLL_WAIT_TIME_MS, TimeUnit.MILLISECONDS);
@@ -1549,7 +1552,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
@Override
public void setStatsProviderLimitAsync(@NonNull String iface, long quota) {
Slog.v(TAG, "setStatsProviderLimitAsync(" + iface + "," + quota + ")");
- invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.setLimit(iface, quota));
+ invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.onSetLimit(iface, quota));
}
}
@@ -1793,6 +1796,24 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
}
}
+ // TODO: It is copied from ConnectivityService, consider refactor these check permission
+ // functions to a proper util.
+ private boolean checkAnyPermissionOf(String... permissions) {
+ for (String permission : permissions) {
+ if (mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void enforceAnyPermissionOf(String... permissions) {
+ if (!checkAnyPermissionOf(permissions)) {
+ throw new SecurityException("Requires one of the following permissions: "
+ + String.join(", ", permissions) + ".");
+ }
+ }
+
/**
* Registers a custom provider of {@link android.net.NetworkStats} to combine the network
* statistics that cannot be seen by the kernel to system. To unregister, invoke the
@@ -1800,16 +1821,15 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
*
* @param tag a human readable identifier of the custom network stats provider.
* @param provider the {@link INetworkStatsProvider} binder corresponding to the
- * {@link android.net.netstats.provider.AbstractNetworkStatsProvider} to be
- * registered.
+ * {@link NetworkStatsProvider} to be registered.
*
- * @return a binder interface of
- * {@link android.net.netstats.provider.NetworkStatsProviderCallback}, which can be
- * used to report events to the system.
+ * @return a {@link INetworkStatsProviderCallback} binder
+ * interface, which can be used to report events to the system.
*/
public @NonNull INetworkStatsProviderCallback registerNetworkStatsProvider(
@NonNull String tag, @NonNull INetworkStatsProvider provider) {
- mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG);
+ enforceAnyPermissionOf(NETWORK_STATS_PROVIDER,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
Objects.requireNonNull(provider, "provider is null");
Objects.requireNonNull(tag, "tag is null");
try {
@@ -1910,7 +1930,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
}
@Override
- public void onStatsUpdated(int token, @Nullable NetworkStats ifaceStats,
+ public void notifyStatsUpdated(int token, @Nullable NetworkStats ifaceStats,
@Nullable NetworkStats uidStats) {
// TODO: 1. Use token to map ifaces to correct NetworkIdentity.
// 2. Store the difference and store it directly to the recorder.
@@ -1922,12 +1942,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
}
@Override
- public void onAlertReached() throws RemoteException {
+ public void notifyAlertReached() throws RemoteException {
mAlertObserver.limitReached(LIMIT_GLOBAL_ALERT, null /* unused */);
}
@Override
- public void onLimitReached() {
+ public void notifyLimitReached() {
Log.d(TAG, mTag + ": onLimitReached");
LocalServices.getService(NetworkPolicyManagerInternal.class)
.onStatsProviderLimitReached(mTag);
diff --git a/services/core/java/com/android/server/notification/BubbleExtractor.java b/services/core/java/com/android/server/notification/BubbleExtractor.java
index c9c8042685d1..c96880cfebb8 100644
--- a/services/core/java/com/android/server/notification/BubbleExtractor.java
+++ b/services/core/java/com/android/server/notification/BubbleExtractor.java
@@ -15,9 +15,27 @@
*/
package com.android.server.notification;
+import static android.app.Notification.CATEGORY_CALL;
+import static android.app.Notification.FLAG_BUBBLE;
+import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
+
+import static com.android.internal.util.FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING;
+import static com.android.internal.util.FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE;
+
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.Person;
import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
+
+import java.util.ArrayList;
+
/**
* Determines whether a bubble can be shown for this notification
*/
@@ -25,10 +43,15 @@ public class BubbleExtractor implements NotificationSignalExtractor {
private static final String TAG = "BubbleExtractor";
private static final boolean DBG = false;
+ private BubbleChecker mBubbleChecker;
private RankingConfig mConfig;
+ private ActivityManager mActivityManager;
+ private Context mContext;
- public void initialize(Context ctx, NotificationUsageStats usageStats) {
+ public void initialize(Context context, NotificationUsageStats usageStats) {
if (DBG) Slog.d(TAG, "Initializing " + getClass().getSimpleName() + ".");
+ mContext = context;
+ mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
}
public RankingReconsideration process(NotificationRecord record) {
@@ -41,6 +64,12 @@ public class BubbleExtractor implements NotificationSignalExtractor {
if (DBG) Slog.d(TAG, "missing config");
return null;
}
+
+ if (mBubbleChecker == null) {
+ if (DBG) Slog.d(TAG, "missing bubble checker");
+ return null;
+ }
+
boolean appCanShowBubble =
mConfig.areBubblesAllowed(record.getSbn().getPackageName(), record.getSbn().getUid());
if (!mConfig.bubblesEnabled() || !appCanShowBubble) {
@@ -52,7 +81,12 @@ public class BubbleExtractor implements NotificationSignalExtractor {
record.setAllowBubble(appCanShowBubble);
}
}
-
+ final boolean applyFlag = mBubbleChecker.isNotificationAppropriateToBubble(record);
+ if (applyFlag) {
+ record.getNotification().flags |= FLAG_BUBBLE;
+ } else {
+ record.getNotification().flags &= ~FLAG_BUBBLE;
+ }
return null;
}
@@ -64,4 +98,185 @@ public class BubbleExtractor implements NotificationSignalExtractor {
@Override
public void setZenHelper(ZenModeHelper helper) {
}
+
+ /**
+ * Expected to be called after {@link #setConfig(RankingConfig)} has occurred.
+ */
+ void setShortcutHelper(ShortcutHelper helper) {
+ if (mConfig == null) {
+ if (DBG) Slog.d(TAG, "setting shortcut helper prior to setConfig");
+ return;
+ }
+ mBubbleChecker = new BubbleChecker(mContext, helper, mConfig, mActivityManager);
+ }
+
+ @VisibleForTesting
+ void setBubbleChecker(BubbleChecker checker) {
+ mBubbleChecker = checker;
+ }
+
+ /**
+ * Encapsulates special checks to see if a notification can be flagged as a bubble. This
+ * makes testing a bit easier.
+ */
+ public static class BubbleChecker {
+
+ private ActivityManager mActivityManager;
+ private RankingConfig mRankingConfig;
+ private Context mContext;
+ private ShortcutHelper mShortcutHelper;
+
+ BubbleChecker(Context context, ShortcutHelper helper, RankingConfig config,
+ ActivityManager activityManager) {
+ mContext = context;
+ mActivityManager = activityManager;
+ mShortcutHelper = helper;
+ mRankingConfig = config;
+ }
+
+ /**
+ * @return whether the provided notification record is allowed to be represented as a
+ * bubble, accounting for user choice & policy.
+ */
+ public boolean isNotificationAppropriateToBubble(NotificationRecord r) {
+ final String pkg = r.getSbn().getPackageName();
+ final int userId = r.getSbn().getUser().getIdentifier();
+ Notification notification = r.getNotification();
+ if (!canBubble(r, pkg, userId)) {
+ // no log: canBubble has its own
+ return false;
+ }
+
+ if (mActivityManager.isLowRamDevice()) {
+ logBubbleError(r.getKey(), "low ram device");
+ return false;
+ }
+
+ // At this point the bubble must fulfill communication policy
+
+ // Communication always needs a person
+ ArrayList<Person> peopleList = notification.extras != null
+ ? notification.extras.getParcelableArrayList(Notification.EXTRA_PEOPLE_LIST)
+ : null;
+ // Message style requires a person & it's not included in the list
+ boolean isMessageStyle = Notification.MessagingStyle.class.equals(
+ notification.getNotificationStyle());
+ if (!isMessageStyle && (peopleList == null || peopleList.isEmpty())) {
+ logBubbleError(r.getKey(), "Must have a person and be "
+ + "Notification.MessageStyle or Notification.CATEGORY_CALL");
+ return false;
+ }
+
+ // Communication is a message or a call
+ boolean isCall = CATEGORY_CALL.equals(notification.category);
+ boolean hasForegroundService = (notification.flags & FLAG_FOREGROUND_SERVICE) != 0;
+ if (hasForegroundService && !isCall) {
+ logBubbleError(r.getKey(),
+ "foreground services must be Notification.CATEGORY_CALL to bubble");
+ return false;
+ }
+ if (isMessageStyle) {
+ return true;
+ } else if (isCall) {
+ if (hasForegroundService) {
+ return true;
+ }
+ logBubbleError(r.getKey(), "calls require foreground service");
+ return false;
+ }
+ logBubbleError(r.getKey(), "Must be "
+ + "Notification.MessageStyle or Notification.CATEGORY_CALL");
+ return false;
+ }
+
+ /**
+ * @return whether the user has enabled the provided notification to bubble, does not
+ * account for policy.
+ */
+ @VisibleForTesting
+ boolean canBubble(NotificationRecord r, String pkg, int userId) {
+ Notification notification = r.getNotification();
+ Notification.BubbleMetadata metadata = notification.getBubbleMetadata();
+ if (metadata == null) {
+ // no log: no need to inform dev if they didn't attach bubble metadata
+ return false;
+ }
+ if (!mRankingConfig.bubblesEnabled()) {
+ logBubbleError(r.getKey(), "bubbles disabled for user: " + userId);
+ return false;
+ }
+ if (!mRankingConfig.areBubblesAllowed(pkg, userId)) {
+ logBubbleError(r.getKey(),
+ "bubbles for package: " + pkg + " disabled for user: " + userId);
+ return false;
+ }
+ if (!r.getChannel().canBubble()) {
+ logBubbleError(r.getKey(),
+ "bubbles for channel " + r.getChannel().getId() + " disabled");
+ return false;
+ }
+
+ String shortcutId = metadata.getShortcutId();
+ boolean shortcutValid = shortcutId != null
+ && mShortcutHelper.hasValidShortcutInfo(shortcutId, pkg, r.getUser());
+ if (metadata.getBubbleIntent() == null && !shortcutValid) {
+ // Should have a shortcut if intent is null
+ logBubbleError(r.getKey(),
+ "couldn't find valid shortcut for bubble with shortcutId: " + shortcutId);
+ return false;
+ }
+ if (shortcutValid) {
+ return true;
+ }
+ // no log: canLaunch method has the failure log
+ return canLaunchInActivityView(mContext, metadata.getBubbleIntent(), pkg);
+ }
+
+ /**
+ * Whether an intent is properly configured to display in an {@link
+ * android.app.ActivityView}.
+ *
+ * @param context the context to use.
+ * @param pendingIntent the pending intent of the bubble.
+ * @param packageName the notification package name for this bubble.
+ */
+ // Keep checks in sync with BubbleController#canLaunchInActivityView.
+ @VisibleForTesting
+ protected boolean canLaunchInActivityView(Context context, PendingIntent pendingIntent,
+ String packageName) {
+ if (pendingIntent == null) {
+ Slog.w(TAG, "Unable to create bubble -- no intent");
+ return false;
+ }
+
+ Intent intent = pendingIntent.getIntent();
+
+ ActivityInfo info = intent != null
+ ? intent.resolveActivityInfo(context.getPackageManager(), 0)
+ : null;
+ if (info == null) {
+ FrameworkStatsLog.write(FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED,
+ packageName,
+ BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING);
+ Slog.w(TAG, "Unable to send as bubble -- couldn't find activity info for intent: "
+ + intent);
+ return false;
+ }
+ if (!ActivityInfo.isResizeableMode(info.resizeMode)) {
+ FrameworkStatsLog.write(FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED,
+ packageName,
+ BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE);
+ Slog.w(TAG, "Unable to send as bubble -- activity is not resizable for intent: "
+ + intent);
+ return false;
+ }
+ return true;
+ }
+
+ private void logBubbleError(String key, String failureMessage) {
+ if (DBG) {
+ Slog.w(TAG, "Bubble notification: " + key + " failed: " + failureMessage);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 69a5b35a5b12..20ad87a096a0 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -17,7 +17,6 @@
package com.android.server.notification;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
-import static android.app.Notification.CATEGORY_CALL;
import static android.app.Notification.FLAG_AUTOGROUP_SUMMARY;
import static android.app.Notification.FLAG_BUBBLE;
import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
@@ -51,9 +50,6 @@ import static android.content.Context.BIND_ALLOW_WHITELIST_MANAGEMENT;
import static android.content.Context.BIND_AUTO_CREATE;
import static android.content.Context.BIND_FOREGROUND_SERVICE;
import static android.content.Context.BIND_NOT_PERCEPTIBLE;
-import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED;
-import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
-import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
import static android.content.pm.PackageManager.FEATURE_LEANBACK;
import static android.content.pm.PackageManager.FEATURE_TELEVISION;
import static android.content.pm.PackageManager.MATCH_ALL;
@@ -93,8 +89,6 @@ import static android.service.notification.NotificationListenerService.TRIM_FULL
import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
-import static com.android.internal.util.FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING;
-import static com.android.internal.util.FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES;
@@ -131,8 +125,6 @@ import android.app.NotificationHistory.HistoricalNotification;
import android.app.NotificationManager;
import android.app.NotificationManager.Policy;
import android.app.PendingIntent;
-import android.app.Person;
-import android.app.RemoteInput;
import android.app.StatsManager;
import android.app.StatusBarManager;
import android.app.UriGrantsManager;
@@ -152,7 +144,6 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.LauncherApps;
@@ -160,7 +151,6 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
-import android.content.pm.ShortcutInfo;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -251,7 +241,6 @@ import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.internal.util.function.TriPredicate;
@@ -296,7 +285,6 @@ import java.nio.charset.StandardCharsets;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
@@ -421,7 +409,7 @@ public class NotificationManagerService extends SystemService {
private RoleObserver mRoleObserver;
private UserManager mUm;
private IPlatformCompat mPlatformCompat;
- private LauncherApps mLauncherAppsService;
+ private ShortcutHelper mShortcutHelper;
final IBinder mForegroundToken = new Binder();
private WorkerHandler mHandler;
@@ -497,7 +485,8 @@ public class NotificationManagerService extends SystemService {
"allow-secure-notifications-on-lockscreen";
private static final String LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_VALUE = "value";
- private RankingHelper mRankingHelper;
+ @VisibleForTesting
+ RankingHelper mRankingHelper;
@VisibleForTesting
PreferencesHelper mPreferencesHelper;
@@ -931,6 +920,8 @@ public class NotificationManagerService extends SystemService {
.setType(MetricsEvent.TYPE_ACTION)
.addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, nv.rank)
.addTaggedData(MetricsEvent.NOTIFICATION_SHADE_COUNT, nv.count));
+ mNotificationRecordLogger.log(
+ NotificationRecordLogger.NotificationEvent.NOTIFICATION_CLICKED, r);
EventLogTags.writeNotificationClicked(key,
r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now),
nv.rank, nv.count);
@@ -970,7 +961,8 @@ public class NotificationManagerService extends SystemService {
generatedByAssistant ? 1 : 0)
.addTaggedData(MetricsEvent.NOTIFICATION_LOCATION,
nv.location.toMetricsEventEnum()));
-
+ mNotificationRecordLogger.log(
+ NotificationRecordLogger.NotificationEvent.NOTIFICATION_ACTION_CLICKED, r);
EventLogTags.writeNotificationActionClicked(key, actionIndex,
r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now),
nv.rank, nv.count);
@@ -1004,6 +996,8 @@ public class NotificationManagerService extends SystemService {
public void onPanelRevealed(boolean clearEffects, int items) {
MetricsLogger.visible(getContext(), MetricsEvent.NOTIFICATION_PANEL);
MetricsLogger.histogram(getContext(), "note_load", items);
+ mNotificationRecordLogger.log(
+ NotificationRecordLogger.NotificationPanelEvent.NOTIFICATION_PANEL_OPEN);
EventLogTags.writeNotificationPanelRevealed(items);
if (clearEffects) {
clearEffects();
@@ -1014,6 +1008,8 @@ public class NotificationManagerService extends SystemService {
@Override
public void onPanelHidden() {
MetricsLogger.hidden(getContext(), MetricsEvent.NOTIFICATION_PANEL);
+ mNotificationRecordLogger.log(
+ NotificationRecordLogger.NotificationPanelEvent.NOTIFICATION_PANEL_CLOSE);
EventLogTags.writeNotificationPanelHidden();
mAssistants.onPanelHidden();
}
@@ -1103,6 +1099,10 @@ public class NotificationManagerService extends SystemService {
MetricsLogger.action(r.getItemLogMaker()
.setType(expanded ? MetricsEvent.TYPE_DETAIL
: MetricsEvent.TYPE_COLLAPSE));
+ mNotificationRecordLogger.log(
+ NotificationRecordLogger.NotificationEvent.fromExpanded(expanded,
+ userAction),
+ r);
}
if (expanded && userAction) {
r.recordExpanded();
@@ -1124,6 +1124,9 @@ public class NotificationManagerService extends SystemService {
mMetricsLogger.write(r.getLogMaker()
.setCategory(MetricsEvent.NOTIFICATION_DIRECT_REPLY_ACTION)
.setType(MetricsEvent.TYPE_ACTION));
+ mNotificationRecordLogger.log(
+ NotificationRecordLogger.NotificationEvent.NOTIFICATION_DIRECT_REPLIED,
+ r);
reportUserInteraction(r);
mAssistants.notifyAssistantNotificationDirectReplyLocked(r.getSbn());
}
@@ -1166,6 +1169,9 @@ public class NotificationManagerService extends SystemService {
MetricsEvent.NOTIFICATION_SMART_REPLY_MODIFIED_BEFORE_SENDING,
modifiedBeforeSending ? 1 : 0);
mMetricsLogger.write(logMaker);
+ mNotificationRecordLogger.log(
+ NotificationRecordLogger.NotificationEvent.NOTIFICATION_SMART_REPLIED,
+ r);
// Treat clicking on a smart reply as a user interaction.
reportUserInteraction(r);
mAssistants.notifyAssistantSuggestedReplySent(
@@ -1186,13 +1192,30 @@ public class NotificationManagerService extends SystemService {
@Override
public void onNotificationBubbleChanged(String key, boolean isBubble) {
+ String pkg;
+ synchronized (mNotificationLock) {
+ NotificationRecord r = mNotificationsByKey.get(key);
+ pkg = r != null && r.getSbn() != null ? r.getSbn().getPackageName() : null;
+ }
+ boolean isAppForeground = pkg != null
+ && mActivityManager.getPackageImportance(pkg) == IMPORTANCE_FOREGROUND;
synchronized (mNotificationLock) {
NotificationRecord r = mNotificationsByKey.get(key);
if (r != null) {
- final StatusBarNotification n = r.getSbn();
- final int callingUid = n.getUid();
- final String pkg = n.getPackageName();
- applyFlagBubble(r, pkg, callingUid, null /* oldEntry */, isBubble);
+ if (!isBubble) {
+ // This happens if the user has dismissed the bubble but the notification
+ // is still active in the shade, enqueuing would create a bubble since
+ // the notification is technically allowed. Flip the flag so that
+ // apps querying noMan will know that their notification is not showing
+ // as a bubble.
+ r.getNotification().flags &= ~FLAG_BUBBLE;
+ } else {
+ // Enqueue will trigger resort & if the flag is allowed to be true it'll
+ // be applied there.
+ r.getNotification().flags |= FLAG_ONLY_ALERT_ONCE;
+ mHandler.post(new EnqueueNotificationRunnable(r.getUser().getIdentifier(),
+ r, isAppForeground));
+ }
}
}
}
@@ -1219,6 +1242,7 @@ public class NotificationManagerService extends SystemService {
flags &= ~Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
}
data.setFlags(flags);
+ r.getNotification().flags |= FLAG_ONLY_ALERT_ONCE;
mHandler.post(new EnqueueNotificationRunnable(r.getUser().getIdentifier(), r,
true /* isAppForeground */));
}
@@ -1310,6 +1334,9 @@ public class NotificationManagerService extends SystemService {
MetricsEvent.NOTIFICATION_SMART_REPLY_EDIT_BEFORE_SENDING,
r.getEditChoicesBeforeSending() ? 1 : 0);
mMetricsLogger.write(logMaker);
+ mNotificationRecordLogger.log(
+ NotificationRecordLogger.NotificationEvent.NOTIFICATION_SMART_REPLY_VISIBLE,
+ r);
}
}
@@ -1595,80 +1622,6 @@ public class NotificationManagerService extends SystemService {
}
};
- // Key: packageName Value: <shortcutId, notifId>
- private HashMap<String, HashMap<String, String>> mActiveShortcutBubbles = new HashMap<>();
-
- private boolean mLauncherAppsCallbackRegistered;
-
- // Bubbles can be created based on a shortcut, we need to listen for changes to
- // that shortcut so that we may update the bubble appropriately.
- private final LauncherApps.Callback mLauncherAppsCallback = new LauncherApps.Callback() {
- @Override
- public void onPackageRemoved(String packageName, UserHandle user) {
- }
-
- @Override
- public void onPackageAdded(String packageName, UserHandle user) {
- }
-
- @Override
- public void onPackageChanged(String packageName, UserHandle user) {
- }
-
- @Override
- public void onPackagesAvailable(String[] packageNames, UserHandle user,
- boolean replacing) {
- }
-
- @Override
- public void onPackagesUnavailable(String[] packageNames, UserHandle user,
- boolean replacing) {
- }
-
- @Override
- public void onShortcutsChanged(@NonNull String packageName,
- @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {
- HashMap<String, String> shortcutBubbles = mActiveShortcutBubbles.get(packageName);
- boolean isAppForeground = packageName != null
- && mActivityManager.getPackageImportance(packageName) == IMPORTANCE_FOREGROUND;
- ArrayList<String> bubbleKeysToRemove = new ArrayList<>();
- if (shortcutBubbles != null) {
- // If we can't find one of our bubbles in the shortcut list, that bubble needs
- // to be removed.
- for (String shortcutId : shortcutBubbles.keySet()) {
- boolean foundShortcut = false;
- for (int i = 0; i < shortcuts.size(); i++) {
- if (shortcuts.get(i).getId().equals(shortcutId)) {
- foundShortcut = true;
- break;
- }
- }
- if (!foundShortcut) {
- bubbleKeysToRemove.add(shortcutBubbles.get(shortcutId));
- }
- }
- }
-
- // Do the removals
- for (int i = 0; i < bubbleKeysToRemove.size(); i++) {
- // update flag bubble
- String bubbleKey = bubbleKeysToRemove.get(i);
- synchronized (mNotificationLock) {
- NotificationRecord r = mNotificationsByKey.get(bubbleKey);
- if (r != null) {
- final StatusBarNotification n = r.getSbn();
- final int callingUid = n.getUid();
- final String pkg = n.getPackageName();
- applyFlagBubble(r, pkg, callingUid, null /* oldEntry */, isAppForeground);
- mHandler.post(new EnqueueNotificationRunnable(user.getIdentifier(), r,
- false /* isAppForeground */));
- }
- }
- }
- }
- };
-
-
private final class SettingsObserver extends ContentObserver {
private final Uri NOTIFICATION_BADGING_URI
= Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_BADGING);
@@ -1763,8 +1716,8 @@ public class NotificationManagerService extends SystemService {
}
@VisibleForTesting
- void setLauncherApps(LauncherApps launcherApps) {
- mLauncherAppsService = launcherApps;
+ ShortcutHelper getShortcutHelper() {
+ return mShortcutHelper;
}
@VisibleForTesting
@@ -2314,8 +2267,13 @@ public class NotificationManagerService extends SystemService {
mRoleObserver = new RoleObserver(getContext().getSystemService(RoleManager.class),
mPackageManager, getContext().getMainExecutor());
mRoleObserver.init();
- mLauncherAppsService =
+ LauncherApps launcherApps =
(LauncherApps) getContext().getSystemService(Context.LAUNCHER_APPS_SERVICE);
+ mShortcutHelper = new ShortcutHelper(launcherApps, mShortcutListener);
+ BubbleExtractor bubbsExtractor = mRankingHelper.findExtractor(BubbleExtractor.class);
+ if (bubbsExtractor != null) {
+ bubbsExtractor.setShortcutHelper(mShortcutHelper);
+ }
registerNotificationPreferencesPullers();
} else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
// This observer will force an update when observe is called, causing us to
@@ -3458,7 +3416,7 @@ public class NotificationManagerService extends SystemService {
ArrayList<ConversationChannelWrapper> conversations =
mPreferencesHelper.getConversations(onlyImportant);
for (ConversationChannelWrapper conversation : conversations) {
- conversation.setShortcutInfo(getShortcutInfo(
+ conversation.setShortcutInfo(mShortcutHelper.getShortcutInfo(
conversation.getNotificationChannel().getConversationId(),
conversation.getPkg(),
UserHandle.of(UserHandle.getUserId(conversation.getUid()))));
@@ -3481,7 +3439,7 @@ public class NotificationManagerService extends SystemService {
ArrayList<ConversationChannelWrapper> conversations =
mPreferencesHelper.getConversations(pkg, uid);
for (ConversationChannelWrapper conversation : conversations) {
- conversation.setShortcutInfo(getShortcutInfo(
+ conversation.setShortcutInfo(mShortcutHelper.getShortcutInfo(
conversation.getNotificationChannel().getConversationId(),
pkg,
UserHandle.of(UserHandle.getUserId(uid))));
@@ -5652,7 +5610,7 @@ public class NotificationManagerService extends SystemService {
}
}
- r.setShortcutInfo(getShortcutInfo(notification.getShortcutId(), pkg, user));
+ r.setShortcutInfo(mShortcutHelper.getShortcutInfo(notification.getShortcutId(), pkg, user));
if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r,
r.getSbn().getOverrideGroupKey() != null)) {
@@ -5780,16 +5738,12 @@ public class NotificationManagerService extends SystemService {
}
/**
- * Updates the flags for this notification to reflect whether it is a bubble or not. Some
- * bubble specific flags only work if the app is foreground, this will strip those flags
+ * Some bubble specific flags only work if the app is foreground, this will strip those flags
* if the app wasn't foreground.
*/
- private void updateNotificationBubbleFlags(NotificationRecord r, String pkg, int userId,
- NotificationRecord oldRecord, boolean isAppForeground) {
- Notification notification = r.getNotification();
- applyFlagBubble(r, pkg, userId, oldRecord, true /* desiredFlag */);
-
+ private void updateNotificationBubbleFlags(NotificationRecord r, boolean isAppForeground) {
// Remove any bubble specific flags that only work when foregrounded
+ Notification notification = r.getNotification();
Notification.BubbleMetadata metadata = notification.getBubbleMetadata();
if (!isAppForeground && metadata != null) {
int flags = metadata.getFlags();
@@ -5799,252 +5753,30 @@ public class NotificationManagerService extends SystemService {
}
}
- /**
- * Handles actually applying or removing {@link Notification#FLAG_BUBBLE}. Performs necessary
- * checks for the provided record to see if it can actually be a bubble.
- * Tracks shortcut based bubbles so that we can find out if they've changed or been removed.
- */
- private void applyFlagBubble(NotificationRecord r, String pkg, int userId,
- NotificationRecord oldRecord, boolean desiredFlag) {
- boolean applyFlag = desiredFlag
- && isNotificationAppropriateToBubble(r, pkg, userId, oldRecord);
- final String shortcutId = r.getNotification().getBubbleMetadata() != null
- ? r.getNotification().getBubbleMetadata().getShortcutId()
- : null;
- if (applyFlag) {
- if (shortcutId != null) {
- // Must track shortcut based bubbles in case the shortcut is removed
- HashMap<String, String> packageBubbles = mActiveShortcutBubbles.get(
- r.getSbn().getPackageName());
- if (packageBubbles == null) {
- packageBubbles = new HashMap<>();
- }
- packageBubbles.put(shortcutId, r.getKey());
- mActiveShortcutBubbles.put(r.getSbn().getPackageName(), packageBubbles);
- if (!mLauncherAppsCallbackRegistered) {
- mLauncherAppsService.registerCallback(mLauncherAppsCallback, mHandler);
- mLauncherAppsCallbackRegistered = true;
- }
- }
- r.getNotification().flags |= FLAG_BUBBLE;
- } else {
- if (shortcutId != null) {
- // No longer track shortcut
- HashMap<String, String> packageBubbles = mActiveShortcutBubbles.get(
- r.getSbn().getPackageName());
- if (packageBubbles != null) {
- packageBubbles.remove(shortcutId);
- }
- if (packageBubbles != null && packageBubbles.isEmpty()) {
- mActiveShortcutBubbles.remove(r.getSbn().getPackageName());
- }
- if (mLauncherAppsCallbackRegistered && mActiveShortcutBubbles.isEmpty()) {
- mLauncherAppsService.unregisterCallback(mLauncherAppsCallback);
- mLauncherAppsCallbackRegistered = false;
- }
- }
- r.getNotification().flags &= ~FLAG_BUBBLE;
- }
- }
-
- /**
- * @return whether the provided notification record is allowed to be represented as a bubble,
- * accounting for user choice & policy.
- */
- private boolean isNotificationAppropriateToBubble(NotificationRecord r, String pkg, int userId,
- NotificationRecord oldRecord) {
- Notification notification = r.getNotification();
- if (!canBubble(r, pkg, userId)) {
- // no log: canBubble has its own
- return false;
- }
-
- if (mActivityManager.isLowRamDevice()) {
- logBubbleError(r.getKey(), "low ram device");
- return false;
- }
-
- if (oldRecord != null && (oldRecord.getNotification().flags & FLAG_BUBBLE) != 0) {
- // This is an update to an active bubble
- return true;
- }
-
- // At this point the bubble must fulfill communication policy
-
- // Communication always needs a person
- ArrayList<Person> peopleList = notification.extras != null
- ? notification.extras.getParcelableArrayList(Notification.EXTRA_PEOPLE_LIST)
- : null;
- // Message style requires a person & it's not included in the list
- boolean isMessageStyle = Notification.MessagingStyle.class.equals(
- notification.getNotificationStyle());
- if (!isMessageStyle && (peopleList == null || peopleList.isEmpty())) {
- logBubbleError(r.getKey(), "Must have a person and be "
- + "Notification.MessageStyle or Notification.CATEGORY_CALL");
- return false;
- }
-
- // Communication is a message or a call
- boolean isCall = CATEGORY_CALL.equals(notification.category);
- boolean hasForegroundService = (notification.flags & FLAG_FOREGROUND_SERVICE) != 0;
- if (hasForegroundService && !isCall) {
- logBubbleError(r.getKey(),
- "foreground services must be Notification.CATEGORY_CALL to bubble");
- return false;
- }
- if (isMessageStyle) {
- if (hasValidRemoteInput(notification)) {
- return true;
- }
- logBubbleError(r.getKey(), "messages require valid remote input");
- return false;
- } else if (isCall) {
- if (hasForegroundService) {
- return true;
- }
- logBubbleError(r.getKey(), "calls require foreground service");
- return false;
- }
- logBubbleError(r.getKey(), "Must be "
- + "Notification.MessageStyle or Notification.CATEGORY_CALL");
- return false;
- }
-
- /**
- * @return whether the user has enabled the provided notification to bubble, does not account
- * for policy.
- */
- private boolean canBubble(NotificationRecord r, String pkg, int userId) {
- Notification notification = r.getNotification();
- Notification.BubbleMetadata metadata = notification.getBubbleMetadata();
- if (metadata == null) {
- // no log: no need to inform dev if they didn't attach bubble metadata
- return false;
- }
- if (!mPreferencesHelper.bubblesEnabled()) {
- logBubbleError(r.getKey(), "bubbles disabled for user: " + userId);
- return false;
- }
- if (!mPreferencesHelper.areBubblesAllowed(pkg, userId)) {
- logBubbleError(r.getKey(),
- "bubbles for package: " + pkg + " disabled for user: " + userId);
- return false;
- }
- if (!r.getChannel().canBubble()) {
- logBubbleError(r.getKey(),
- "bubbles for channel " + r.getChannel().getId() + " disabled");
- return false;
- }
-
- String shortcutId = metadata.getShortcutId();
- boolean shortcutValid = shortcutId != null
- && hasValidShortcutInfo(shortcutId, pkg, r.getUser());
- if (metadata.getBubbleIntent() == null && !shortcutValid) {
- // Should have a shortcut if intent is null
- logBubbleError(r.getKey(), "couldn't find shortcutId for bubble: " + shortcutId);
- return false;
- }
- if (shortcutValid) {
- return true;
- }
- // no log: canLaunch method has the failure log
- return canLaunchInActivityView(getContext(), metadata.getBubbleIntent(), pkg);
- }
-
- private boolean hasValidRemoteInput(Notification n) {
- // Also check for inline reply
- Notification.Action[] actions = n.actions;
- if (actions != null) {
- // Get the remote inputs
- for (int i = 0; i < actions.length; i++) {
- Notification.Action action = actions[i];
- RemoteInput[] inputs = action.getRemoteInputs();
- if (inputs != null && inputs.length > 0) {
- return true;
+ private ShortcutHelper.ShortcutListener mShortcutListener =
+ new ShortcutHelper.ShortcutListener() {
+ @Override
+ public void onShortcutRemoved(String key) {
+ String packageName;
+ synchronized (mNotificationLock) {
+ NotificationRecord r = mNotificationsByKey.get(key);
+ packageName = r != null ? r.getSbn().getPackageName() : null;
+ }
+ boolean isAppForeground = packageName != null
+ && mActivityManager.getPackageImportance(packageName)
+ == IMPORTANCE_FOREGROUND;
+ synchronized (mNotificationLock) {
+ NotificationRecord r = mNotificationsByKey.get(key);
+ if (r != null) {
+ // Enqueue will trigger resort & flag is updated that way.
+ r.getNotification().flags |= FLAG_ONLY_ALERT_ONCE;
+ mHandler.post(
+ new NotificationManagerService.EnqueueNotificationRunnable(
+ r.getUser().getIdentifier(), r, isAppForeground));
+ }
+ }
}
- }
- }
- return false;
- }
-
- private ShortcutInfo getShortcutInfo(String shortcutId, String packageName, UserHandle user) {
- final long token = Binder.clearCallingIdentity();
- try {
- if (shortcutId == null || packageName == null || user == null) {
- return null;
- }
- LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery();
- if (packageName != null) {
- query.setPackage(packageName);
- }
- if (shortcutId != null) {
- query.setShortcutIds(Arrays.asList(shortcutId));
- }
- query.setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_CACHED);
- List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts(query, user);
- ShortcutInfo shortcutInfo = shortcuts != null && shortcuts.size() > 0
- ? shortcuts.get(0)
- : null;
- return shortcutInfo;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- private boolean hasValidShortcutInfo(String shortcutId, String packageName, UserHandle user) {
- ShortcutInfo shortcutInfo = getShortcutInfo(shortcutId, packageName, user);
- return shortcutInfo != null && shortcutInfo.isLongLived();
- }
-
- private void logBubbleError(String key, String failureMessage) {
- if (DBG) {
- Log.w(TAG, "Bubble notification: " + key + " failed: " + failureMessage);
- }
- }
- /**
- * Whether an intent is properly configured to display in an {@link android.app.ActivityView}.
- *
- * @param context the context to use.
- * @param pendingIntent the pending intent of the bubble.
- * @param packageName the notification package name for this bubble.
- */
- // Keep checks in sync with BubbleController#canLaunchInActivityView.
- @VisibleForTesting
- protected boolean canLaunchInActivityView(Context context, PendingIntent pendingIntent,
- String packageName) {
- if (pendingIntent == null) {
- Log.w(TAG, "Unable to create bubble -- no intent");
- return false;
- }
-
- // Need escalated privileges to get the intent.
- final long token = Binder.clearCallingIdentity();
- Intent intent;
- try {
- intent = pendingIntent.getIntent();
- } finally {
- Binder.restoreCallingIdentity(token);
- }
-
- ActivityInfo info = intent != null
- ? intent.resolveActivityInfo(context.getPackageManager(), 0)
- : null;
- if (info == null) {
- FrameworkStatsLog.write(FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
- BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING);
- Log.w(TAG, "Unable to send as bubble -- couldn't find activity info for intent: "
- + intent);
- return false;
- }
- if (!ActivityInfo.isResizeableMode(info.resizeMode)) {
- FrameworkStatsLog.write(FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
- BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE);
- Log.w(TAG, "Unable to send as bubble -- activity is not resizable for intent: "
- + intent);
- return false;
- }
- return true;
- }
+ };
private void doChannelWarningToast(CharSequence toastText) {
Binder.withCleanCallingIdentity(() -> {
@@ -6150,6 +5882,9 @@ public class NotificationManagerService extends SystemService {
MetricsLogger.action(r.getLogMaker()
.setType(MetricsProto.MetricsEvent.TYPE_UPDATE)
.setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED));
+ mNotificationRecordLogger.log(
+ NotificationRecordLogger.NotificationEvent.NOTIFICATION_NOT_POSTED_SNOOZED,
+ r);
if (DBG) {
Slog.d(TAG, "Ignored enqueue for snoozed notification " + r.getKey());
}
@@ -6279,6 +6014,8 @@ public class NotificationManagerService extends SystemService {
mDuration)
.addTaggedData(MetricsEvent.NOTIFICATION_SNOOZED_CRITERIA,
mSnoozeCriterionId == null ? 0 : 1));
+ mNotificationRecordLogger.log(
+ NotificationRecordLogger.NotificationEvent.NOTIFICATION_SNOOZED, r);
reportUserInteraction(r);
boolean wasPosted = removeFromNotificationListsLocked(r);
cancelNotificationLocked(r, false, REASON_SNOOZED, wasPosted, null);
@@ -6406,6 +6143,8 @@ public class NotificationManagerService extends SystemService {
cancelGroupChildrenLocked(r, mCallingUid, mCallingPid, listenerName,
mSendDelete, childrenFlagChecker);
updateLightsLocked();
+ mShortcutHelper.maybeListenForShortcutChangesForBubbles(r, true /* isRemoved */,
+ mHandler);
} else {
// No notification was found, assume that it is snoozed and cancel it.
if (mReason != REASON_SNOOZED) {
@@ -6473,7 +6212,7 @@ public class NotificationManagerService extends SystemService {
final String tag = n.getTag();
// We need to fix the notification up a little for bubbles
- updateNotificationBubbleFlags(r, pkg, callingUid, old, isAppForeground);
+ updateNotificationBubbleFlags(r, isAppForeground);
// Handle grouped notifications and bail out early if we
// can to avoid extracting signals.
@@ -6643,6 +6382,10 @@ public class NotificationManagerService extends SystemService {
+ n.getPackageName());
}
+ mShortcutHelper.maybeListenForShortcutChangesForBubbles(r,
+ false /* isRemoved */,
+ mHandler);
+
maybeRecordInterruptionLocked(r);
// Log event to statsd
@@ -7402,6 +7145,7 @@ public class NotificationManagerService extends SystemService {
int[] visibilities = new int[N];
boolean[] showBadges = new boolean[N];
boolean[] allowBubbles = new boolean[N];
+ boolean[] isBubble = new boolean[N];
ArrayList<NotificationChannel> channelBefore = new ArrayList<>(N);
ArrayList<String> groupKeyBefore = new ArrayList<>(N);
ArrayList<ArrayList<String>> overridePeopleBefore = new ArrayList<>(N);
@@ -7417,6 +7161,7 @@ public class NotificationManagerService extends SystemService {
visibilities[i] = r.getPackageVisibilityOverride();
showBadges[i] = r.canShowBadge();
allowBubbles[i] = r.canBubble();
+ isBubble[i] = r.getNotification().isBubbleNotification();
channelBefore.add(r.getChannel());
groupKeyBefore.add(r.getGroupKey());
overridePeopleBefore.add(r.getPeopleOverride());
@@ -7435,6 +7180,7 @@ public class NotificationManagerService extends SystemService {
|| visibilities[i] != r.getPackageVisibilityOverride()
|| showBadges[i] != r.canShowBadge()
|| allowBubbles[i] != r.canBubble()
+ || isBubble[i] != r.getNotification().isBubbleNotification()
|| !Objects.equals(channelBefore.get(i), r.getChannel())
|| !Objects.equals(groupKeyBefore.get(i), r.getGroupKey())
|| !Objects.equals(overridePeopleBefore.get(i), r.getPeopleOverride())
@@ -8597,7 +8343,8 @@ public class NotificationManagerService extends SystemService {
record.canBubble(),
record.isInterruptive(),
record.isConversation(),
- record.getShortcutInfo()
+ record.getShortcutInfo(),
+ record.getNotification().isBubbleNotification()
);
rankings.add(ranking);
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
index f4ee4616df27..6c833f966d36 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
@@ -34,11 +34,14 @@ import java.util.ArrayList;
import java.util.Objects;
/**
- * Interface for writing NotificationReported atoms to statsd log.
+ * Interface for writing NotificationReported atoms to statsd log. Use NotificationRecordLoggerImpl
+ * in production. Use NotificationRecordLoggerFake for testing.
* @hide
*/
public interface NotificationRecordLogger {
+ // The high-level interface used by clients.
+
/**
* May log a NotificationReported atom reflecting the posting or update of a notification.
* @param r The new NotificationRecord. If null, no action is taken.
@@ -57,9 +60,11 @@ public interface NotificationRecordLogger {
* @param reason The reason the notification was canceled.
* @param dismissalSurface The surface the notification was dismissed from.
*/
- void logNotificationCancelled(@Nullable NotificationRecord r,
+ default void logNotificationCancelled(@Nullable NotificationRecord r,
@NotificationListenerService.NotificationCancelReason int reason,
- @NotificationStats.DismissalSurface int dismissalSurface);
+ @NotificationStats.DismissalSurface int dismissalSurface) {
+ log(NotificationCancelledEvent.fromCancelReason(reason, dismissalSurface), r);
+ }
/**
* Logs a notification visibility change event using UiEventReported (event ids from the
@@ -67,7 +72,17 @@ public interface NotificationRecordLogger {
* @param r The NotificationRecord. If null, no action is taken.
* @param visible True if the notification became visible.
*/
- void logNotificationVisibility(@Nullable NotificationRecord r, boolean visible);
+ default void logNotificationVisibility(@Nullable NotificationRecord r, boolean visible) {
+ log(NotificationEvent.fromVisibility(visible), r);
+ }
+
+ // The UiEventReported logging methods are implemented in terms of this lower-level interface.
+
+ /** Logs a UiEventReported event for the given notification. */
+ void log(UiEventLogger.UiEventEnum event, NotificationRecord r);
+
+ /** Logs a UiEventReported event that is not associated with any notification. */
+ void log(UiEventLogger.UiEventEnum event);
/**
* The UiEvent enums that this class can log.
@@ -204,7 +219,30 @@ public interface NotificationRecordLogger {
@UiEvent(doc = "Notification became visible.")
NOTIFICATION_OPEN(197),
@UiEvent(doc = "Notification stopped being visible.")
- NOTIFICATION_CLOSE(198);
+ NOTIFICATION_CLOSE(198),
+ @UiEvent(doc = "Notification was snoozed.")
+ NOTIFICATION_SNOOZED(317),
+ @UiEvent(doc = "Notification was not posted because its app is snoozed.")
+ NOTIFICATION_NOT_POSTED_SNOOZED(319),
+ @UiEvent(doc = "Notification was clicked.")
+ NOTIFICATION_CLICKED(320),
+ @UiEvent(doc = "Notification action was clicked.")
+ NOTIFICATION_ACTION_CLICKED(321),
+ @UiEvent(doc = "Notification detail was expanded due to non-user action.")
+ NOTIFICATION_DETAIL_OPEN_SYSTEM(327),
+ @UiEvent(doc = "Notification detail was collapsed due to non-user action.")
+ NOTIFICATION_DETAIL_CLOSE_SYSTEM(328),
+ @UiEvent(doc = "Notification detail was expanded due to user action.")
+ NOTIFICATION_DETAIL_OPEN_USER(329),
+ @UiEvent(doc = "Notification detail was collapsed due to user action.")
+ NOTIFICATION_DETAIL_CLOSE_USER(330),
+ @UiEvent(doc = "Notification direct reply action was used.")
+ NOTIFICATION_DIRECT_REPLIED(331),
+ @UiEvent(doc = "Notification smart reply action was used.")
+ NOTIFICATION_SMART_REPLIED(332),
+ @UiEvent(doc = "Notification smart reply action was visible.")
+ NOTIFICATION_SMART_REPLY_VISIBLE(333),
+ ;
private final int mId;
NotificationEvent(int id) {
@@ -217,7 +255,29 @@ public interface NotificationRecordLogger {
public static NotificationEvent fromVisibility(boolean visible) {
return visible ? NOTIFICATION_OPEN : NOTIFICATION_CLOSE;
}
+ public static NotificationEvent fromExpanded(boolean expanded, boolean userAction) {
+ if (userAction) {
+ return expanded ? NOTIFICATION_DETAIL_OPEN_USER : NOTIFICATION_DETAIL_CLOSE_USER;
+ }
+ return expanded ? NOTIFICATION_DETAIL_OPEN_SYSTEM : NOTIFICATION_DETAIL_CLOSE_SYSTEM;
+ }
}
+
+ enum NotificationPanelEvent implements UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "Notification panel became visible.")
+ NOTIFICATION_PANEL_OPEN(325),
+ @UiEvent(doc = "Notification panel stopped being visible.")
+ NOTIFICATION_PANEL_CLOSE(326);
+
+ private final int mId;
+ NotificationPanelEvent(int id) {
+ mId = id;
+ }
+ @Override public int getId() {
+ return mId;
+ }
+ }
+
/**
* A helper for extracting logging information from one or two NotificationRecords.
*/
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
index 9fcac257d328..494ff314a8aa 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
@@ -65,20 +65,16 @@ public class NotificationRecordLoggerImpl implements NotificationRecordLogger {
}
@Override
- public void logNotificationCancelled(NotificationRecord r, int reason, int dismissalSurface) {
- log(NotificationCancelledEvent.fromCancelReason(reason, dismissalSurface), r);
- }
-
- @Override
- public void logNotificationVisibility(NotificationRecord r, boolean visible) {
- log(NotificationEvent.fromVisibility(visible), r);
- }
-
- void log(UiEventLogger.UiEventEnum event, NotificationRecord r) {
+ public void log(UiEventLogger.UiEventEnum event, NotificationRecord r) {
if (r == null) {
return;
}
mUiEventLogger.logWithInstanceId(event, r.getUid(), r.getSbn().getPackageName(),
r.getSbn().getInstanceId());
}
+
+ @Override
+ public void log(UiEventLogger.UiEventEnum event) {
+ mUiEventLogger.log(event);
+ }
}
diff --git a/services/core/java/com/android/server/notification/ShortcutHelper.java b/services/core/java/com/android/server/notification/ShortcutHelper.java
new file mode 100644
index 000000000000..7bbb3b117517
--- /dev/null
+++ b/services/core/java/com/android/server/notification/ShortcutHelper.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.notification;
+
+import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED;
+import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
+import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
+
+import android.annotation.NonNull;
+import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutInfo;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.UserHandle;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Helper for querying shortcuts.
+ */
+class ShortcutHelper {
+
+ /**
+ * Listener to call when a shortcut we're tracking has been removed.
+ */
+ interface ShortcutListener {
+ void onShortcutRemoved(String key);
+ }
+
+ private LauncherApps mLauncherAppsService;
+ private ShortcutListener mShortcutListener;
+
+ // Key: packageName Value: <shortcutId, notifId>
+ private HashMap<String, HashMap<String, String>> mActiveShortcutBubbles = new HashMap<>();
+ private boolean mLauncherAppsCallbackRegistered;
+
+ // Bubbles can be created based on a shortcut, we need to listen for changes to
+ // that shortcut so that we may update the bubble appropriately.
+ private final LauncherApps.Callback mLauncherAppsCallback = new LauncherApps.Callback() {
+ @Override
+ public void onPackageRemoved(String packageName, UserHandle user) {
+ }
+
+ @Override
+ public void onPackageAdded(String packageName, UserHandle user) {
+ }
+
+ @Override
+ public void onPackageChanged(String packageName, UserHandle user) {
+ }
+
+ @Override
+ public void onPackagesAvailable(String[] packageNames, UserHandle user,
+ boolean replacing) {
+ }
+
+ @Override
+ public void onPackagesUnavailable(String[] packageNames, UserHandle user,
+ boolean replacing) {
+ }
+
+ @Override
+ public void onShortcutsChanged(@NonNull String packageName,
+ @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {
+ HashMap<String, String> shortcutBubbles = mActiveShortcutBubbles.get(packageName);
+ ArrayList<String> bubbleKeysToRemove = new ArrayList<>();
+ if (shortcutBubbles != null) {
+ // If we can't find one of our bubbles in the shortcut list, that bubble needs
+ // to be removed.
+ for (String shortcutId : shortcutBubbles.keySet()) {
+ boolean foundShortcut = false;
+ for (int i = 0; i < shortcuts.size(); i++) {
+ if (shortcuts.get(i).getId().equals(shortcutId)) {
+ foundShortcut = true;
+ break;
+ }
+ }
+ if (!foundShortcut) {
+ bubbleKeysToRemove.add(shortcutBubbles.get(shortcutId));
+ }
+ }
+ }
+
+ // Let NoMan know about the updates
+ for (int i = 0; i < bubbleKeysToRemove.size(); i++) {
+ // update flag bubble
+ String bubbleKey = bubbleKeysToRemove.get(i);
+ if (mShortcutListener != null) {
+ mShortcutListener.onShortcutRemoved(bubbleKey);
+ }
+ }
+ }
+ };
+
+ ShortcutHelper(LauncherApps launcherApps, ShortcutListener listener) {
+ mLauncherAppsService = launcherApps;
+ mShortcutListener = listener;
+ }
+
+ @VisibleForTesting
+ void setLauncherApps(LauncherApps launcherApps) {
+ mLauncherAppsService = launcherApps;
+ }
+
+ ShortcutInfo getShortcutInfo(String shortcutId, String packageName, UserHandle user) {
+ if (mLauncherAppsService == null) {
+ return null;
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (shortcutId == null || packageName == null || user == null) {
+ return null;
+ }
+ LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery();
+ query.setPackage(packageName);
+ query.setShortcutIds(Arrays.asList(shortcutId));
+ query.setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_CACHED);
+ List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts(query, user);
+ return shortcuts != null && shortcuts.size() > 0
+ ? shortcuts.get(0)
+ : null;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ boolean hasValidShortcutInfo(String shortcutId, String packageName,
+ UserHandle user) {
+ ShortcutInfo shortcutInfo = getShortcutInfo(shortcutId, packageName, user);
+ return shortcutInfo != null && shortcutInfo.isLongLived();
+ }
+
+ /**
+ * Shortcut based bubbles require some extra work to listen for shortcut changes.
+ *
+ * @param r the notification record to check
+ * @param removedNotification true if this notification is being removed
+ * @param handler handler to register the callback with
+ */
+ void maybeListenForShortcutChangesForBubbles(NotificationRecord r, boolean removedNotification,
+ Handler handler) {
+ final String shortcutId = r.getNotification().getBubbleMetadata() != null
+ ? r.getNotification().getBubbleMetadata().getShortcutId()
+ : null;
+ if (shortcutId == null) {
+ return;
+ }
+ if (r.getNotification().isBubbleNotification() && !removedNotification) {
+ // Must track shortcut based bubbles in case the shortcut is removed
+ HashMap<String, String> packageBubbles = mActiveShortcutBubbles.get(
+ r.getSbn().getPackageName());
+ if (packageBubbles == null) {
+ packageBubbles = new HashMap<>();
+ }
+ packageBubbles.put(shortcutId, r.getKey());
+ mActiveShortcutBubbles.put(r.getSbn().getPackageName(), packageBubbles);
+ if (!mLauncherAppsCallbackRegistered) {
+ mLauncherAppsService.registerCallback(mLauncherAppsCallback, handler);
+ mLauncherAppsCallbackRegistered = true;
+ }
+ } else {
+ // No longer track shortcut
+ HashMap<String, String> packageBubbles = mActiveShortcutBubbles.get(
+ r.getSbn().getPackageName());
+ if (packageBubbles != null) {
+ packageBubbles.remove(shortcutId);
+ }
+ if (packageBubbles != null && packageBubbles.isEmpty()) {
+ mActiveShortcutBubbles.remove(r.getSbn().getPackageName());
+ }
+ if (mLauncherAppsCallbackRegistered && mActiveShortcutBubbles.isEmpty()) {
+ mLauncherAppsService.unregisterCallback(mLauncherAppsCallback);
+ mLauncherAppsCallbackRegistered = false;
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index f45e66e79c76..2853956d0a79 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -141,6 +141,7 @@ public class ZenModeHelper {
updateDefaultAutomaticRuleNames();
mConfig = mDefaultConfig.copy();
mConfigs.put(UserHandle.USER_SYSTEM, mConfig);
+ mConsolidatedPolicy = mConfig.toNotificationPolicy();
mSettingsObserver = new SettingsObserver(mHandler);
mSettingsObserver.observe();
@@ -821,9 +822,6 @@ public class ZenModeHelper {
* @return a copy of the zen mode consolidated policy
*/
public Policy getConsolidatedNotificationPolicy() {
- if (mConsolidatedPolicy == null) {
- return null;
- }
return mConsolidatedPolicy.copy();
}
diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
index 6c2d77cac6d3..8349632a3743 100644
--- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
+++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
@@ -66,7 +66,7 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub {
@RequiresPermission(android.Manifest.permission.DUMP)
public void startBugreport(int callingUidUnused, String callingPackage,
FileDescriptor bugreportFd, FileDescriptor screenshotFd,
- int bugreportMode, IDumpstateListener listener) {
+ int bugreportMode, IDumpstateListener listener, boolean isScreenshotRequested) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, "startBugreport");
Objects.requireNonNull(callingPackage);
Objects.requireNonNull(bugreportFd);
@@ -88,7 +88,7 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub {
}
synchronized (mLock) {
startBugreportLocked(callingUid, callingPackage, bugreportFd, screenshotFd,
- bugreportMode, listener);
+ bugreportMode, listener, isScreenshotRequested);
}
}
@@ -145,7 +145,7 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub {
@GuardedBy("mLock")
private void startBugreportLocked(int callingUid, String callingPackage,
FileDescriptor bugreportFd, FileDescriptor screenshotFd,
- int bugreportMode, IDumpstateListener listener) {
+ int bugreportMode, IDumpstateListener listener, boolean isScreenshotRequested) {
if (isDumpstateBinderServiceRunningLocked()) {
Slog.w(TAG, "'dumpstate' is already running. Cannot start a new bugreport"
+ " while another one is currently in progress.");
@@ -165,7 +165,7 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub {
IDumpstateListener myListener = new DumpstateListener(listener, ds);
try {
ds.startBugreport(callingUid, callingPackage,
- bugreportFd, screenshotFd, bugreportMode, myListener);
+ bugreportFd, screenshotFd, bugreportMode, myListener, isScreenshotRequested);
} catch (RemoteException e) {
// bugreportd service is already started now. We need to kill it to manage the
// lifecycle correctly. If we don't subsequent callers will get
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index d12e03d32080..c37ea8ba68bd 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -301,6 +301,14 @@ public abstract class ApexManager {
public abstract boolean destroyDeSnapshots(int rollbackId);
/**
+ * Deletes snapshots of the credential encrypted apex data directories for the specified user,
+ * where the rollback id is not included in {@code retainRollbackIds}.
+ *
+ * @return boolean true if the delete was successful
+ */
+ public abstract boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds);
+
+ /**
* Dumps various state information to the provided {@link PrintWriter} object.
*
* @param pw the {@link PrintWriter} object to send information to.
@@ -745,6 +753,17 @@ public abstract class ApexManager {
}
}
+ @Override
+ public boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds) {
+ try {
+ mApexService.destroyCeSnapshotsNotSpecified(userId, retainRollbackIds);
+ return true;
+ } catch (Exception e) {
+ Slog.e(TAG, e.getMessage(), e);
+ return false;
+ }
+ }
+
/**
* Dump information about the packages contained in a particular cache
* @param packagesCache the cache to print information about.
@@ -963,6 +982,11 @@ public abstract class ApexManager {
}
@Override
+ public boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds) {
+ return true;
+ }
+
+ @Override
void dump(PrintWriter pw, String packageName) {
// No-op
}
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 79a4da2a4a65..690b9f71d90b 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -481,6 +481,12 @@ public class AppsFilter {
mQueriesViaPackage.add(newPkgSetting.appId, existingSetting.appId);
}
}
+ // if either package instruments the other, mark both as visible to one another
+ if (pkgInstruments(newPkgSetting, existingSetting)
+ || pkgInstruments(existingSetting, newPkgSetting)) {
+ mQueriesViaPackage.add(newPkgSetting.appId, existingSetting.appId);
+ mQueriesViaPackage.add(existingSetting.appId, newPkgSetting.appId);
+ }
}
int existingSize = existingSettings.size();
@@ -715,19 +721,6 @@ public class AppsFilter {
Trace.endSection();
}
- if (callingPkgSetting != null) {
- if (callingPkgInstruments(callingPkgSetting, targetPkgSetting, targetName)) {
- return false;
- }
- } else {
- for (int i = callingSharedPkgSettings.size() - 1; i >= 0; i--) {
- if (callingPkgInstruments(callingSharedPkgSettings.valueAt(i),
- targetPkgSetting, targetName)) {
- return false;
- }
- }
- }
-
try {
Trace.beginSection("mOverlayReferenceMapper");
if (callingSharedPkgSettings != null) {
@@ -762,16 +755,16 @@ public class AppsFilter {
}
}
- private static boolean callingPkgInstruments(PackageSetting callingPkgSetting,
- PackageSetting targetPkgSetting,
- String targetName) {
+ /** Returns {@code true} if the source package instruments the target package. */
+ private static boolean pkgInstruments(PackageSetting source, PackageSetting target) {
try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "callingPkgInstruments");
- final List<ParsedInstrumentation> inst = callingPkgSetting.pkg.getInstrumentations();
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "pkgInstruments");
+ final String packageName = target.pkg.getPackageName();
+ final List<ParsedInstrumentation> inst = source.pkg.getInstrumentations();
for (int i = ArrayUtils.size(inst) - 1; i >= 0; i--) {
- if (Objects.equals(inst.get(i).getTargetPackage(), targetName)) {
+ if (Objects.equals(inst.get(i).getTargetPackage(), packageName)) {
if (DEBUG_LOGGING) {
- log(callingPkgSetting, targetPkgSetting, "instrumentation");
+ log(source, target, "instrumentation");
}
return true;
}
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 8031eaa62084..1b271a739777 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -819,6 +819,18 @@ public class LauncherAppsService extends SystemService {
}
@Override
+ public String getShortcutIconUri(String callingPackage, String packageName,
+ String shortcutId, int userId) {
+ ensureShortcutPermission(callingPackage);
+ if (!canAccessProfile(userId, "Cannot access shortcuts")) {
+ return null;
+ }
+
+ return mShortcutServiceInternal.getShortcutIconUri(getCallingUserId(), callingPackage,
+ packageName, shortcutId, userId);
+ }
+
+ @Override
public boolean hasShortcutHostPermission(String callingPackage) {
verifyCallingPackage(callingPackage);
return mShortcutServiceInternal.hasShortcutHostPermission(getCallingUserId(),
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 483f83efd508..28d7c13d5b13 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -16,6 +16,7 @@
package com.android.server.pm;
+import static android.app.AppOpsManager.MODE_DEFAULT;
import static android.content.pm.DataLoaderType.INCREMENTAL;
import static android.content.pm.DataLoaderType.STREAMING;
import static android.content.pm.PackageInstaller.LOCATION_DATA_APP;
@@ -166,6 +167,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private static final String TAG_GRANTED_RUNTIME_PERMISSION = "granted-runtime-permission";
private static final String TAG_WHITELISTED_RESTRICTED_PERMISSION =
"whitelisted-restricted-permission";
+ private static final String TAG_AUTO_REVOKE_PERMISSIONS_MODE =
+ "auto-revoke-permissions-mode";
private static final String ATTR_SESSION_ID = "sessionId";
private static final String ATTR_USER_ID = "userId";
private static final String ATTR_INSTALLER_PACKAGE_NAME = "installerPackageName";
@@ -311,8 +314,41 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@GuardedBy("mLock")
private int mParentSessionId;
+ static class FileEntry {
+ private final int mIndex;
+ private final InstallationFile mFile;
+
+ FileEntry(int index, InstallationFile file) {
+ this.mIndex = index;
+ this.mFile = file;
+ }
+
+ int getIndex() {
+ return this.mIndex;
+ }
+
+ InstallationFile getFile() {
+ return this.mFile;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof FileEntry)) {
+ return false;
+ }
+ final FileEntry rhs = (FileEntry) obj;
+ return (mFile.getLocation() == rhs.mFile.getLocation()) && TextUtils.equals(
+ mFile.getName(), rhs.mFile.getName());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mFile.getLocation(), mFile.getName());
+ }
+ }
+
@GuardedBy("mLock")
- private ArrayList<InstallationFile> mFiles = new ArrayList<>();
+ private ArraySet<FileEntry> mFiles = new ArraySet<>();
@GuardedBy("mLock")
private boolean mStagedSessionApplied;
@@ -516,8 +552,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
this.mParentSessionId = parentSessionId;
if (files != null) {
- for (InstallationFile file : files) {
- mFiles.add(file);
+ for (int i = 0, size = files.length; i < size; ++i) {
+ InstallationFile file = files[i];
+ if (!mFiles.add(new FileEntry(i, file))) {
+ throw new IllegalArgumentException(
+ "Trying to add a duplicate installation file");
+ }
}
}
@@ -623,6 +663,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
info.grantedRuntimePermissions = params.grantedRuntimePermissions;
info.whitelistedRestrictedPermissions = params.whitelistedRestrictedPermissions;
+ info.autoRevokePermissionsMode = params.autoRevokePermissionsMode;
info.installFlags = params.installFlags;
info.isMultiPackage = params.isMultiPackage;
info.isStaged = params.isStaged;
@@ -750,9 +791,19 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
return result;
}
- String[] result = new String[mFiles.size()];
- for (int i = 0, size = mFiles.size(); i < size; ++i) {
- result[i] = mFiles.get(i).getName();
+ InstallationFile[] files = getInstallationFilesLocked();
+ String[] result = new String[files.length];
+ for (int i = 0, size = files.length; i < size; ++i) {
+ result[i] = files[i].getName();
+ }
+ return result;
+ }
+
+ @GuardedBy("mLock")
+ private InstallationFile[] getInstallationFilesLocked() {
+ final InstallationFile[] result = new InstallationFile[mFiles.size()];
+ for (FileEntry fileEntry : mFiles) {
+ result[fileEntry.getIndex()] = fileEntry.getFile();
}
return result;
}
@@ -2444,7 +2495,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
assertCallerIsOwnerOrRootLocked();
assertPreparedAndNotSealedLocked("addFile");
- mFiles.add(new InstallationFile(location, name, lengthBytes, metadata, signature));
+ if (!mFiles.add(new FileEntry(mFiles.size(),
+ new InstallationFile(location, name, lengthBytes, metadata, signature)))) {
+ throw new IllegalArgumentException("File already added: " + name);
+ }
}
}
@@ -2462,7 +2516,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
assertCallerIsOwnerOrRootLocked();
assertPreparedAndNotSealedLocked("removeFile");
- mFiles.add(new InstallationFile(location, getRemoveMarkerName(name), -1, null, null));
+ if (!mFiles.add(new FileEntry(mFiles.size(),
+ new InstallationFile(location, getRemoveMarkerName(name), -1, null, null)))) {
+ throw new IllegalArgumentException("File already removed: " + name);
+ }
}
}
@@ -2482,7 +2539,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final List<InstallationFileParcel> addedFiles = new ArrayList<>();
final List<String> removedFiles = new ArrayList<>();
- for (InstallationFile file : mFiles) {
+ final InstallationFile[] files = getInstallationFilesLocked();
+ for (InstallationFile file : files) {
if (sAddedFilter.accept(new File(this.stageDir, file.getName()))) {
addedFiles.add(file.getData());
continue;
@@ -2889,6 +2947,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
+ private static void writeAutoRevokePermissionsMode(@NonNull XmlSerializer out, int mode)
+ throws IOException {
+ out.startTag(null, TAG_AUTO_REVOKE_PERMISSIONS_MODE);
+ writeIntAttribute(out, ATTR_MODE, mode);
+ out.endTag(null, TAG_AUTO_REVOKE_PERMISSIONS_MODE);
+ }
+
private static File buildAppIconFile(int sessionId, @NonNull File sessionsDir) {
return new File(sessionsDir, "app_icon." + sessionId + ".png");
@@ -2969,6 +3034,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
writeGrantedRuntimePermissionsLocked(out, params.grantedRuntimePermissions);
writeWhitelistedRestrictedPermissionsLocked(out,
params.whitelistedRestrictedPermissions);
+ writeAutoRevokePermissionsMode(out, params.autoRevokePermissionsMode);
// Persist app icon if changed since last written
File appIconFile = buildAppIconFile(sessionId, sessionsDir);
@@ -2995,7 +3061,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
writeIntAttribute(out, ATTR_SESSION_ID, childSessionId);
out.endTag(null, TAG_CHILD_SESSION);
}
- for (InstallationFile file : mFiles) {
+
+ final InstallationFile[] files = getInstallationFilesLocked();
+ for (InstallationFile file : getInstallationFilesLocked()) {
out.startTag(null, TAG_SESSION_FILE);
writeIntAttribute(out, ATTR_LOCATION, file.getLocation());
writeStringAttribute(out, ATTR_NAME, file.getName());
@@ -3112,6 +3180,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// depth.
List<String> grantedRuntimePermissions = new ArrayList<>();
List<String> whitelistedRestrictedPermissions = new ArrayList<>();
+ int autoRevokePermissionsMode = MODE_DEFAULT;
List<Integer> childSessionIds = new ArrayList<>();
List<InstallationFile> files = new ArrayList<>();
int outerDepth = in.getDepth();
@@ -3128,6 +3197,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
whitelistedRestrictedPermissions.add(readStringAttribute(in, ATTR_NAME));
}
+ if (TAG_AUTO_REVOKE_PERMISSIONS_MODE.equals(in.getName())) {
+ autoRevokePermissionsMode = readIntAttribute(in, ATTR_MODE);
+ }
if (TAG_CHILD_SESSION.equals(in.getName())) {
childSessionIds.add(readIntAttribute(in, ATTR_SESSION_ID, SessionInfo.INVALID_ID));
}
@@ -3150,6 +3222,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
params.whitelistedRestrictedPermissions = whitelistedRestrictedPermissions;
}
+ params.autoRevokePermissionsMode = autoRevokePermissionsMode;
+
int[] childSessionIdsArray;
if (childSessionIds.size() > 0) {
childSessionIdsArray = new int[childSessionIds.size()];
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ca2e2406d14d..24ebd32f2e2d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -25,6 +25,7 @@ import static android.Manifest.permission.REQUEST_DELETE_PACKAGES;
import static android.Manifest.permission.SET_HARMFUL_APP_WARNINGS;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_DEFAULT;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.content.Intent.ACTION_MAIN;
import static android.content.Intent.CATEGORY_DEFAULT;
@@ -1656,12 +1657,13 @@ public class PackageManagerService extends IPackageManager.Stub
&& parentRes.pkg != null)
? parentRes.pkg.getRequestedPermissions()
: args.whitelistedRestrictedPermissions;
+ int autoRevokePermissionsMode = args.autoRevokePermissionsMode;
// Handle the parent package
handlePackagePostInstall(parentRes, grantPermissions,
killApp, virtualPreload, grantedPermissions,
- whitelistedRestrictedPermissions, didRestore,
- args.installSource.installerPackageName, args.observer,
+ whitelistedRestrictedPermissions, autoRevokePermissionsMode,
+ didRestore, args.installSource.installerPackageName, args.observer,
args.mDataLoaderType);
// Handle the child packages
@@ -1671,7 +1673,8 @@ public class PackageManagerService extends IPackageManager.Stub
PackageInstalledInfo childRes = parentRes.addedChildPackages.valueAt(i);
handlePackagePostInstall(childRes, grantPermissions,
killApp, virtualPreload, grantedPermissions,
- whitelistedRestrictedPermissions, false /*didRestore*/,
+ whitelistedRestrictedPermissions, autoRevokePermissionsMode,
+ false /*didRestore*/,
args.installSource.installerPackageName, args.observer,
args.mDataLoaderType);
}
@@ -2000,6 +2003,7 @@ public class PackageManagerService extends IPackageManager.Stub
private void handlePackagePostInstall(PackageInstalledInfo res, boolean grantPermissions,
boolean killApp, boolean virtualPreload,
String[] grantedPermissions, List<String> whitelistedRestrictedPermissions,
+ int autoRevokePermissionsMode,
boolean launchedForRestore, String installerPackage,
IPackageInstallObserver2 installObserver, int dataLoaderType) {
final boolean succeeded = res.returnCode == PackageManager.INSTALL_SUCCEEDED;
@@ -2020,6 +2024,11 @@ public class PackageManagerService extends IPackageManager.Stub
Process.myUid(), FLAG_PERMISSION_WHITELIST_INSTALLER);
}
+ if (autoRevokePermissionsMode == MODE_ALLOWED || autoRevokePermissionsMode == MODE_IGNORED) {
+ mPermissionManager.setAutoRevokeWhitelisted(res.pkg.getPackageName(),
+ autoRevokePermissionsMode == MODE_IGNORED, UserHandle.myUserId());
+ }
+
// Now that we successfully installed the package, grant runtime
// permissions if requested before broadcasting the install. Also
// for legacy apps in permission review mode we clear the permission
@@ -14297,6 +14306,7 @@ public class PackageManagerService extends IPackageManager.Stub
final String packageAbiOverride;
final String[] grantedRuntimePermissions;
final List<String> whitelistedRestrictedPermissions;
+ final int autoRevokePermissionsMode;
final VerificationInfo verificationInfo;
final PackageParser.SigningDetails signingDetails;
final int installReason;
@@ -14311,6 +14321,7 @@ public class PackageManagerService extends IPackageManager.Stub
int installFlags, InstallSource installSource, String volumeUuid,
VerificationInfo verificationInfo, UserHandle user, String packageAbiOverride,
String[] grantedPermissions, List<String> whitelistedRestrictedPermissions,
+ int autoRevokePermissionsMode,
SigningDetails signingDetails, int installReason,
long requiredInstalledVersionCode, int dataLoaderType) {
super(user);
@@ -14324,6 +14335,7 @@ public class PackageManagerService extends IPackageManager.Stub
this.packageAbiOverride = packageAbiOverride;
this.grantedRuntimePermissions = grantedPermissions;
this.whitelistedRestrictedPermissions = whitelistedRestrictedPermissions;
+ this.autoRevokePermissionsMode = autoRevokePermissionsMode;
this.signingDetails = signingDetails;
this.installReason = installReason;
this.requiredInstalledVersionCode = requiredInstalledVersionCode;
@@ -14360,6 +14372,7 @@ public class PackageManagerService extends IPackageManager.Stub
packageAbiOverride = sessionParams.abiOverride;
grantedRuntimePermissions = sessionParams.grantedRuntimePermissions;
whitelistedRestrictedPermissions = sessionParams.whitelistedRestrictedPermissions;
+ autoRevokePermissionsMode = sessionParams.autoRevokePermissionsMode;
signingDetails = activeInstallSession.getSigningDetails();
requiredInstalledVersionCode = sessionParams.requiredInstalledVersionCode;
forceQueryableOverride = sessionParams.forceQueryableOverride;
@@ -14728,9 +14741,8 @@ public class PackageManagerService extends IPackageManager.Stub
verificationState.setRequiredVerifierUid(requiredUid);
final int installerUid =
verificationInfo == null ? -1 : verificationInfo.installerUid;
- if (!origin.existing && requiredUid != -1
- && isVerificationEnabled(
- pkgLite, verifierUser.getIdentifier(), installFlags, installerUid)) {
+ if (!origin.existing && isVerificationEnabled(pkgLite, verifierUser.getIdentifier(),
+ installFlags, installerUid)) {
final Intent verification = new Intent(
Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
verification.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
@@ -14796,9 +14808,9 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- final ComponentName requiredVerifierComponent = matchComponentForVerifier(
- mRequiredVerifierPackage, receivers);
if (mRequiredVerifierPackage != null) {
+ final ComponentName requiredVerifierComponent = matchComponentForVerifier(
+ mRequiredVerifierPackage, receivers);
/*
* Send the intent to the required verification agent,
* but only start the verification timeout after the
@@ -14956,6 +14968,7 @@ public class PackageManagerService extends IPackageManager.Stub
final String abiOverride;
final String[] installGrantPermissions;
final List<String> whitelistedRestrictedPermissions;
+ final int autoRevokePermissionsMode;
/** If non-null, drop an async trace when the install completes */
final String traceMethod;
final int traceCookie;
@@ -14975,6 +14988,7 @@ public class PackageManagerService extends IPackageManager.Stub
UserHandle user, String[] instructionSets,
String abiOverride, String[] installGrantPermissions,
List<String> whitelistedRestrictedPermissions,
+ int autoRevokePermissionsMode,
String traceMethod, int traceCookie, SigningDetails signingDetails,
int installReason, boolean forceQueryableOverride,
MultiPackageInstallParams multiPackageInstallParams, int dataLoaderType) {
@@ -14989,6 +15003,7 @@ public class PackageManagerService extends IPackageManager.Stub
this.abiOverride = abiOverride;
this.installGrantPermissions = installGrantPermissions;
this.whitelistedRestrictedPermissions = whitelistedRestrictedPermissions;
+ this.autoRevokePermissionsMode = autoRevokePermissionsMode;
this.traceMethod = traceMethod;
this.traceCookie = traceCookie;
this.signingDetails = signingDetails;
@@ -15004,6 +15019,7 @@ public class PackageManagerService extends IPackageManager.Stub
params.installSource, params.volumeUuid,
params.getUser(), null /*instructionSets*/, params.packageAbiOverride,
params.grantedRuntimePermissions, params.whitelistedRestrictedPermissions,
+ params.autoRevokePermissionsMode,
params.traceMethod, params.traceCookie, params.signingDetails,
params.installReason, params.forceQueryableOverride,
params.mParentInstallParams, params.mDataLoaderType);
@@ -15095,7 +15111,7 @@ public class PackageManagerService extends IPackageManager.Stub
/** Existing install */
FileInstallArgs(String codePath, String resourcePath, String[] instructionSets) {
super(OriginInfo.fromNothing(), null, null, 0, InstallSource.EMPTY,
- null, null, instructionSets, null, null, null, null, 0,
+ null, null, instructionSets, null, null, null, MODE_DEFAULT, null, 0,
PackageParser.SigningDetails.UNKNOWN,
PackageManager.INSTALL_REASON_UNKNOWN, false, null /* parent */,
DataLoaderType.NONE);
@@ -22471,7 +22487,8 @@ public class PackageManagerService extends IPackageManager.Stub
final InstallParams params = new InstallParams(origin, move, installObserver, installFlags,
installSource, volumeUuid, null /*verificationInfo*/, user,
packageAbiOverride, null /*grantedPermissions*/,
- null /*whitelistedRestrictedPermissions*/, PackageParser.SigningDetails.UNKNOWN,
+ null /*whitelistedRestrictedPermissions*/, MODE_DEFAULT /* autoRevokePermissions */,
+ PackageParser.SigningDetails.UNKNOWN,
PackageManager.INSTALL_REASON_UNKNOWN, PackageManager.VERSION_CODE_HIGHEST,
DataLoaderType.NONE);
params.setTraceMethod("movePackage").setTraceCookie(System.identityHashCode(params));
@@ -24175,6 +24192,12 @@ public class PackageManagerService extends IPackageManager.Stub
}
mAppsFilter.getFeatureConfig().enableLogging(pkg.appId, enable);
}
+
+ @Override
+ public boolean isSystemPackage(@NonNull String packageName) {
+ return packageName.equals(
+ PackageManagerService.this.ensureSystemPackageName(packageName));
+ }
}
@GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index be17dd8989a3..8a9f1b3afd02 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -3017,9 +3017,11 @@ class PackageManagerShellCommand extends ShellCommand {
private int doAddFiles(int sessionId, ArrayList<String> args, long sessionSizeBytes,
boolean isApex) throws RemoteException {
- PackageInstaller.Session session = new PackageInstaller.Session(
- mInterface.getPackageInstaller().openSession(sessionId));
+ PackageInstaller.Session session = null;
try {
+ session = new PackageInstaller.Session(
+ mInterface.getPackageInstaller().openSession(sessionId));
+
// 1. Single file from stdin.
if (args.isEmpty() || STDIN_PATH.equals(args.get(0))) {
final String name = "base." + (isApex ? "apex" : "apk");
@@ -3043,6 +3045,10 @@ class PackageManagerShellCommand extends ShellCommand {
}
}
return 0;
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println("Failed to add file(s), reason: " + e);
+ getOutPrintWriter().println("Failure [failed to add file(s)]");
+ return 1;
} finally {
IoUtils.closeQuietly(session);
}
diff --git a/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java b/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java
index dc534a702946..1c5f0a7fc0f3 100644
--- a/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java
+++ b/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java
@@ -28,7 +28,6 @@ import android.util.Log;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
import com.android.server.pm.ShortcutService.FileOutputStreamWithPath;
import libcore.io.IoUtils;
@@ -150,9 +149,10 @@ public class ShortcutBitmapSaver {
shortcut.setIconResourceId(0);
shortcut.setIconResName(null);
shortcut.setBitmapPath(null);
+ shortcut.setIconUri(null);
shortcut.clearFlags(ShortcutInfo.FLAG_HAS_ICON_FILE |
ShortcutInfo.FLAG_ADAPTIVE_BITMAP | ShortcutInfo.FLAG_HAS_ICON_RES |
- ShortcutInfo.FLAG_ICON_FILE_PENDING_SAVE);
+ ShortcutInfo.FLAG_ICON_FILE_PENDING_SAVE | ShortcutInfo.FLAG_HAS_ICON_URI);
}
public void saveBitmapLocked(ShortcutInfo shortcut,
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index c8df5c731845..1fc0a38e1ed3 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -102,6 +102,7 @@ class ShortcutPackage extends ShortcutPackageItem {
private static final String ATTR_ICON_RES_ID = "icon-res";
private static final String ATTR_ICON_RES_NAME = "icon-resname";
private static final String ATTR_BITMAP_PATH = "bitmap-path";
+ private static final String ATTR_ICON_URI = "icon-uri";
private static final String ATTR_LOCUS_ID = "locus-id";
private static final String ATTR_PERSON_NAME = "name";
@@ -1628,7 +1629,8 @@ class ShortcutPackage extends ShortcutPackageItem {
int flags = si.getFlags() &
~(ShortcutInfo.FLAG_HAS_ICON_FILE | ShortcutInfo.FLAG_HAS_ICON_RES
| ShortcutInfo.FLAG_ICON_FILE_PENDING_SAVE
- | ShortcutInfo.FLAG_DYNAMIC);
+ | ShortcutInfo.FLAG_DYNAMIC
+ | ShortcutInfo.FLAG_HAS_ICON_URI | ShortcutInfo.FLAG_ADAPTIVE_BITMAP);
ShortcutService.writeAttr(out, ATTR_FLAGS, flags);
// Set the publisher version code at every backup.
@@ -1646,6 +1648,7 @@ class ShortcutPackage extends ShortcutPackageItem {
ShortcutService.writeAttr(out, ATTR_ICON_RES_ID, si.getIconResourceId());
ShortcutService.writeAttr(out, ATTR_ICON_RES_NAME, si.getIconResName());
ShortcutService.writeAttr(out, ATTR_BITMAP_PATH, si.getBitmapPath());
+ ShortcutService.writeAttr(out, ATTR_ICON_URI, si.getIconUri());
}
if (shouldBackupDetails) {
@@ -1764,6 +1767,7 @@ class ShortcutPackage extends ShortcutPackageItem {
int iconResId;
String iconResName;
String bitmapPath;
+ String iconUri;
final String locusIdString;
int backupVersionCode;
ArraySet<String> categories = null;
@@ -1791,6 +1795,7 @@ class ShortcutPackage extends ShortcutPackageItem {
iconResId = (int) ShortcutService.parseLongAttribute(parser, ATTR_ICON_RES_ID);
iconResName = ShortcutService.parseStringAttribute(parser, ATTR_ICON_RES_NAME);
bitmapPath = ShortcutService.parseStringAttribute(parser, ATTR_BITMAP_PATH);
+ iconUri = ShortcutService.parseStringAttribute(parser, ATTR_ICON_URI);
locusIdString = ShortcutService.parseStringAttribute(parser, ATTR_LOCUS_ID);
final int outerDepth = parser.getDepth();
@@ -1866,8 +1871,8 @@ class ShortcutPackage extends ShortcutPackageItem {
categories,
intents.toArray(new Intent[intents.size()]),
rank, extras, lastChangedTimestamp, flags,
- iconResId, iconResName, bitmapPath, disabledReason,
- persons.toArray(new Person[persons.size()]), locusId);
+ iconResId, iconResName, bitmapPath, iconUri,
+ disabledReason, persons.toArray(new Person[persons.size()]), locusId);
}
private static Intent parseIntent(XmlPullParser parser)
@@ -1991,16 +1996,26 @@ class ShortcutPackage extends ShortcutPackageItem {
Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
+ " still has an icon");
}
- if (si.hasAdaptiveBitmap() && !si.hasIconFile()) {
+ if (si.hasAdaptiveBitmap() && !(si.hasIconFile() || si.hasIconUri())) {
failed = true;
Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
- + " has adaptive bitmap but was not saved to a file.");
+ + " has adaptive bitmap but was not saved to a file nor has icon uri.");
}
if (si.hasIconFile() && si.hasIconResource()) {
failed = true;
Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
+ " has both resource and bitmap icons");
}
+ if (si.hasIconFile() && si.hasIconUri()) {
+ failed = true;
+ Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
+ + " has both url and bitmap icons");
+ }
+ if (si.hasIconUri() && si.hasIconResource()) {
+ failed = true;
+ Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
+ + " has both url and resource icons");
+ }
if (si.isEnabled()
!= (si.getDisabledReason() == ShortcutInfo.DISABLED_REASON_NOT_DISABLED)) {
failed = true;
diff --git a/services/core/java/com/android/server/pm/ShortcutParser.java b/services/core/java/com/android/server/pm/ShortcutParser.java
index f9c0db0e9a1d..d3aace19672a 100644
--- a/services/core/java/com/android/server/pm/ShortcutParser.java
+++ b/services/core/java/com/android/server/pm/ShortcutParser.java
@@ -449,6 +449,7 @@ public class ShortcutParser {
iconResId,
null, // icon res name
null, // bitmap path
+ null, // icon Url
disabledReason,
null /* persons */,
null /* locusId */);
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 66f3574d83a0..8768ab0a683b 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -24,6 +24,8 @@ import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
import android.app.IUidObserver;
+import android.app.IUriGrantsManager;
+import android.app.UriGrantsManager;
import android.app.usage.UsageStatsManagerInternal;
import android.appwidget.AppWidgetProviderInfo;
import android.content.BroadcastReceiver;
@@ -65,6 +67,7 @@ import android.os.Bundle;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Handler;
+import android.os.IBinder;
import android.os.LocaleList;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
@@ -106,6 +109,7 @@ import com.android.internal.util.StatLogger;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.pm.ShortcutUser.PackageWithUser;
+import com.android.server.uri.UriGrantsManagerInternal;
import libcore.io.IoUtils;
@@ -327,6 +331,9 @@ public class ShortcutService extends IShortcutService.Stub {
private final UserManagerInternal mUserManagerInternal;
private final UsageStatsManagerInternal mUsageStatsManagerInternal;
private final ActivityManagerInternal mActivityManagerInternal;
+ private final IUriGrantsManager mUriGrantsManager;
+ private final UriGrantsManagerInternal mUriGrantsManagerInternal;
+ private final IBinder mUriPermissionOwner;
private final ShortcutRequestPinProcessor mShortcutRequestPinProcessor;
private final ShortcutBitmapSaver mShortcutBitmapSaver;
@@ -449,6 +456,11 @@ public class ShortcutService extends IShortcutService.Stub {
mActivityManagerInternal = Objects.requireNonNull(
LocalServices.getService(ActivityManagerInternal.class));
+ mUriGrantsManager = UriGrantsManager.getService();
+ mUriGrantsManagerInternal = Objects.requireNonNull(
+ LocalServices.getService(UriGrantsManagerInternal.class));
+ mUriPermissionOwner = mUriGrantsManagerInternal.newUriPermissionOwner(TAG);
+
mShortcutRequestPinProcessor = new ShortcutRequestPinProcessor(this, mLock);
mShortcutBitmapSaver = new ShortcutBitmapSaver(this);
mShortcutDumpFiles = new ShortcutDumpFiles(this);
@@ -1414,7 +1426,7 @@ public class ShortcutService extends IShortcutService.Stub {
}
void saveIconAndFixUpShortcutLocked(ShortcutInfo shortcut) {
- if (shortcut.hasIconFile() || shortcut.hasIconResource()) {
+ if (shortcut.hasIconFile() || shortcut.hasIconResource() || shortcut.hasIconUri()) {
return;
}
@@ -1438,6 +1450,15 @@ public class ShortcutService extends IShortcutService.Stub {
shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_RES);
return;
}
+ case Icon.TYPE_URI:
+ shortcut.setIconUri(icon.getUriString());
+ shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_URI);
+ return;
+ case Icon.TYPE_URI_ADAPTIVE_BITMAP:
+ shortcut.setIconUri(icon.getUriString());
+ shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_URI
+ | ShortcutInfo.FLAG_ADAPTIVE_BITMAP);
+ return;
case Icon.TYPE_BITMAP:
bitmap = icon.getBitmap(); // Don't recycle in this case.
break;
@@ -3129,6 +3150,59 @@ public class ShortcutService extends IShortcutService.Stub {
}
@Override
+ public String getShortcutIconUri(int launcherUserId, @NonNull String launcherPackage,
+ @NonNull String packageName, @NonNull String shortcutId, int userId) {
+ Objects.requireNonNull(launcherPackage, "launcherPackage");
+ Objects.requireNonNull(packageName, "packageName");
+ Objects.requireNonNull(shortcutId, "shortcutId");
+
+ synchronized (mLock) {
+ throwIfUserLockedL(userId);
+ throwIfUserLockedL(launcherUserId);
+
+ getLauncherShortcutsLocked(launcherPackage, userId, launcherUserId)
+ .attemptToRestoreIfNeededAndSave();
+
+ final ShortcutPackage p = getUserShortcutsLocked(userId)
+ .getPackageShortcutsIfExists(packageName);
+ if (p == null) {
+ return null;
+ }
+
+ final ShortcutInfo shortcutInfo = p.findShortcutById(shortcutId);
+ if (shortcutInfo == null || !shortcutInfo.hasIconUri()) {
+ return null;
+ }
+ String uri = shortcutInfo.getIconUri();
+ if (uri == null) {
+ Slog.w(TAG, "null uri detected in getShortcutIconUri()");
+ return null;
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ int packageUid = mPackageManagerInternal.getPackageUidInternal(packageName,
+ PackageManager.MATCH_DIRECT_BOOT_AUTO, userId);
+ // Grant read uri permission to the caller on behalf of the shortcut owner. All
+ // granted permissions are revoked when the default launcher changes, or when
+ // device is rebooted.
+ // b/151572645 is tracking a bug where Uri permissions are persisted across
+ // reboots, even when Intent#FLAG_GRANT_PERSISTABLE_URI_PERMISSION is not used.
+ mUriGrantsManager.grantUriPermissionFromOwner(mUriPermissionOwner, packageUid,
+ launcherPackage, Uri.parse(uri), Intent.FLAG_GRANT_READ_URI_PERMISSION,
+ userId, launcherUserId);
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to grant uri access to " + launcherPackage + " for " + uri,
+ e);
+ uri = null;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return uri;
+ }
+ }
+
+ @Override
public boolean hasShortcutHostPermission(int launcherUserId,
@NonNull String callingPackage, int callingPid, int callingUid) {
return ShortcutService.this.hasShortcutHostPermission(callingPackage, launcherUserId,
@@ -3241,7 +3315,14 @@ public class ShortcutService extends IShortcutService.Stub {
user.clearLauncher();
}
if (Intent.ACTION_PREFERRED_ACTIVITY_CHANGED.equals(action)) {
- // Nothing farther to do.
+ final ShortcutUser user = getUserShortcutsLocked(userId);
+ final ComponentName lastLauncher = user.getLastKnownLauncher();
+ final ComponentName currentLauncher = getDefaultLauncher(userId);
+ if (currentLauncher == null || !currentLauncher.equals(lastLauncher)) {
+ // Default launcher is removed or changed, revoke all URI permissions.
+ mUriGrantsManagerInternal.revokeUriPermissionFromOwner(mUriPermissionOwner,
+ null, ~0, 0);
+ }
return;
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index df3c83aec85a..8280a614fc54 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -455,11 +455,13 @@ public class UserManagerService extends IUserManager.Stub {
@Override
public void onFinished(int id, Bundle extras) {
- try {
- mContext.startIntentSender(mTarget, null, 0, 0, 0);
- } catch (IntentSender.SendIntentException e) {
- Slog.e(LOG_TAG, "Failed to start the target in the callback", e);
- }
+ mHandler.post(() -> {
+ try {
+ mContext.startIntentSender(mTarget, null, 0, 0, 0);
+ } catch (IntentSender.SendIntentException e) {
+ Slog.e(LOG_TAG, "Failed to start the target in the callback", e);
+ }
+ });
}
}
@@ -1300,14 +1302,15 @@ public class UserManagerService extends IUserManager.Stub {
@Override
public boolean hasBadge(@UserIdInt int userId) {
- checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "hasBadge");
+ checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "hasBadge");
final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
return userTypeDetails != null && userTypeDetails.hasBadge();
}
@Override
public @StringRes int getUserBadgeLabelResId(@UserIdInt int userId) {
- checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getUserBadgeLabelResId");
+ checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId,
+ "getUserBadgeLabelResId");
final UserInfo userInfo = getUserInfoNoChecks(userId);
final UserTypeDetails userTypeDetails = getUserTypeDetails(userInfo);
if (userInfo == null || userTypeDetails == null || !userTypeDetails.hasBadge()) {
@@ -1320,7 +1323,8 @@ public class UserManagerService extends IUserManager.Stub {
@Override
public @ColorRes int getUserBadgeColorResId(@UserIdInt int userId) {
- checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getUserBadgeColorResId");
+ checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId,
+ "getUserBadgeColorResId");
final UserInfo userInfo = getUserInfoNoChecks(userId);
final UserTypeDetails userTypeDetails = getUserTypeDetails(userInfo);
if (userInfo == null || userTypeDetails == null || !userTypeDetails.hasBadge()) {
@@ -1333,7 +1337,7 @@ public class UserManagerService extends IUserManager.Stub {
@Override
public @DrawableRes int getUserIconBadgeResId(@UserIdInt int userId) {
- checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getUserIconBadgeResId");
+ checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "getUserIconBadgeResId");
final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
if (userTypeDetails == null || !userTypeDetails.hasBadge()) {
Slog.e(LOG_TAG, "Requested icon badge for non-badged user " + userId);
@@ -1344,7 +1348,7 @@ public class UserManagerService extends IUserManager.Stub {
@Override
public @DrawableRes int getUserBadgeResId(@UserIdInt int userId) {
- checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getUserBadgeResId");
+ checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "getUserBadgeResId");
final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
if (userTypeDetails == null || !userTypeDetails.hasBadge()) {
Slog.e(LOG_TAG, "Requested badge for non-badged user " + userId);
@@ -1355,7 +1359,7 @@ public class UserManagerService extends IUserManager.Stub {
@Override
public @DrawableRes int getUserBadgeNoBackgroundResId(@UserIdInt int userId) {
- checkManageOrInteractPermIfCallerInOtherProfileGroup(userId,
+ checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId,
"getUserBadgeNoBackgroundResId");
final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
if (userTypeDetails == null || !userTypeDetails.hasBadge()) {
@@ -1367,7 +1371,7 @@ public class UserManagerService extends IUserManager.Stub {
@Override
public boolean isProfile(@UserIdInt int userId) {
- checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "isProfile");
+ checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isProfile");
synchronized (mUsersLock) {
UserInfo userInfo = getUserInfoLU(userId);
return userInfo != null && userInfo.isProfile();
@@ -1376,7 +1380,7 @@ public class UserManagerService extends IUserManager.Stub {
@Override
public boolean isManagedProfile(@UserIdInt int userId) {
- checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "isManagedProfile");
+ checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isManagedProfile");
synchronized (mUsersLock) {
UserInfo userInfo = getUserInfoLU(userId);
return userInfo != null && userInfo.isManagedProfile();
@@ -1385,19 +1389,20 @@ public class UserManagerService extends IUserManager.Stub {
@Override
public boolean isUserUnlockingOrUnlocked(@UserIdInt int userId) {
- checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "isUserUnlockingOrUnlocked");
+ checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId,
+ "isUserUnlockingOrUnlocked");
return mLocalService.isUserUnlockingOrUnlocked(userId);
}
@Override
public boolean isUserUnlocked(@UserIdInt int userId) {
- checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "isUserUnlocked");
+ checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isUserUnlocked");
return mLocalService.isUserUnlocked(userId);
}
@Override
public boolean isUserRunning(@UserIdInt int userId) {
- checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "isUserRunning");
+ checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isUserRunning");
return mLocalService.isUserRunning(userId);
}
@@ -1437,7 +1442,7 @@ public class UserManagerService extends IUserManager.Stub {
}
}
- private void checkManageOrInteractPermIfCallerInOtherProfileGroup(@UserIdInt int userId,
+ private void checkManageOrInteractPermissionIfCallerInOtherProfileGroup(@UserIdInt int userId,
String name) {
final int callingUserId = UserHandle.getCallingUserId();
if (callingUserId == userId || isSameProfileGroupNoChecks(callingUserId, userId) ||
@@ -1466,11 +1471,7 @@ public class UserManagerService extends IUserManager.Stub {
@Override
public boolean isPreCreated(@UserIdInt int userId) {
- final int callingUserId = UserHandle.getCallingUserId();
- if (callingUserId != userId && !hasManageUsersPermission()) {
- throw new SecurityException("You need MANAGE_USERS permission to query if u=" + userId
- + " is pre-created");
- }
+ checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isPreCreated");
synchronized (mUsersLock) {
UserInfo userInfo = getUserInfoLU(userId);
return userInfo != null && userInfo.preCreated;
@@ -1826,7 +1827,7 @@ public class UserManagerService extends IUserManager.Stub {
/** @return a specific user restriction that's in effect currently. */
@Override
public boolean hasUserRestriction(String restrictionKey, @UserIdInt int userId) {
- checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "hasUserRestriction");
+ checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "hasUserRestriction");
return mLocalService.hasUserRestriction(restrictionKey, userId);
}
@@ -1951,7 +1952,7 @@ public class UserManagerService extends IUserManager.Stub {
*/
@Override
public Bundle getUserRestrictions(@UserIdInt int userId) {
- checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getUserRestrictions");
+ checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "getUserRestrictions");
return UserRestrictionsUtils.clone(getEffectiveUserRestrictions(userId));
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 5141191dd8b7..46f121d8c09d 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -19,6 +19,8 @@ package com.android.server.pm.permission;
import static android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_IGNORED;
import static android.content.pm.PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
@@ -53,6 +55,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
+import android.app.AppOpsManager;
import android.app.ApplicationPackageManager;
import android.app.IActivityManager;
import android.app.admin.DeviceAdminInfo;
@@ -217,6 +220,9 @@ public class PermissionManagerService extends IPermissionManager.Stub {
/** Default permission policy to provide proper behaviour out-of-the-box */
private final DefaultPermissionGrantPolicy mDefaultPermissionGrantPolicy;
+ /** App ops manager */
+ private final AppOpsManager mAppOpsManager;
+
/**
* Built-in permissions. Read from system configuration files. Mapping is from
* UID to permission name.
@@ -356,6 +362,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class);
mUserManagerInt = LocalServices.getService(UserManagerInternal.class);
mSettings = new PermissionSettings(mLock);
+ mAppOpsManager = context.getSystemService(AppOpsManager.class);
mHandlerThread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
@@ -1199,6 +1206,77 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
@Override
+ public boolean setAutoRevokeWhitelisted(
+ @NonNull String packageName, boolean whitelisted, int userId) {
+ Objects.requireNonNull(packageName);
+
+ final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName);
+ final int callingUid = Binder.getCallingUid();
+
+ if (!checkAutoRevokeAccess(pkg, callingUid)) {
+ return false;
+ }
+
+ if (mAppOpsManager
+ .checkOpNoThrow(AppOpsManager.OP_AUTO_REVOKE_MANAGED_BY_INSTALLER,
+ callingUid, packageName)
+ != MODE_ALLOWED) {
+ // Whitelist user set - don't override
+ return false;
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mAppOpsManager.setMode(AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
+ callingUid, packageName,
+ whitelisted ? MODE_IGNORED : MODE_ALLOWED);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ return true;
+ }
+
+ private boolean checkAutoRevokeAccess(AndroidPackage pkg, int callingUid) {
+ if (pkg == null) {
+ return false;
+ }
+
+ final boolean isCallerPrivileged = mContext.checkCallingOrSelfPermission(
+ Manifest.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS)
+ == PackageManager.PERMISSION_GRANTED;
+ final boolean isCallerInstallerOnRecord =
+ mPackageManagerInt.isCallerInstallerOfRecord(pkg, callingUid);
+
+ if (!isCallerPrivileged && !isCallerInstallerOnRecord) {
+ throw new SecurityException("Caller must either hold "
+ + Manifest.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS
+ + " or be the installer on record");
+ }
+ return true;
+ }
+
+ @Override
+ public boolean isAutoRevokeWhitelisted(@NonNull String packageName, int userId) {
+ Objects.requireNonNull(packageName);
+
+ final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName);
+ final int callingUid = Binder.getCallingUid();
+
+ if (!checkAutoRevokeAccess(pkg, callingUid)) {
+ return false;
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return mAppOpsManager.checkOpNoThrow(
+ AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, callingUid, packageName)
+ == MODE_IGNORED;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void grantRuntimePermission(String packageName, String permName, final int userId) {
final int callingUid = Binder.getCallingUid();
final boolean overridePolicy =
@@ -4377,6 +4455,12 @@ public class PermissionManagerService extends IPermissionManager.Stub {
packageName, permissions, flags, userId);
}
@Override
+ public void setAutoRevokeWhitelisted(
+ @NonNull String packageName, boolean whitelisted, int userId) {
+ PermissionManagerService.this.setAutoRevokeWhitelisted(
+ packageName, whitelisted, userId);
+ }
+ @Override
public void updatePermissions(@NonNull String packageName, @Nullable AndroidPackage pkg) {
PermissionManagerService.this
.updatePermissions(packageName, pkg, mDefaultPermissionCallback);
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index 32ef2cee5685..356d0abc1d19 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -189,7 +189,9 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager
/** Sets the whitelisted, restricted permissions for the given package. */
public abstract void setWhitelistedRestrictedPermissions(
@NonNull String packageName, @NonNull List<String> permissions,
- @PackageManager.PermissionWhitelistFlags int flags, @NonNull int userId);
+ @PackageManager.PermissionWhitelistFlags int flags, int userId);
+ public abstract void setAutoRevokeWhitelisted(
+ @NonNull String packageName, boolean whitelisted, int userId);
/**
* Update permissions when a package changed.
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 64edacd438fc..1e1256547f2c 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2139,8 +2139,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
// check if user has enabled this operation. SecurityException will be thrown if this app
- // has not been allowed by the user
- final int mode = mAppOpsManager.noteOpNoThrow(outAppOp[0], callingUid, packageName);
+ // has not been allowed by the user. The reason to use "noteOp" (instead of checkOp) is to
+ // make sure the usage is logged.
+ final int mode = mAppOpsManager.noteOpNoThrow(outAppOp[0], callingUid, packageName,
+ null /* featureId */, "check-add");
switch (mode) {
case AppOpsManager.MODE_ALLOWED:
case AppOpsManager.MODE_IGNORED:
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 294deba459fe..3257b63843ec 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -18,6 +18,8 @@ package com.android.server.power;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_DEFAULT;
+import static android.os.PowerManagerInternal.MODE_DEVICE_IDLE;
+import static android.os.PowerManagerInternal.MODE_DISPLAY_INACTIVE;
import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP;
import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE;
import static android.os.PowerManagerInternal.WAKEFULNESS_DOZING;
@@ -42,8 +44,6 @@ import android.hardware.SystemSensorManager;
import android.hardware.display.AmbientDisplayConfiguration;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
-import android.hardware.power.Boost;
-import android.hardware.power.Mode;
import android.hardware.power.V1_0.PowerHint;
import android.net.Uri;
import android.os.BatteryManager;
@@ -2975,6 +2975,8 @@ public final class PowerManagerService extends SystemService
synchronized (mLock) {
if (mDisplayState != state) {
mDisplayState = state;
+ setPowerModeInternal(MODE_DISPLAY_INACTIVE,
+ !Display.isActiveState(state));
if (state == Display.STATE_OFF) {
if (!mDecoupleHalInteractiveModeFromDisplayConfig) {
setHalInteractiveModeLocked(false);
@@ -3297,6 +3299,7 @@ public final class PowerManagerService extends SystemService
}
mDeviceIdleMode = enabled;
updateWakeLockDisabledStatesLocked();
+ setPowerModeInternal(MODE_DEVICE_IDLE, mDeviceIdleMode || mLightDeviceIdleMode);
}
if (enabled) {
EventLogTags.writeDeviceIdleOnPhase("power");
@@ -3310,6 +3313,7 @@ public final class PowerManagerService extends SystemService
synchronized (mLock) {
if (mLightDeviceIdleMode != enabled) {
mLightDeviceIdleMode = enabled;
+ setPowerModeInternal(MODE_DEVICE_IDLE, mDeviceIdleMode || mLightDeviceIdleMode);
return true;
}
return false;
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
index beba106ca0a6..8b6e9a46ca91 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
@@ -16,11 +16,13 @@
package com.android.server.power.batterysaver;
import android.Manifest;
+import android.annotation.Nullable;
import android.app.ActivityManagerInternal;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManagerInternal;
import android.hardware.power.V1_0.PowerHint;
import android.os.BatteryManager;
import android.os.BatterySaverPolicyConfig;
@@ -35,6 +37,7 @@ import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Slog;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
@@ -49,6 +52,7 @@ import com.android.server.power.batterysaver.BatterySavingStats.InteractiveState
import java.util.ArrayList;
import java.util.Objects;
+import java.util.Optional;
/**
* Responsible for battery saver mode transition logic.
@@ -112,6 +116,14 @@ public class BatterySaverController implements BatterySaverPolicyListener {
*/
private final Plugin[] mPlugins;
+ /**
+ * Package name that will receive an explicit manifest broadcast for
+ * {@link PowerManager#ACTION_POWER_SAVE_MODE_CHANGED}. It's {@code null} if it hasn't been
+ * retrieved yet.
+ */
+ @Nullable
+ private Optional<String> mPowerSaveModeChangedListenerPackage;
+
public static final int REASON_PERCENTAGE_AUTOMATIC_ON = 0;
public static final int REASON_PERCENTAGE_AUTOMATIC_OFF = 1;
public static final int REASON_MANUAL_ON = 2;
@@ -494,6 +506,14 @@ public class BatterySaverController implements BatterySaverPolicyListener {
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ // Send the broadcast to a manifest-registered receiver that is specified in the config.
+ if (getPowerSaveModeChangedListenerPackage().isPresent()) {
+ intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)
+ .setPackage(getPowerSaveModeChangedListenerPackage().get())
+ .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
// Send internal version that requires signature permission.
intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED_INTERNAL);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
@@ -508,6 +528,20 @@ public class BatterySaverController implements BatterySaverPolicyListener {
}
}
+ private Optional<String> getPowerSaveModeChangedListenerPackage() {
+ if (mPowerSaveModeChangedListenerPackage == null) {
+ String configPowerSaveModeChangedListenerPackage =
+ mContext.getString(R.string.config_powerSaveModeChangedListenerPackage);
+ mPowerSaveModeChangedListenerPackage =
+ LocalServices
+ .getService(PackageManagerInternal.class)
+ .isSystemPackage(configPowerSaveModeChangedListenerPackage)
+ ? Optional.of(configPowerSaveModeChangedListenerPackage)
+ : Optional.empty();
+ }
+ return mPowerSaveModeChangedListenerPackage;
+ }
+
private void updateBatterySavingStats() {
final PowerManager pm = getPowerManager();
if (pm == null) {
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 42fada19672e..b50c22ea09e3 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -63,6 +63,7 @@ import com.android.server.LocalServices;
import com.android.server.PackageWatchdog;
import com.android.server.SystemConfig;
import com.android.server.Watchdog;
+import com.android.server.pm.ApexManager;
import com.android.server.pm.Installer;
import java.io.File;
@@ -485,6 +486,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
}
latch.countDown();
+
+ destroyCeSnapshotsForExpiredRollbacks(userId);
});
try {
@@ -495,6 +498,15 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
}
@WorkerThread
+ private void destroyCeSnapshotsForExpiredRollbacks(int userId) {
+ int[] rollbackIds = new int[mRollbacks.size()];
+ for (int i = 0; i < rollbackIds.length; i++) {
+ rollbackIds[i] = mRollbacks.get(i).info.getRollbackId();
+ }
+ ApexManager.getInstance().destroyCeSnapshotsNotSpecified(userId, rollbackIds);
+ }
+
+ @WorkerThread
private void updateRollbackLifetimeDurationInMillis() {
mRollbackLifetimeDurationInMillis = DeviceConfig.getLong(
DeviceConfig.NAMESPACE_ROLLBACK_BOOT,
diff --git a/services/core/java/com/android/server/security/TEST_MAPPING b/services/core/java/com/android/server/security/TEST_MAPPING
new file mode 100644
index 000000000000..9a5e90e8681f
--- /dev/null
+++ b/services/core/java/com/android/server/security/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+ "presubmit": [
+ {
+ "name": "ApkVerityTest",
+ "file_patterns": ["VerityUtils\\.java"]
+ }
+ ]
+}
diff --git a/services/core/java/com/android/server/security/VerityUtils.java b/services/core/java/com/android/server/security/VerityUtils.java
index 856a40f3ef12..2b793c894eb0 100644
--- a/services/core/java/com/android/server/security/VerityUtils.java
+++ b/services/core/java/com/android/server/security/VerityUtils.java
@@ -81,22 +81,18 @@ abstract public class VerityUtils {
/** Returns whether the file has fs-verity enabled. */
public static boolean hasFsverity(@NonNull String filePath) {
- // NB: only measure but not check the actual measurement here. As long as this succeeds,
- // the file is on readable if the measurement can be verified against a trusted key, and
- // this is good enough for installed apps.
- int errno = measureFsverityNative(filePath);
- if (errno != 0) {
- if (errno != OsConstants.ENODATA) {
- Slog.e(TAG, "Failed to measure fs-verity, errno " + errno + ": " + filePath);
- }
+ int retval = statxForFsverityNative(filePath);
+ if (retval < 0) {
+ Slog.e(TAG, "Failed to check whether fs-verity is enabled, errno " + -retval + ": "
+ + filePath);
return false;
}
- return true;
+ return (retval == 1);
}
private static native int enableFsverityNative(@NonNull String filePath,
@NonNull byte[] pkcs7Signature);
- private static native int measureFsverityNative(@NonNull String filePath);
+ private static native int statxForFsverityNative(@NonNull String filePath);
/**
* Generates legacy Merkle tree and fs-verity metadata with Signing Block skipped.
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 7f7d668ea8ac..fd275d83ba56 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -2888,6 +2888,44 @@ public class StatsPullAtomService extends SystemService {
HistoricalOps histOps = ops.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS,
TimeUnit.MILLISECONDS);
processHistoricalOps(histOps, atomTag, pulledData);
+
+ for (int uidIdx = 0; uidIdx < histOps.getUidCount(); uidIdx++) {
+ final HistoricalUidOps uidOps = histOps.getUidOpsAt(uidIdx);
+ final int uid = uidOps.getUid();
+ for (int pkgIdx = 0; pkgIdx < uidOps.getPackageCount(); pkgIdx++) {
+ final HistoricalPackageOps packageOps = uidOps.getPackageOpsAt(pkgIdx);
+ for (int opIdx = 0; opIdx < packageOps.getOpCount(); opIdx++) {
+ final AppOpsManager.HistoricalOp op = packageOps.getOpAt(opIdx);
+
+ StatsEvent.Builder e = StatsEvent.newBuilder();
+ e.setAtomId(atomTag);
+ e.writeInt(uid);
+ e.writeString(packageOps.getPackageName());
+ e.writeInt(op.getLoggingOpCode());
+ e.writeLong(op.getForegroundAccessCount(OP_FLAGS_PULLED));
+ e.writeLong(op.getBackgroundAccessCount(OP_FLAGS_PULLED));
+ e.writeLong(op.getForegroundRejectCount(OP_FLAGS_PULLED));
+ e.writeLong(op.getBackgroundRejectCount(OP_FLAGS_PULLED));
+ e.writeLong(op.getForegroundAccessDuration(OP_FLAGS_PULLED));
+ e.writeLong(op.getBackgroundAccessDuration(OP_FLAGS_PULLED));
+
+ String perm = AppOpsManager.opToPermission(op.getOpCode());
+ if (perm == null) {
+ e.writeBoolean(false);
+ } else {
+ PermissionInfo permInfo;
+ try {
+ permInfo = mContext.getPackageManager().getPermissionInfo(perm, 0);
+ e.writeBoolean(permInfo.getProtection() == PROTECTION_DANGEROUS);
+ } catch (PackageManager.NameNotFoundException exception) {
+ e.writeBoolean(false);
+ }
+ }
+
+ pulledData.add(e.build());
+ }
+ }
+ }
} catch (Throwable t) {
// TODO: catch exceptions at a more granular level
Slog.e(TAG, "Could not read appops", t);
@@ -3006,11 +3044,7 @@ public class StatsPullAtomService extends SystemService {
if (atomTag == FrameworkStatsLog.ATTRIBUTED_APP_OPS) {
e.writeString(attributionTag);
}
- if (atomTag == FrameworkStatsLog.ATTRIBUTED_APP_OPS) {
- e.writeString(op.getOpName());
- } else {
- e.writeInt(op.getOpCode());
- }
+ e.writeInt(op.getLoggingOpCode());
e.writeLong(op.getForegroundAccessCount(OP_FLAGS_PULLED));
e.writeLong(op.getBackgroundAccessCount(OP_FLAGS_PULLED));
e.writeLong(op.getForegroundRejectCount(OP_FLAGS_PULLED));
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 74a6383bc01c..0d16fccc81fd 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -234,7 +234,7 @@ public final class TextClassificationManagerService extends ITextClassifierServi
handleRequest(
event.getSystemTextClassifierMetadata(),
- /* verifyCallingPackage= */ false,
+ /* verifyCallingPackage= */ true,
/* attemptToBind= */ false,
service -> service.onSelectionEvent(sessionId, event),
"onSelectionEvent",
@@ -253,7 +253,7 @@ public final class TextClassificationManagerService extends ITextClassifierServi
handleRequest(
systemTcMetadata,
- /* verifyCallingPackage= */ false,
+ /* verifyCallingPackage= */ true,
/* attemptToBind= */ false,
service -> service.onTextClassifierEvent(sessionId, event),
"onTextClassifierEvent",
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index b43d8b7daf4a..25585b3ae794 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -70,6 +70,9 @@ public class TunerResourceManagerService extends SystemService {
private TvInputManager mManager;
private UseCasePriorityHints mPriorityCongfig = new UseCasePriorityHints();
+ // An internal resource request count to help generate resource handle.
+ private int mResourceRequestCount = 0;
+
// Used to synchronize the access to the service.
private final Object mLock = new Object();
@@ -96,7 +99,7 @@ public class TunerResourceManagerService extends SystemService {
public void registerClientProfile(@NonNull ResourceClientProfile profile,
@NonNull IResourcesReclaimListener listener, @NonNull int[] clientId)
throws RemoteException {
- enforceAccessPermission();
+ enforceTrmAccessPermission("registerClientProfile");
if (profile == null) {
throw new RemoteException("ResourceClientProfile can't be null");
}
@@ -120,7 +123,7 @@ public class TunerResourceManagerService extends SystemService {
@Override
public void unregisterClientProfile(int clientId) throws RemoteException {
- enforceAccessPermission();
+ enforceTrmAccessPermission("unregisterClientProfile");
synchronized (mLock) {
if (!checkClientExists(clientId)) {
Slog.e(TAG, "Unregistering non exists client:" + clientId);
@@ -132,7 +135,7 @@ public class TunerResourceManagerService extends SystemService {
@Override
public boolean updateClientPriority(int clientId, int priority, int niceValue) {
- enforceAccessPermission();
+ enforceTrmAccessPermission("updateClientPriority");
synchronized (mLock) {
return updateClientPriorityInternal(clientId, priority, niceValue);
}
@@ -140,7 +143,7 @@ public class TunerResourceManagerService extends SystemService {
@Override
public void setFrontendInfoList(@NonNull TunerFrontendInfo[] infos) throws RemoteException {
- enforceAccessPermission();
+ enforceTrmAccessPermission("setFrontendInfoList");
if (infos == null) {
throw new RemoteException("TunerFrontendInfo can't be null");
}
@@ -151,6 +154,7 @@ public class TunerResourceManagerService extends SystemService {
@Override
public void updateCasInfo(int casSystemId, int maxSessionNum) {
+ enforceTrmAccessPermission("updateCasInfo");
if (DEBUG) {
Slog.d(TAG,
"updateCasInfo(casSystemId=" + casSystemId
@@ -160,6 +164,7 @@ public class TunerResourceManagerService extends SystemService {
@Override
public void setLnbInfoList(int[] lnbIds) {
+ enforceTrmAccessPermission("setLnbInfoList");
if (DEBUG) {
for (int i = 0; i < lnbIds.length; i++) {
Slog.d(TAG, "updateLnbInfo(lnbId=" + lnbIds[i] + ")");
@@ -169,14 +174,15 @@ public class TunerResourceManagerService extends SystemService {
@Override
public boolean requestFrontend(@NonNull TunerFrontendRequest request,
- @NonNull int[] frontendId) throws RemoteException {
- enforceAccessPermission();
- if (frontendId == null) {
- throw new RemoteException("frontendId can't be null");
+ @NonNull int[] frontendHandle) throws RemoteException {
+ enforceTunerAccessPermission("requestFrontend");
+ enforceTrmAccessPermission("requestFrontend");
+ if (frontendHandle == null) {
+ throw new RemoteException("frontendHandle can't be null");
}
synchronized (mLock) {
try {
- return requestFrontendInternal(request, frontendId);
+ return requestFrontendInternal(request, frontendHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -185,6 +191,8 @@ public class TunerResourceManagerService extends SystemService {
@Override
public void shareFrontend(int selfClientId, int targetClientId) {
+ enforceTunerAccessPermission("shareFrontend");
+ enforceTrmAccessPermission("shareFrontend");
if (DEBUG) {
Slog.d(TAG, "shareFrontend from " + selfClientId + " with " + targetClientId);
}
@@ -192,25 +200,34 @@ public class TunerResourceManagerService extends SystemService {
@Override
public boolean requestDemux(@NonNull TunerDemuxRequest request,
- @NonNull int[] demuxHandle) {
- if (DEBUG) {
- Slog.d(TAG, "requestDemux(request=" + request + ")");
+ @NonNull int[] demuxHandle) throws RemoteException {
+ enforceTunerAccessPermission("requestDemux");
+ enforceTrmAccessPermission("requestDemux");
+ if (demuxHandle == null) {
+ throw new RemoteException("demuxHandle can't be null");
+ }
+ synchronized (mLock) {
+ return requestDemuxInternal(request, demuxHandle);
}
- return true;
}
@Override
public boolean requestDescrambler(@NonNull TunerDescramblerRequest request,
- @NonNull int[] descrambleHandle) {
- if (DEBUG) {
- Slog.d(TAG, "requestDescrambler(request=" + request + ")");
+ @NonNull int[] descrambleHandle) throws RemoteException {
+ enforceDescramblerAccessPermission("requestDescrambler");
+ enforceTrmAccessPermission("requestDescrambler");
+ if (descrambleHandle == null) {
+ throw new RemoteException("descrambleHandle can't be null");
+ }
+ synchronized (mLock) {
+ return requestDescramblerInternal(request, descrambleHandle);
}
- return true;
}
@Override
public boolean requestCasSession(
- @NonNull CasSessionRequest request, @NonNull int[] sessionResourceId) {
+ @NonNull CasSessionRequest request, @NonNull int[] sessionResourceHandle) {
+ enforceTrmAccessPermission("requestCasSession");
if (DEBUG) {
Slog.d(TAG, "requestCasSession(request=" + request + ")");
}
@@ -219,7 +236,9 @@ public class TunerResourceManagerService extends SystemService {
}
@Override
- public boolean requestLnb(@NonNull TunerLnbRequest request, @NonNull int[] lnbId) {
+ public boolean requestLnb(@NonNull TunerLnbRequest request, @NonNull int[] lnbHandle) {
+ enforceTunerAccessPermission("requestLnb");
+ enforceTrmAccessPermission("requestLnb");
if (DEBUG) {
Slog.d(TAG, "requestLnb(request=" + request + ")");
}
@@ -228,6 +247,8 @@ public class TunerResourceManagerService extends SystemService {
@Override
public void releaseFrontend(int frontendId) {
+ enforceTunerAccessPermission("releaseFrontend");
+ enforceTrmAccessPermission("releaseFrontend");
if (DEBUG) {
Slog.d(TAG, "releaseFrontend(id=" + frontendId + ")");
}
@@ -235,6 +256,8 @@ public class TunerResourceManagerService extends SystemService {
@Override
public void releaseDemux(int demuxHandle) {
+ enforceTunerAccessPermission("releaseDemux");
+ enforceTrmAccessPermission("releaseDemux");
if (DEBUG) {
Slog.d(TAG, "releaseDemux(demuxHandle=" + demuxHandle + ")");
}
@@ -242,6 +265,8 @@ public class TunerResourceManagerService extends SystemService {
@Override
public void releaseDescrambler(int descramblerHandle) {
+ enforceTunerAccessPermission("releaseDescrambler");
+ enforceTrmAccessPermission("releaseDescrambler");
if (DEBUG) {
Slog.d(TAG, "releaseDescrambler(descramblerHandle=" + descramblerHandle + ")");
}
@@ -249,6 +274,7 @@ public class TunerResourceManagerService extends SystemService {
@Override
public void releaseCasSession(int sessionResourceId) {
+ enforceTrmAccessPermission("releaseCasSession");
if (DEBUG) {
Slog.d(TAG, "releaseCasSession(sessionResourceId=" + sessionResourceId + ")");
}
@@ -256,6 +282,8 @@ public class TunerResourceManagerService extends SystemService {
@Override
public void releaseLnb(int lnbId) {
+ enforceTunerAccessPermission("releaseLnb");
+ enforceTrmAccessPermission("releaseLnb");
if (DEBUG) {
Slog.d(TAG, "releaseLnb(lnbId=" + lnbId + ")");
}
@@ -264,6 +292,7 @@ public class TunerResourceManagerService extends SystemService {
@Override
public boolean isHigherPriority(
ResourceClientProfile challengerProfile, ResourceClientProfile holderProfile) {
+ enforceTrmAccessPermission("isHigherPriority");
if (DEBUG) {
Slog.d(TAG,
"isHigherPriority(challengerProfile=" + challengerProfile
@@ -371,13 +400,13 @@ public class TunerResourceManagerService extends SystemService {
}
@VisibleForTesting
- protected boolean requestFrontendInternal(TunerFrontendRequest request, int[] frontendId)
+ protected boolean requestFrontendInternal(TunerFrontendRequest request, int[] frontendHandle)
throws RemoteException {
if (DEBUG) {
Slog.d(TAG, "requestFrontend(request=" + request + ")");
}
- frontendId[0] = TunerResourceManager.INVALID_FRONTEND_ID;
+ frontendHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
if (!checkClientExists(request.getClientId())) {
Slog.e(TAG, "Request frontend from unregistered client:" + request.getClientId());
return false;
@@ -413,17 +442,20 @@ public class TunerResourceManagerService extends SystemService {
// Grant frontend when there is unused resource.
if (grantingFrontendId > -1) {
- frontendId[0] = grantingFrontendId;
- updateFrontendClientMappingOnNewGrant(frontendId[0], request.getClientId());
+ frontendHandle[0] = generateResourceHandle(
+ TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND, grantingFrontendId);
+ updateFrontendClientMappingOnNewGrant(grantingFrontendId, request.getClientId());
return true;
}
// When all the resources are occupied, grant the lowest priority resource if the
// request client has higher priority.
if (inUseLowestPriorityFrId > -1 && (requestClient.getPriority() > currentLowestPriority)) {
- frontendId[0] = inUseLowestPriorityFrId;
- reclaimFrontendResource(getFrontendResource(frontendId[0]).getOwnerClientId());
- updateFrontendClientMappingOnNewGrant(frontendId[0], request.getClientId());
+ frontendHandle[0] = generateResourceHandle(
+ TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND, inUseLowestPriorityFrId);
+ reclaimFrontendResource(getFrontendResource(
+ inUseLowestPriorityFrId).getOwnerClientId());
+ updateFrontendClientMappingOnNewGrant(inUseLowestPriorityFrId, request.getClientId());
return true;
}
@@ -431,6 +463,25 @@ public class TunerResourceManagerService extends SystemService {
}
@VisibleForTesting
+ boolean requestDemuxInternal(TunerDemuxRequest request, int[] demuxHandle) {
+ if (DEBUG) {
+ Slog.d(TAG, "requestDemux(request=" + request + ")");
+ }
+ demuxHandle[0] = generateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, 0);
+ return true;
+ }
+
+ @VisibleForTesting
+ boolean requestDescramblerInternal(TunerDescramblerRequest request, int[] descramblerHandle) {
+ if (DEBUG) {
+ Slog.d(TAG, "requestDescrambler(request=" + request + ")");
+ }
+ descramblerHandle[0] =
+ generateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_DESCRAMBLER, 0);
+ return true;
+ }
+
+ @VisibleForTesting
protected class ResourcesReclaimListenerRecord implements IBinder.DeathRecipient {
private final IResourcesReclaimListener mListener;
private final int mClientId;
@@ -592,8 +643,25 @@ public class TunerResourceManagerService extends SystemService {
return mClientProfiles.keySet().contains(clientId);
}
- private void enforceAccessPermission() {
- getContext().enforceCallingOrSelfPermission(
- "android.permission.TUNER_RESOURCE_ACCESS", TAG);
+ private int generateResourceHandle(
+ @TunerResourceManager.TunerResourceType int resourceType, int resourceId) {
+ return (resourceType & 0x000000ff) << 24
+ | (resourceId << 16)
+ | (mResourceRequestCount++ & 0xffff);
+ }
+
+ private void enforceTrmAccessPermission(String apiName) {
+ getContext().enforceCallingPermission("android.permission.TUNER_RESOURCE_ACCESS",
+ TAG + ": " + "apiName");
+ }
+
+ private void enforceTunerAccessPermission(String apiName) {
+ getContext().enforceCallingPermission("android.Manifest.permission.ACCESS_TV_TUNER",
+ TAG + ": " + "apiName");
+ }
+
+ private void enforceDescramblerAccessPermission(String apiName) {
+ getContext().enforceCallingPermission("android.Manifest.permission.ACCESS_TV_DESCRAMBLER",
+ TAG + ": " + "apiName");
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index e73c928ef925..c4545fa23cc8 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1351,11 +1351,16 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mLetterbox.attachInput(w);
}
getPosition(mTmpPoint);
- // Get the bounds of the "space-to-fill". In multi-window mode, the task-level
- // represents this. In fullscreen-mode, the stack does (since the orientation letterbox
- // is also applied to the task).
- Rect spaceToFill = (inMultiWindowMode() || getStack() == null)
- ? task.getDisplayedBounds() : getStack().getDisplayedBounds();
+ // Get the bounds of the "space-to-fill". The transformed bounds have the highest
+ // priority because the activity is launched in a rotated environment. In multi-window
+ // mode, the task-level represents this. In fullscreen-mode, the task container does
+ // (since the orientation letterbox is also applied to the task).
+ final Rect transformedBounds = getFixedRotationTransformDisplayBounds();
+ final Rect spaceToFill = transformedBounds != null
+ ? transformedBounds
+ : inMultiWindowMode()
+ ? task.getDisplayedBounds()
+ : getRootTask().getParent().getDisplayedBounds();
mLetterbox.layout(spaceToFill, w.getFrameLw(), mTmpPoint);
} else if (mLetterbox != null) {
mLetterbox.hide();
@@ -6362,33 +6367,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (mCompatDisplayInsets != null) {
resolveSizeCompatModeConfiguration(newParentConfiguration);
} else {
- // We ignore activities' requested orientation in multi-window modes. Task level may
- // take them into consideration when calculating bounds.
if (inMultiWindowMode()) {
+ // We ignore activities' requested orientation in multi-window modes. Task level may
+ // take them into consideration when calculating bounds.
resolvedConfig.orientation = Configuration.ORIENTATION_UNDEFINED;
- }
- final Rect parentAppBounds = newParentConfiguration.windowConfiguration.getAppBounds();
- final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
- // Use tmp bounds to calculate aspect ratio so we can know whether the activity should
- // use restricted size (resolvedBounds may be the requested override bounds).
- mTmpBounds.setEmpty();
- applyAspectRatio(mTmpBounds, parentAppBounds, parentBounds);
- // If the out bounds is not empty, it means the activity cannot fill parent's app
- // bounds, then the relative configuration (e.g. screen size, layout) needs to be
- // resolved according to the bounds.
- if (!mTmpBounds.isEmpty()) {
- final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
- resolvedBounds.set(mTmpBounds);
- // Exclude the horizontal decor area because the activity will be centered
- // horizontally in parent's app bounds to balance the visual appearance.
- resolvedBounds.left = parentAppBounds.left;
- task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
- getFixedRotationTransformDisplayInfo());
- final int offsetX = getHorizontalCenterOffset(
- parentAppBounds.width(), resolvedBounds.width());
- if (offsetX > 0) {
- offsetBounds(resolvedConfig, offsetX - resolvedBounds.left, 0 /* offsetY */);
+ // If the activity has requested override bounds, the configuration needs to be
+ // computed accordingly.
+ if (!matchParentBounds()) {
+ task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration);
}
+ } else {
+ resolveFullscreenConfiguration(newParentConfiguration);
}
}
@@ -6400,6 +6389,43 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
/**
+ * Resolves the configuration of activity in fullscreen mode. If the bounds are restricted by
+ * aspect ratio, the position will be centered horizontally in parent's app bounds to balance
+ * the visual appearance. The policy of aspect ratio has higher priority than the requested
+ * override bounds.
+ */
+ private void resolveFullscreenConfiguration(Configuration newParentConfiguration) {
+ final Configuration resolvedConfig = getResolvedOverrideConfiguration();
+ final Rect parentAppBounds = newParentConfiguration.windowConfiguration.getAppBounds();
+ final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
+ final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
+ // Use tmp bounds to calculate aspect ratio so we can know whether the activity should use
+ // restricted size (resolved bounds may be the requested override bounds).
+ mTmpBounds.setEmpty();
+ applyAspectRatio(mTmpBounds, parentAppBounds, parentBounds);
+ // If the out bounds is not empty, it means the activity cannot fill parent's app bounds,
+ // then there is space to be centered.
+ final boolean needToBeCentered = !mTmpBounds.isEmpty();
+ if (needToBeCentered) {
+ resolvedBounds.set(mTmpBounds);
+ // Exclude the horizontal decor area.
+ resolvedBounds.left = parentAppBounds.left;
+ }
+ if (!resolvedBounds.isEmpty() && !resolvedBounds.equals(parentBounds)) {
+ // Compute the configuration based on the resolved bounds. If aspect ratio doesn't
+ // restrict, the bounds should be the requested override bounds.
+ task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
+ getFixedRotationTransformDisplayInfo());
+ }
+ if (needToBeCentered) {
+ // Offset to center relative to parent's app bounds.
+ final int offsetX = getHorizontalCenterOffset(
+ parentAppBounds.width(), resolvedBounds.width());
+ offsetBounds(resolvedConfig, offsetX, 0 /* offsetY */);
+ }
+ }
+
+ /**
* Resolves consistent screen configuration for orientation and rotation changes without
* inheriting the parent bounds.
*/
@@ -6507,7 +6533,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Above coordinates are in "@" space, now place "*" and "#" to screen space.
final int screenPosX = parentAppBounds.left + offsetX;
final int screenPosY = parentBounds.top;
- if (screenPosX > 0 || screenPosY > 0) {
+ if (screenPosX != 0 || screenPosY != 0) {
if (mSizeCompatBounds != null) {
mSizeCompatBounds.offset(screenPosX, screenPosY);
}
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 4ebb4236b84f..e8bfe8ef63ac 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -1510,7 +1510,7 @@ class ActivityStack extends Task {
*/
@StackVisibility
int getVisibility(ActivityRecord starting) {
- if (!isAttached() || mForceHidden) {
+ if (!isAttached() || isForceHidden()) {
return STACK_VISIBILITY_INVISIBLE;
}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 6d7f8fb6adde..57f357d384b5 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -75,6 +75,7 @@ import static com.android.server.wm.RootWindowContainer.MATCH_TASK_IN_STACKS_ONL
import static com.android.server.wm.RootWindowContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
import static com.android.server.wm.RootWindowContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
import static com.android.server.wm.RootWindowContainer.TAG_STATES;
+import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK;
import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE;
import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
import static com.android.server.wm.Task.LOCK_TASK_AUTH_WHITELISTED;
@@ -1565,9 +1566,9 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
* stopping list by handling the idle.
*/
stack.cancelAnimation();
- stack.mForceHidden = true;
+ stack.setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, true /* set */);
stack.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
- stack.mForceHidden = false;
+ stack.setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, false /* set */);
activityIdleInternal(null /* idleActivity */, false /* fromTimeout */,
true /* processPausingActivities */, null /* configuration */);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 98e3d07fc2e4..aad242dbc338 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -375,7 +375,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
*/
final DisplayMetrics mRealDisplayMetrics = new DisplayMetrics();
- /** @see #computeCompatSmallestWidth(boolean, int, int, int, DisplayCutout) */
+ /** @see #computeCompatSmallestWidth(boolean, int, int, int) */
private final DisplayMetrics mTmpDisplayMetrics = new DisplayMetrics();
/**
@@ -1814,7 +1814,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
outConfig.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, uiMode, dw,
- dh, displayCutout);
+ dh);
}
/**
@@ -1922,8 +1922,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mWmService.mPolicy.adjustConfigurationLw(config, keyboardPresence, navigationPresence);
}
- private int computeCompatSmallestWidth(boolean rotated, int uiMode, int dw, int dh,
- DisplayCutout displayCutout) {
+ private int computeCompatSmallestWidth(boolean rotated, int uiMode, int dw, int dh) {
mTmpDisplayMetrics.setTo(mDisplayMetrics);
final DisplayMetrics tmpDm = mTmpDisplayMetrics;
final int unrotDw, unrotDh;
@@ -1934,19 +1933,21 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
unrotDw = dw;
unrotDh = dh;
}
- int sw = reduceCompatConfigWidthSize(0, Surface.ROTATION_0, uiMode, tmpDm, unrotDw, unrotDh,
- displayCutout);
- sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_90, uiMode, tmpDm, unrotDh, unrotDw,
- displayCutout);
- sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_180, uiMode, tmpDm, unrotDw, unrotDh,
- displayCutout);
- sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_270, uiMode, tmpDm, unrotDh, unrotDw,
- displayCutout);
+ int sw = reduceCompatConfigWidthSize(0, Surface.ROTATION_0, uiMode, tmpDm, unrotDw,
+ unrotDh);
+ sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_90, uiMode, tmpDm, unrotDh,
+ unrotDw);
+ sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_180, uiMode, tmpDm, unrotDw,
+ unrotDh);
+ sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_270, uiMode, tmpDm, unrotDh,
+ unrotDw);
return sw;
}
private int reduceCompatConfigWidthSize(int curSize, int rotation, int uiMode,
- DisplayMetrics dm, int dw, int dh, DisplayCutout displayCutout) {
+ DisplayMetrics dm, int dw, int dh) {
+ final DisplayCutout displayCutout = calculateDisplayCutoutForRotation(
+ rotation).getDisplayCutout();
dm.noncompatWidthPixels = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode,
displayCutout);
dm.noncompatHeightPixels = mDisplayPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode,
@@ -1987,20 +1988,20 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return;
}
int sl = Configuration.resetScreenLayout(outConfig.screenLayout);
- sl = reduceConfigLayout(sl, Surface.ROTATION_0, density, unrotDw, unrotDh, uiMode,
- displayInfo.displayCutout);
- sl = reduceConfigLayout(sl, Surface.ROTATION_90, density, unrotDh, unrotDw, uiMode,
- displayInfo.displayCutout);
- sl = reduceConfigLayout(sl, Surface.ROTATION_180, density, unrotDw, unrotDh, uiMode,
- displayInfo.displayCutout);
- sl = reduceConfigLayout(sl, Surface.ROTATION_270, density, unrotDh, unrotDw, uiMode,
- displayInfo.displayCutout);
+ sl = reduceConfigLayout(sl, Surface.ROTATION_0, density, unrotDw, unrotDh, uiMode);
+ sl = reduceConfigLayout(sl, Surface.ROTATION_90, density, unrotDh, unrotDw, uiMode);
+ sl = reduceConfigLayout(sl, Surface.ROTATION_180, density, unrotDw, unrotDh, uiMode);
+ sl = reduceConfigLayout(sl, Surface.ROTATION_270, density, unrotDh, unrotDw, uiMode);
outConfig.smallestScreenWidthDp = (int)(displayInfo.smallestNominalAppWidth / density);
outConfig.screenLayout = sl;
}
private int reduceConfigLayout(int curLayout, int rotation, float density, int dw, int dh,
- int uiMode, DisplayCutout displayCutout) {
+ int uiMode) {
+ // Get the display cutout at this rotation.
+ final DisplayCutout displayCutout = calculateDisplayCutoutForRotation(
+ rotation).getDisplayCutout();
+
// Get the app screen size at this rotation.
int w = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode, displayCutout);
int h = mDisplayPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode, displayCutout);
@@ -6523,6 +6524,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
lastReparentedStack.postReparent();
}
releaseSelfIfNeeded();
+ mDisplayPolicy.release();
if (!mAllSleepTokens.isEmpty()) {
mRootWindowContainer.mSleepTokens.removeAll(mAllSleepTokens);
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index ba61667cc871..38a406e03300 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -28,6 +28,7 @@ import static android.view.Display.TYPE_INTERNAL;
import static android.view.InsetsState.ITYPE_BOTTOM_DISPLAY_CUTOUT;
import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES;
import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
+import static android.view.InsetsState.ITYPE_CAPTION_BAR;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_LEFT_DISPLAY_CUTOUT;
import static android.view.InsetsState.ITYPE_LEFT_GESTURES;
@@ -179,6 +180,7 @@ import android.view.accessibility.AccessibilityManager;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.policy.GestureNavigationSettingsObserver;
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.internal.util.ScreenshotHelper;
import com.android.internal.util.function.TriConsumer;
@@ -259,7 +261,9 @@ public class DisplayPolicy {
@Px
private int mBottomGestureAdditionalInset;
@Px
- private int mSideGestureInset;
+ private int mLeftGestureInset;
+ @Px
+ private int mRightGestureInset;
StatusBarManagerInternal getStatusBarManagerInternal() {
synchronized (mServiceAcquireLock) {
@@ -427,6 +431,8 @@ public class DisplayPolicy {
private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS = 0;
private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION = 1;
+ private final GestureNavigationSettingsObserver mGestureNavigationSettingsObserver;
+
private class PolicyHandler extends Handler {
PolicyHandler(Looper looper) {
@@ -651,6 +657,16 @@ public class DisplayPolicy {
mRefreshRatePolicy = new RefreshRatePolicy(mService,
mDisplayContent.getDisplayInfo(),
mService.mHighRefreshRateBlacklist);
+
+ mGestureNavigationSettingsObserver = new GestureNavigationSettingsObserver(mHandler,
+ mContext, () -> {
+ synchronized (mLock) {
+ onConfigurationChanged();
+ mSystemGestures.onConfigurationChanged();
+ mDisplayContent.updateSystemGestureExclusion();
+ }
+ });
+ mHandler.post(mGestureNavigationSettingsObserver::register);
}
void systemReady() {
@@ -723,7 +739,7 @@ public class DisplayPolicy {
}
boolean hasSideGestures() {
- return mHasNavigationBar && mSideGestureInset > 0;
+ return mHasNavigationBar && (mLeftGestureInset > 0 || mRightGestureInset > 0);
}
public boolean navigationBarCanMove() {
@@ -1009,6 +1025,13 @@ public class DisplayPolicy {
case TYPE_STATUS_BAR_PANEL:
return WindowManagerGlobal.ADD_INVALID_TYPE;
}
+
+ if (attrs.providesInsetsTypes != null) {
+ mContext.enforcePermission(
+ android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
+ "DisplayPolicy");
+ enforceSingleInsetsTypeCorrespondingToWindowType(attrs.providesInsetsTypes);
+ }
return ADD_OKAY;
}
@@ -1076,11 +1099,12 @@ public class DisplayPolicy {
inOutFrame.left = 0;
inOutFrame.top = 0;
inOutFrame.bottom = displayFrames.mDisplayHeight;
- inOutFrame.right = displayFrames.mUnrestricted.left + mSideGestureInset;
+ inOutFrame.right = displayFrames.mUnrestricted.left + mLeftGestureInset;
});
mDisplayContent.setInsetProvider(ITYPE_RIGHT_GESTURES, win,
(displayFrames, windowState, inOutFrame) -> {
- inOutFrame.left = displayFrames.mUnrestricted.right - mSideGestureInset;
+ inOutFrame.left = displayFrames.mUnrestricted.right
+ - mRightGestureInset;
inOutFrame.top = 0;
inOutFrame.bottom = displayFrames.mDisplayHeight;
inOutFrame.right = displayFrames.mDisplayWidth;
@@ -1094,6 +1118,28 @@ public class DisplayPolicy {
});
if (DEBUG_LAYOUT) Slog.i(TAG, "NAVIGATION BAR: " + mNavigationBar);
break;
+ default:
+ if (attrs.providesInsetsTypes != null) {
+ for (int insetsType : attrs.providesInsetsTypes) {
+ mDisplayContent.setInsetProvider(insetsType, win, null);
+ }
+ }
+ break;
+ }
+ }
+
+ private static void enforceSingleInsetsTypeCorrespondingToWindowType(int[] insetsTypes) {
+ int count = 0;
+ for (int insetsType : insetsTypes) {
+ switch (insetsType) {
+ case ITYPE_NAVIGATION_BAR:
+ case ITYPE_STATUS_BAR:
+ case ITYPE_CAPTION_BAR:
+ if (++count > 1) {
+ throw new IllegalArgumentException(
+ "Multiple InsetsTypes corresponding to Window type");
+ }
+ }
}
}
@@ -2819,7 +2865,8 @@ public class DisplayPolicy {
}
mNavBarOpacityMode = res.getInteger(R.integer.config_navBarOpacityMode);
- mSideGestureInset = res.getDimensionPixelSize(R.dimen.config_backGestureInset);
+ mLeftGestureInset = mGestureNavigationSettingsObserver.getLeftSensitivity(res);
+ mRightGestureInset = mGestureNavigationSettingsObserver.getRightSensitivity(res);
mNavigationBarLetsThroughTaps = res.getBoolean(R.bool.config_navBarTapThrough);
mNavigationBarAlwaysShowOnSideGesture =
res.getBoolean(R.bool.config_navBarAlwaysShowOnSideEdgeGesture);
@@ -3953,6 +4000,10 @@ public class DisplayPolicy {
return false;
}
+ void release() {
+ mHandler.post(mGestureNavigationSettingsObserver::unregister);
+ }
+
@VisibleForTesting
static boolean isOverlappingWithNavBar(WindowState targetWindow, WindowState navBarWindow) {
if (navBarWindow == null || !navBarWindow.isVisibleLw()
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index fc358ce7675f..bd5666dd9a27 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -846,10 +846,9 @@ class RecentTasks {
@VisibleForTesting
Set<Integer> getProfileIds(int userId) {
Set<Integer> userIds = new ArraySet<>();
- final List<UserInfo> profiles = mService.getUserManager().getProfiles(userId,
- false /* enabledOnly */);
- for (int i = profiles.size() - 1; i >= 0; --i) {
- userIds.add(profiles.get(i).id);
+ int[] profileIds = mService.getUserManager().getProfileIds(userId, false /* enabledOnly */);
+ for (int i = 0; i < profileIds.length; i++) {
+ userIds.add(Integer.valueOf(profileIds[i]));
}
return userIds;
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 15a49a791216..ebf1bc988b91 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2695,7 +2695,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
return true;
}
} else {
- if (r.intent.getComponent().equals(cls)) {
+ // Compare the target component instead of intent component so we don't miss if the
+ // activity uses alias.
+ if (r.mActivityComponent.equals(cls)) {
return true;
}
}
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 19f8ca912449..aa817fd38b25 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -36,6 +36,7 @@ import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.function.Supplier;
/**
* A class that can run animations on objects that have a set of child surfaces. We do this by
@@ -145,7 +146,7 @@ class SurfaceAnimator {
if (mLeash == null) {
mLeash = createAnimationLeash(mAnimatable, surface, t, type,
mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), 0 /* x */,
- 0 /* y */, hidden);
+ 0 /* y */, hidden, mService.mTransactionFactory);
mAnimatable.onAnimationLeashCreated(t, mLeash);
}
mAnimatable.onLeashAnimationStarting(t, mLeash);
@@ -374,13 +375,21 @@ class SurfaceAnimator {
static SurfaceControl createAnimationLeash(Animatable animatable, SurfaceControl surface,
Transaction t, @AnimationType int type, int width, int height, int x, int y,
- boolean hidden) {
+ boolean hidden, Supplier<Transaction> transactionFactory) {
if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to leash");
final SurfaceControl.Builder builder = animatable.makeAnimationLeash()
.setParent(animatable.getAnimationLeashParent())
- .setHidden(hidden)
.setName(surface + " - animation-leash");
final SurfaceControl leash = builder.build();
+ if (!hidden) {
+ // TODO(b/151665759) Defer reparent calls
+ // We want the leash to be visible immediately but we want to set the effects on
+ // the layer. Since the transaction used in this function may be deferred, we apply
+ // another transaction immediately with the correct visibility and effects.
+ // If this doesn't work, you will can see the 2/3 button nav bar flicker during
+ // seamless rotation.
+ transactionFactory.get().unsetColor(leash).show(leash).apply();
+ }
t.setWindowCrop(leash, width, height);
t.setPosition(leash, x, y);
t.show(leash);
diff --git a/services/core/java/com/android/server/wm/SurfaceFreezer.java b/services/core/java/com/android/server/wm/SurfaceFreezer.java
index 36861aa1f88e..8ab5043dc5d9 100644
--- a/services/core/java/com/android/server/wm/SurfaceFreezer.java
+++ b/services/core/java/com/android/server/wm/SurfaceFreezer.java
@@ -75,7 +75,8 @@ class SurfaceFreezer {
mLeash = SurfaceAnimator.createAnimationLeash(mAnimatable, mAnimatable.getSurfaceControl(),
t, ANIMATION_TYPE_SCREEN_ROTATION, startBounds.width(), startBounds.height(),
- startBounds.left, startBounds.top, false /* hidden */);
+ startBounds.left, startBounds.top, false /* hidden */,
+ mWmService.mTransactionFactory);
mAnimatable.onAnimationLeashCreated(t, mLeash);
SurfaceControl freezeTarget = mAnimatable.getFreezeSnapshotTarget();
@@ -103,7 +104,7 @@ class SurfaceFreezer {
*/
void unfreeze(SurfaceControl.Transaction t) {
if (mSnapshot != null) {
- mSnapshot.destroy(t);
+ mSnapshot.cancelAnimation(t, false /* restarting */);
}
if (mLeash == null) {
return;
@@ -212,7 +213,7 @@ class SurfaceFreezer {
* @param t The transaction to use for all cancelling surface operations.
* @param restarting Whether we are restarting the animation.
*/
- private void cancelAnimation(SurfaceControl.Transaction t, boolean restarting) {
+ void cancelAnimation(SurfaceControl.Transaction t, boolean restarting) {
final SurfaceControl leash = mSurfaceControl;
final AnimationAdapter animation = mAnimation;
final SurfaceAnimator.OnAnimationFinishedCallback animationFinishedCallback =
@@ -229,7 +230,6 @@ class SurfaceFreezer {
}
}
if (!restarting) {
- // TODO: do we need to destroy?
destroy(t);
}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 5ab5fbcf693d..f826deb6926a 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -431,7 +431,10 @@ class Task extends WindowContainer<WindowContainer> {
private boolean mForceShowForAllUsers;
/** When set, will force the task to report as invisible. */
- boolean mForceHidden = false;
+ static final int FLAG_FORCE_HIDDEN_FOR_PINNED_TASK = 1;
+ static final int FLAG_FORCE_HIDDEN_FOR_TASK_ORG = 1 << 1;
+ private int mForceHiddenFlags = 0;
+
SurfaceControl.Transaction mMainWindowSizeChangeTransaction;
@@ -2127,14 +2130,26 @@ class Task extends WindowContainer<WindowContainer> {
intersectWithInsetsIfFits(outStableBounds, mTmpBounds, mTmpInsets);
}
+ /**
+ * Forces the app bounds related configuration can be computed by
+ * {@link #computeConfigResourceOverrides(Configuration, Configuration, DisplayInfo,
+ * ActivityRecord.CompatDisplayInsets)}.
+ */
+ private static void invalidateAppBoundsConfig(@NonNull Configuration inOutConfig) {
+ final Rect appBounds = inOutConfig.windowConfiguration.getAppBounds();
+ if (appBounds != null) {
+ appBounds.setEmpty();
+ }
+ inOutConfig.screenWidthDp = Configuration.SCREEN_WIDTH_DP_UNDEFINED;
+ inOutConfig.screenHeightDp = Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
+ }
+
void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
@NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo) {
if (overrideDisplayInfo != null) {
// Make sure the screen related configs can be computed by the provided display info.
- inOutConfig.windowConfiguration.setAppBounds(null);
inOutConfig.screenLayout = Configuration.SCREENLAYOUT_UNDEFINED;
- inOutConfig.screenWidthDp = Configuration.SCREEN_WIDTH_DP_UNDEFINED;
- inOutConfig.screenHeightDp = Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
+ invalidateAppBoundsConfig(inOutConfig);
}
computeConfigResourceOverrides(inOutConfig, parentConfig, overrideDisplayInfo,
null /* compatInsets */);
@@ -2149,6 +2164,10 @@ class Task extends WindowContainer<WindowContainer> {
void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
@NonNull Configuration parentConfig,
@Nullable ActivityRecord.CompatDisplayInsets compatInsets) {
+ if (compatInsets != null) {
+ // Make sure the app bounds can be computed by the compat insets.
+ invalidateAppBoundsConfig(inOutConfig);
+ }
computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */,
compatInsets);
}
@@ -3031,7 +3050,7 @@ class Task extends WindowContainer<WindowContainer> {
*/
@VisibleForTesting
boolean isTranslucent(ActivityRecord starting) {
- if (!isAttached() || mForceHidden) {
+ if (!isAttached() || isForceHidden()) {
return true;
}
final PooledPredicate p = PooledLambda.obtainPredicate(Task::isOpaqueActivity,
@@ -3370,6 +3389,9 @@ class Task extends WindowContainer<WindowContainer> {
} else {
info.pictureInPictureParams = mPictureInPictureParams;
}
+ info.topActivityInfo = mReuseActivitiesReport.top != null
+ ? mReuseActivitiesReport.top.info
+ : null;
}
/**
@@ -4026,17 +4048,17 @@ class Task extends WindowContainer<WindowContainer> {
return;
}
// Let the old organizer know it has lost control.
- if (mTaskOrganizer != null) {
- sendTaskVanished();
- }
+ sendTaskVanished();
mTaskOrganizer = organizer;
sendTaskAppeared();
+ onTaskOrganizerChanged();
}
// Called on Binder death.
void taskOrganizerDied() {
mTaskOrganizer = null;
mLastTaskOrganizerWindowingMode = -1;
+ onTaskOrganizerChanged();
}
/**
@@ -4071,6 +4093,14 @@ class Task extends WindowContainer<WindowContainer> {
mLastTaskOrganizerWindowingMode = windowingMode;
}
+ private void onTaskOrganizerChanged() {
+ if (mTaskOrganizer == null) {
+ // If this task is no longer controlled by a task organizer, then reset the force hidden
+ // state
+ setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, false /* set */);
+ }
+ }
+
@Override
void setSurfaceControl(SurfaceControl sc) {
super.setSurfaceControl(sc);
@@ -4181,6 +4211,31 @@ class Task extends WindowContainer<WindowContainer> {
c.recycle();
}
+ /**
+ * Sets/unsets the forced-hidden state flag for this task depending on {@param set}.
+ * @return Whether the force hidden state changed
+ */
+ boolean setForceHidden(int flags, boolean set) {
+ int newFlags = mForceHiddenFlags;
+ if (set) {
+ newFlags |= flags;
+ } else {
+ newFlags &= ~flags;
+ }
+ if (mForceHiddenFlags == newFlags) {
+ return false;
+ }
+ mForceHiddenFlags = newFlags;
+ return true;
+ }
+
+ /**
+ * Returns whether this task is currently forced to be hidden for any reason.
+ */
+ protected boolean isForceHidden() {
+ return mForceHiddenFlags != 0;
+ }
+
@Override
long getProtoFieldId() {
return TASK;
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 9cbc9ee3f9ee..8f09f3faae29 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -23,6 +23,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMAR
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
@@ -475,6 +476,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub
if (!(container instanceof Task)) {
throw new RuntimeException("Invalid token in task transaction");
}
+ final Task task = (Task) container;
// The "client"-facing API should prevent bad changes; however, just in case, sanitize
// masks here.
int configMask = change.getConfigSetMask();
@@ -498,6 +500,11 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub
effects |= TRANSACT_EFFECTS_LIFECYCLE;
}
}
+ if ((change.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) {
+ if (task.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, change.getHidden())) {
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
+ }
+ }
return effects;
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index f83b0522846c..0f5cafe9e4e6 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -352,9 +352,19 @@ class TaskSnapshotController {
}
task.getBounds(mTmpRect);
mTmpRect.offsetTo(0, 0);
+
+ SurfaceControl[] excludeLayers;
+ final WindowState imeWindow = task.getDisplayContent().mInputMethodWindow;
+ if (imeWindow != null) {
+ excludeLayers = new SurfaceControl[1];
+ excludeLayers[0] = imeWindow.getSurfaceControl();
+ } else {
+ excludeLayers = new SurfaceControl[0];
+ }
final SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer =
- SurfaceControl.captureLayers(
- task.getSurfaceControl(), mTmpRect, scaleFraction, pixelFormat);
+ SurfaceControl.captureLayersExcluding(
+ task.getSurfaceControl(), mTmpRect, scaleFraction,
+ pixelFormat, excludeLayers);
if (outTaskSize != null) {
outTaskSize.x = mTmpRect.width();
outTaskSize.y = mTmpRect.height();
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index 164d3e055e94..45023acf4466 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -99,10 +99,10 @@ class TaskSnapshotPersister {
if (lowResTaskSnapshotScale > 0) {
mLowResScaleFactor = lowResTaskSnapshotScale / highResTaskSnapshotScale;
- setEnableLowResSnapshots(true);
+ mEnableLowResSnapshots = true;
} else {
mLowResScaleFactor = 0;
- setEnableLowResSnapshots(false);
+ mEnableLowResSnapshots = false;
}
mUse16BitFormat = service.mContext.getResources().getBoolean(
@@ -175,14 +175,6 @@ class TaskSnapshotPersister {
}
/**
- * Not to be used. Only here for testing.
- */
- @VisibleForTesting
- void setEnableLowResSnapshots(boolean enabled) {
- mEnableLowResSnapshots = enabled;
- }
-
- /**
* Return if task snapshots are stored in 16 bit pixel format.
*
* @return true if task snapshots are stored in 16 bit pixel format.
@@ -405,7 +397,7 @@ class TaskSnapshotPersister {
return false;
}
- if (!enableLowResSnapshots()) {
+ if (!mEnableLowResSnapshots) {
swBitmap.recycle();
return true;
}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index a60db94ad1e3..0b11dd28e595 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -96,8 +96,8 @@ public class WindowAnimator {
mAnimationFrameCallback = frameTimeNs -> {
synchronized (mService.mGlobalLock) {
mAnimationFrameCallbackScheduled = false;
+ animate(frameTimeNs);
}
- animate(frameTimeNs);
};
}
@@ -115,110 +115,94 @@ public class WindowAnimator {
mInitialized = true;
}
- /**
- * DO NOT HOLD THE WINDOW MANAGER LOCK WHILE CALLING THIS METHOD. Reason: the method closes
- * an animation transaction, that might be blocking until the next sf-vsync, so we want to make
- * sure other threads can make progress if this happens.
- */
private void animate(long frameTimeNs) {
+ if (!mInitialized) {
+ return;
+ }
- synchronized (mService.mGlobalLock) {
- if (!mInitialized) {
- return;
- }
+ // Schedule next frame already such that back-pressure happens continuously.
+ scheduleAnimation();
- // Schedule next frame already such that back-pressure happens continuously
- scheduleAnimation();
+ mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS;
+ mBulkUpdateParams = SET_ORIENTATION_CHANGE_COMPLETE;
+ if (DEBUG_WINDOW_TRACE) {
+ Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
}
- synchronized (mService.mGlobalLock) {
- mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS;
- mBulkUpdateParams = SET_ORIENTATION_CHANGE_COMPLETE;
- if (DEBUG_WINDOW_TRACE) {
- Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, ">>> OPEN TRANSACTION animate");
+ mService.openSurfaceTransaction();
+ try {
+ final AccessibilityController accessibilityController =
+ mService.mAccessibilityController;
+ final int numDisplays = mDisplayContentsAnimators.size();
+ for (int i = 0; i < numDisplays; i++) {
+ final int displayId = mDisplayContentsAnimators.keyAt(i);
+ final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
+ // Update animations of all applications, including those associated with
+ // exiting/removed apps.
+ dc.updateWindowsForAnimator();
+ dc.prepareSurfaces();
}
- ProtoLog.i(WM_SHOW_TRANSACTIONS, ">>> OPEN TRANSACTION animate");
- mService.openSurfaceTransaction();
- try {
- final AccessibilityController accessibilityController =
- mService.mAccessibilityController;
- final int numDisplays = mDisplayContentsAnimators.size();
- for (int i = 0; i < numDisplays; i++) {
- final int displayId = mDisplayContentsAnimators.keyAt(i);
- final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
- // Update animations of all applications, including those
- // associated with exiting/removed apps
- dc.updateWindowsForAnimator();
- dc.prepareSurfaces();
- }
-
- for (int i = 0; i < numDisplays; i++) {
- final int displayId = mDisplayContentsAnimators.keyAt(i);
- final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
-
- dc.checkAppWindowsReadyToShow();
- if (accessibilityController != null) {
- accessibilityController.drawMagnifiedRegionBorderIfNeededLocked(displayId,
- mTransaction);
- }
- }
-
- cancelAnimation();
+ for (int i = 0; i < numDisplays; i++) {
+ final int displayId = mDisplayContentsAnimators.keyAt(i);
+ final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
- if (mService.mWatermark != null) {
- mService.mWatermark.drawIfNeeded();
+ dc.checkAppWindowsReadyToShow();
+ if (accessibilityController != null) {
+ accessibilityController.drawMagnifiedRegionBorderIfNeededLocked(displayId,
+ mTransaction);
}
-
- SurfaceControl.mergeToGlobalTransaction(mTransaction);
- } catch (RuntimeException e) {
- Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
- } finally {
- mService.closeSurfaceTransaction("WindowAnimator");
- ProtoLog.i(WM_SHOW_TRANSACTIONS, "<<< CLOSE TRANSACTION animate");
}
- boolean hasPendingLayoutChanges = mService.mRoot.hasPendingLayoutChanges(this);
- boolean doRequest = false;
- if (mBulkUpdateParams != 0) {
- doRequest = mService.mRoot.copyAnimToLayoutParams();
- }
+ cancelAnimation();
- if (hasPendingLayoutChanges || doRequest) {
- mService.mWindowPlacerLocked.requestTraversal();
+ if (mService.mWatermark != null) {
+ mService.mWatermark.drawIfNeeded();
}
- final boolean rootAnimating = mService.mRoot.isAnimating(TRANSITION | CHILDREN);
- if (rootAnimating && !mLastRootAnimating) {
+ SurfaceControl.mergeToGlobalTransaction(mTransaction);
+ } catch (RuntimeException e) {
+ Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
+ } finally {
+ mService.closeSurfaceTransaction("WindowAnimator");
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, "<<< CLOSE TRANSACTION animate");
+ }
- // Usually app transitions but quite a load onto the system already (with all the
- // things happening in app), so pause task snapshot persisting to not increase the
- // load.
- mService.mTaskSnapshotController.setPersisterPaused(true);
- Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
- }
- if (!rootAnimating && mLastRootAnimating) {
- mService.mWindowPlacerLocked.requestTraversal();
- mService.mTaskSnapshotController.setPersisterPaused(false);
- Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
- }
+ final boolean hasPendingLayoutChanges = mService.mRoot.hasPendingLayoutChanges(this);
+ final boolean doRequest = mBulkUpdateParams != 0 && mService.mRoot.copyAnimToLayoutParams();
+ if (hasPendingLayoutChanges || doRequest) {
+ mService.mWindowPlacerLocked.requestTraversal();
+ }
- mLastRootAnimating = rootAnimating;
+ final boolean rootAnimating = mService.mRoot.isAnimating(TRANSITION | CHILDREN);
+ if (rootAnimating && !mLastRootAnimating) {
+ // Usually app transitions but quite a load onto the system already (with all the things
+ // happening in app), so pause task snapshot persisting to not increase the load.
+ mService.mTaskSnapshotController.setPersisterPaused(true);
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
+ }
+ if (!rootAnimating && mLastRootAnimating) {
+ mService.mWindowPlacerLocked.requestTraversal();
+ mService.mTaskSnapshotController.setPersisterPaused(false);
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
+ }
- if (mRemoveReplacedWindows) {
- mService.mRoot.removeReplacedWindows();
- mRemoveReplacedWindows = false;
- }
+ mLastRootAnimating = rootAnimating;
- mService.destroyPreservedSurfaceLocked();
+ if (mRemoveReplacedWindows) {
+ mService.mRoot.removeReplacedWindows();
+ mRemoveReplacedWindows = false;
+ }
- executeAfterPrepareSurfacesRunnables();
+ mService.destroyPreservedSurfaceLocked();
- if (DEBUG_WINDOW_TRACE) {
- Slog.i(TAG, "!!! animate: exit"
- + " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
- + " hasPendingLayoutChanges=" + hasPendingLayoutChanges);
- }
+ executeAfterPrepareSurfacesRunnables();
+
+ if (DEBUG_WINDOW_TRACE) {
+ Slog.i(TAG, "!!! animate: exit"
+ + " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
+ + " hasPendingLayoutChanges=" + hasPendingLayoutChanges);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index ec90097e75e9..9a9283257b03 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -2461,6 +2461,19 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
mWaitingSyncId = -1;
}
+ /**
+ * Returns true if any of the children elected to participate in the Sync
+ */
+ boolean addChildrenToSyncSet(int localId) {
+ boolean willSync = false;
+
+ for (int i = 0; i < mChildren.size(); i++) {
+ final WindowContainer child = mChildren.get(i);
+ willSync |= mBLASTSyncEngine.addToSyncSet(localId, child);
+ }
+ return willSync;
+ }
+
boolean prepareForSync(BLASTSyncEngine.TransactionReadyListener waitingListener,
int waitingId) {
boolean willSync = false;
@@ -2469,16 +2482,12 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
mUsingBLASTSyncTransaction = true;
- int localId = mBLASTSyncEngine.startSyncSet(this);
- for (int i = 0; i < mChildren.size(); i++) {
- final WindowContainer child = mChildren.get(i);
- willSync = mBLASTSyncEngine.addToSyncSet(localId, child) | willSync;
- }
-
// Make sure to set these before we call setReady in case the sync was a no-op
mWaitingSyncId = waitingId;
mWaitingListener = waitingListener;
+ int localId = mBLASTSyncEngine.startSyncSet(this);
+ willSync |= addChildrenToSyncSet(localId);
mBLASTSyncEngine.setReady(localId);
return willSync;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ecbbb03a7c94..7400d17b21ce 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -186,7 +186,6 @@ import android.os.SystemService;
import android.os.Trace;
import android.os.UserHandle;
import android.os.WorkSource;
-import android.provider.DeviceConfig;
import android.provider.Settings;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
@@ -1007,9 +1006,7 @@ public class WindowManagerService extends IWindowManager.Stub
void openSurfaceTransaction() {
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "openSurfaceTransaction");
- synchronized (mGlobalLock) {
- SurfaceControl.openTransaction();
- }
+ SurfaceControl.openTransaction();
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
@@ -1022,10 +1019,8 @@ public class WindowManagerService extends IWindowManager.Stub
void closeSurfaceTransaction(String where) {
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "closeSurfaceTransaction");
- synchronized (mGlobalLock) {
- SurfaceControl.closeTransaction();
- mWindowTracing.logState(where);
- }
+ SurfaceControl.closeTransaction();
+ mWindowTracing.logState(where);
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
@@ -1166,9 +1161,7 @@ public class WindowManagerService extends IWindowManager.Stub
mAnimator = new WindowAnimator(this);
mRoot = new RootWindowContainer(this);
- mUseBLAST = DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT,
- WM_USE_BLAST_ADAPTER_FLAG, false);
+ mUseBLAST = true;
mWindowPlacerLocked = new WindowSurfacePlacer(this);
mTaskSnapshotController = new TaskSnapshotController(this);
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 32eb932ea0ed..f356329cbefb 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -1058,7 +1058,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
}
private void registerActivityConfigurationListener(ActivityRecord activityRecord) {
- if (activityRecord == null) {
+ if (activityRecord == null || activityRecord.containsListener(this)) {
return;
}
// A process can only register to one activityRecord to listen to the override configuration
@@ -1093,7 +1093,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
for (int i = mActivities.size() - 1; i >= 0; i--) {
final ActivityRecord activityRecord = mActivities.get(i);
- if (!activityRecord.finishing && !activityRecord.containsListener(this)) {
+ if (!activityRecord.finishing) {
// Eligible activity is found, update listener.
registerActivityConfigurationListener(activityRecord);
return;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c44be4d00a84..36175705b848 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -679,6 +679,15 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
*/
int mFrameRateSelectionPriority = RefreshRatePolicy.LAYER_PRIORITY_UNSET;
+ /**
+ * BLASTSyncEngine ID corresponding to a sync-set for all
+ * our children. We add our children to this set in Sync,
+ * but we save it and don't mark it as ready until finishDrawing
+ * this way we have a two way latch between all our children finishing
+ * and drawing ourselves.
+ */
+ private int mLocalSyncId = -1;
+
static final int BLAST_TIMEOUT_DURATION = 5000; /* milliseconds */
/**
@@ -2929,8 +2938,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// and add the window only if the permission was granted. Therefore, if
// the mode is MODE_DEFAULT we want the op to succeed as the window is
// shown.
- final int mode = mWmService.mAppOps.startOpNoThrow(mAppOp,
- getOwningUid(), getOwningPackage(), true);
+ final int mode = mWmService.mAppOps.startOpNoThrow(mAppOp, getOwningUid(),
+ getOwningPackage(), true /* startIfModeDefault */, null /* featureId */,
+ "init-default-visibility");
if (mode != MODE_ALLOWED && mode != MODE_DEFAULT) {
setAppOpVisibilityLw(false);
}
@@ -2938,7 +2948,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
void resetAppOpsState() {
if (mAppOp != OP_NONE && mAppOpVisibility) {
- mWmService.mAppOps.finishOp(mAppOp, getOwningUid(), getOwningPackage());
+ mWmService.mAppOps.finishOp(mAppOp, getOwningUid(), getOwningPackage(),
+ null /* featureId */);
}
}
@@ -2953,11 +2964,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// as this would mean we will get another change callback and will reconcile.
int mode = mWmService.mAppOps.checkOpNoThrow(mAppOp, uid, packageName);
if (mode != MODE_ALLOWED && mode != MODE_DEFAULT) {
- mWmService.mAppOps.finishOp(mAppOp, uid, packageName);
+ mWmService.mAppOps.finishOp(mAppOp, uid, packageName, null /* featureId */);
setAppOpVisibilityLw(false);
}
} else {
- final int mode = mWmService.mAppOps.startOpNoThrow(mAppOp, uid, packageName, true);
+ final int mode = mWmService.mAppOps.startOpNoThrow(mAppOp, uid, packageName,
+ true /* startIfModeDefault */, null /* featureId */, "attempt-to-be-visible");
if (mode == MODE_ALLOWED || mode == MODE_DEFAULT) {
setAppOpVisibilityLw(true);
}
@@ -5684,11 +5696,16 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
@Override
boolean prepareForSync(BLASTSyncEngine.TransactionReadyListener waitingListener,
int waitingId) {
- // TODO(b/148871522): Support child window
+ if (!isVisible()) {
+ return false;
+ }
mWaitingListener = waitingListener;
mWaitingSyncId = waitingId;
mUsingBLASTSyncTransaction = true;
+ mLocalSyncId = mBLASTSyncEngine.startSyncSet(this);
+ addChildrenToSyncSet(mLocalSyncId);
+
mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this);
mWmService.mH.sendNewMessageDelayed(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this,
BLAST_TIMEOUT_DURATION);
@@ -5702,11 +5719,21 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this);
- if (postDrawTransaction == null) {
- postDrawTransaction = new SurfaceControl.Transaction();
+ if (postDrawTransaction != null) {
+ mBLASTSyncTransaction.merge(postDrawTransaction);
}
- postDrawTransaction.merge(mBLASTSyncTransaction);
- mWaitingListener.transactionReady(mWaitingSyncId, postDrawTransaction);
+
+ // If localSyncId is >0 then we are syncing with children and will
+ // invoke transaction ready from our own #transactionReady callback
+ // we just need to signal our side of the sync (setReady). But if we
+ // have no sync operation at this level transactionReady will never
+ // be invoked and we need to invoke it ourself.
+ if (mLocalSyncId >= 0) {
+ mBLASTSyncEngine.setReady(mLocalSyncId);
+ } else {
+ mWaitingListener.transactionReady(mWaitingSyncId, mBLASTSyncTransaction);
+ }
+
mUsingBLASTSyncTransaction = false;
mWaitingSyncId = 0;
diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
index 6f9d012d3145..29b2cd650565 100644
--- a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
+++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
@@ -172,26 +172,25 @@ static bool readChunk(int fd, std::vector<uint8_t>& data) {
BlockHeader readHeader(std::span<uint8_t>& data);
-static inline int32_t readBEInt32(borrowed_fd fd) {
+static inline int32_t readLEInt32(borrowed_fd fd) {
int32_t result;
ReadFully(fd, &result, sizeof(result));
- result = int32_t(be32toh(result));
+ result = int32_t(le32toh(result));
return result;
}
static inline std::vector<char> readBytes(borrowed_fd fd) {
- int32_t size = readBEInt32(fd);
+ int32_t size = readLEInt32(fd);
std::vector<char> result(size);
ReadFully(fd, result.data(), size);
return result;
}
static inline int32_t skipIdSigHeaders(borrowed_fd fd) {
- readBEInt32(fd); // version
- readBytes(fd); // verityRootHash
- readBytes(fd); // v3Digest
- readBytes(fd); // pkcs7SignatureBlock
- return readBEInt32(fd); // size of the verity tree
+ readLEInt32(fd); // version
+ readBytes(fd); // hashingInfo
+ readBytes(fd); // signingInfo
+ return readLEInt32(fd); // size of the verity tree
}
static inline IncFsSize verityTreeSizeForFile(IncFsSize fileSize) {
@@ -365,13 +364,8 @@ private:
}
}
void onDestroy() final {
- ALOGE("Sending EXIT to server.");
- sendRequest(mOutFd, EXIT);
// Make sure the receiver thread stopped.
CHECK(!mReceiverThread.joinable());
-
- mInFd.reset();
- mOutFd.reset();
}
// Installation.
@@ -569,13 +563,6 @@ private:
// Streaming.
bool initStreaming(unique_fd inout) {
- mInFd.reset(dup(inout));
- mOutFd.reset(dup(inout));
- if (mInFd < 0 || mOutFd < 0) {
- ALOGE("Failed to dup FDs.");
- return false;
- }
-
mEventFd.reset(eventfd(0, EFD_CLOEXEC));
if (mEventFd < 0) {
ALOGE("Failed to create eventfd.");
@@ -584,7 +571,7 @@ private:
// Awaiting adb handshake.
char okay_buf[OKAY.size()];
- if (!android::base::ReadFully(mInFd, okay_buf, OKAY.size())) {
+ if (!android::base::ReadFully(inout, okay_buf, OKAY.size())) {
ALOGE("Failed to receive OKAY. Abort.");
return false;
}
@@ -594,13 +581,23 @@ private:
return false;
}
- mReceiverThread = std::thread([this]() { receiver(); });
+ {
+ std::lock_guard lock{mOutFdLock};
+ mOutFd.reset(::dup(inout));
+ if (mOutFd < 0) {
+ ALOGE("Failed to create streaming fd.");
+ }
+ }
+
+ mReceiverThread =
+ std::thread([this, io = std::move(inout)]() mutable { receiver(std::move(io)); });
ALOGI("Started streaming...");
return true;
}
// IFS callbacks.
void onPendingReads(dataloader::PendingReads pendingReads) final {
+ std::lock_guard lock{mOutFdLock};
CHECK(mIfs);
for (auto&& pendingRead : pendingReads) {
const android::dataloader::FileId& fileId = pendingRead.id;
@@ -626,12 +623,12 @@ private:
}
}
- void receiver() {
+ void receiver(unique_fd inout) {
std::vector<uint8_t> data;
std::vector<IncFsDataBlock> instructions;
std::unordered_map<FileIdx, unique_fd> writeFds;
while (!mStopReceiving) {
- const int res = waitForDataOrSignal(mInFd, mEventFd);
+ const int res = waitForDataOrSignal(inout, mEventFd);
if (res == 0) {
continue;
}
@@ -641,10 +638,11 @@ private:
break;
}
if (res == mEventFd) {
- ALOGE("Received stop signal. Exit.");
+ ALOGE("Received stop signal. Sending EXIT to server.");
+ sendRequest(inout, EXIT);
break;
}
- if (!readChunk(mInFd, data)) {
+ if (!readChunk(inout, data)) {
ALOGE("Failed to read a message. Abort.");
mStatusListener->reportStatus(DATA_LOADER_NO_CONNECTION);
break;
@@ -657,7 +655,7 @@ private:
ALOGI("Stop signal received. Sending exit command (remaining bytes: %d).",
int(remainingData.size()));
- sendRequest(mOutFd, EXIT);
+ sendRequest(inout, EXIT);
mStopReceiving = true;
break;
}
@@ -700,6 +698,11 @@ private:
writeInstructions(instructions);
}
writeInstructions(instructions);
+
+ {
+ std::lock_guard lock{mOutFdLock};
+ mOutFd.reset();
+ }
}
void writeInstructions(std::vector<IncFsDataBlock>& instructions) {
@@ -743,7 +746,7 @@ private:
std::string mArgs;
android::dataloader::FilesystemConnectorPtr mIfs = nullptr;
android::dataloader::StatusListenerPtr mStatusListener = nullptr;
- android::base::unique_fd mInFd;
+ std::mutex mOutFdLock;
android::base::unique_fd mOutFd;
android::base::unique_fd mEventFd;
std::thread mReceiverThread;
diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp
index 239a1011422c..fe05d4ecc2fc 100644
--- a/services/core/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp
@@ -178,63 +178,18 @@ bool processPowerHalReturn(bool isOk, const char* functionName) {
return isOk;
}
-static void sendPowerHint(PowerHint hintId, uint32_t data) {
- std::unique_lock<std::mutex> lock(gPowerHalMutex);
- switch (connectPowerHalLocked()) {
- case HalVersion::NONE:
- return;
- case HalVersion::HIDL_1_0: {
- sp<IPowerV1_0> handle = gPowerHalHidlV1_0_;
- lock.unlock();
- auto ret = handle->powerHint(hintId, data);
- processPowerHalReturn(ret.isOk(), "powerHint");
- break;
- }
- case HalVersion::HIDL_1_1: {
- sp<IPowerV1_1> handle = gPowerHalHidlV1_1_;
- lock.unlock();
- auto ret = handle->powerHintAsync(hintId, data);
- processPowerHalReturn(ret.isOk(), "powerHintAsync");
- break;
- }
- case HalVersion::AIDL: {
- if (hintId == PowerHint::INTERACTION) {
- sp<IPowerAidl> handle = gPowerHalAidl_;
- lock.unlock();
- auto ret = handle->setBoost(Boost::INTERACTION, data);
- processPowerHalReturn(ret.isOk(), "setBoost");
- break;
- } else if (hintId == PowerHint::LAUNCH) {
- sp<IPowerAidl> handle = gPowerHalAidl_;
- lock.unlock();
- auto ret = handle->setMode(Mode::LAUNCH, static_cast<bool>(data));
- processPowerHalReturn(ret.isOk(), "setMode");
- break;
- } else {
- ALOGE("Unsupported power hint: %s.", toString(hintId).c_str());
- return;
- }
- }
- default: {
- ALOGE("Unknown power HAL state");
- return;
- }
- }
- SurfaceComposerClient::notifyPowerHint(static_cast<int32_t>(hintId));
-}
-
enum class HalSupport {
UNKNOWN = 0,
ON,
OFF,
};
-static void setPowerBoost(Boost boost, int32_t durationMs) {
+static void setPowerBoostWithHandle(sp<IPowerAidl> handle, Boost boost, int32_t durationMs) {
// Android framework only sends boost upto DISPLAY_UPDATE_IMMINENT.
// Need to increase the array size if more boost supported.
static std::array<std::atomic<HalSupport>,
static_cast<int32_t>(Boost::DISPLAY_UPDATE_IMMINENT) + 1>
- boostSupportedArray = {HalSupport::UNKNOWN};
+ boostSupportedArray = {HalSupport::UNKNOWN};
// Quick return if boost is not supported by HAL
if (boost > Boost::DISPLAY_UPDATE_IMMINENT ||
@@ -243,14 +198,6 @@ static void setPowerBoost(Boost boost, int32_t durationMs) {
return;
}
- std::unique_lock<std::mutex> lock(gPowerHalMutex);
- if (connectPowerHalLocked() != HalVersion::AIDL) {
- ALOGV("Power HAL AIDL not available");
- return;
- }
- sp<IPowerAidl> handle = gPowerHalAidl_;
- lock.unlock();
-
if (boostSupportedArray[static_cast<int32_t>(boost)] == HalSupport::UNKNOWN) {
bool isSupported = false;
handle->isBoostSupported(boost, &isSupported);
@@ -267,12 +214,22 @@ static void setPowerBoost(Boost boost, int32_t durationMs) {
processPowerHalReturn(ret.isOk(), "setPowerBoost");
}
-static void setPowerMode(Mode mode, bool enabled) {
+static void setPowerBoost(Boost boost, int32_t durationMs) {
+ std::unique_lock<std::mutex> lock(gPowerHalMutex);
+ if (connectPowerHalLocked() != HalVersion::AIDL) {
+ ALOGV("Power HAL AIDL not available");
+ return;
+ }
+ sp<IPowerAidl> handle = gPowerHalAidl_;
+ lock.unlock();
+ setPowerBoostWithHandle(handle, boost, durationMs);
+}
+
+static void setPowerModeWithHandle(sp<IPowerAidl> handle, Mode mode, bool enabled) {
// Android framework only sends mode upto DISPLAY_INACTIVE.
// Need to increase the array if more mode supported.
- static std::array<std::atomic<HalSupport>,
- static_cast<int32_t>(Mode::DISPLAY_INACTIVE) + 1>
- modeSupportedArray = {HalSupport::UNKNOWN};
+ static std::array<std::atomic<HalSupport>, static_cast<int32_t>(Mode::DISPLAY_INACTIVE) + 1>
+ modeSupportedArray = {HalSupport::UNKNOWN};
// Quick return if mode is not supported by HAL
if (mode > Mode::DISPLAY_INACTIVE ||
@@ -281,14 +238,6 @@ static void setPowerMode(Mode mode, bool enabled) {
return;
}
- std::unique_lock<std::mutex> lock(gPowerHalMutex);
- if (connectPowerHalLocked() != HalVersion::AIDL) {
- ALOGV("Power HAL AIDL not available");
- return;
- }
- sp<IPowerAidl> handle = gPowerHalAidl_;
- lock.unlock();
-
if (modeSupportedArray[static_cast<int32_t>(mode)] == HalSupport::UNKNOWN) {
bool isSupported = false;
handle->isModeSupported(mode, &isSupported);
@@ -304,6 +253,76 @@ static void setPowerMode(Mode mode, bool enabled) {
processPowerHalReturn(ret.isOk(), "setPowerMode");
}
+static void setPowerMode(Mode mode, bool enabled) {
+ std::unique_lock<std::mutex> lock(gPowerHalMutex);
+ if (connectPowerHalLocked() != HalVersion::AIDL) {
+ ALOGV("Power HAL AIDL not available");
+ return;
+ }
+ sp<IPowerAidl> handle = gPowerHalAidl_;
+ lock.unlock();
+ setPowerModeWithHandle(handle, mode, enabled);
+}
+
+static void sendPowerHint(PowerHint hintId, uint32_t data) {
+ std::unique_lock<std::mutex> lock(gPowerHalMutex);
+ switch (connectPowerHalLocked()) {
+ case HalVersion::NONE:
+ return;
+ case HalVersion::HIDL_1_0: {
+ sp<IPowerV1_0> handle = gPowerHalHidlV1_0_;
+ lock.unlock();
+ auto ret = handle->powerHint(hintId, data);
+ processPowerHalReturn(ret.isOk(), "powerHint");
+ break;
+ }
+ case HalVersion::HIDL_1_1: {
+ sp<IPowerV1_1> handle = gPowerHalHidlV1_1_;
+ lock.unlock();
+ auto ret = handle->powerHintAsync(hintId, data);
+ processPowerHalReturn(ret.isOk(), "powerHintAsync");
+ break;
+ }
+ case HalVersion::AIDL: {
+ if (hintId == PowerHint::INTERACTION) {
+ sp<IPowerAidl> handle = gPowerHalAidl_;
+ lock.unlock();
+ setPowerBoostWithHandle(handle, Boost::INTERACTION, data);
+ break;
+ } else if (hintId == PowerHint::LAUNCH) {
+ sp<IPowerAidl> handle = gPowerHalAidl_;
+ lock.unlock();
+ setPowerModeWithHandle(handle, Mode::LAUNCH, static_cast<bool>(data));
+ break;
+ } else if (hintId == PowerHint::LOW_POWER) {
+ sp<IPowerAidl> handle = gPowerHalAidl_;
+ lock.unlock();
+ setPowerModeWithHandle(handle, Mode::LOW_POWER, static_cast<bool>(data));
+ break;
+ } else if (hintId == PowerHint::SUSTAINED_PERFORMANCE) {
+ sp<IPowerAidl> handle = gPowerHalAidl_;
+ lock.unlock();
+ setPowerModeWithHandle(handle, Mode::SUSTAINED_PERFORMANCE,
+ static_cast<bool>(data));
+ break;
+ } else if (hintId == PowerHint::VR_MODE) {
+ sp<IPowerAidl> handle = gPowerHalAidl_;
+ lock.unlock();
+ setPowerModeWithHandle(handle, Mode::VR, static_cast<bool>(data));
+ break;
+ } else {
+ ALOGE("Unsupported power hint: %s.", toString(hintId).c_str());
+ return;
+ }
+ }
+ default: {
+ ALOGE("Unknown power HAL state");
+ return;
+ }
+ }
+ SurfaceComposerClient::notifyPowerHint(static_cast<int32_t>(hintId));
+}
+
void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType) {
if (gPowerManagerServiceObj) {
// Throttle calls into user activity by event type.
@@ -426,8 +445,7 @@ static void nativeSetInteractive(JNIEnv* /* env */, jclass /* clazz */, jboolean
case HalVersion::AIDL: {
sp<IPowerAidl> handle = gPowerHalAidl_;
lock.unlock();
- auto ret = handle->setMode(Mode::INTERACTIVE, enable);
- processPowerHalReturn(ret.isOk(), "setMode");
+ setPowerModeWithHandle(handle, Mode::INTERACTIVE, enable);
return;
}
default: {
@@ -481,8 +499,9 @@ static void nativeSetFeature(JNIEnv* /* env */, jclass /* clazz */, jint feature
return;
}
case HalVersion::AIDL: {
- auto ret = gPowerHalAidl_->setMode(Mode::DOUBLE_TAP_TO_WAKE, static_cast<bool>(data));
- processPowerHalReturn(ret.isOk(), "setMode");
+ sp<IPowerAidl> handle = gPowerHalAidl_;
+ lock.unlock();
+ setPowerModeWithHandle(handle, Mode::DOUBLE_TAP_TO_WAKE, static_cast<bool>(data));
return;
}
default: {
diff --git a/services/core/jni/com_android_server_security_VerityUtils.cpp b/services/core/jni/com_android_server_security_VerityUtils.cpp
index be11b8620bda..0277f16d5e54 100644
--- a/services/core/jni/com_android_server_security_VerityUtils.cpp
+++ b/services/core/jni/com_android_server_security_VerityUtils.cpp
@@ -25,17 +25,14 @@
#include <errno.h>
#include <fcntl.h>
#include <linux/fsverity.h>
+#include <linux/stat.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
-#include <type_traits>
-
#include <android-base/unique_fd.h>
-const int kSha256Bytes = 32;
-
namespace android {
namespace {
@@ -69,30 +66,28 @@ int enableFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath, jbyteArra
return 0;
}
-int measureFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath) {
- using Storage = std::aligned_storage_t<sizeof(fsverity_digest) + kSha256Bytes>;
-
- Storage bytes;
- fsverity_digest *data = reinterpret_cast<fsverity_digest *>(&bytes);
- data->digest_size = kSha256Bytes; // the only input/output parameter
-
+// Returns whether the file has fs-verity enabled.
+// 0 if it is not present, 1 if is present, and -errno if there was an error.
+int statxForFsverity(JNIEnv *env, jobject /* clazz */, jstring filePath) {
ScopedUtfChars path(env, filePath);
- if (path.c_str() == nullptr) {
- return EINVAL;
- }
- ::android::base::unique_fd rfd(open(path.c_str(), O_RDONLY | O_CLOEXEC));
- if (rfd.get() < 0) {
- return errno;
+
+ struct statx out = {};
+ if (statx(AT_FDCWD, path.c_str(), 0 /* flags */, STATX_ALL, &out) != 0) {
+ return -errno;
}
- if (ioctl(rfd.get(), FS_IOC_MEASURE_VERITY, data) < 0) {
- return errno;
+
+ // Sanity check.
+ if ((out.stx_attributes_mask & STATX_ATTR_VERITY) == 0) {
+ ALOGE("Unexpected, STATX_ATTR_VERITY not supported by kernel");
+ return -ENOSYS;
}
- return 0;
+
+ return (out.stx_attributes & STATX_ATTR_VERITY) != 0;
}
const JNINativeMethod sMethods[] = {
- { "enableFsverityNative", "(Ljava/lang/String;[B)I", (void *)enableFsverity },
- { "measureFsverityNative", "(Ljava/lang/String;)I", (void *)measureFsverity },
+ {"enableFsverityNative", "(Ljava/lang/String;[B)I", (void *)enableFsverity},
+ {"statxForFsverityNative", "(Ljava/lang/String;)I", (void *)statxForFsverity},
};
} // namespace
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 6f41631a8b88..fc9044a8cfa1 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -393,6 +393,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
private static final long MS_PER_DAY = TimeUnit.DAYS.toMillis(1);
private static final long EXPIRATION_GRACE_PERIOD_MS = 5 * MS_PER_DAY; // 5 days, in ms
+ private static final long MANAGED_PROFILE_MAXIMUM_TIME_OFF_THRESHOLD = 3 * MS_PER_DAY;
private static final String ACTION_EXPIRED_PASSWORD_NOTIFICATION =
"com.android.server.ACTION_EXPIRED_PASSWORD_NOTIFICATION";
@@ -786,7 +787,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
List<String> mLockTaskPackages = new ArrayList<>();
// List of packages protected by device owner
- List<String> mProtectedPackages = new ArrayList<>();
+ List<String> mUserControlDisabledPackages = new ArrayList<>();
// Bitfield of feature flags to be enabled during LockTask mode.
// We default on the power button menu, in order to be consistent with pre-P behaviour.
@@ -3541,8 +3542,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
out.endTag(null, TAG_OWNER_INSTALLED_CA_CERT);
}
- for (int i = 0, size = policy.mProtectedPackages.size(); i < size; i++) {
- String packageName = policy.mProtectedPackages.get(i);
+ for (int i = 0, size = policy.mUserControlDisabledPackages.size(); i < size; i++) {
+ String packageName = policy.mUserControlDisabledPackages.get(i);
out.startTag(null, TAG_PROTECTED_PACKAGES);
out.attribute(null, ATTR_NAME, packageName);
out.endTag(null, TAG_PROTECTED_PACKAGES);
@@ -3667,7 +3668,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
policy.mAdminMap.clear();
policy.mAffiliationIds.clear();
policy.mOwnerInstalledCaCerts.clear();
- policy.mProtectedPackages.clear();
+ policy.mUserControlDisabledPackages.clear();
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
@@ -3769,7 +3770,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
} else if (TAG_OWNER_INSTALLED_CA_CERT.equals(tag)) {
policy.mOwnerInstalledCaCerts.add(parser.getAttributeValue(null, ATTR_ALIAS));
} else if (TAG_PROTECTED_PACKAGES.equals(tag)) {
- policy.mProtectedPackages.add(parser.getAttributeValue(null, ATTR_NAME));
+ policy.mUserControlDisabledPackages.add(
+ parser.getAttributeValue(null, ATTR_NAME));
} else if (TAG_APPS_SUSPENDED.equals(tag)) {
policy.mAppsSuspended =
Boolean.parseBoolean(parser.getAttributeValue(null, ATTR_VALUE));
@@ -3804,7 +3806,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
updateMaximumTimeToLockLocked(userHandle);
updateLockTaskPackagesLocked(policy.mLockTaskPackages, userHandle);
updateLockTaskFeaturesLocked(policy.mLockTaskFeatures, userHandle);
- updateProtectedPackagesLocked(policy.mProtectedPackages);
+ updateUserControlDisabledPackagesLocked(policy.mUserControlDisabledPackages);
if (policy.mStatusBarDisabled) {
setStatusBarDisabledInternal(policy.mStatusBarDisabled, userHandle);
}
@@ -3830,7 +3832,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
- private void updateProtectedPackagesLocked(List<String> packages) {
+ private void updateUserControlDisabledPackagesLocked(List<String> packages) {
mInjector.getPackageManagerInternal().setDeviceOwnerProtectedPackages(packages);
}
@@ -8830,8 +8832,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
policy.mLockTaskPackages.clear();
updateLockTaskPackagesLocked(policy.mLockTaskPackages, userId);
policy.mLockTaskFeatures = DevicePolicyManager.LOCK_TASK_FEATURE_NONE;
- policy.mProtectedPackages.clear();
- updateProtectedPackagesLocked(policy.mProtectedPackages);
+ policy.mUserControlDisabledPackages.clear();
+ updateUserControlDisabledPackagesLocked(policy.mUserControlDisabledPackages);
saveSettingsLocked(userId);
try {
@@ -9584,7 +9586,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
pw.println();
pw.increaseIndent();
pw.print("mPasswordOwner="); pw.println(policy.mPasswordOwner);
- pw.print("mProtectedPackages="); pw.println(policy.mProtectedPackages);
+ pw.print("mUserControlDisabledPackages=");
+ pw.println(policy.mUserControlDisabledPackages);
pw.print("mAppsSuspended="); pw.println(policy.mAppsSuspended);
pw.decreaseIndent();
}
@@ -15559,39 +15562,39 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
@Override
- public void setProtectedPackages(ComponentName who, List<String> packages) {
+ public void setUserControlDisabledPackages(ComponentName who, List<String> packages) {
Preconditions.checkNotNull(who, "ComponentName is null");
Preconditions.checkNotNull(packages, "packages is null");
enforceDeviceOwner(who);
synchronized (getLockObject()) {
final int userHandle = mInjector.userHandleGetCallingUserId();
- setProtectedPackagesLocked(userHandle, packages);
+ setUserControlDisabledPackagesLocked(userHandle, packages);
DevicePolicyEventLogger
- .createEvent(DevicePolicyEnums.SET_PACKAGES_PROTECTED)
+ .createEvent(DevicePolicyEnums.SET_USER_CONTROL_DISABLED_PACKAGES)
.setAdmin(who)
.setStrings(packages.toArray(new String[packages.size()]))
.write();
}
}
- private void setProtectedPackagesLocked(int userHandle, List<String> packages) {
+ private void setUserControlDisabledPackagesLocked(int userHandle, List<String> packages) {
final DevicePolicyData policy = getUserData(userHandle);
- policy.mProtectedPackages = packages;
+ policy.mUserControlDisabledPackages = packages;
// Store the settings persistently.
saveSettingsLocked(userHandle);
- updateProtectedPackagesLocked(packages);
+ updateUserControlDisabledPackagesLocked(packages);
}
@Override
- public List<String> getProtectedPackages(ComponentName who) {
+ public List<String> getUserControlDisabledPackages(ComponentName who) {
Preconditions.checkNotNull(who, "ComponentName is null");
enforceDeviceOwner(who);
final int userHandle = mInjector.binderGetCallingUserHandle().getIdentifier();
synchronized (getLockObject()) {
- final List<String> packages = getUserData(userHandle).mProtectedPackages;
+ final List<String> packages = getUserData(userHandle).mUserControlDisabledPackages;
return packages == null ? Collections.EMPTY_LIST : packages;
}
}
@@ -15847,6 +15850,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// DO shouldn't be able to use this method.
enforceProfileOwnerOfOrganizationOwnedDevice(admin);
enforceHandlesCheckPolicyComplianceIntent(userId, admin.info.getPackageName());
+ Preconditions.checkArgument(timeoutMillis >= 0, "Timeout must be non-negative.");
+ // Ensure the timeout is long enough to avoid having bad user experience.
+ if (timeoutMillis > 0 && timeoutMillis < MANAGED_PROFILE_MAXIMUM_TIME_OFF_THRESHOLD
+ && !isAdminTestOnlyLocked(who, userId)) {
+ timeoutMillis = MANAGED_PROFILE_MAXIMUM_TIME_OFF_THRESHOLD;
+ }
if (admin.mProfileMaximumTimeOffMillis == timeoutMillis) {
return;
}
diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp
index 3fcb57a83cf5..2dbbc5ac6806 100644
--- a/services/incremental/BinderIncrementalService.cpp
+++ b/services/incremental/BinderIncrementalService.cpp
@@ -178,15 +178,9 @@ static std::tuple<int, incfs::FileId, incfs::NewFileParams> toMakeFileParams(
nfp.size = params.size;
nfp.metadata = {(const char*)params.metadata.data(), (IncFsSize)params.metadata.size()};
if (!params.signature) {
- nfp.verification = {};
+ nfp.signature = {};
} else {
- nfp.verification.hashAlgorithm = IncFsHashAlgortithm(params.signature->hashAlgorithm);
- nfp.verification.rootHash = {(const char*)params.signature->rootHash.data(),
- (IncFsSize)params.signature->rootHash.size()};
- nfp.verification.additionalData = {(const char*)params.signature->additionalData.data(),
- (IncFsSize)params.signature->additionalData.size()};
- nfp.verification.signature = {(const char*)params.signature->signature.data(),
- (IncFsSize)params.signature->signature.size()};
+ nfp.signature = {(const char*)params.signature->data(), (IncFsSize)params.signature->size()};
}
return {0, id, nfp};
}
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 44a2010dd207..4da0091af0db 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -1157,7 +1157,7 @@ bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_
// Create new lib file without signature info
incfs::NewFileParams libFileParams{};
libFileParams.size = uncompressedLen;
- libFileParams.verification.hashAlgorithm = INCFS_HASH_NONE;
+ libFileParams.signature = {};
// Metadata of the new lib file is its relative path
IncFsSpan libFileMetadata;
libFileMetadata.data = targetLibPath.c_str();
diff --git a/services/robotests/Android.bp b/services/robotests/Android.bp
index 602e4e1ac8ce..25ab5d36169e 100644
--- a/services/robotests/Android.bp
+++ b/services/robotests/Android.bp
@@ -27,7 +27,7 @@ android_app {
"services.net",
],
- libs: ["ike-stubs"],
+ libs: ["android.net.ipsec.ike.stubs.system"],
}
//##################################################################
diff --git a/services/robotests/backup/Android.bp b/services/robotests/backup/Android.bp
index 5160eae060f6..32587accf160 100644
--- a/services/robotests/backup/Android.bp
+++ b/services/robotests/backup/Android.bp
@@ -29,7 +29,7 @@ android_app {
"services.net",
],
- libs: ["ike-stubs"],
+ libs: ["android.net.ipsec.ike.stubs.system"],
}
//##################################################################
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
index c45ee7b4d802..efe8119a62a5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
@@ -127,11 +127,14 @@ public class ApplicationExitInfoTest {
mAms.mAtmInternal = spy(mAms.mActivityTaskManager.getAtmInternal());
mAms.mPackageManagerInt = mPackageManagerInt;
doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent();
+ // Remove stale instance of PackageManagerInternal if there is any
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt);
}
@After
public void tearDown() {
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
mHandlerThread.quit();
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 053a7984b1a3..7a175ca1b7f5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -94,6 +94,7 @@ import com.android.server.wm.ActivityServiceConnectionsHolder;
import com.android.server.wm.ActivityTaskManagerService;
import com.android.server.wm.WindowProcessController;
+import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -141,6 +142,8 @@ public class MockingOomAdjusterTests {
sPackageManagerInternal = mock(PackageManagerInternal.class);
doReturn(new ComponentName("", "")).when(sPackageManagerInternal)
.getSystemUiServiceComponent();
+ // Remove stale instance of PackageManagerInternal if there is any
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.addService(PackageManagerInternal.class, sPackageManagerInternal);
sService = mock(ActivityManagerService.class);
@@ -172,6 +175,11 @@ public class MockingOomAdjusterTests {
sService.mOomAdjuster.mAdjSeq = 10000;
}
+ @AfterClass
+ public static void tearDownOnce() {
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ }
+
private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) {
try {
Field field = clazz.getDeclaredField(fieldName);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 7571f0916974..d038d6c1ca7f 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -5843,7 +5843,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
assertTrue(dpm.isPackageAllowedToAccessCalendar(testPackage));
}
- public void testSetProtectedPackages_asDO() throws Exception {
+ public void testSetUserControlDisabledPackages_asDO() throws Exception {
final List<String> testPackages = new ArrayList<>();
testPackages.add("package_1");
testPackages.add("package_2");
@@ -5851,14 +5851,14 @@ public class DevicePolicyManagerTest extends DpmTestBase {
mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
setDeviceOwner();
- dpm.setProtectedPackages(admin1, testPackages);
+ dpm.setUserControlDisabledPackages(admin1, testPackages);
verify(getServices().packageManagerInternal).setDeviceOwnerProtectedPackages(testPackages);
- assertEquals(testPackages, dpm.getProtectedPackages(admin1));
+ assertEquals(testPackages, dpm.getUserControlDisabledPackages(admin1));
}
- public void testSetProtectedPackages_failingAsPO() throws Exception {
+ public void testSetUserControlDisabledPackages_failingAsPO() throws Exception {
final List<String> testPackages = new ArrayList<>();
testPackages.add("package_1");
testPackages.add("package_2");
@@ -5867,10 +5867,10 @@ public class DevicePolicyManagerTest extends DpmTestBase {
setAsProfileOwner(admin1);
assertExpectException(SecurityException.class, /* messageRegex= */ null,
- () -> dpm.setProtectedPackages(admin1, testPackages));
+ () -> dpm.setUserControlDisabledPackages(admin1, testPackages));
assertExpectException(SecurityException.class, /* messageRegex= */ null,
- () -> dpm.getProtectedPackages(admin1));
+ () -> dpm.getUserControlDisabledPackages(admin1));
}
private void configureProfileOwnerOfOrgOwnedDevice(ComponentName who, int userId) {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 0551f2ec1968..a587029914a7 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -538,6 +538,7 @@ public class HdmiCecLocalDeviceAudioSystemTest {
}
@Test
+ @Ignore("b/151150320")
public void handleSystemAudioModeRequest_fromNonTV_tVNotSupport() {
HdmiCecMessage message =
HdmiCecMessageBuilder.buildSystemAudioModeRequest(
@@ -587,6 +588,7 @@ public class HdmiCecLocalDeviceAudioSystemTest {
}
@Test
+ @Ignore("b/151150320")
public void handleRoutingChange_currentActivePortIsHome() {
HdmiCecMessage message =
HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x3000, SELF_PHYSICAL_ADDRESS);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index 4a1af51deab6..f72d622c00c3 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -139,6 +139,7 @@ public class HdmiCecLocalDevicePlaybackTest {
}
@Test
+ @Ignore("b/151147315")
public void doNotWakeUpOnHotPlug_PlugIn() {
mWokenUp = false;
mHdmiCecLocalDevicePlayback.onHotplug(0, true);
@@ -146,6 +147,7 @@ public class HdmiCecLocalDevicePlaybackTest {
}
@Test
+ @Ignore("b/151147315")
public void doNotWakeUpOnHotPlug_PlugOut() {
mWokenUp = false;
mHdmiCecLocalDevicePlayback.onHotplug(0, false);
diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
index 81545d4c74ba..d3a3e7e32ece 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
@@ -480,6 +480,13 @@ public class AppIntegrityManagerServiceImplTest {
assertThat(mService.getCurrentRules().getList()).containsExactly(rule);
}
+ @Test
+ public void getWhitelistedRuleProviders() throws Exception {
+ whitelistUsAsRuleProvider();
+
+ assertThat(mService.getWhitelistedRuleProviders()).containsExactly(TEST_FRAMEWORK_PACKAGE);
+ }
+
private void whitelistUsAsRuleProvider() {
Resources mockResources = mock(Resources.class);
when(mockResources.getStringArray(R.array.config_integrityRuleProviderPackages))
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/FakeSettings.java b/services/tests/servicestests/src/com/android/server/locksettings/FakeSettings.java
index 70a927c216ec..c5e924be2612 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/FakeSettings.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/FakeSettings.java
@@ -15,16 +15,23 @@
*/
package com.android.server.locksettings;
+import android.content.ContentResolver;
+import android.os.UserHandle;
import android.provider.Settings;
public class FakeSettings {
private int mDeviceProvisioned;
+ private int mSecureFrpMode;
public void setDeviceProvisioned(boolean provisioned) {
mDeviceProvisioned = provisioned ? 1 : 0;
}
+ public void setSecureFrpMode(boolean secure) {
+ mSecureFrpMode = secure ? 1 : 0;
+ }
+
public int globalGetInt(String keyName) {
switch (keyName) {
case Settings.Global.DEVICE_PROVISIONED:
@@ -33,4 +40,12 @@ public class FakeSettings {
throw new IllegalArgumentException("Unhandled global settings: " + keyName);
}
}
+
+ public int secureGetInt(ContentResolver contentResolver, String keyName, int defaultValue,
+ int userId) {
+ if (Settings.Secure.SECURE_FRP_MODE.equals(keyName) && userId == UserHandle.USER_SYSTEM) {
+ return mSecureFrpMode;
+ }
+ return defaultValue;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
index 1ff451b50c42..4e1454bd0962 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
@@ -124,6 +124,12 @@ public class LockSettingsServiceTestable extends LockSettingsService {
}
@Override
+ public int settingsSecureGetInt(ContentResolver contentResolver, String keyName,
+ int defaultValue, int userId) {
+ return mSettings.secureGetInt(contentResolver, keyName, defaultValue, userId);
+ }
+
+ @Override
public UserManagerInternal getUserManagerInternal() {
return mUserManagerInternal;
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
index 684bbd4fc8eb..661ce113e81e 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
@@ -416,6 +416,15 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
eq(CREDENTIAL_TYPE_PASSWORD), any(), eq(MANAGED_PROFILE_USER_ID));
}
+ @Test
+ public void testCredentialChangeNotPossibleInSecureFrpMode() {
+ mSettings.setSecureFrpMode(true);
+ try {
+ mService.setLockCredential(newPassword("1234"), nonePassword(), PRIMARY_USER_ID);
+ fail("Password shouldn't be changeable before FRP unlock");
+ } catch (SecurityException e) { }
+ }
+
private void testCreateCredential(int userId, LockscreenCredential credential)
throws RemoteException {
assertTrue(mService.setLockCredential(credential, nonePassword(), userId));
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 7e3cfc8fb2a5..128177b073b0 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -1084,7 +1084,7 @@ public class NetworkPolicyManagerServiceTest {
// pretend that 512 bytes total have happened
stats = new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 256L, 2L, 256L, 2L);
+ .insertEntry(TEST_IFACE, 256L, 2L, 256L, 2L);
when(mStatsService.getNetworkTotalBytes(sTemplateWifi, CYCLE_START, CYCLE_END))
.thenReturn(stats.getTotalBytes());
@@ -1276,11 +1276,11 @@ public class NetworkPolicyManagerServiceTest {
history.recordData(start, end,
new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1440), 0L, 0L, 0L, 0));
stats.clear();
- stats.addEntry(IFACE_ALL, UID_A, SET_ALL, TAG_ALL,
+ stats.insertEntry(IFACE_ALL, UID_A, SET_ALL, TAG_ALL,
DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
- stats.addEntry(IFACE_ALL, UID_B, SET_ALL, TAG_ALL,
+ stats.insertEntry(IFACE_ALL, UID_B, SET_ALL, TAG_ALL,
DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
- stats.addEntry(IFACE_ALL, UID_C, SET_ALL, TAG_ALL,
+ stats.insertEntry(IFACE_ALL, UID_C, SET_ALL, TAG_ALL,
DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
reset(mNotifManager);
@@ -1304,9 +1304,9 @@ public class NetworkPolicyManagerServiceTest {
history.recordData(start, end,
new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1440), 0L, 0L, 0L, 0));
stats.clear();
- stats.addEntry(IFACE_ALL, UID_A, SET_ALL, TAG_ALL,
+ stats.insertEntry(IFACE_ALL, UID_A, SET_ALL, TAG_ALL,
DataUnit.MEGABYTES.toBytes(960), 0, 0, 0, 0);
- stats.addEntry(IFACE_ALL, UID_B, SET_ALL, TAG_ALL,
+ stats.insertEntry(IFACE_ALL, UID_B, SET_ALL, TAG_ALL,
DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
reset(mNotifManager);
@@ -1338,7 +1338,7 @@ public class NetworkPolicyManagerServiceTest {
// bring up wifi network with metered policy
state = new NetworkState[] { buildWifi() };
stats = new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 0L, 0L, 0L, 0L);
+ .insertEntry(TEST_IFACE, 0L, 0L, 0L, 0L);
{
when(mConnManager.getAllNetworkState()).thenReturn(state);
@@ -1769,7 +1769,7 @@ public class NetworkPolicyManagerServiceTest {
final int CYCLE_DAY = 15;
final NetworkStats stats = new NetworkStats(0L, 1);
- stats.addEntry(TEST_IFACE, UID_A, SET_ALL, TAG_NONE,
+ stats.insertEntry(TEST_IFACE, UID_A, SET_ALL, TAG_NONE,
2999, 1, 2000, 1, 0);
when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
.thenReturn(stats.getTotalBytes());
@@ -1793,7 +1793,7 @@ public class NetworkPolicyManagerServiceTest {
reset(mStatsService);
// Increase the usage.
- stats.addEntry(TEST_IFACE, UID_A, SET_ALL, TAG_NONE,
+ stats.insertEntry(TEST_IFACE, UID_A, SET_ALL, TAG_NONE,
1000, 1, 999, 1, 0);
when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
.thenReturn(stats.getTotalBytes());
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index 5d5c71471aec..22591c6e10a7 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -31,6 +31,7 @@ import android.content.pm.PackageParser;
import android.content.pm.Signature;
import android.content.pm.parsing.ParsingPackage;
import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedInstrumentation;
import android.content.pm.parsing.component.ParsedIntentInfo;
import android.content.pm.parsing.component.ParsedProvider;
import android.os.Build;
@@ -135,6 +136,13 @@ public class AppsFilterTest {
.addActivity(activity);
}
+ private static ParsingPackage pkgWithInstrumentation(
+ String packageName, String instrumentationTargetPackage) {
+ ParsedInstrumentation instrumentation = new ParsedInstrumentation();
+ instrumentation.setTargetPackage(instrumentationTargetPackage);
+ return pkg(packageName).addInstrumentation(instrumentation);
+ }
+
private static ParsingPackage pkgWithProvider(String packageName, String authority) {
ParsedProvider provider = new ParsedProvider();
provider.setPackageName(packageName);
@@ -608,6 +616,25 @@ public class AppsFilterTest {
assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
}
+ @Test
+ public void testInstrumentation_DoesntFilter() {
+ final AppsFilter appsFilter =
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+ appsFilter.onSystemReady();
+
+
+ PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"),
+ DUMMY_TARGET_UID);
+ PackageSetting instrumentation = simulateAddPackage(appsFilter,
+ pkgWithInstrumentation("com.some.other.package", "com.some.package"),
+ DUMMY_CALLING_UID);
+
+ assertFalse(
+ appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, instrumentation, target, 0));
+ assertFalse(
+ appsFilter.shouldFilterApplication(DUMMY_TARGET_UID, target, instrumentation, 0));
+ }
+
private interface WithSettingBuilder {
PackageSettingBuilder withBuilder(PackageSettingBuilder builder);
}
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 6c769485f6ad..6a882985d305 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -94,6 +94,8 @@ import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.pm.LauncherAppsService.LauncherAppsImpl;
import com.android.server.pm.ShortcutUser.PackageWithUser;
+import com.android.server.uri.UriGrantsManagerInternal;
+import com.android.server.uri.UriPermissionOwner;
import com.android.server.wm.ActivityTaskManagerInternal;
import org.junit.Assert;
@@ -630,6 +632,9 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
protected UsageStatsManagerInternal mMockUsageStatsManagerInternal;
protected ActivityManagerInternal mMockActivityManagerInternal;
protected ActivityTaskManagerInternal mMockActivityTaskManagerInternal;
+ protected UriGrantsManagerInternal mMockUriGrantsManagerInternal;
+
+ protected UriPermissionOwner mUriPermissionOwner;
protected static final String CALLING_PACKAGE_1 = "com.android.test.1";
protected static final int CALLING_UID_1 = 10001;
@@ -771,6 +776,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
mMockUsageStatsManagerInternal = mock(UsageStatsManagerInternal.class);
mMockActivityManagerInternal = mock(ActivityManagerInternal.class);
mMockActivityTaskManagerInternal = mock(ActivityTaskManagerInternal.class);
+ mMockUriGrantsManagerInternal = mock(UriGrantsManagerInternal.class);
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.addService(PackageManagerInternal.class, mMockPackageManagerInternal);
@@ -782,6 +788,10 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
LocalServices.addService(ActivityTaskManagerInternal.class, mMockActivityTaskManagerInternal);
LocalServices.removeServiceForTest(UserManagerInternal.class);
LocalServices.addService(UserManagerInternal.class, mMockUserManagerInternal);
+ LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
+ LocalServices.addService(UriGrantsManagerInternal.class, mMockUriGrantsManagerInternal);
+
+ mUriPermissionOwner = new UriPermissionOwner(mMockUriGrantsManagerInternal, TAG);
// Prepare injection values.
@@ -2193,6 +2203,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
for (ShortcutInfo s : actualShortcuts) {
assertTrue("ID " + s.getId() + " not have icon res ID", s.hasIconResource());
assertFalse("ID " + s.getId() + " shouldn't have icon FD", s.hasIconFile());
+ assertFalse("ID " + s.getId() + " shouldn't have icon URI", s.hasIconUri());
}
return actualShortcuts;
}
@@ -2202,6 +2213,17 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
for (ShortcutInfo s : actualShortcuts) {
assertFalse("ID " + s.getId() + " shouldn't have icon res ID", s.hasIconResource());
assertTrue("ID " + s.getId() + " not have icon FD", s.hasIconFile());
+ assertFalse("ID " + s.getId() + " shouldn't have icon URI", s.hasIconUri());
+ }
+ return actualShortcuts;
+ }
+
+ public static List<ShortcutInfo> assertAllHaveIconUri(
+ List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertFalse("ID " + s.getId() + " shouldn't have icon res ID", s.hasIconResource());
+ assertFalse("ID " + s.getId() + " shouldn't have have icon FD", s.hasIconFile());
+ assertTrue("ID " + s.getId() + " not have icon URI", s.hasIconUri());
}
return actualShortcuts;
}
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 56460fb6f0a0..2cbb6d5c5bd6 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -114,8 +114,11 @@ import org.xmlpull.v1.XmlSerializer;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.io.InputStreamReader;
+import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
@@ -133,6 +136,16 @@ import java.util.function.BiConsumer;
*/
@SmallTest
public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
+
+ @Override
+ protected void tearDown() throws Exception {
+ deleteUriFile("file32x32.jpg");
+ deleteUriFile("file64x64.jpg");
+ deleteUriFile("file512x512.jpg");
+
+ super.tearDown();
+ }
+
/**
* Test for the first launch path, no settings file available.
*/
@@ -724,6 +737,17 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
final Icon bmp512x512 = Icon.createWithBitmap(BitmapFactory.decodeResource(
getTestContext().getResources(), R.drawable.black_512x512));
+ // The corresponding files will be deleted in tearDown()
+ final Icon uri32x32 = Icon.createWithContentUri(
+ getFileUriFromResource("file32x32.jpg", R.drawable.black_32x32));
+ final Icon uri64x64_maskable = Icon.createWithAdaptiveBitmapContentUri(
+ getFileUriFromResource("file64x64.jpg", R.drawable.black_64x64));
+ final Icon uri512x512 = Icon.createWithContentUri(
+ getFileUriFromResource("file512x512.jpg", R.drawable.black_512x512));
+
+ doReturn(mUriPermissionOwner.getExternalToken())
+ .when(mMockUriGrantsManagerInternal).newUriPermissionOwner(anyString());
+
// Set from package 1
setCaller(CALLING_PACKAGE_1);
assertTrue(mManager.setDynamicShortcuts(list(
@@ -732,6 +756,9 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
makeShortcutWithIcon("bmp32x32", bmp32x32),
makeShortcutWithIcon("bmp64x64", bmp64x64_maskable),
makeShortcutWithIcon("bmp512x512", bmp512x512),
+ makeShortcutWithIcon("uri32x32", uri32x32),
+ makeShortcutWithIcon("uri64x64", uri64x64_maskable),
+ makeShortcutWithIcon("uri512x512", uri512x512),
makeShortcut("none")
)));
@@ -742,6 +769,9 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
"bmp32x32",
"bmp64x64",
"bmp512x512",
+ "uri32x32",
+ "uri64x64",
+ "uri512x512",
"none");
// Call from another caller with the same ID, just to make sure storage is per-package.
@@ -749,11 +779,15 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertTrue(mManager.setDynamicShortcuts(list(
makeShortcutWithIcon("res32x32", res512x512),
makeShortcutWithIcon("res64x64", res512x512),
+ makeShortcutWithIcon("uri32x32", uri512x512),
+ makeShortcutWithIcon("uri64x64", uri512x512),
makeShortcutWithIcon("none", res512x512)
)));
assertShortcutIds(assertAllNotHaveIcon(mManager.getDynamicShortcuts()),
"res32x32",
"res64x64",
+ "uri32x32",
+ "uri64x64",
"none");
// Different profile. Note the names and the contents don't match.
@@ -795,6 +829,18 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "bmp512x512", USER_0))),
"bmp512x512");
+ assertShortcutIds(assertAllHaveIconUri(
+ list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "uri32x32", USER_0))),
+ "uri32x32");
+
+ assertShortcutIds(assertAllHaveIconUri(
+ list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "uri64x64", USER_0))),
+ "uri64x64");
+
+ assertShortcutIds(assertAllHaveIconUri(
+ list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "uri512x512", USER_0))),
+ "uri512x512");
+
assertShortcutIds(assertAllHaveIconResId(
list(getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "res32x32", USER_P0))),
"res32x32");
@@ -860,15 +906,37 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
bmp = pfdToBitmap(
mLauncherApps.getShortcutIconFd(CALLING_PACKAGE_1, "bmp32x32", HANDLE_USER_P0));
assertBitmapSize(128, 128, bmp);
+/*
+ bmp = pfdToBitmap(mLauncherApps.getUriShortcutIconFd(
+ getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "uri32x32", USER_0)));
+ assertBitmapSize(32, 32, bmp);
+
+ bmp = pfdToBitmap(mLauncherApps.getUriShortcutIconFd(
+ getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "uri64x64", USER_0)));
+ assertBitmapSize(64, 64, bmp);
+
+ bmp = pfdToBitmap(mLauncherApps.getUriShortcutIconFd(
+ getShortcutInfoAsLauncher(CALLING_PACKAGE_1, "uri512x512", USER_0)));
+ assertBitmapSize(512, 512, bmp);
+*/
- Drawable dr = mLauncherApps.getShortcutIconDrawable(
+ Drawable dr_bmp = mLauncherApps.getShortcutIconDrawable(
makeShortcutWithIcon("bmp64x64", bmp64x64_maskable), 0);
- assertTrue(dr instanceof AdaptiveIconDrawable);
+ assertTrue(dr_bmp instanceof AdaptiveIconDrawable);
float viewportPercentage = 1 / (1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction());
assertEquals((int) (bmp64x64_maskable.getBitmap().getWidth() * viewportPercentage),
- dr.getIntrinsicWidth());
+ dr_bmp.getIntrinsicWidth());
+ assertEquals((int) (bmp64x64_maskable.getBitmap().getHeight() * viewportPercentage),
+ dr_bmp.getIntrinsicHeight());
+/*
+ Drawable dr_uri = mLauncherApps.getShortcutIconDrawable(
+ makeShortcutWithIcon("uri64x64", uri64x64_maskable), 0);
+ assertTrue(dr_uri instanceof AdaptiveIconDrawable);
+ assertEquals((int) (bmp64x64_maskable.getBitmap().getWidth() * viewportPercentage),
+ dr_uri.getIntrinsicWidth());
assertEquals((int) (bmp64x64_maskable.getBitmap().getHeight() * viewportPercentage),
- dr.getIntrinsicHeight());
+ dr_uri.getIntrinsicHeight());
+*/
}
public void testCleanupDanglingBitmaps() throws Exception {
@@ -1274,6 +1342,18 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
makeShortcut("s1")
)));
+ // Set uri icon
+ assertTrue(mManager.updateShortcuts(list(
+ new ShortcutInfo.Builder(mClientContext, "s1")
+ .setIcon(Icon.createWithContentUri("test_uri"))
+ .build()
+ )));
+ mService.waitForBitmapSavesForTest();
+ assertWith(getCallerShortcuts())
+ .forShortcutWithId("s1", si -> {
+ assertTrue(si.hasIconUri());
+ assertEquals("test_uri", si.getIconUri());
+ });
// Set resource icon
assertTrue(mManager.updateShortcuts(list(
new ShortcutInfo.Builder(mClientContext, "s1")
@@ -1287,6 +1367,9 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertEquals(R.drawable.black_32x32, si.getIconResourceId());
});
mService.waitForBitmapSavesForTest();
+
+ mInjectedCurrentTimeMillis += INTERVAL; // reset throttling
+
// Set bitmap icon
assertTrue(mManager.updateShortcuts(list(
new ShortcutInfo.Builder(mClientContext, "s1")
@@ -1300,9 +1383,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertTrue(si.hasIconFile());
});
- mInjectedCurrentTimeMillis += INTERVAL; // reset throttling
-
- // Do it again, with the reverse order (bitmap -> icon)
+ // Do it again, with the reverse order (bitmap -> resource -> uri)
assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s1")
)));
@@ -1320,6 +1401,8 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertTrue(si.hasIconFile());
});
+ mInjectedCurrentTimeMillis += INTERVAL; // reset throttling
+
// Set resource icon
assertTrue(mManager.updateShortcuts(list(
new ShortcutInfo.Builder(mClientContext, "s1")
@@ -1332,6 +1415,18 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertTrue(si.hasIconResource());
assertEquals(R.drawable.black_32x32, si.getIconResourceId());
});
+ // Set uri icon
+ assertTrue(mManager.updateShortcuts(list(
+ new ShortcutInfo.Builder(mClientContext, "s1")
+ .setIcon(Icon.createWithContentUri("test_uri"))
+ .build()
+ )));
+ mService.waitForBitmapSavesForTest();
+ assertWith(getCallerShortcuts())
+ .forShortcutWithId("s1", si -> {
+ assertTrue(si.hasIconUri());
+ assertEquals("test_uri", si.getIconUri());
+ });
});
}
@@ -8499,4 +8594,26 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
}
}
}
+
+ 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.
+ assertFalse(file.exists());
+ try (InputStream source = getTestContext().getResources().openRawResource(resId);
+ OutputStream target = new FileOutputStream(file)) {
+ byte[] buffer = new byte[1024];
+ for (int len = source.read(buffer); len >= 0; len = source.read(buffer)) {
+ target.write(buffer, 0, len);
+ }
+ }
+ assertTrue(file.exists());
+ return Uri.fromFile(file);
+ }
+
+ private void deleteUriFile(String fileName) {
+ File file = new File(getTestContext().getFilesDir(), fileName);
+ if (file.exists()) {
+ file.delete();
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
index 7b101c728d0f..ca7704950b51 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
@@ -257,6 +257,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
si.addFlags(ShortcutInfo.FLAG_PINNED);
si.setBitmapPath("abc");
si.setIconResourceId(456);
+ si.setIconUri("test_uri");
si = parceled(si);
@@ -279,6 +280,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_LONG_LIVED, si.getFlags());
assertEquals("abc", si.getBitmapPath());
assertEquals(456, si.getIconResourceId());
+ assertEquals("test_uri", si.getIconUri());
assertEquals(0, si.getTitleResId());
assertEquals(null, si.getTitleResName());
@@ -310,6 +312,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
si.addFlags(ShortcutInfo.FLAG_PINNED);
si.setBitmapPath("abc");
si.setIconResourceId(456);
+ si.setIconUri("test_uri");
lookupAndFillInResourceNames(si);
@@ -335,6 +338,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertEquals("abc", si.getBitmapPath());
assertEquals(456, si.getIconResourceId());
assertEquals("string/r456", si.getIconResName());
+ assertEquals("test_uri", si.getIconUri());
}
public void testShortcutInfoClone() {
@@ -359,6 +363,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
sorig.addFlags(ShortcutInfo.FLAG_PINNED);
sorig.setBitmapPath("abc");
sorig.setIconResourceId(456);
+ sorig.setIconUri("test_uri");
lookupAndFillInResourceNames(sorig);
@@ -386,6 +391,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertEquals("abc", si.getBitmapPath());
assertEquals(456, si.getIconResourceId());
assertEquals("string/r456", si.getIconResName());
+ assertEquals("test_uri", si.getIconUri());
si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR);
@@ -407,6 +413,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_LONG_LIVED, si.getFlags());
assertEquals(null, si.getBitmapPath());
+ assertNull(si.getIconUri());
assertEquals(456, si.getIconResourceId());
assertEquals(null, si.getIconResName());
@@ -428,6 +435,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_LONG_LIVED, si.getFlags());
assertEquals(null, si.getBitmapPath());
+ assertNull(si.getIconUri());
assertEquals(456, si.getIconResourceId());
assertEquals(null, si.getIconResName());
@@ -450,6 +458,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_KEY_FIELDS_ONLY
| ShortcutInfo.FLAG_LONG_LIVED, si.getFlags());
assertEquals(null, si.getBitmapPath());
+ assertNull(si.getIconUri());
assertEquals(456, si.getIconResourceId());
assertEquals(null, si.getIconResName());
@@ -474,6 +483,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_LONG_LIVED, si.getFlags());
assertEquals(null, si.getBitmapPath());
+ assertNull(si.getIconUri());
assertEquals(456, si.getIconResourceId());
assertEquals(null, si.getIconResName());
@@ -499,6 +509,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
sorig.addFlags(ShortcutInfo.FLAG_PINNED);
sorig.setBitmapPath("abc");
sorig.setIconResourceId(456);
+ sorig.setIconUri("test_uri");
lookupAndFillInResourceNames(sorig);
@@ -526,6 +537,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertEquals("abc", si.getBitmapPath());
assertEquals(456, si.getIconResourceId());
assertEquals("string/r456", si.getIconResName());
+ assertEquals("test_uri", si.getIconUri());
si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR);
@@ -547,6 +559,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
assertEquals(null, si.getBitmapPath());
+ assertNull(si.getIconUri());
assertEquals(456, si.getIconResourceId());
assertEquals(null, si.getIconResName());
@@ -570,6 +583,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
assertEquals(null, si.getBitmapPath());
+ assertNull(si.getIconUri());
assertEquals(456, si.getIconResourceId());
assertEquals(null, si.getIconResName());
@@ -593,6 +607,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_KEY_FIELDS_ONLY, si.getFlags());
assertEquals(null, si.getBitmapPath());
+ assertNull(si.getIconUri());
assertEquals(456, si.getIconResourceId());
assertEquals(null, si.getIconResName());
@@ -657,6 +672,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
sorig.addFlags(ShortcutInfo.FLAG_PINNED);
sorig.setBitmapPath("abc");
sorig.setIconResourceId(456);
+ sorig.setIconUri("test_uri");
lookupAndFillInResourceNames(sorig);
@@ -677,6 +693,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertEquals(0, si.getIconResourceId());
assertEquals(null, si.getIconResName());
assertEquals(null, si.getBitmapPath());
+ assertNull(si.getIconUri());
si = sorig.clone(/* flags=*/ 0);
si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
@@ -787,6 +804,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
sorig.addFlags(ShortcutInfo.FLAG_PINNED);
sorig.setBitmapPath("abc");
sorig.setIconResourceId(456);
+ sorig.setIconUri("test_uri");
ShortcutInfo si;
@@ -804,6 +822,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertEquals(0, si.getIconResourceId());
assertEquals(null, si.getIconResName());
assertEquals(null, si.getBitmapPath());
+ assertNull(si.getIconUri());
si = sorig.clone(/* flags=*/ 0);
si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
@@ -977,6 +996,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
| ShortcutInfo.FLAG_STRINGS_RESOLVED, si.getFlags());
assertNotNull(si.getBitmapPath()); // Something should be set.
assertEquals(0, si.getIconResourceId());
+ assertNull(si.getIconUri());
assertTrue(si.getLastChangedTimestamp() < now);
// Make sure ranks are saved too. Because of the auto-adjusting, we need two shortcuts
@@ -1053,6 +1073,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
si.getFlags());
assertNotNull(si.getBitmapPath()); // Something should be set.
assertEquals(0, si.getIconResourceId());
+ assertNull(si.getIconUri());
assertTrue(si.getLastChangedTimestamp() < now);
dumpUserFile(USER_10);
@@ -1125,6 +1146,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertEquals(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_HAS_ICON_RES
| ShortcutInfo.FLAG_STRINGS_RESOLVED, si.getFlags());
assertNull(si.getBitmapPath());
+ assertNull(si.getIconUri());
assertEquals(R.drawable.black_32x32, si.getIconResourceId());
assertTrue(si.getLastChangedTimestamp() < now);
@@ -1134,6 +1156,94 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertEquals(1, si.getRank());
}
+ public void testShortcutInfoSaveAndLoad_uri() throws InterruptedException {
+ mRunningUsers.put(USER_10, true);
+
+ setCaller(CALLING_PACKAGE_1, USER_10);
+
+ final Icon uriIcon = Icon.createWithContentUri("test_uri");
+
+ PersistableBundle pb = new PersistableBundle();
+ pb.putInt("k", 1);
+ ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext)
+ .setId("id")
+ .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class))
+ .setIcon(uriIcon)
+ .setTitleResId(10)
+ .setTextResId(11)
+ .setDisabledMessageResId(12)
+ .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"))
+ .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+ .setRank(123)
+ .setExtras(pb)
+ .build();
+ sorig.setTimestamp(mInjectedCurrentTimeMillis);
+
+ final Icon uriMaskableIcon = Icon.createWithAdaptiveBitmapContentUri("uri_maskable");
+
+ ShortcutInfo sorig2 = new ShortcutInfo.Builder(mClientContext)
+ .setId("id2")
+ .setTitle("x")
+ .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class))
+ .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+ .setRank(456)
+ .setIcon(uriMaskableIcon)
+ .build();
+ sorig2.setTimestamp(mInjectedCurrentTimeMillis);
+
+ mManager.addDynamicShortcuts(list(sorig, sorig2));
+
+ mInjectedCurrentTimeMillis += 1;
+ final long now = mInjectedCurrentTimeMillis;
+ mInjectedCurrentTimeMillis += 1;
+
+ // Save and load.
+ mService.saveDirtyInfo();
+ initService();
+ mService.handleUnlockUser(USER_10);
+
+ ShortcutInfo si;
+ si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id", USER_10);
+
+ assertEquals(USER_10, si.getUserId());
+ assertEquals(HANDLE_USER_10, si.getUserHandle());
+ assertEquals(CALLING_PACKAGE_1, si.getPackage());
+ assertEquals("id", si.getId());
+ assertEquals(ShortcutActivity2.class.getName(), si.getActivity().getClassName());
+ assertEquals(null, si.getIcon());
+ assertEquals(10, si.getTitleResId());
+ assertEquals("r10", si.getTitleResName());
+ assertEquals(11, si.getTextResId());
+ assertEquals("r11", si.getTextResName());
+ assertEquals(12, si.getDisabledMessageResourceId());
+ assertEquals("r12", si.getDisabledMessageResName());
+ assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
+ assertEquals("action", si.getIntent().getAction());
+ assertEquals("val", si.getIntent().getStringExtra("key"));
+ assertEquals(0, si.getRank());
+ assertEquals(1, si.getExtras().getInt("k"));
+
+ assertEquals(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_HAS_ICON_URI
+ | ShortcutInfo.FLAG_STRINGS_RESOLVED, si.getFlags());
+ assertNull(si.getBitmapPath());
+ assertNull(si.getIconResName());
+ assertEquals(0, si.getIconResourceId());
+ assertEquals("test_uri", si.getIconUri());
+ assertTrue(si.getLastChangedTimestamp() < now);
+
+ // Make sure ranks are saved too. Because of the auto-adjusting, we need two shortcuts
+ // to test it.
+ si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id2", USER_10);
+ assertEquals(1, si.getRank());
+ assertEquals(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_HAS_ICON_URI
+ | ShortcutInfo.FLAG_STRINGS_RESOLVED | ShortcutInfo.FLAG_ADAPTIVE_BITMAP,
+ si.getFlags());
+ assertNull(si.getBitmapPath());
+ assertNull(si.getIconResName());
+ assertEquals(0, si.getIconResourceId());
+ assertEquals("uri_maskable", si.getIconUri());
+ }
+
public void testShortcutInfoSaveAndLoad_forBackup() {
setCaller(CALLING_PACKAGE_1, USER_0);
@@ -1196,6 +1306,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
| ShortcutInfo.FLAG_SHADOW , si.getFlags());
assertNull(si.getBitmapPath()); // No icon.
assertEquals(0, si.getIconResourceId());
+ assertNull(si.getIconUri());
// Note when restored from backup, it's no longer dynamic, so shouldn't have a rank.
si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id2", USER_0);
@@ -1265,6 +1376,77 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertNull(si.getBitmapPath()); // No icon.
assertEquals(0, si.getIconResourceId());
assertEquals(null, si.getIconResName());
+ assertNull(si.getIconUri());
+
+ // Note when restored from backup, it's no longer dynamic, so shouldn't have a rank.
+ si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id2", USER_0);
+ assertEquals(0, si.getRank());
+ }
+
+ public void testShortcutInfoSaveAndLoad_forBackup_uri() {
+ setCaller(CALLING_PACKAGE_1, USER_0);
+
+ final Icon uriIcon = Icon.createWithContentUri("test_uri");
+
+ PersistableBundle pb = new PersistableBundle();
+ pb.putInt("k", 1);
+ ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext)
+ .setId("id")
+ .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class))
+ .setIcon(uriIcon)
+ .setTitleResId(10)
+ .setTextResId(11)
+ .setDisabledMessageResId(12)
+ .setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"))
+ .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+ .setRank(123)
+ .setExtras(pb)
+ .build();
+
+ ShortcutInfo sorig2 = new ShortcutInfo.Builder(mClientContext)
+ .setId("id2")
+ .setTitle("x")
+ .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class))
+ .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+ .setRank(456)
+ .build();
+
+ mManager.addDynamicShortcuts(list(sorig, sorig2));
+
+ // Dynamic shortcuts won't be backed up, so we need to pin it.
+ setCaller(LAUNCHER_1, USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("id", "id2"), HANDLE_USER_0);
+
+ // Do backup & restore.
+ backupAndRestore();
+
+ mService.handleUnlockUser(USER_0); // Load user-0.
+
+ ShortcutInfo si;
+ si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id", USER_0);
+
+ assertEquals(CALLING_PACKAGE_1, si.getPackage());
+ assertEquals("id", si.getId());
+ assertEquals(ShortcutActivity2.class.getName(), si.getActivity().getClassName());
+ assertEquals(null, si.getIcon());
+ assertEquals(10, si.getTitleResId());
+ assertEquals("r10", si.getTitleResName());
+ assertEquals(11, si.getTextResId());
+ assertEquals("r11", si.getTextResName());
+ assertEquals(12, si.getDisabledMessageResourceId());
+ assertEquals("r12", si.getDisabledMessageResName());
+ assertEquals(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"), si.getCategories());
+ assertEquals("action", si.getIntent().getAction());
+ assertEquals("val", si.getIntent().getStringExtra("key"));
+ assertEquals(0, si.getRank());
+ assertEquals(1, si.getExtras().getInt("k"));
+
+ assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_STRINGS_RESOLVED
+ | ShortcutInfo.FLAG_SHADOW , si.getFlags());
+ assertNull(si.getBitmapPath()); // No icon.
+ assertEquals(0, si.getIconResourceId());
+ assertEquals(null, si.getIconResName());
+ assertNull(si.getIconUri());
// Note when restored from backup, it's no longer dynamic, so shouldn't have a rank.
si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id2", USER_0);
diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
index 8da3bdfdf38c..155c6ddd4507 100644
--- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
@@ -28,6 +28,8 @@ import android.media.tv.TvInputService;
import android.media.tv.tuner.frontend.FrontendSettings;
import android.media.tv.tunerresourcemanager.IResourcesReclaimListener;
import android.media.tv.tunerresourcemanager.ResourceClientProfile;
+import android.media.tv.tunerresourcemanager.TunerDemuxRequest;
+import android.media.tv.tunerresourcemanager.TunerDescramblerRequest;
import android.media.tv.tunerresourcemanager.TunerFrontendInfo;
import android.media.tv.tunerresourcemanager.TunerFrontendRequest;
import android.media.tv.tunerresourcemanager.TunerResourceManager;
@@ -92,6 +94,13 @@ public class TunerResourceManagerServiceTest {
}
};
+ private static int getResourceIdFromHandle(int resourceHandle) {
+ if (resourceHandle == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
+ return resourceHandle;
+ }
+ return (resourceHandle & 0x00ff0000) >> 16;
+ }
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -229,14 +238,15 @@ public class TunerResourceManagerServiceTest {
public void requestFrontendTest_ClientNotRegistered() {
TunerFrontendRequest request =
new TunerFrontendRequest(0 /*clientId*/, FrontendSettings.TYPE_DVBT);
- int[] frontendId = new int[1];
+ int[] frontendHandle = new int[1];
try {
- assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
- .isFalse();
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle)).isFalse();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- assertThat(frontendId[0]).isEqualTo(TunerResourceManager.INVALID_FRONTEND_ID);
+ assertThat(getResourceIdFromHandle(frontendHandle[0]))
+ .isEqualTo(TunerResourceManager.INVALID_RESOURCE_HANDLE);
}
@Test
@@ -256,14 +266,15 @@ public class TunerResourceManagerServiceTest {
TunerFrontendRequest request =
new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
- int[] frontendId = new int[1];
+ int[] frontendHandle = new int[1];
try {
- assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
- .isFalse();
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle)).isFalse();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- assertThat(frontendId[0]).isEqualTo(TunerResourceManager.INVALID_FRONTEND_ID);
+ assertThat(getResourceIdFromHandle(frontendHandle[0]))
+ .isEqualTo(TunerResourceManager.INVALID_RESOURCE_HANDLE);
}
@Test
@@ -287,14 +298,14 @@ public class TunerResourceManagerServiceTest {
TunerFrontendRequest request =
new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
- int[] frontendId = new int[1];
+ int[] frontendHandle = new int[1];
try {
- assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
- .isTrue();
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle)).isTrue();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- assertThat(frontendId[0]).isEqualTo(0);
+ assertThat(getResourceIdFromHandle(frontendHandle[0])).isEqualTo(0);
}
@Test
@@ -322,26 +333,26 @@ public class TunerResourceManagerServiceTest {
new TunerFrontendInfo(2 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
mTunerResourceManagerService.setFrontendInfoListInternal(infos);
- int[] frontendId = new int[1];
+ int[] frontendHandle = new int[1];
TunerFrontendRequest request =
new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
try {
- assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
- .isTrue();
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle)).isTrue();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- assertThat(frontendId[0]).isEqualTo(infos[0].getId());
+ assertThat(getResourceIdFromHandle(frontendHandle[0])).isEqualTo(infos[0].getId());
request =
new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
try {
- assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
- .isTrue();
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle)).isTrue();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- assertThat(frontendId[0]).isEqualTo(infos[1].getId());
+ assertThat(getResourceIdFromHandle(frontendHandle[0])).isEqualTo(infos[1].getId());
assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId())
.isInUse()).isTrue();
assertThat(mTunerResourceManagerService.getFrontendResource(infos[2].getId())
@@ -382,10 +393,10 @@ public class TunerResourceManagerServiceTest {
TunerFrontendRequest request =
new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
- int[] frontendId = new int[1];
+ int[] frontendHandle = new int[1];
try {
- assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
- .isTrue();
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle)).isTrue();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -393,8 +404,8 @@ public class TunerResourceManagerServiceTest {
request =
new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
try {
- assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
- .isFalse();
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle)).isFalse();
assertThat(listener.isRelaimed()).isFalse();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -403,8 +414,8 @@ public class TunerResourceManagerServiceTest {
request =
new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS);
try {
- assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
- .isFalse();
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle)).isFalse();
assertThat(listener.isRelaimed()).isFalse();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -444,24 +455,24 @@ public class TunerResourceManagerServiceTest {
TunerFrontendRequest request =
new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
- int[] frontendId = new int[1];
+ int[] frontendHandle = new int[1];
try {
- assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
- .isTrue();
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle)).isTrue();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- assertThat(frontendId[0]).isEqualTo(infos[0].getId());
+ assertThat(getResourceIdFromHandle(frontendHandle[0])).isEqualTo(infos[0].getId());
request =
new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS);
try {
- assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
- .isTrue();
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle)).isTrue();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- assertThat(frontendId[0]).isEqualTo(infos[1].getId());
+ assertThat(getResourceIdFromHandle(frontendHandle[0])).isEqualTo(infos[1].getId());
assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getId())
.isInUse()).isTrue();
assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId())
@@ -493,14 +504,14 @@ public class TunerResourceManagerServiceTest {
TunerFrontendRequest request =
new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
- int[] frontendId = new int[1];
+ int[] frontendHandle = new int[1];
try {
- assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
- .isTrue();
+ assertThat(mTunerResourceManagerService
+ .requestFrontendInternal(request, frontendHandle)).isTrue();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- assertThat(frontendId[0]).isEqualTo(infos[0].getId());
+ assertThat(getResourceIdFromHandle(frontendHandle[0])).isEqualTo(infos[0].getId());
assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getId())
.isInUse()).isTrue();
assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId())
@@ -515,4 +526,38 @@ public class TunerResourceManagerServiceTest {
assertThat(mTunerResourceManagerService.checkClientExists(clientId[0])).isFalse();
}
+
+ @Test
+ public void requestDemuxTest() {
+ // Register client
+ ResourceClientProfile profile = new ResourceClientProfile("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ int[] clientId = new int[1];
+ mTunerResourceManagerService.registerClientProfileInternal(
+ profile, null /*listener*/, clientId);
+ assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+
+ int[] demuxHandle = new int[1];
+ TunerDemuxRequest request = new TunerDemuxRequest(clientId[0]);
+ assertThat(mTunerResourceManagerService.requestDemuxInternal(request, demuxHandle))
+ .isTrue();
+ assertThat(getResourceIdFromHandle(demuxHandle[0])).isEqualTo(0);
+ }
+
+ @Test
+ public void requestDescramblerTest() {
+ // Register client
+ ResourceClientProfile profile = new ResourceClientProfile("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ int[] clientId = new int[1];
+ mTunerResourceManagerService.registerClientProfileInternal(
+ profile, null /*listener*/, clientId);
+ assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+
+ int[] desHandle = new int[1];
+ TunerDescramblerRequest request = new TunerDescramblerRequest(clientId[0]);
+ assertThat(mTunerResourceManagerService.requestDescramblerInternal(request, desHandle))
+ .isTrue();
+ assertThat(getResourceIdFromHandle(desHandle[0])).isEqualTo(0);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
index 7af3ec62e651..88f368e403a6 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
@@ -20,7 +20,7 @@ import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_SYSTEM;
import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_USER;
import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
-import static android.app.usage.UsageStatsManager.REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
@@ -103,7 +103,8 @@ public class AppIdleHistoryTests extends AndroidTestCase {
aih.setAppStandbyBucket(PACKAGE_2, USER_ID, 2000, STANDBY_BUCKET_ACTIVE,
REASON_MAIN_USAGE);
aih.setAppStandbyBucket(PACKAGE_3, USER_ID, 2500, STANDBY_BUCKET_RESTRICTED,
- REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE);
+ REASON_MAIN_FORCED_BY_SYSTEM
+ | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE);
aih.setAppStandbyBucket(PACKAGE_4, USER_ID, 2750, STANDBY_BUCKET_RESTRICTED,
REASON_MAIN_FORCED_BY_USER);
aih.setAppStandbyBucket(PACKAGE_1, USER_ID, 3000, STANDBY_BUCKET_RARE,
@@ -114,7 +115,8 @@ public class AppIdleHistoryTests extends AndroidTestCase {
assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 3000), REASON_MAIN_TIMEOUT);
assertEquals(aih.getAppStandbyBucket(PACKAGE_3, USER_ID, 3000), STANDBY_BUCKET_RESTRICTED);
assertEquals(aih.getAppStandbyReason(PACKAGE_3, USER_ID, 3000),
- REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE);
+ REASON_MAIN_FORCED_BY_SYSTEM
+ | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE);
assertEquals(aih.getAppStandbyReason(PACKAGE_4, USER_ID, 3000),
REASON_MAIN_FORCED_BY_USER);
@@ -133,7 +135,8 @@ public class AppIdleHistoryTests extends AndroidTestCase {
assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 5000), REASON_MAIN_TIMEOUT);
assertEquals(aih.getAppStandbyBucket(PACKAGE_3, USER_ID, 3000), STANDBY_BUCKET_RESTRICTED);
assertEquals(aih.getAppStandbyReason(PACKAGE_3, USER_ID, 3000),
- REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE);
+ REASON_MAIN_FORCED_BY_SYSTEM
+ | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE);
assertEquals(aih.getAppStandbyReason(PACKAGE_4, USER_ID, 3000),
REASON_MAIN_FORCED_BY_USER);
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 387e62d7e257..f070bff6f906 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -28,6 +28,10 @@ import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_USER;
import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY;
+import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_UNDEFINED;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYNC_ADAPTER;
@@ -434,6 +438,11 @@ public class AppStandbyControllerTests {
true);
}
+ private int getStandbyBucketReason(String packageName) {
+ return mController.getAppStandbyBucketReason(packageName, USER_ID,
+ mInjector.mElapsedRealtime);
+ }
+
private void assertBucket(int bucket) {
assertEquals(bucket, getStandbyBucket(mController, PACKAGE_1));
}
@@ -810,7 +819,7 @@ public class AppStandbyControllerTests {
}
@Test
- public void testPredictionRaiseFromRestrictedTimeout() {
+ public void testPredictionRaiseFromRestrictedTimeout_highBucket() {
reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
// Way past all timeouts. App times out into RESTRICTED bucket.
@@ -823,6 +832,24 @@ public class AppStandbyControllerTests {
mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
REASON_MAIN_PREDICTED);
+ assertBucket(STANDBY_BUCKET_ACTIVE);
+ }
+
+ @Test
+ public void testPredictionRaiseFromRestrictedTimeout_lowBucket() {
+ reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
+
+ // Way past all timeouts. App times out into RESTRICTED bucket.
+ mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
+ mController.checkIdleStates(USER_ID);
+ assertBucket(STANDBY_BUCKET_RESTRICTED);
+
+ // Prediction into a low bucket means no expectation of the app being used, so we shouldn't
+ // elevate the app from RESTRICTED.
+ mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD;
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
+ REASON_MAIN_PREDICTED);
+ assertBucket(STANDBY_BUCKET_RESTRICTED);
}
@Test
@@ -987,6 +1014,79 @@ public class AppStandbyControllerTests {
}
@Test
+ public void testSystemForcedFlags_NotAddedForUserForce() throws Exception {
+ final int expectedReason = REASON_MAIN_FORCED_BY_USER;
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+ REASON_MAIN_FORCED_BY_USER);
+ assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+ assertEquals(expectedReason, getStandbyBucketReason(PACKAGE_1));
+
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+ REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE);
+ assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+ assertEquals(expectedReason, getStandbyBucketReason(PACKAGE_1));
+ }
+
+ @Test
+ public void testSystemForcedFlags_AddedForSystemForce() throws Exception {
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
+ REASON_MAIN_DEFAULT);
+ mInjector.mElapsedRealtime += 4 * RESTRICTED_THRESHOLD;
+
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+ REASON_MAIN_FORCED_BY_SYSTEM
+ | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE);
+ assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+ assertEquals(REASON_MAIN_FORCED_BY_SYSTEM
+ | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE,
+ getStandbyBucketReason(PACKAGE_1));
+
+ mController.restrictApp(PACKAGE_1, USER_ID, REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE);
+ assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+ // Flags should be combined
+ assertEquals(REASON_MAIN_FORCED_BY_SYSTEM
+ | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE
+ | REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE, getStandbyBucketReason(PACKAGE_1));
+ }
+
+ @Test
+ public void testSystemForcedFlags_SystemForceChangesBuckets() throws Exception {
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
+ REASON_MAIN_DEFAULT);
+ mInjector.mElapsedRealtime += 4 * RESTRICTED_THRESHOLD;
+
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
+ REASON_MAIN_FORCED_BY_SYSTEM
+ | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE);
+ assertEquals(STANDBY_BUCKET_FREQUENT, getStandbyBucket(mController, PACKAGE_1));
+ assertEquals(REASON_MAIN_FORCED_BY_SYSTEM
+ | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE,
+ getStandbyBucketReason(PACKAGE_1));
+
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
+ REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE);
+ assertEquals(STANDBY_BUCKET_FREQUENT, getStandbyBucket(mController, PACKAGE_1));
+ // Flags should be combined
+ assertEquals(REASON_MAIN_FORCED_BY_SYSTEM
+ | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE
+ | REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE,
+ getStandbyBucketReason(PACKAGE_1));
+
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
+ REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY);
+ assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
+ // Flags should be combined
+ assertEquals(REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY,
+ getStandbyBucketReason(PACKAGE_1));
+
+ mController.restrictApp(PACKAGE_1, USER_ID, REASON_SUB_FORCED_SYSTEM_FLAG_UNDEFINED);
+ assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+ // Flags should not be combined since the bucket changed.
+ assertEquals(REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_UNDEFINED,
+ getStandbyBucketReason(PACKAGE_1));
+ }
+
+ @Test
public void testAddActiveDeviceAdmin() {
assertActiveAdmins(USER_ID, (String[]) null);
assertActiveAdmins(USER_ID2, (String[]) null);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BubbleCheckerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BubbleCheckerTest.java
new file mode 100644
index 000000000000..9636342c3f41
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BubbleCheckerTest.java
@@ -0,0 +1,257 @@
+/*
+ * 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.notification;
+
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertFalse;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.UiServiceTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BubbleCheckerTest extends UiServiceTestCase {
+
+ private static final String SHORTCUT_ID = "shortcut";
+ private static final String PKG = "pkg";
+ private static final String KEY = "key";
+ private static final int USER_ID = 1;
+
+ @Mock
+ ActivityManager mActivityManager;
+ @Mock
+ RankingConfig mRankingConfig;
+ @Mock
+ ShortcutHelper mShortcutHelper;
+
+ @Mock
+ NotificationRecord mNr;
+ @Mock
+ UserHandle mUserHandle;
+ @Mock
+ Notification mNotif;
+ @Mock
+ StatusBarNotification mSbn;
+ @Mock
+ NotificationChannel mChannel;
+ @Mock
+ Notification.BubbleMetadata mBubbleMetadata;
+ @Mock
+ PendingIntent mPendingIntent;
+ @Mock
+ Intent mIntent;
+
+ BubbleExtractor.BubbleChecker mBubbleChecker;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mNr.getKey()).thenReturn(KEY);
+ when(mNr.getSbn()).thenReturn(mSbn);
+ when(mNr.getUser()).thenReturn(mUserHandle);
+ when(mUserHandle.getIdentifier()).thenReturn(USER_ID);
+ when(mNr.getChannel()).thenReturn(mChannel);
+ when(mSbn.getPackageName()).thenReturn(PKG);
+ when(mSbn.getUser()).thenReturn(mUserHandle);
+ when(mNr.getNotification()).thenReturn(mNotif);
+ when(mNotif.getBubbleMetadata()).thenReturn(mBubbleMetadata);
+
+ mBubbleChecker = new BubbleExtractor.BubbleChecker(mContext,
+ mShortcutHelper,
+ mRankingConfig,
+ mActivityManager);
+ }
+
+ void setUpIntentBubble() {
+ when(mPendingIntent.getIntent()).thenReturn(mIntent);
+ when(mBubbleMetadata.getBubbleIntent()).thenReturn(mPendingIntent);
+ when(mBubbleMetadata.getShortcutId()).thenReturn(null);
+ }
+
+ void setUpShortcutBubble(boolean isValid) {
+ when(mBubbleMetadata.getShortcutId()).thenReturn(SHORTCUT_ID);
+ when(mShortcutHelper.hasValidShortcutInfo(SHORTCUT_ID, PKG, mUserHandle))
+ .thenReturn(isValid);
+ when(mBubbleMetadata.getBubbleIntent()).thenReturn(null);
+ }
+
+ void setUpBubblesEnabled(boolean feature, boolean app, boolean channel) {
+ when(mRankingConfig.bubblesEnabled()).thenReturn(feature);
+ when(mRankingConfig.areBubblesAllowed(PKG, USER_ID)).thenReturn(app);
+ when(mChannel.canBubble()).thenReturn(channel);
+ }
+
+ void setUpActivityIntent(boolean isResizable) {
+ when(mPendingIntent.getIntent()).thenReturn(mIntent);
+ ActivityInfo info = new ActivityInfo();
+ info.resizeMode = isResizable
+ ? RESIZE_MODE_RESIZEABLE
+ : RESIZE_MODE_UNRESIZEABLE;
+ when(mIntent.resolveActivityInfo(any(), anyInt())).thenReturn(info);
+ }
+
+ //
+ // canBubble
+ //
+
+ @Test
+ public void testCanBubble_true_intentBubble() {
+ setUpBubblesEnabled(true /* feature */, true /* app */, true /* channel */);
+ setUpIntentBubble();
+ setUpActivityIntent(true /* isResizable */);
+ when(mActivityManager.isLowRamDevice()).thenReturn(false);
+ assertTrue(mBubbleChecker.canBubble(mNr, PKG, USER_ID));
+ }
+
+ @Test
+ public void testCanBubble_true_shortcutBubble() {
+ setUpBubblesEnabled(true /* feature */, true /* app */, true /* channel */);
+ setUpShortcutBubble(true /* isValid */);
+ assertTrue(mBubbleChecker.canBubble(mNr, PKG, USER_ID));
+ }
+
+ @Test
+ public void testCanBubble_false_noIntentInvalidShortcut() {
+ setUpBubblesEnabled(true /* feature */, true /* app */, true /* channel */);
+ setUpShortcutBubble(false /* isValid */);
+ assertFalse(mBubbleChecker.canBubble(mNr, PKG, USER_ID));
+ }
+
+ @Test
+ public void testCanBubble_false_noIntentNoShortcut() {
+ setUpBubblesEnabled(true /* feature */, true /* app */, true /* channel */);
+ when(mBubbleMetadata.getBubbleIntent()).thenReturn(null);
+ when(mBubbleMetadata.getShortcutId()).thenReturn(null);
+ assertFalse(mBubbleChecker.canBubble(mNr, PKG, USER_ID));
+ }
+
+ @Test
+ public void testCanBubbble_false_noMetadata() {
+ setUpBubblesEnabled(true/* feature */, true /* app */, true /* channel */);
+ when(mNotif.getBubbleMetadata()).thenReturn(null);
+ assertFalse(mBubbleChecker.canBubble(mNr, PKG, USER_ID));
+ }
+
+ @Test
+ public void testCanBubble_false_bubblesNotEnabled() {
+ setUpBubblesEnabled(false /* feature */, true /* app */, true /* channel */);
+ assertFalse(mBubbleChecker.canBubble(mNr, PKG, USER_ID));
+ }
+
+ @Test
+ public void testCanBubble_false_packageNotAllowed() {
+ setUpBubblesEnabled(true /* feature */, false /* app */, true /* channel */);
+ assertFalse(mBubbleChecker.canBubble(mNr, PKG, USER_ID));
+ }
+
+ @Test
+ public void testCanBubble_false_channelNotAllowed() {
+ setUpBubblesEnabled(true /* feature */, true /* app */, false /* channel */);
+ assertFalse(mBubbleChecker.canBubble(mNr, PKG, USER_ID));
+ }
+
+ //
+ // canLaunchInActivityView
+ //
+
+ @Test
+ public void testCanLaunchInActivityView_true() {
+ setUpActivityIntent(true /* resizable */);
+ assertTrue(mBubbleChecker.canLaunchInActivityView(mContext, mPendingIntent, PKG));
+ }
+
+ @Test
+ public void testCanLaunchInActivityView_false_noIntent() {
+ when(mPendingIntent.getIntent()).thenReturn(null);
+ assertFalse(mBubbleChecker.canLaunchInActivityView(mContext, mPendingIntent, PKG));
+ }
+
+ @Test
+ public void testCanLaunchInActivityView_false_noInfo() {
+ when(mPendingIntent.getIntent()).thenReturn(mIntent);
+ when(mIntent.resolveActivityInfo(any(), anyInt())).thenReturn(null);
+ assertFalse(mBubbleChecker.canLaunchInActivityView(mContext, mPendingIntent, PKG));
+ }
+
+ @Test
+ public void testCanLaunchInActivityView_false_notResizable() {
+ setUpActivityIntent(false /* resizable */);
+ assertFalse(mBubbleChecker.canLaunchInActivityView(mContext, mPendingIntent, PKG));
+ }
+
+ //
+ // isNotificationAppropriateToBubble
+ //
+
+ @Test
+ public void testIsNotifAppropriateToBubble_true() {
+ setUpBubblesEnabled(true /* feature */, true /* app */, true /* channel */);
+ setUpIntentBubble();
+ when(mActivityManager.isLowRamDevice()).thenReturn(false);
+ setUpActivityIntent(true /* resizable */);
+ doReturn(Notification.MessagingStyle.class).when(mNotif).getNotificationStyle();
+
+ assertTrue(mBubbleChecker.isNotificationAppropriateToBubble(mNr));
+ }
+
+ @Test
+ public void testIsNotifAppropriateToBubble_false_lowRam() {
+ setUpBubblesEnabled(true /* feature */, true /* app */, true /* channel */);
+ when(mActivityManager.isLowRamDevice()).thenReturn(true);
+ setUpActivityIntent(true /* resizable */);
+ doReturn(Notification.MessagingStyle.class).when(mNotif).getNotificationStyle();
+
+ assertFalse(mBubbleChecker.isNotificationAppropriateToBubble(mNr));
+ }
+
+ @Test
+ public void testIsNotifAppropriateToBubble_false_notMessageStyle() {
+ setUpBubblesEnabled(true /* feature */, true /* app */, true /* channel */);
+ when(mActivityManager.isLowRamDevice()).thenReturn(false);
+ setUpActivityIntent(true /* resizable */);
+ doReturn(Notification.BigPictureStyle.class).when(mNotif).getNotificationStyle();
+
+ assertFalse(mBubbleChecker.isNotificationAppropriateToBubble(mNr));
+ }
+
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java
index 7459c4b0610e..c7cef05ce442 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java
@@ -21,6 +21,7 @@ import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
@@ -46,6 +47,7 @@ import org.mockito.MockitoAnnotations;
public class BubbleExtractorTest extends UiServiceTestCase {
@Mock RankingConfig mConfig;
+ BubbleExtractor mBubbleExtractor;
private String mPkg = "com.android.server.notification";
private int mId = 1001;
@@ -57,6 +59,10 @@ public class BubbleExtractorTest extends UiServiceTestCase {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ mBubbleExtractor = new BubbleExtractor();
+ mBubbleExtractor.initialize(mContext, mock(NotificationUsageStats.class));
+ mBubbleExtractor.setConfig(mConfig);
+ mBubbleExtractor.setShortcutHelper(mock(ShortcutHelper.class));
}
private NotificationRecord getNotificationRecord(boolean allow, int importanceHigh) {
@@ -83,70 +89,55 @@ public class BubbleExtractorTest extends UiServiceTestCase {
@Test
public void testAppYesChannelNo() {
- BubbleExtractor extractor = new BubbleExtractor();
- extractor.setConfig(mConfig);
-
when(mConfig.bubblesEnabled()).thenReturn(true);
when(mConfig.areBubblesAllowed(mPkg, mUid)).thenReturn(true);
NotificationRecord r = getNotificationRecord(false, IMPORTANCE_UNSPECIFIED);
- extractor.process(r);
+ mBubbleExtractor.process(r);
assertFalse(r.canBubble());
}
@Test
public void testAppNoChannelYes() throws Exception {
- BubbleExtractor extractor = new BubbleExtractor();
- extractor.setConfig(mConfig);
-
when(mConfig.bubblesEnabled()).thenReturn(true);
when(mConfig.areBubblesAllowed(mPkg, mUid)).thenReturn(false);
NotificationRecord r = getNotificationRecord(true, IMPORTANCE_HIGH);
- extractor.process(r);
+ mBubbleExtractor.process(r);
assertFalse(r.canBubble());
}
@Test
public void testAppYesChannelYes() {
- BubbleExtractor extractor = new BubbleExtractor();
- extractor.setConfig(mConfig);
-
when(mConfig.bubblesEnabled()).thenReturn(true);
when(mConfig.areBubblesAllowed(mPkg, mUid)).thenReturn(true);
NotificationRecord r = getNotificationRecord(true, IMPORTANCE_UNSPECIFIED);
- extractor.process(r);
+ mBubbleExtractor.process(r);
assertTrue(r.canBubble());
}
@Test
public void testAppNoChannelNo() {
- BubbleExtractor extractor = new BubbleExtractor();
- extractor.setConfig(mConfig);
-
when(mConfig.bubblesEnabled()).thenReturn(true);
when(mConfig.areBubblesAllowed(mPkg, mUid)).thenReturn(false);
NotificationRecord r = getNotificationRecord(false, IMPORTANCE_UNSPECIFIED);
- extractor.process(r);
+ mBubbleExtractor.process(r);
assertFalse(r.canBubble());
}
@Test
public void testAppYesChannelYesUserNo() {
- BubbleExtractor extractor = new BubbleExtractor();
- extractor.setConfig(mConfig);
-
when(mConfig.bubblesEnabled()).thenReturn(false);
when(mConfig.areBubblesAllowed(mPkg, mUid)).thenReturn(true);
NotificationRecord r = getNotificationRecord(true, IMPORTANCE_HIGH);
- extractor.process(r);
+ mBubbleExtractor.process(r);
assertFalse(r.canBubble());
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
index dc8d0104353a..010f8ac334a7 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -35,7 +35,6 @@ import android.app.INotificationManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.PendingIntent;
-import android.app.Person;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -191,7 +190,8 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
tweak.canBubble(),
tweak.visuallyInterruptive(),
tweak.isConversation(),
- tweak.getShortcutInfo()
+ tweak.getShortcutInfo(),
+ tweak.isBubble()
);
assertNotEquals(nru, nru2);
}
@@ -270,7 +270,8 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
canBubble(i),
visuallyInterruptive(i),
isConversation(i),
- getShortcutInfo(i)
+ getShortcutInfo(i),
+ isBubble(i)
);
rankings[i] = ranking;
}
@@ -389,11 +390,15 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
"title", 0, "titleResName", "text", 0, "textResName",
"disabledMessage", 0, "disabledMessageResName",
null, null, 0, null, 0, 0,
- 0, "iconResName", "bitmapPath", 0,
+ 0, "iconResName", "bitmapPath", null, 0,
null, null);
return si;
}
+ private boolean isBubble(int index) {
+ return index % 4 == 0;
+ }
+
private void assertActionsEqual(
List<Notification.Action> expecteds, List<Notification.Action> actuals) {
assertEquals(expecteds.size(), actuals.size());
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 64d481a2e6a0..f179840fba11 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -41,8 +41,6 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
-import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
-import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
import static android.content.pm.PackageManager.FEATURE_WATCH;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -204,6 +202,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
private TestableNotificationManagerService mService;
private INotificationManager mBinderService;
private NotificationManagerInternal mInternalService;
+ private TestableBubbleChecker mTestableBubbleChecker;
+ private ShortcutHelper mShortcutHelper;
@Mock
private IPackageManager mPackageManager;
@Mock
@@ -286,6 +286,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
super(context, logger, notificationInstanceIdSequence);
}
+ RankingHelper getRankingHelper() {
+ return mRankingHelper;
+ }
+
@Override
protected boolean isCallingUidSystem() {
countSystemChecks++;
@@ -337,6 +341,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
interface NotificationAssistantAccessGrantedCallback {
void onGranted(ComponentName assistant, int userId, boolean granted);
}
+ }
+
+ private class TestableBubbleChecker extends BubbleExtractor.BubbleChecker {
+
+ TestableBubbleChecker(Context context, ShortcutHelper helper, RankingConfig config,
+ ActivityManager manager) {
+ super(context, helper, config, manager);
+ }
@Override
protected boolean canLaunchInActivityView(Context context, PendingIntent pendingIntent,
@@ -448,7 +460,16 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
mService.setAudioManager(mAudioManager);
- mService.setLauncherApps(mLauncherApps);
+
+ mShortcutHelper = mService.getShortcutHelper();
+ mShortcutHelper.setLauncherApps(mLauncherApps);
+
+ // Set the testable bubble extractor
+ RankingHelper rankingHelper = mService.getRankingHelper();
+ BubbleExtractor extractor = rankingHelper.findExtractor(BubbleExtractor.class);
+ mTestableBubbleChecker = new TestableBubbleChecker(mContext, mShortcutHelper,
+ mService.mPreferencesHelper, mActivityManager);
+ extractor.setBubbleChecker(mTestableBubbleChecker);
// Tests call directly into the Binder.
mBinderService = mService.getBinderService();
@@ -462,6 +483,15 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@After
+ public void assertNotificationRecordLoggerCallsValid() {
+ for (NotificationRecordLoggerFake.CallRecord call : mNotificationRecordLogger.getCalls()) {
+ if (call.wasLogged) {
+ assertNotNull(call.event);
+ }
+ }
+ }
+
+ @After
public void tearDown() throws Exception {
if (mFile != null) mFile.delete();
clearDeviceConfig();
@@ -1150,10 +1180,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0,
generateNotificationRecord(null).getNotification(), 0);
waitForIdle();
- assertEquals(1, mNotificationRecordLogger.getCalls().size());
+ assertEquals(1, mNotificationRecordLogger.numCalls());
NotificationRecordLoggerFake.CallRecord call = mNotificationRecordLogger.get(0);
- assertTrue(call.shouldLogReported);
+ assertTrue(call.wasLogged);
assertEquals(NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
call.event);
assertNotNull(call.r);
@@ -1179,18 +1209,18 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.setCategory(Notification.CATEGORY_ALARM).build();
mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0, update, 0);
waitForIdle();
- assertEquals(2, mNotificationRecordLogger.getCalls().size());
+ assertEquals(2, mNotificationRecordLogger.numCalls());
- assertTrue(mNotificationRecordLogger.get(0).shouldLogReported);
+ assertTrue(mNotificationRecordLogger.get(0).wasLogged);
assertEquals(
NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
- mNotificationRecordLogger.get(0).event);
+ mNotificationRecordLogger.event(0));
assertEquals(1, mNotificationRecordLogger.get(0).getInstanceId());
- assertTrue(mNotificationRecordLogger.get(1).shouldLogReported);
+ assertTrue(mNotificationRecordLogger.get(1).wasLogged);
assertEquals(
NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_UPDATED,
- mNotificationRecordLogger.get(1).event);
+ mNotificationRecordLogger.event(1));
// Instance ID doesn't change on update of an active notification
assertEquals(1, mNotificationRecordLogger.get(1).getInstanceId());
}
@@ -1203,13 +1233,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0,
generateNotificationRecord(null).getNotification(), 0);
waitForIdle();
- assertEquals(2, mNotificationRecordLogger.getCalls().size());
- assertTrue(mNotificationRecordLogger.get(0).shouldLogReported);
+ assertEquals(2, mNotificationRecordLogger.numCalls());
+ assertTrue(mNotificationRecordLogger.get(0).wasLogged);
assertEquals(
NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
- mNotificationRecordLogger.get(0).event);
- assertFalse(mNotificationRecordLogger.get(1).shouldLogReported);
- assertNull(mNotificationRecordLogger.get(1).event);
+ mNotificationRecordLogger.event(0));
+ assertFalse(mNotificationRecordLogger.get(1).wasLogged);
+ assertNull(mNotificationRecordLogger.event(1));
}
@Test
@@ -1222,11 +1252,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
notif.extras.putString(Notification.EXTRA_TITLE, "Changed title");
mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0, notif, 0);
waitForIdle();
- assertEquals(2, mNotificationRecordLogger.getCalls().size());
+ assertEquals(2, mNotificationRecordLogger.numCalls());
assertEquals(
NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
- mNotificationRecordLogger.get(0).event);
- assertNull(mNotificationRecordLogger.get(1).event);
+ mNotificationRecordLogger.event(0));
+ assertNull(mNotificationRecordLogger.event(1));
}
@Test
@@ -1241,23 +1271,23 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
waitForIdle();
mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0, notification, 0);
waitForIdle();
- assertEquals(3, mNotificationRecordLogger.getCalls().size());
+ assertEquals(3, mNotificationRecordLogger.numCalls());
assertEquals(
NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
- mNotificationRecordLogger.get(0).event);
- assertTrue(mNotificationRecordLogger.get(0).shouldLogReported);
+ mNotificationRecordLogger.event(0));
+ assertTrue(mNotificationRecordLogger.get(0).wasLogged);
assertEquals(1, mNotificationRecordLogger.get(0).getInstanceId());
assertEquals(
NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_APP_CANCEL,
- mNotificationRecordLogger.get(1).event);
+ mNotificationRecordLogger.event(1));
assertEquals(1, mNotificationRecordLogger.get(1).getInstanceId());
assertEquals(
NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
- mNotificationRecordLogger.get(2).event);
- assertTrue(mNotificationRecordLogger.get(2).shouldLogReported);
+ mNotificationRecordLogger.event(2));
+ assertTrue(mNotificationRecordLogger.get(2).wasLogged);
// New instance ID because notification was canceled before re-post
assertEquals(2, mNotificationRecordLogger.get(2).getInstanceId());
}
@@ -1269,7 +1299,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
waitForIdle();
// The notification record logger doesn't even get called when a nonexistent notification
// is cancelled, because that happens very frequently and is not interesting.
- assertEquals(0, mNotificationRecordLogger.getCalls().size());
+ assertEquals(0, mNotificationRecordLogger.numCalls());
}
@Test
@@ -2527,6 +2557,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// only snooze the one notification
verify(mSnoozeHelper, times(1)).snooze(any(NotificationRecord.class), anyLong());
assertTrue(nonGrouped.getStats().hasSnoozed());
+
+ assertEquals(2, mNotificationRecordLogger.numCalls());
+ assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_SNOOZED,
+ mNotificationRecordLogger.event(0));
+ assertEquals(
+ NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_SNOOZED,
+ mNotificationRecordLogger.event(1));
}
@Test
@@ -2569,6 +2606,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// only snooze the one child
verify(mSnoozeHelper, times(1)).snooze(any(NotificationRecord.class), anyLong());
+
+ assertEquals(2, mNotificationRecordLogger.numCalls());
+ assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_SNOOZED,
+ mNotificationRecordLogger.event(0));
+ assertEquals(NotificationRecordLogger.NotificationCancelledEvent
+ .NOTIFICATION_CANCEL_SNOOZED, mNotificationRecordLogger.event(1));
}
@Test
@@ -2588,6 +2631,18 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// snooze child and summary
verify(mSnoozeHelper, times(2)).snooze(any(NotificationRecord.class), anyLong());
+
+ assertEquals(4, mNotificationRecordLogger.numCalls());
+ assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_SNOOZED,
+ mNotificationRecordLogger.event(0));
+ assertEquals(
+ NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_SNOOZED,
+ mNotificationRecordLogger.event(1));
+ assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_SNOOZED,
+ mNotificationRecordLogger.event(2));
+ assertEquals(
+ NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_SNOOZED,
+ mNotificationRecordLogger.event(3));
}
@Test
@@ -2603,6 +2658,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// snooze child only
verify(mSnoozeHelper, times(1)).snooze(any(NotificationRecord.class), anyLong());
+
+ assertEquals(2, mNotificationRecordLogger.numCalls());
+ assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_SNOOZED,
+ mNotificationRecordLogger.event(0));
+ assertEquals(
+ NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_SNOOZED,
+ mNotificationRecordLogger.event(1));
}
@Test
@@ -3386,6 +3448,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mService.mNotificationDelegate.onNotificationDirectReplied(r.getKey());
assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasDirectReplied());
verify(mAssistants).notifyAssistantNotificationDirectReplyLocked(eq(r.getSbn()));
+
+ assertEquals(1, mNotificationRecordLogger.numCalls());
+ assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_DIRECT_REPLIED,
+ mNotificationRecordLogger.event(0));
}
@Test
@@ -3404,6 +3470,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.getSbn()), eq(true),
eq((false)));
assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
+
+ assertEquals(2, mNotificationRecordLogger.numCalls());
+ assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_DETAIL_OPEN_USER,
+ mNotificationRecordLogger.event(0));
+ assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_DETAIL_CLOSE_USER,
+ mNotificationRecordLogger.event(1));
}
@Test
@@ -3465,11 +3537,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// Using mService.addNotification() does not generate a NotificationRecordLogger log,
// so we only get the cancel notification.
- assertEquals(1, mNotificationRecordLogger.getCalls().size());
+ assertEquals(1, mNotificationRecordLogger.numCalls());
assertEquals(
NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_USER_AOD,
- mNotificationRecordLogger.get(0).event);
+ mNotificationRecordLogger.event(0));
assertEquals(1, mNotificationRecordLogger.get(0).getInstanceId());
}
@@ -4351,9 +4423,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
{NotificationVisibility.obtain(r.getKey(), 1, 1, true)},
new NotificationVisibility[]{});
- assertEquals(1, mNotificationRecordLogger.getCalls().size());
+ assertEquals(1, mNotificationRecordLogger.numCalls());
assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_OPEN,
- mNotificationRecordLogger.get(0).event);
+ mNotificationRecordLogger.event(0));
assertEquals(1, mNotificationRecordLogger.get(0).getInstanceId());
mService.mNotificationDelegate.onNotificationVisibilityChanged(
@@ -4362,9 +4434,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
{NotificationVisibility.obtain(r.getKey(), 1, 1, true)}
);
- assertEquals(2, mNotificationRecordLogger.getCalls().size());
+ assertEquals(2, mNotificationRecordLogger.numCalls());
assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_CLOSE,
- mNotificationRecordLogger.get(1).event);
+ mNotificationRecordLogger.event(1));
assertEquals(1, mNotificationRecordLogger.get(1).getInstanceId());
}
@@ -4788,6 +4860,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mService.mNotificationDelegate.onPanelHidden();
verify(mAssistants, times(1)).onPanelHidden();
+
+ assertEquals(2, mNotificationRecordLogger.numCalls());
+ assertEquals(NotificationRecordLogger.NotificationPanelEvent.NOTIFICATION_PANEL_OPEN,
+ mNotificationRecordLogger.event(0));
+ assertEquals(NotificationRecordLogger.NotificationPanelEvent.NOTIFICATION_PANEL_CLOSE,
+ mNotificationRecordLogger.event(1));
}
@Test
@@ -4806,6 +4884,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
modifiedBeforeSending);
verify(mAssistants).notifyAssistantSuggestedReplySent(
eq(r.getSbn()), eq(reply), eq(generatedByAssistant));
+ assertEquals(1, mNotificationRecordLogger.numCalls());
+ assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_SMART_REPLIED,
+ mNotificationRecordLogger.event(0));
}
@Test
@@ -4825,6 +4906,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
generatedByAssistant);
verify(mAssistants).notifyAssistantActionClicked(
eq(r.getSbn()), eq(actionIndex), eq(action), eq(generatedByAssistant));
+
+ assertEquals(1, mNotificationRecordLogger.numCalls());
+ assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_ACTION_CLICKED,
+ mNotificationRecordLogger.event(0));
}
@Test
@@ -5489,6 +5574,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG);
assertEquals(1, notifs.length);
assertEquals(1, mService.getNotificationRecordCount());
+
+ assertEquals(1, mNotificationRecordLogger.numCalls());
+ assertEquals(NotificationRecordLogger.NotificationCancelledEvent
+ .NOTIFICATION_CANCEL_LISTENER_CANCEL, mNotificationRecordLogger.event(0));
}
@Test
@@ -5710,6 +5799,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testOnBubbleNotificationSuppressionChanged() throws Exception {
+ // Bubbles are allowed!
+ setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, true /* channel */);
+
// Bubble notification
NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel, "tag");
@@ -6111,8 +6203,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
assertTrue(notif.isBubbleNotification());
// Test: Remove the shortcut
+ when(mLauncherApps.getShortcuts(any(), any())).thenReturn(null);
launcherAppsCallback.getValue().onShortcutsChanged(PKG, Collections.emptyList(),
new UserHandle(mUid));
+ waitForIdle();
// Verify:
@@ -6125,6 +6219,58 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
assertFalse(notif2.isBubbleNotification());
}
+
+ @Test
+ public void testNotificationBubbles_shortcut_stopListeningWhenNotifRemoved()
+ throws RemoteException {
+ // Bubbles are allowed!
+ setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, true /* channel */);
+
+ ArgumentCaptor<LauncherApps.Callback> launcherAppsCallback =
+ ArgumentCaptor.forClass(LauncherApps.Callback.class);
+
+ // Messaging notification with shortcut info
+ Notification.BubbleMetadata metadata =
+ getBubbleMetadataBuilder().createShortcutBubble("someshortcutId").build();
+ Notification.Builder nb = getMessageStyleNotifBuilder(false /* addDefaultMetadata */,
+ null /* groupKey */, false /* isSummary */);
+ nb.setBubbleMetadata(metadata);
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
+ "tag", mUid, 0, nb.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ // Pretend the shortcut exists
+ List<ShortcutInfo> shortcutInfos = new ArrayList<>();
+ ShortcutInfo info = mock(ShortcutInfo.class);
+ when(info.isLongLived()).thenReturn(true);
+ shortcutInfos.add(info);
+ when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcutInfos);
+
+ // Test: Send the bubble notification
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
+ waitForIdle();
+
+ // Verify:
+
+ // Make sure we register the callback for shortcut changes
+ verify(mLauncherApps, times(1)).registerCallback(launcherAppsCallback.capture(), any());
+
+ // yes allowed, yes messaging w/shortcut, yes bubble
+ Notification notif = mService.getNotificationRecord(nr.getSbn().getKey()).getNotification();
+ assertTrue(notif.isBubbleNotification());
+
+ // Test: Remove the notification
+ mBinderService.cancelNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+ nr.getSbn().getId(), nr.getSbn().getUserId());
+ waitForIdle();
+
+ // Verify:
+
+ // Make sure callback is unregistered
+ verify(mLauncherApps, times(1)).unregisterCallback(launcherAppsCallback.getValue());
+ }
+
@Test
public void testNotificationBubbles_bubbleChildrenStay_whenGroupSummaryDismissed()
throws Exception {
@@ -6168,6 +6314,17 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// The bubble should still exist
StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG);
assertEquals(1, notifsAfter.length);
+
+ // Check we got the click log and associated dismissal logs
+ assertEquals(6, mNotificationRecordLogger.numCalls());
+ // Skip the notification-creation logs
+ assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_CLICKED,
+ mNotificationRecordLogger.event(3));
+ assertEquals(NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_CLICK,
+ mNotificationRecordLogger.event(4));
+ assertEquals(NotificationRecordLogger.NotificationCancelledEvent
+ .NOTIFICATION_CANCEL_GROUP_SUMMARY_CANCELED,
+ mNotificationRecordLogger.event(5));
}
@Test
@@ -6188,6 +6345,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// THEN the bubble should still exist
StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG);
assertEquals(1, notifsAfter.length);
+
+ // Check we got the click log
+ assertEquals(1, mNotificationRecordLogger.numCalls());
+ assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_CLICKED,
+ mNotificationRecordLogger.event(0));
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java
index 2a17bae57c8e..6b18cc64c5fb 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java
@@ -31,25 +31,29 @@ class NotificationRecordLoggerFake implements NotificationRecordLogger {
// The following fields are only relevant to maybeLogNotificationPosted() calls.
static final int INVALID = -1;
public int position = INVALID, buzzBeepBlink = INVALID;
- public boolean shouldLogReported;
+ public boolean wasLogged;
CallRecord(NotificationRecord r, NotificationRecord old, int position,
int buzzBeepBlink) {
super(r, old);
this.position = position;
this.buzzBeepBlink = buzzBeepBlink;
- shouldLogReported = shouldLogReported(buzzBeepBlink);
- event = shouldLogReported ? NotificationReportedEvent.fromRecordPair(this) : null;
+ wasLogged = shouldLogReported(buzzBeepBlink);
+ event = wasLogged ? NotificationReportedEvent.fromRecordPair(this) : null;
}
CallRecord(NotificationRecord r, UiEventLogger.UiEventEnum event) {
super(r, null);
- shouldLogReported = false;
+ wasLogged = true;
this.event = event;
}
}
private List<CallRecord> mCalls = new ArrayList<>();
+ public int numCalls() {
+ return mCalls.size();
+ }
+
List<CallRecord> getCalls() {
return mCalls;
}
@@ -57,6 +61,9 @@ class NotificationRecordLoggerFake implements NotificationRecordLogger {
CallRecord get(int index) {
return mCalls.get(index);
}
+ UiEventLogger.UiEventEnum event(int index) {
+ return mCalls.get(index).event;
+ }
@Override
public void maybeLogNotificationPosted(NotificationRecord r, NotificationRecord old,
@@ -65,13 +72,12 @@ class NotificationRecordLoggerFake implements NotificationRecordLogger {
}
@Override
- public void logNotificationCancelled(NotificationRecord r, int reason, int dismissalSurface) {
- mCalls.add(new CallRecord(r,
- NotificationCancelledEvent.fromCancelReason(reason, dismissalSurface)));
+ public void log(UiEventLogger.UiEventEnum event, NotificationRecord r) {
+ mCalls.add(new CallRecord(r, event));
}
@Override
- public void logNotificationVisibility(NotificationRecord r, boolean visible) {
- mCalls.add(new CallRecord(r, NotificationEvent.fromVisibility(visible)));
+ public void log(UiEventLogger.UiEventEnum event) {
+ mCalls.add(new CallRecord(null, event));
}
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java
new file mode 100644
index 000000000000..50fb9b425652
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java
@@ -0,0 +1,141 @@
+/*
+ * 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.notification;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutInfo;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.TestableLooper;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.UiServiceTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@TestableLooper.RunWithLooper
+public class ShortcutHelperTest extends UiServiceTestCase {
+
+ private static final String SHORTCUT_ID = "shortcut";
+ private static final String PKG = "pkg";
+ private static final String KEY = "key";
+
+ @Mock
+ LauncherApps mLauncherApps;
+ @Mock
+ ShortcutHelper.ShortcutListener mShortcutListener;
+ @Mock
+ NotificationRecord mNr;
+ @Mock
+ Notification mNotif;
+ @Mock
+ StatusBarNotification mSbn;
+ @Mock
+ Notification.BubbleMetadata mBubbleMetadata;
+
+ ShortcutHelper mShortcutHelper;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mShortcutHelper = new ShortcutHelper(mLauncherApps, mShortcutListener);
+ when(mNr.getKey()).thenReturn(KEY);
+ when(mNr.getSbn()).thenReturn(mSbn);
+ when(mSbn.getPackageName()).thenReturn(PKG);
+ when(mNr.getNotification()).thenReturn(mNotif);
+ when(mNotif.getBubbleMetadata()).thenReturn(mBubbleMetadata);
+ when(mBubbleMetadata.getShortcutId()).thenReturn(SHORTCUT_ID);
+ }
+
+ private LauncherApps.Callback addShortcutBubbleAndVerifyListener() {
+ when(mNotif.isBubbleNotification()).thenReturn(true);
+
+ mShortcutHelper.maybeListenForShortcutChangesForBubbles(mNr,
+ false /* removed */,
+ null /* handler */);
+
+ ArgumentCaptor<LauncherApps.Callback> launcherAppsCallback =
+ ArgumentCaptor.forClass(LauncherApps.Callback.class);
+
+ verify(mLauncherApps, times(1)).registerCallback(
+ launcherAppsCallback.capture(), any());
+ return launcherAppsCallback.getValue();
+ }
+
+ @Test
+ public void testBubbleAdded_listenedAdded() {
+ addShortcutBubbleAndVerifyListener();
+ }
+
+ @Test
+ public void testBubbleRemoved_listenerRemoved() {
+ // First set it up to listen
+ addShortcutBubbleAndVerifyListener();
+
+ // Then remove the notif
+ mShortcutHelper.maybeListenForShortcutChangesForBubbles(mNr,
+ true /* removed */,
+ null /* handler */);
+
+ verify(mLauncherApps, times(1)).unregisterCallback(any());
+ }
+
+ @Test
+ public void testBubbleNoLongerBubble_listenerRemoved() {
+ // First set it up to listen
+ addShortcutBubbleAndVerifyListener();
+
+ // Then make it not a bubble
+ when(mNotif.isBubbleNotification()).thenReturn(false);
+ mShortcutHelper.maybeListenForShortcutChangesForBubbles(mNr,
+ false /* removed */,
+ null /* handler */);
+
+ verify(mLauncherApps, times(1)).unregisterCallback(any());
+ }
+
+ @Test
+ public void testListenerNotifiedOnShortcutRemoved() {
+ LauncherApps.Callback callback = addShortcutBubbleAndVerifyListener();
+
+ List<ShortcutInfo> shortcutInfos = new ArrayList<>();
+ when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcutInfos);
+
+ callback.onShortcutsChanged(PKG, shortcutInfos, mock(UserHandle.class));
+ verify(mShortcutListener).onShortcutRemoved(mNr.getKey());
+ }
+}
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
index cdba9a10fa82..6cb8b8641ecc 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -20,6 +20,7 @@ android_test {
"mockito-target-extended-minus-junit4",
"platform-test-annotations",
"servicestests-utils",
+ "testng",
"truth-prebuilt",
"testables",
"ub-uiautomator",
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 c9c3649783b8..9e874211fcb3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -1332,6 +1332,40 @@ public class ActivityRecordTests extends ActivityTestsBase {
assertNotEquals(initialConf, wpc.getRequestedOverrideConfiguration());
}
+ @Test
+ public void testActivityDestroyDoesntChangeProcessOverride() {
+ final ActivityRecord firstActivity =
+ createActivityOnDisplay(true /* defaultDisplay */, null /* process */);
+ final WindowProcessController wpc = firstActivity.app;
+ assertTrue(wpc.registeredForActivityConfigChanges());
+ assertEquals(0, firstActivity.getMergedOverrideConfiguration()
+ .diff(wpc.getRequestedOverrideConfiguration()));
+
+ final ActivityRecord secondActivity =
+ createActivityOnDisplay(false /* defaultDisplay */, wpc);
+ assertTrue(wpc.registeredForActivityConfigChanges());
+ assertEquals(0, secondActivity.getMergedOverrideConfiguration()
+ .diff(wpc.getRequestedOverrideConfiguration()));
+
+ final ActivityRecord thirdActivity =
+ createActivityOnDisplay(false /* defaultDisplay */, wpc);
+ assertTrue(wpc.registeredForActivityConfigChanges());
+ assertEquals(0, thirdActivity.getMergedOverrideConfiguration()
+ .diff(wpc.getRequestedOverrideConfiguration()));
+
+ secondActivity.destroyImmediately(true, "");
+
+ assertTrue(wpc.registeredForActivityConfigChanges());
+ assertEquals(0, thirdActivity.getMergedOverrideConfiguration()
+ .diff(wpc.getRequestedOverrideConfiguration()));
+
+ firstActivity.destroyImmediately(true, "");
+
+ assertTrue(wpc.registeredForActivityConfigChanges());
+ assertEquals(0, thirdActivity.getMergedOverrideConfiguration()
+ .diff(wpc.getRequestedOverrideConfiguration()));
+ }
+
/**
* Creates an activity on display. For non-default display request it will also create a new
* display with custom DisplayInfo.
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index e2c27ea55b13..7928e7602df3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -20,6 +20,9 @@ import static android.view.Gravity.BOTTOM;
import static android.view.Gravity.LEFT;
import static android.view.Gravity.RIGHT;
import static android.view.Gravity.TOP;
+import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.ITYPE_STATUS_BAR;
+import static android.view.InsetsState.ITYPE_TOP_GESTURES;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
@@ -42,13 +45,17 @@ import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assume.assumeTrue;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.spy;
+import static org.testng.Assert.expectThrows;
import android.app.WindowConfiguration;
import android.graphics.Insets;
@@ -154,6 +161,56 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
}
@Test
+ public void addingWindow_withInsetsTypes() {
+ WindowState win = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "StatusBarSubPanel");
+ win.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR, ITYPE_TOP_GESTURES};
+ win.getFrameLw().set(0, 0, 500, 100);
+
+ addWindow(win);
+ InsetsStateController controller = mDisplayContent.getInsetsStateController();
+ controller.onPostLayout();
+
+ InsetsSourceProvider statusBarProvider = controller.getSourceProvider(ITYPE_STATUS_BAR);
+ assertEquals(new Rect(0, 0, 500, 100), statusBarProvider.getSource().getFrame());
+ assertEquals(Insets.of(0, 100, 0, 0),
+ statusBarProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500),
+ false /* ignoreVisibility */));
+
+ InsetsSourceProvider topGesturesProvider = controller.getSourceProvider(ITYPE_TOP_GESTURES);
+ assertEquals(new Rect(0, 0, 500, 100), topGesturesProvider.getSource().getFrame());
+ assertEquals(Insets.of(0, 100, 0, 0),
+ topGesturesProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500),
+ false /* ignoreVisibility */));
+
+ InsetsSourceProvider navigationBarProvider = controller.getSourceProvider(
+ ITYPE_NAVIGATION_BAR);
+ assertNotEquals(new Rect(0, 0, 500, 100), navigationBarProvider.getSource().getFrame());
+ }
+
+ @Test
+ public void addingWindow_ignoresInsetsTypes_InWindowTypeWithPredefinedInsets() {
+ mDisplayPolicy.removeWindowLw(mStatusBarWindow); // Removes the existing one.
+ WindowState win = createWindow(null, TYPE_STATUS_BAR, "StatusBar");
+ win.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR};
+ win.getFrameLw().set(0, 0, 500, 100);
+
+ addWindow(win);
+ mDisplayContent.getInsetsStateController().onPostLayout();
+
+ InsetsSourceProvider provider =
+ mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_STATUS_BAR);
+ assertNotEquals(new Rect(0, 0, 500, 100), provider.getSource().getFrame());
+ }
+
+ @Test
+ public void addingWindow_throwsException_WithMultipleInsetTypes() {
+ WindowState win = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "StatusBarSubPanel");
+ win.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR};
+
+ expectThrows(IllegalArgumentException.class, () -> addWindow(win));
+ }
+
+ @Test
public void layoutWindowLw_fitStatusBars() {
assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 277bc41cf34e..67b1dacbb47b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -27,6 +27,8 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.app.WindowConfiguration;
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
@@ -109,5 +111,27 @@ public class RootWindowContainerTests extends WindowTestsBase {
assertEquals(WindowConfiguration.WINDOWING_MODE_FREEFORM,
mWm.getDefaultDisplayContentLocked().getWindowingMode());
}
+
+ /**
+ * This test ensures that an existing single instance activity with alias name can be found by
+ * the same activity info. So {@link ActivityStarter#getReusableTask} won't miss it that leads
+ * to create an unexpected new instance.
+ */
+ @Test
+ public void testFindActivityByTargetComponent() {
+ final ComponentName aliasComponent = ComponentName.createRelative(
+ ActivityTestsBase.DEFAULT_COMPONENT_PACKAGE_NAME, ".AliasActivity");
+ final ComponentName targetComponent = ComponentName.createRelative(
+ aliasComponent.getPackageName(), ".TargetActivity");
+ final ActivityRecord activity = new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
+ .setComponent(aliasComponent)
+ .setTargetActivity(targetComponent.getClassName())
+ .setLaunchMode(ActivityInfo.LAUNCH_SINGLE_INSTANCE)
+ .setCreateTask(true)
+ .build();
+
+ assertEquals(activity, mWm.mRoot.findActivity(activity.intent, activity.info,
+ false /* compareIntentFilters */));
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index ba4a82fc2d54..edf81ea32ad3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -107,10 +107,16 @@ public class SizeCompatTests extends ActivityTestsBase {
setUpApp(display);
// Put app window into freeform and then make it a compat app.
- mTask.setBounds(100, 100, 400, 600);
+ final Rect bounds = new Rect(100, 100, 400, 600);
+ mTask.setBounds(bounds);
prepareUnresizable(-1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
+ assertEquals(bounds, mActivity.getBounds());
+
+ // The activity should be able to accept negative x position [-150, 100 - 150, 600].
+ final int dx = bounds.left + bounds.width() / 2;
+ mTask.setBounds(bounds.left - dx, bounds.top, bounds.right - dx, bounds.bottom);
+ assertEquals(mTask.getBounds(), mActivity.getBounds());
- final Rect bounds = new Rect(mActivity.getBounds());
final int density = mActivity.getConfiguration().densityDpi;
// change display configuration to fullscreen
@@ -231,11 +237,7 @@ public class SizeCompatTests extends ActivityTestsBase {
// The position should be horizontal centered.
assertEquals((displayWidth - bounds.width()) / 2, bounds.left);
- final WindowTestUtils.TestWindowState w = new WindowTestUtils.TestWindowState(
- mService.mWindowManager, mock(Session.class), new TestIWindow(),
- new WindowManager.LayoutParams(), mActivity);
- mActivity.addWindow(w);
- mActivity.mDisplayContent.mInputMethodTarget = w;
+ mActivity.mDisplayContent.mInputMethodTarget = addWindowToActivity(mActivity);
// Make sure IME cannot attach to the app, otherwise IME window will also be shifted.
assertFalse(mActivity.mDisplayContent.isImeAttachedToApp());
}
@@ -475,6 +477,25 @@ public class SizeCompatTests extends ActivityTestsBase {
assertEquals((int) (dw * maxAspect), mActivity.getBounds().width());
// The bounds should be horizontal centered: (2500-1900)/2=350.
assertEquals((dh - mActivity.getBounds().width()) / 2, mActivity.getBounds().left);
+
+ // The letterbox needs a main window to layout.
+ addWindowToActivity(mActivity);
+ // Compute the frames of the window and invoke {@link ActivityRecord#layoutLetterbox}.
+ mActivity.mRootWindowContainer.performSurfacePlacement(false /* recoveringMemory */);
+ // The letterbox insets should be [350, 0 - 350, 0].
+ assertEquals(new Rect(mActivity.getBounds().left, 0, dh - mActivity.getBounds().right, 0),
+ mActivity.getLetterboxInsets());
+ }
+
+ private WindowState addWindowToActivity(ActivityRecord activity) {
+ final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
+ params.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+ final WindowTestUtils.TestWindowState w = new WindowTestUtils.TestWindowState(
+ mService.mWindowManager, mock(Session.class), new TestIWindow(), params, mActivity);
+ WindowTestsBase.makeWindowVisible(w);
+ w.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
+ mActivity.addWindow(w);
+ return w;
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 560d03fe7d9e..091f493b4687 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -322,6 +322,8 @@ public class SystemServicesTestRule implements TestRule {
}
private void tearDown() {
+ mWmService.mRoot.forAllDisplayPolicies(DisplayPolicy::release);
+
// Unregister display listener from root to avoid issues with subsequent tests.
mContext.getSystemService(DisplayManager.class)
.unregisterDisplayListener(mAtmService.mRootWindowContainer);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
index 19ed7a9101de..8019e9df5143 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
@@ -32,6 +32,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -277,7 +278,7 @@ public class TaskOrganizerTests extends WindowTestsBase {
}
@Test
- public void testContainerChanges() {
+ public void testContainerFocusableChanges() {
removeGlobalMinSizeRestriction();
final ActivityStack stack = new ActivityTestsBase.StackBuilder(mWm.mRoot)
.setWindowingMode(WINDOWING_MODE_FREEFORM).build();
@@ -287,6 +288,24 @@ public class TaskOrganizerTests extends WindowTestsBase {
t.setFocusable(stack.mRemoteToken, false);
mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
assertFalse(task.isFocusable());
+ t.setFocusable(stack.mRemoteToken, true);
+ mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
+ assertTrue(task.isFocusable());
+ }
+
+ @Test
+ public void testContainerHiddenChanges() {
+ removeGlobalMinSizeRestriction();
+ final ActivityStack stack = new ActivityTestsBase.StackBuilder(mWm.mRoot)
+ .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+ WindowContainerTransaction t = new WindowContainerTransaction();
+ assertTrue(stack.shouldBeVisible(null));
+ t.setHidden(stack.mRemoteToken, true);
+ mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
+ assertFalse(stack.shouldBeVisible(null));
+ t.setHidden(stack.mRemoteToken, false);
+ mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
+ assertTrue(stack.shouldBeVisible(null));
}
@Test
@@ -550,6 +569,7 @@ public class TaskOrganizerTests extends WindowTestsBase {
final Task task = createTaskInStack(stackController1, 0 /* userId */);
final ITaskOrganizer organizer = registerMockOrganizer();
final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window");
+ makeWindowVisible(w);
BLASTSyncEngine bse = new BLASTSyncEngine();
@@ -567,6 +587,60 @@ public class TaskOrganizerTests extends WindowTestsBase {
.transactionReady(anyInt(), any());
}
+ @Test
+ public void testBLASTCallbackWithInvisibleWindow() {
+ final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
+ final Task task = createTaskInStack(stackController1, 0 /* userId */);
+ final ITaskOrganizer organizer = registerMockOrganizer();
+ final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window");
+
+ BLASTSyncEngine bse = new BLASTSyncEngine();
+
+ BLASTSyncEngine.TransactionReadyListener transactionListener =
+ mock(BLASTSyncEngine.TransactionReadyListener.class);
+
+ int id = bse.startSyncSet(transactionListener);
+ bse.addToSyncSet(id, task);
+ bse.setReady(id);
+
+ // Since the window was invisible, the Task had no visible leaves and the sync should
+ // complete as soon as we call setReady.
+ verify(transactionListener)
+ .transactionReady(anyInt(), any());
+ }
+
+ @Test
+ public void testBLASTCallbackWithChildWindow() {
+ final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
+ final Task task = createTaskInStack(stackController1, 0 /* userId */);
+ final ITaskOrganizer organizer = registerMockOrganizer();
+ final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window");
+ final WindowState child = createWindow(w, TYPE_APPLICATION, "Other Window");
+
+ w.mActivityRecord.setVisible(true);
+ makeWindowVisible(w, child);
+
+ BLASTSyncEngine bse = new BLASTSyncEngine();
+
+ BLASTSyncEngine.TransactionReadyListener transactionListener =
+ mock(BLASTSyncEngine.TransactionReadyListener.class);
+
+ int id = bse.startSyncSet(transactionListener);
+ assertEquals(true, bse.addToSyncSet(id, task));
+ bse.setReady(id);
+ w.finishDrawing(null);
+
+ // Since we have a child window we still shouldn't be done.
+ verify(transactionListener, never())
+ .transactionReady(anyInt(), any());
+ reset(transactionListener);
+
+ child.finishDrawing(null);
+ // Ah finally! Done
+ verify(transactionListener)
+ .transactionReady(anyInt(), any());
+ }
+
class StubOrganizer extends ITaskOrganizer.Stub {
RunningTaskInfo mInfo;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java
index 2fb0b4c24ba2..8f913ddcc5b4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java
@@ -81,9 +81,9 @@ public class TaskSnapshotLowResDisabledTest extends TaskSnapshotPersisterTestBas
assertEquals(TEST_INSETS, snapshot.getContentInsets());
assertNotNull(snapshot.getSnapshot());
assertEquals(Configuration.ORIENTATION_PORTRAIT, snapshot.getOrientation());
+ assertNull(mLoader.loadTask(1, mTestUserId, true /* isLowResolution */));
}
-
@Test
public void testRemoveObsoleteFiles() {
mPersister.persistSnapshot(1, mTestUserId, createSnapshot());
@@ -132,10 +132,18 @@ public class TaskSnapshotLowResDisabledTest extends TaskSnapshotPersisterTestBas
assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
false /* restoreFromDisk */, false /* isLowResolution */));
- // Load it from disk
+ // Attempt to load the low-res snapshot from the disk
assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
true /* restoreFromDisk */, true /* isLowResolution */));
+ // Load the high-res (default) snapshot from disk
+ assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
+ true /* restoreFromDisk */, false /* isLowResolution */));
+
+ // Make sure it's not in the cache now.
+ assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
+ false /* restoreFromDisk */, true /* isLowResolution */));
+
// Make sure it's not in the cache now.
assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
false /* restoreFromDisk */, false /* isLowResolution */));
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
index 9a9abbac1fd1..bfee894c3fc4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
@@ -293,35 +293,6 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa
}
@Test
- public void testDisabledLowResolutionPersistAndLoadSnapshot() {
- mPersister.setEnableLowResSnapshots(false);
-
- TaskSnapshot a = new TaskSnapshotBuilder()
- .setScaleFraction(0.5f)
- .setIsLowResolution(true)
- .build();
- assertTrue(a.isLowResolution());
- mPersister.persistSnapshot(1, mTestUserId, a);
- mPersister.waitForQueueEmpty();
- final File[] files = new File[]{new File(FILES_DIR.getPath() + "/snapshots/1.proto"),
- new File(FILES_DIR.getPath() + "/snapshots/1.jpg")};
- final File[] nonExistsFiles = new File[]{
- new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg"),
- };
- assertTrueForFiles(files, File::exists, " must exist");
- assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist");
- final TaskSnapshot snapshot = mLoader.loadTask(1, mTestUserId, false /* isLowResolution */);
- assertNotNull(snapshot);
- assertEquals(TEST_INSETS, snapshot.getContentInsets());
- assertNotNull(snapshot.getSnapshot());
- assertEquals(Configuration.ORIENTATION_PORTRAIT, snapshot.getOrientation());
-
- final TaskSnapshot snapshotNotExist = mLoader.loadTask(1, mTestUserId,
- true /* isLowResolution */);
- assertNull(snapshotNotExist);
- }
-
- @Test
public void testIsRealSnapshotPersistAndLoadSnapshot() {
TaskSnapshot a = new TaskSnapshotBuilder()
.setIsRealSnapshot(true)
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
index 6d78658a0550..88de34dd36b0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
@@ -127,7 +127,6 @@ class TaskSnapshotPersisterTestBase extends WindowTestsBase {
private static final int SNAPSHOT_HEIGHT = 100;
private float mScaleFraction = 1f;
- private boolean mIsLowResolution = false;
private boolean mIsRealSnapshot = true;
private boolean mIsTranslucent = false;
private int mWindowingMode = WINDOWING_MODE_FULLSCREEN;
@@ -142,11 +141,6 @@ class TaskSnapshotPersisterTestBase extends WindowTestsBase {
return this;
}
- TaskSnapshotBuilder setIsLowResolution(boolean isLowResolution) {
- mIsLowResolution = isLowResolution;
- return this;
- }
-
TaskSnapshotBuilder setIsRealSnapshot(boolean isRealSnapshot) {
mIsRealSnapshot = isRealSnapshot;
return this;
@@ -186,8 +180,11 @@ class TaskSnapshotPersisterTestBase extends WindowTestsBase {
return new TaskSnapshot(MOCK_SNAPSHOT_ID, new ComponentName("", ""), buffer,
ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT,
mRotation, taskSize, TEST_INSETS,
- mIsLowResolution, mIsRealSnapshot,
- mWindowingMode, mSystemUiVisibility, mIsTranslucent);
+ // When building a TaskSnapshot with the Builder class, isLowResolution
+ // is always false. Low-res snapshots are only created when loading from
+ // disk.
+ false /* isLowResolution */,
+ mIsRealSnapshot, mWindowingMode, mSystemUiVisibility, mIsTranslucent);
}
}
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index ef9a73b794f7..9d48955c87be 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -98,8 +98,7 @@ public class UsageStatsDatabase {
// Persist versioned backup files.
// Should be false, except when testing new versions
- // STOPSHIP: b/139937606 this should be false on launch
- static final boolean KEEP_BACKUP_DIR = true;
+ static final boolean KEEP_BACKUP_DIR = false;
private static final String TAG = "UsageStatsDatabase";
private static final boolean DEBUG = UsageStatsService.DEBUG;
@@ -412,6 +411,7 @@ public class UsageStatsDatabase {
}
if (mBackupsDir.exists() && !KEEP_BACKUP_DIR) {
+ mUpgradePerformed = true; // updated here to ensure that data is cleaned up
deleteDirectory(mBackupsDir);
}
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 0d1b3523bf9b..bbe9851520a1 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -137,8 +137,7 @@ public class UsageStatsService extends SystemService implements
private static final File USAGE_STATS_LEGACY_DIR = new File(
Environment.getDataSystemDirectory(), "usagestats");
// For migration purposes, indicates whether to keep the legacy usage stats directory or not
- // STOPSHIP: b/138323140 this should be false on launch
- private static final boolean KEEP_LEGACY_DIR = true;
+ private static final boolean KEEP_LEGACY_DIR = false;
private static final char TOKEN_DELIMITER = '/';
@@ -648,7 +647,7 @@ public class UsageStatsService extends SystemService implements
private void deleteLegacyDir(int userId) {
final File legacyUserDir = new File(USAGE_STATS_LEGACY_DIR, Integer.toString(userId));
- if (!KEEP_LEGACY_DIR) {
+ if (!KEEP_LEGACY_DIR && legacyUserDir.exists()) {
deleteRecursively(legacyUserDir);
if (legacyUserDir.exists()) {
Slog.w(TAG, "Error occurred while attempting to delete legacy usage stats "
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 8e85bb23b5c7..747c8d9d0890 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -445,7 +445,6 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
abstract static class UsbHandler extends Handler {
// current USB state
- private boolean mConnected;
private boolean mHostConnected;
private boolean mSourcePower;
private boolean mSinkPower;
@@ -473,6 +472,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
private final UsbPermissionManager mPermissionManager;
private NotificationManager mNotificationManager;
+ protected boolean mConnected;
protected long mScreenUnlockedFunctions;
protected boolean mBootCompleted;
protected boolean mCurrentFunctionsApplied;
@@ -1794,7 +1794,8 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
case MSG_SET_FUNCTIONS_TIMEOUT:
Slog.e(TAG, "Set functions timed out! no reply from usb hal");
if (msg.arg1 != 1) {
- setEnabledFunctions(UsbManager.FUNCTION_NONE, false);
+ // Set this since default function may be selected from Developer options
+ setEnabledFunctions(mScreenUnlockedFunctions, false);
}
break;
case MSG_GET_CURRENT_USB_FUNCTIONS:
@@ -1816,7 +1817,8 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
* Dont force to default when the configuration is already set to default.
*/
if (msg.arg1 != 1) {
- setEnabledFunctions(UsbManager.FUNCTION_NONE, !isAdbEnabled());
+ // Set this since default function may be selected from Developer options
+ setEnabledFunctions(mScreenUnlockedFunctions, false);
}
break;
case MSG_GADGET_HAL_REGISTERED:
@@ -1936,8 +1938,11 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
SET_FUNCTIONS_TIMEOUT_MS - SET_FUNCTIONS_LEEWAY_MS);
sendMessageDelayed(MSG_SET_FUNCTIONS_TIMEOUT, chargingFunctions,
SET_FUNCTIONS_TIMEOUT_MS);
- sendMessageDelayed(MSG_FUNCTION_SWITCH_TIMEOUT, chargingFunctions,
- SET_FUNCTIONS_TIMEOUT_MS + ENUMERATION_TIME_OUT_MS);
+ if (mConnected) {
+ // Only queue timeout of enumeration when the USB is connected
+ sendMessageDelayed(MSG_FUNCTION_SWITCH_TIMEOUT, chargingFunctions,
+ SET_FUNCTIONS_TIMEOUT_MS + ENUMERATION_TIME_OUT_MS);
+ }
if (DEBUG) Slog.d(TAG, "timeout message queued");
} catch (RemoteException e) {
Slog.e(TAG, "Remoteexception while calling setCurrentUsbFunctions", e);
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index 767010ae821a..3196758996be 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -19,6 +19,7 @@ package com.android.server.soundtrigger;
import static android.Manifest.permission.BIND_SOUND_TRIGGER_DETECTION_SERVICE;
import static android.content.Context.BIND_AUTO_CREATE;
import static android.content.Context.BIND_FOREGROUND_SERVICE;
+import static android.content.Context.BIND_INCLUDE_CAPABILITIES;
import static android.content.pm.PackageManager.GET_META_DATA;
import static android.content.pm.PackageManager.GET_SERVICES;
import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
@@ -1133,7 +1134,8 @@ public class SoundTriggerService extends SystemService {
}
mIsBound = mContext.bindServiceAsUser(i, this,
- BIND_AUTO_CREATE | BIND_FOREGROUND_SERVICE, mUser);
+ BIND_AUTO_CREATE | BIND_FOREGROUND_SERVICE | BIND_INCLUDE_CAPABILITIES,
+ mUser);
if (mIsBound) {
mRemoteServiceWakeLock.acquire();
diff --git a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
index d9ae48f6b833..b3d7c0d36763 100644
--- a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
+++ b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
@@ -30,6 +30,7 @@ import android.telephony.TelephonyManager;
import android.util.ArrayMap;
import android.util.Log;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.util.ArrayUtils;
@@ -162,7 +163,7 @@ public final class CarrierAppUtils {
for (ApplicationInfo ai : candidates) {
String packageName = ai.packageName;
String[] restrictedCarrierApps = Resources.getSystem().getStringArray(
- android.R.array.config_restrictedPreinstalledCarrierApps);
+ R.array.config_restrictedPreinstalledCarrierApps);
boolean hasPrivileges = telephonyManager != null
&& telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName)
== TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS
diff --git a/telephony/common/com/android/internal/telephony/GsmAlphabet.java b/telephony/common/com/android/internal/telephony/GsmAlphabet.java
index c62cec270440..5c53f7e5a4d0 100644
--- a/telephony/common/com/android/internal/telephony/GsmAlphabet.java
+++ b/telephony/common/com/android/internal/telephony/GsmAlphabet.java
@@ -19,10 +19,12 @@ package com.android.internal.telephony;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.Resources;
import android.os.Build;
-import android.text.TextUtils;
import android.util.Log;
+import android.text.TextUtils;
import android.util.SparseIntArray;
+import com.android.internal.R;
+
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
@@ -1087,10 +1089,8 @@ public class GsmAlphabet {
private static void enableCountrySpecificEncodings() {
Resources r = Resources.getSystem();
// See comments in frameworks/base/core/res/res/values/config.xml for allowed values
- sEnabledSingleShiftTables = r.getIntArray(
- android.R.array.config_sms_enabled_single_shift_tables);
- sEnabledLockingShiftTables = r.getIntArray(
- android.R.array.config_sms_enabled_locking_shift_tables);
+ sEnabledSingleShiftTables = r.getIntArray(R.array.config_sms_enabled_single_shift_tables);
+ sEnabledLockingShiftTables = r.getIntArray(R.array.config_sms_enabled_locking_shift_tables);
if (sEnabledSingleShiftTables.length > 0) {
sHighestEnabledSingleShiftCode =
diff --git a/telephony/common/com/google/android/mms/util/SqliteWrapper.java b/telephony/common/com/google/android/mms/util/SqliteWrapper.java
index 4871434ebc31..31fe4d7683d6 100644
--- a/telephony/common/com/google/android/mms/util/SqliteWrapper.java
+++ b/telephony/common/com/google/android/mms/util/SqliteWrapper.java
@@ -60,7 +60,8 @@ public final class SqliteWrapper {
@UnsupportedAppUsage
public static void checkSQLiteException(Context context, SQLiteException e) {
if (isLowMemory(e)) {
- Toast.makeText(context, android.R.string.low_memory, Toast.LENGTH_SHORT).show();
+ Toast.makeText(context, com.android.internal.R.string.low_memory,
+ Toast.LENGTH_SHORT).show();
} else {
throw e;
}
diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java
index dcd4eb519b74..28cc7d0378a1 100644
--- a/telephony/java/android/telephony/Annotation.java
+++ b/telephony/java/android/telephony/Annotation.java
@@ -598,48 +598,6 @@ public class Annotation {
}
/**
- * Call forwarding function status
- */
- @IntDef(prefix = { "STATUS_" }, value = {
- CallForwardingInfo.STATUS_ACTIVE,
- CallForwardingInfo.STATUS_INACTIVE,
- CallForwardingInfo.STATUS_UNKNOWN_ERROR,
- CallForwardingInfo.STATUS_NOT_SUPPORTED,
- CallForwardingInfo.STATUS_FDN_CHECK_FAILURE
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface CallForwardingStatus {
- }
-
- /**
- * Call forwarding reason types
- */
- @IntDef(flag = true, prefix = { "REASON_" }, value = {
- CallForwardingInfo.REASON_UNCONDITIONAL,
- CallForwardingInfo.REASON_BUSY,
- CallForwardingInfo.REASON_NO_REPLY,
- CallForwardingInfo.REASON_NOT_REACHABLE,
- CallForwardingInfo.REASON_ALL,
- CallForwardingInfo.REASON_ALL_CONDITIONAL
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface CallForwardingReason {
- }
-
- /**
- * Call waiting function status
- */
- @IntDef(prefix = { "CALL_WAITING_STATUS_" }, value = {
- TelephonyManager.CALL_WAITING_STATUS_ACTIVE,
- TelephonyManager.CALL_WAITING_STATUS_INACTIVE,
- TelephonyManager.CALL_WAITING_STATUS_NOT_SUPPORTED,
- TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface CallWaitingStatus {
- }
-
- /**
* UICC SIM Application Types
*/
@IntDef(prefix = { "APPTYPE_" }, value = {
diff --git a/telephony/java/android/telephony/CallForwardingInfo.java b/telephony/java/android/telephony/CallForwardingInfo.java
index 1dd7539420ac..7e777fae46eb 100644
--- a/telephony/java/android/telephony/CallForwardingInfo.java
+++ b/telephony/java/android/telephony/CallForwardingInfo.java
@@ -15,24 +15,24 @@
*/
package android.telephony;
+
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
-import android.telephony.Annotation.CallForwardingReason;
-import android.telephony.Annotation.CallForwardingStatus;
import com.android.telephony.Rlog;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
* Defines the call forwarding information.
* @hide
*/
-@SystemApi
public final class CallForwardingInfo implements Parcelable {
private static final String TAG = "CallForwardingInfo";
@@ -41,7 +41,6 @@ public final class CallForwardingInfo implements Parcelable {
*
* @hide
*/
- @SystemApi
public static final int STATUS_INACTIVE = 0;
/**
@@ -49,7 +48,6 @@ public final class CallForwardingInfo implements Parcelable {
*
* @hide
*/
- @SystemApi
public static final int STATUS_ACTIVE = 1;
/**
@@ -58,7 +56,6 @@ public final class CallForwardingInfo implements Parcelable {
*
* @hide
*/
- @SystemApi
public static final int STATUS_FDN_CHECK_FAILURE = 2;
/**
@@ -66,7 +63,6 @@ public final class CallForwardingInfo implements Parcelable {
*
* @hide
*/
- @SystemApi
public static final int STATUS_UNKNOWN_ERROR = 3;
/**
@@ -74,7 +70,6 @@ public final class CallForwardingInfo implements Parcelable {
*
* @hide
*/
- @SystemApi
public static final int STATUS_NOT_SUPPORTED = 4;
/**
@@ -83,7 +78,6 @@ public final class CallForwardingInfo implements Parcelable {
* and conditions +CCFC
* @hide
*/
- @SystemApi
public static final int REASON_UNCONDITIONAL = 0;
/**
@@ -92,7 +86,6 @@ public final class CallForwardingInfo implements Parcelable {
* and conditions +CCFC
* @hide
*/
- @SystemApi
public static final int REASON_BUSY = 1;
/**
@@ -101,7 +94,6 @@ public final class CallForwardingInfo implements Parcelable {
* and conditions +CCFC
* @hide
*/
- @SystemApi
public static final int REASON_NO_REPLY = 2;
/**
@@ -110,7 +102,6 @@ public final class CallForwardingInfo implements Parcelable {
* and conditions +CCFC
* @hide
*/
- @SystemApi
public static final int REASON_NOT_REACHABLE = 3;
/**
@@ -120,7 +111,6 @@ public final class CallForwardingInfo implements Parcelable {
* and conditions +CCFC
* @hide
*/
- @SystemApi
public static final int REASON_ALL = 4;
/**
@@ -130,20 +120,48 @@ public final class CallForwardingInfo implements Parcelable {
* and conditions +CCFC
* @hide
*/
- @SystemApi
public static final int REASON_ALL_CONDITIONAL = 5;
/**
+ * Call forwarding function status
+ */
+ @IntDef(prefix = { "STATUS_" }, value = {
+ STATUS_ACTIVE,
+ STATUS_INACTIVE,
+ STATUS_UNKNOWN_ERROR,
+ STATUS_NOT_SUPPORTED,
+ STATUS_FDN_CHECK_FAILURE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CallForwardingStatus {
+ }
+
+ /**
+ * Call forwarding reason types
+ */
+ @IntDef(flag = true, prefix = { "REASON_" }, value = {
+ REASON_UNCONDITIONAL,
+ REASON_BUSY,
+ REASON_NO_REPLY,
+ REASON_NOT_REACHABLE,
+ REASON_ALL,
+ REASON_ALL_CONDITIONAL
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CallForwardingReason {
+ }
+
+ /**
* The call forwarding status.
*/
- private @CallForwardingStatus int mStatus;
+ private int mStatus;
/**
* The call forwarding reason indicates the condition under which calls will be forwarded.
* Reference: 3GPP TS 27.007 version 10.3.0 Release 10 - 7.11 Call forwarding number
* and conditions +CCFC
*/
- private @CallForwardingReason int mReason;
+ private int mReason;
/**
* The phone number to which calls will be forwarded.
@@ -166,7 +184,6 @@ public final class CallForwardingInfo implements Parcelable {
* @param timeSeconds the timeout (in seconds) before the forwarding is attempted
* @hide
*/
- @SystemApi
public CallForwardingInfo(@CallForwardingStatus int status, @CallForwardingReason int reason,
@Nullable String number, int timeSeconds) {
mStatus = status;
@@ -182,7 +199,6 @@ public final class CallForwardingInfo implements Parcelable {
*
* @hide
*/
- @SystemApi
public @CallForwardingStatus int getStatus() {
return mStatus;
}
@@ -196,7 +212,6 @@ public final class CallForwardingInfo implements Parcelable {
*
* @hide
*/
- @SystemApi
public @CallForwardingReason int getReason() {
return mReason;
}
@@ -209,7 +224,6 @@ public final class CallForwardingInfo implements Parcelable {
*
* @hide
*/
- @SystemApi
@Nullable
public String getNumber() {
return mNumber;
@@ -227,7 +241,6 @@ public final class CallForwardingInfo implements Parcelable {
*
* @hide
*/
- @SystemApi
@SuppressLint("MethodNameUnits")
public int getTimeoutSeconds() {
return mTimeSeconds;
diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java
index 34bac5de4c43..3984bd769edd 100644
--- a/telephony/java/android/telephony/ImsManager.java
+++ b/telephony/java/android/telephony/ImsManager.java
@@ -19,9 +19,7 @@ package android.telephony.ims;
import android.annotation.NonNull;
import android.annotation.SdkConstant;
import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
import android.annotation.SystemService;
-import android.annotation.TestApi;
import android.content.Context;
import android.telephony.SubscriptionManager;
@@ -44,8 +42,6 @@ public class ImsManager {
* issues.
* @hide
*/
- @SystemApi
- @TestApi
// Moved from TelephonyIntents, need to keep backwards compatibility with OEM apps that have
// this value hard-coded in BroadcastReceiver.
@SuppressLint("ActionValue")
diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java
index 1a79bf7d0c22..4940cb2b143e 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -297,7 +297,7 @@ public final class NetworkRegistrationInfo implements Parcelable {
mDataSpecificInfo = new DataSpecificRegistrationInfo(
maxDataCalls, isDcNrRestricted, isNrAvailable, isEndcAvailable, lteVopsSupportInfo,
isUsingCarrierAggregation);
- updateNrState(mDataSpecificInfo);
+ updateNrState();
}
private NetworkRegistrationInfo(Parcel source) {
@@ -686,12 +686,12 @@ public final class NetworkRegistrationInfo implements Parcelable {
* DCNR is not restricted and NR is supported by the selected PLMN. Otherwise the use of 5G
* NR is restricted.
*
- * @param state data specific registration state contains the 5G NR indicators.
+ * @hide
*/
- private void updateNrState(DataSpecificRegistrationInfo state) {
+ public void updateNrState() {
mNrState = NR_STATE_NONE;
- if (state.isEnDcAvailable) {
- if (!state.isDcNrRestricted && state.isNrAvailable) {
+ if (mDataSpecificInfo.isEnDcAvailable) {
+ if (!mDataSpecificInfo.isDcNrRestricted && mDataSpecificInfo.isNrAvailable) {
mNrState = NR_STATE_NOT_RESTRICTED;
} else {
mNrState = NR_STATE_RESTRICTED;
diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java
index e37a9b9e7a8c..c2cfbef98740 100644
--- a/telephony/java/android/telephony/PreciseDataConnectionState.java
+++ b/telephony/java/android/telephony/PreciseDataConnectionState.java
@@ -95,7 +95,6 @@ public final class PreciseDataConnectionState implements Parcelable {
* if there is no valid APN setting for the specific type, then this will be null
* @hide
*/
- @SystemApi
public PreciseDataConnectionState(@DataState int state,
@NetworkType int networkType,
@ApnType int apnTypes, @NonNull String apn,
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index dd20d065fc83..deba5519ce22 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -1650,7 +1650,6 @@ public class ServiceState implements Parcelable {
* @return Current data network type
* @hide
*/
- @SystemApi
@TestApi
public @NetworkType int getDataNetworkType() {
final NetworkRegistrationInfo iwlanRegInfo = getNetworkRegistrationInfo(
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index d3afa4a133a6..6aea5eeca3fd 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -975,11 +975,7 @@ public final class SmsManager {
*
* @param packageName serves as the default package name if the package name that is
* associated with the user id is null.
- *
- * @hide
*/
- @SystemApi
- @TestApi
public void sendMultipartTextMessage(
@NonNull String destinationAddress, @Nullable String scAddress,
@NonNull List<String> parts, @Nullable List<PendingIntent> sentIntents,
@@ -2898,7 +2894,7 @@ public final class SmsManager {
getSubscriptionId(), null);
}
} catch (RemoteException ex) {
- // ignore it
+ throw new RuntimeException(ex);
}
return smsc;
}
@@ -2920,7 +2916,7 @@ public final class SmsManager {
* </p>
*
* @param smsc the SMSC address string.
- * @return true for success, false otherwise.
+ * @return true for success, false otherwise. Failure can be due modem returning an error.
*/
@SuppressAutoDoc // for carrier privileges and default SMS application.
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -2932,7 +2928,7 @@ public final class SmsManager {
smsc, getSubscriptionId(), null);
}
} catch (RemoteException ex) {
- // ignore it
+ throw new RuntimeException(ex);
}
return false;
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 610ec5eee897..5339fb900480 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -70,13 +70,12 @@ import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telephony.Annotation.ApnType;
-import android.telephony.Annotation.CallForwardingReason;
import android.telephony.Annotation.CallState;
-import android.telephony.Annotation.CallWaitingStatus;
import android.telephony.Annotation.NetworkType;
import android.telephony.Annotation.RadioPowerState;
import android.telephony.Annotation.SimActivationState;
import android.telephony.Annotation.UiccAppType;
+import android.telephony.CallForwardingInfo.CallForwardingReason;
import android.telephony.VisualVoicemailService.VisualVoicemailTask;
import android.telephony.data.ApnSetting;
import android.telephony.data.ApnSetting.MvnoType;
@@ -1565,6 +1564,7 @@ public class TelephonyManager {
* @hide
*/
@SystemApi
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@SuppressLint("ActionValue")
public static final String ACTION_EMERGENCY_CALLBACK_MODE_CHANGED =
"android.intent.action.EMERGENCY_CALLBACK_MODE_CHANGED";
@@ -1786,6 +1786,7 @@ public class TelephonyManager {
* @hide
*/
@SystemApi
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@SuppressLint("ActionValue")
public static final String ACTION_EMERGENCY_CALL_STATE_CHANGED =
"android.intent.action.EMERGENCY_CALL_STATE_CHANGED";
@@ -5280,8 +5281,8 @@ public class TelephonyManager {
* not present or not loaded
* @hide
*/
+ @UnsupportedAppUsage
@Nullable
- @SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public String[] getIsimImpu() {
try {
@@ -9189,7 +9190,6 @@ public class TelephonyManager {
*
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public @CdmaRoamingMode int getCdmaRoamingMode() {
int mode = CDMA_ROAMING_MODE_RADIO_DEFAULT;
@@ -9218,7 +9218,6 @@ public class TelephonyManager {
*
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public boolean setCdmaRoamingMode(@CdmaRoamingMode int mode) {
try {
@@ -9244,19 +9243,16 @@ public class TelephonyManager {
/** Used for CDMA subscription mode, it'll be UNKNOWN if there is no Subscription source.
* @hide
*/
- @SystemApi
public static final int CDMA_SUBSCRIPTION_UNKNOWN = -1;
/** Used for CDMA subscription mode: RUIM/SIM (default)
* @hide
*/
- @SystemApi
public static final int CDMA_SUBSCRIPTION_RUIM_SIM = 0;
/** Used for CDMA subscription mode: NV -> non-volatile memory
* @hide
*/
- @SystemApi
public static final int CDMA_SUBSCRIPTION_NV = 1;
/** @hide */
@@ -9275,7 +9271,6 @@ public class TelephonyManager {
*
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public boolean setCdmaSubscriptionMode(@CdmaSubscription int mode) {
try {
@@ -9983,18 +9978,30 @@ public class TelephonyManager {
}
/**
- * Gets the default Respond Via Message application
- * @param context context from the calling app
- * @param updateIfNeeded update the default app if there is no valid default app configured.
+ * Gets the default Respond Via Message application, updating the cache if there is no
+ * respond-via-message application currently configured.
+ * @return component name of the app and class to direct Respond Via Message intent to, or
+ * {@code null} if the functionality is not supported.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+ public @Nullable ComponentName getAndUpdateDefaultRespondViaMessageApplication() {
+ return SmsApplication.getDefaultRespondViaMessageApplication(mContext, true);
+ }
+
+ /**
+ * Gets the default Respond Via Message application.
* @return component name of the app and class to direct Respond Via Message intent to, or
* {@code null} if the functionality is not supported.
* @hide
*/
@SystemApi
@TestApi
- public static @Nullable ComponentName getDefaultRespondViaMessageApplication(
- @NonNull Context context, boolean updateIfNeeded) {
- return SmsApplication.getDefaultRespondViaMessageApplication(context, updateIfNeeded);
+ @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+ public @Nullable ComponentName getDefaultRespondViaMessageApplication() {
+ return SmsApplication.getDefaultRespondViaMessageApplication(mContext, false);
}
/**
@@ -11171,6 +11178,8 @@ public class TelephonyManager {
* PackageManager.FEATURE_TELEPHONY system feature, which is available
* on any device with a telephony radio, even if the device is
* voice-only.
+ *
+ * @hide
*/
public boolean isDataCapable() {
if (mContext == null) return true;
@@ -12706,7 +12715,6 @@ public class TelephonyManager {
*
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@NonNull
public CallForwardingInfo getCallForwarding(@CallForwardingReason int callForwardingReason) {
@@ -12753,7 +12761,6 @@ public class TelephonyManager {
*
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public boolean setCallForwarding(@NonNull CallForwardingInfo callForwardingInfo) {
if (callForwardingInfo == null) {
@@ -12794,7 +12801,6 @@ public class TelephonyManager {
*
* @hide
*/
- @SystemApi
public static final int CALL_WAITING_STATUS_ACTIVE = 1;
/**
@@ -12802,7 +12808,6 @@ public class TelephonyManager {
*
* @hide
*/
- @SystemApi
public static final int CALL_WAITING_STATUS_INACTIVE = 2;
/**
@@ -12810,7 +12815,6 @@ public class TelephonyManager {
*
* @hide
*/
- @SystemApi
public static final int CALL_WAITING_STATUS_UNKNOWN_ERROR = 3;
/**
@@ -12818,10 +12822,24 @@ public class TelephonyManager {
*
* @hide
*/
- @SystemApi
public static final int CALL_WAITING_STATUS_NOT_SUPPORTED = 4;
/**
+ * Call waiting function status
+ *
+ * @hide
+ */
+ @IntDef(prefix = { "CALL_WAITING_STATUS_" }, value = {
+ CALL_WAITING_STATUS_ACTIVE,
+ CALL_WAITING_STATUS_INACTIVE,
+ CALL_WAITING_STATUS_NOT_SUPPORTED,
+ CALL_WAITING_STATUS_UNKNOWN_ERROR
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CallWaitingStatus {
+ }
+
+ /**
* Gets the status of voice call waiting function. Call waiting function enables the waiting
* for the incoming call when it reaches the user who is busy to make another call and allows
* users to decide whether to switch to the incoming call.
@@ -12829,7 +12847,6 @@ public class TelephonyManager {
* @return the status of call waiting function.
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public @CallWaitingStatus int getCallWaitingStatus() {
try {
@@ -12855,7 +12872,6 @@ public class TelephonyManager {
*
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public boolean setCallWaitingStatus(boolean isEnable) {
try {
@@ -12886,7 +12902,6 @@ public class TelephonyManager {
*
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public boolean setDataAllowedDuringVoiceCall(boolean allow) {
try {
@@ -12915,7 +12930,6 @@ public class TelephonyManager {
*
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isDataAllowedInVoiceCall() {
try {
@@ -12962,7 +12976,6 @@ public class TelephonyManager {
* The IccLock state or password was changed successfully.
* @hide
*/
- @SystemApi
public static final int CHANGE_ICC_LOCK_SUCCESS = Integer.MAX_VALUE;
/**
@@ -12975,7 +12988,6 @@ public class TelephonyManager {
*
* @hide
*/
- @SystemApi
@WorkerThread
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isIccLockEnabled() {
@@ -13012,7 +13024,6 @@ public class TelephonyManager {
*
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public int setIccLockEnabled(boolean enabled, @NonNull String password) {
checkNotNull(password, "setIccLockEnabled password can't be null.");
@@ -13046,7 +13057,6 @@ public class TelephonyManager {
*
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public int changeIccLockPassword(@NonNull String oldPassword, @NonNull String newPassword) {
checkNotNull(oldPassword, "changeIccLockPassword oldPassword can't be null.");
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index 4b5303f42be0..bd531daff6b9 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -21,7 +21,6 @@ import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SuppressAutoDoc;
import android.annotation.SuppressLint;
@@ -124,7 +123,7 @@ public class ImsMmTelManager implements RegistrationManager {
* @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
*/
@Override
- public void onUnregistered(@Nullable ImsReasonInfo info) {
+ public void onUnregistered(@NonNull ImsReasonInfo info) {
}
/**
@@ -136,7 +135,7 @@ public class ImsMmTelManager implements RegistrationManager {
@Override
public void onTechnologyChangeFailed(
@AccessNetworkConstants.TransportType int imsTransportType,
- @Nullable ImsReasonInfo info) {
+ @NonNull ImsReasonInfo info) {
}
}
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index 00fa94201297..1a606b7ae6a7 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -85,6 +85,7 @@ public class ProvisioningManager {
/**
* There is no existing configuration for the queried provisioning key.
+ * @hide
*/
public static final int PROVISIONING_RESULT_UNKNOWN = -1;
@@ -120,6 +121,7 @@ public class ProvisioningManager {
* Value is in String format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_AMR_CODEC_MODE_SET_VALUES = 0;
@@ -143,6 +145,7 @@ public class ProvisioningManager {
* Value is in String format.
* @see #setProvisioningStringValue(int, String)
* @see #getProvisioningStringValue(int)
+ * @hide
*/
public static final int KEY_AMR_WB_CODEC_MODE_SET_VALUES = 1;
@@ -154,6 +157,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_SIP_SESSION_TIMER_SEC = 2;
@@ -165,6 +169,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_MINIMUM_SIP_SESSION_EXPIRATION_TIMER_SEC = 3;
@@ -176,6 +181,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_SIP_INVITE_CANCELLATION_TIMER_MS = 4;
@@ -184,6 +190,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_TRANSITION_TO_LTE_DELAY_MS = 5;
@@ -192,6 +199,7 @@ public class ProvisioningManager {
* Value is in boolean format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_ENABLE_SILENT_REDIAL = 6;
@@ -207,6 +215,7 @@ public class ProvisioningManager {
* The value is an integer.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_T1_TIMER_VALUE_MS = 7;
@@ -218,6 +227,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_T2_TIMER_VALUE_MS = 8;
@@ -229,6 +239,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_TF_TIMER_VALUE_MS = 9;
@@ -241,6 +252,7 @@ public class ProvisioningManager {
* {@link #PROVISIONING_VALUE_DISABLED} to disable VoLTE provisioning.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_VOLTE_PROVISIONING_STATUS = 10;
@@ -253,6 +265,7 @@ public class ProvisioningManager {
* {@link #PROVISIONING_VALUE_DISABLED} to disable VT provisioning.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_VT_PROVISIONING_STATUS = 11;
@@ -261,6 +274,7 @@ public class ProvisioningManager {
* Value is in String format.
* @see #setProvisioningStringValue(int, String)
* @see #getProvisioningStringValue(int)
+ * @hide
*/
public static final int KEY_REGISTRATION_DOMAIN_NAME = 12;
@@ -270,18 +284,21 @@ public class ProvisioningManager {
* Valid values are {@link #SMS_FORMAT_3GPP} and {@link #SMS_FORMAT_3GPP2}.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_SMS_FORMAT = 13;
/**
* Value used with {@link #KEY_SMS_FORMAT} to indicate 3GPP2 SMS format is used.
* See {@link android.telephony.SmsMessage#FORMAT_3GPP2} for more information.
+ * @hide
*/
public static final int SMS_FORMAT_3GPP2 = 0;
/**
* Value used with {@link #KEY_SMS_FORMAT} to indicate 3GPP SMS format is used.
* See {@link android.telephony.SmsMessage#FORMAT_3GPP} for more information.
+ * @hide
*/
public static final int SMS_FORMAT_3GPP = 1;
@@ -290,6 +307,7 @@ public class ProvisioningManager {
* Value is in Integer format. ON (1), OFF(0).
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_SMS_OVER_IP_ENABLED = 14;
@@ -300,6 +318,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_RCS_PUBLISH_TIMER_SEC = 15;
@@ -310,6 +329,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_RCS_PUBLISH_OFFLINE_AVAILABILITY_TIMER_SEC = 16;
@@ -322,6 +342,7 @@ public class ProvisioningManager {
* enabled.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_RCS_CAPABILITY_DISCOVERY_ENABLED = 17;
@@ -334,6 +355,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC = 18;
@@ -345,6 +367,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC = 19;
@@ -356,6 +379,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_RCS_CAPABILITIES_POLL_INTERVAL_SEC = 20;
@@ -366,6 +390,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS = 21;
@@ -376,6 +401,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_RCS_MAX_NUM_ENTRIES_IN_RCL = 22;
@@ -387,6 +413,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_RCS_CAPABILITY_POLL_LIST_SUB_EXP_SEC = 23;
@@ -395,6 +422,7 @@ public class ProvisioningManager {
* Value is in Integer format. Enable (1), Disable(0).
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_USE_GZIP_FOR_LIST_SUBSCRIPTION = 24;
@@ -407,6 +435,7 @@ public class ProvisioningManager {
* {@link #PROVISIONING_VALUE_DISABLED} to disable EAB provisioning.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_EAB_PROVISIONING_STATUS = 25;
@@ -440,6 +469,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE = 28;
@@ -448,6 +478,7 @@ public class ProvisioningManager {
* Value is in Integer format. On (1), OFF(0).
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_MOBILE_DATA_ENABLED = 29;
@@ -456,12 +487,14 @@ public class ProvisioningManager {
* Value is in Integer format. Opted-in (1) Opted-out (0).
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_VOLTE_USER_OPT_IN_STATUS = 30;
/**
* Proxy for Call Session Control Function(P-CSCF) address for Local-BreakOut(LBO).
* Value is in String format.
+ * @hide
*/
public static final int KEY_LOCAL_BREAKOUT_PCSCF_ADDRESS = 31;
@@ -470,6 +503,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_SIP_KEEP_ALIVE_ENABLED = 32;
@@ -479,6 +513,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_REGISTRATION_RETRY_BASE_TIME_SEC = 33;
@@ -488,6 +523,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_REGISTRATION_RETRY_MAX_TIME_SEC = 34;
@@ -496,6 +532,7 @@ public class ProvisioningManager {
* Value is in integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_RTP_SPEECH_START_PORT = 35;
@@ -505,6 +542,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_RTP_SPEECH_END_PORT = 36;
@@ -514,6 +552,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_SIP_INVITE_REQUEST_TRANSMIT_INTERVAL_MS = 37;
@@ -523,6 +562,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_SIP_INVITE_ACK_WAIT_TIME_MS = 38;
@@ -532,6 +572,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_WAIT_TIME_MS = 39;
@@ -541,6 +582,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMIT_INTERVAL_MS = 40;
@@ -550,6 +592,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_SIP_NON_INVITE_TRANSACTION_TIMEOUT_TIMER_MS = 41;
@@ -559,6 +602,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_INTERVAL_MS = 42;
@@ -568,6 +612,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_SIP_ACK_RECEIPT_WAIT_TIME_MS = 43;
@@ -577,6 +622,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_SIP_ACK_RETRANSMIT_WAIT_TIME_MS = 44;
@@ -586,6 +632,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMISSION_WAIT_TIME_MS = 45;
@@ -595,6 +642,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_SIP_NON_INVITE_RESPONSE_RETRANSMISSION_WAIT_TIME_MS = 46;
@@ -603,6 +651,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_AMR_WB_OCTET_ALIGNED_PAYLOAD_TYPE = 47;
@@ -611,6 +660,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_AMR_WB_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 48;
@@ -619,6 +669,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_AMR_OCTET_ALIGNED_PAYLOAD_TYPE = 49;
@@ -627,6 +678,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_AMR_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 50;
@@ -635,6 +687,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_DTMF_WB_PAYLOAD_TYPE = 51;
@@ -643,6 +696,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_DTMF_NB_PAYLOAD_TYPE = 52;
@@ -651,12 +705,14 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_AMR_DEFAULT_ENCODING_MODE = 53;
/**
* SMS Public Service Identity.
* Value is in String format.
+ * @hide
*/
public static final int KEY_SMS_PUBLIC_SERVICE_IDENTITY = 54;
@@ -666,16 +722,19 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_VIDEO_QUALITY = 55;
/**
* Used with {@link #KEY_VIDEO_QUALITY} to indicate low video quality.
+ * @hide
*/
public static final int VIDEO_QUALITY_LOW = 0;
/**
* Used with {@link #KEY_VIDEO_QUALITY} to indicate high video quality.
+ * @hide
*/
public static final int VIDEO_QUALITY_HIGH = 1;
@@ -685,6 +744,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_LTE_THRESHOLD_1 = 56;
@@ -696,6 +756,7 @@ public class ProvisioningManager {
*
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_LTE_THRESHOLD_2 = 57;
@@ -707,6 +768,7 @@ public class ProvisioningManager {
*
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_LTE_THRESHOLD_3 = 58;
@@ -716,6 +778,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_1X_THRESHOLD = 59;
@@ -727,6 +790,7 @@ public class ProvisioningManager {
*
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_WIFI_THRESHOLD_A = 60;
@@ -737,6 +801,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_WIFI_THRESHOLD_B = 61;
@@ -746,6 +811,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_LTE_EPDG_TIMER_SEC = 62;
@@ -755,12 +821,14 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_WIFI_EPDG_TIMER_SEC = 63;
/**
* 1x ePDG timer (in seconds).
* Device shall not re-register on 1x until the T_ePDG_1x timer expires.
+ * @hide
*/
public static final int KEY_1X_EPDG_TIMER_SEC = 64;
@@ -769,6 +837,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_MULTIENDPOINT_ENABLED = 65;
@@ -777,6 +846,7 @@ public class ProvisioningManager {
* Value is in Integer format.
* @see #setProvisioningIntValue(int, int)
* @see #getProvisioningIntValue(int)
+ * @hide
*/
public static final int KEY_RTT_ENABLED = 66;
@@ -928,7 +998,7 @@ public class ProvisioningManager {
*
* @param key An integer that represents the provisioning key, which is defined by the OEM.
* @return an integer value for the provided key, or
- * {@link #PROVISIONING_RESULT_UNKNOWN} if the key doesn't exist.
+ * {@link ImsConfigImplBase#CONFIG_RESULT_UNKNOWN} if the key doesn't exist.
* @throws IllegalArgumentException if the key provided was invalid.
*/
@WorkerThread
diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
index 57b9b7a30f8c..dc36edf5aad9 100644
--- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java
+++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
@@ -19,8 +19,6 @@ package android.telephony.ims;
import android.annotation.LongDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.annotation.TestApi;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
@@ -37,8 +35,6 @@ import java.util.Map;
* Contains the User Capability Exchange capabilities corresponding to a contact's URI.
* @hide
*/
-@SystemApi
-@TestApi
public final class RcsContactUceCapability implements Parcelable {
/** Supports 1-to-1 chat */
diff --git a/telephony/java/android/telephony/ims/RegistrationManager.java b/telephony/java/android/telephony/ims/RegistrationManager.java
index 5c86ba732701..1dbaff5df802 100644
--- a/telephony/java/android/telephony/ims/RegistrationManager.java
+++ b/telephony/java/android/telephony/ims/RegistrationManager.java
@@ -196,11 +196,11 @@ public interface RegistrationManager {
}
/**
- * Notifies the framework when the IMS Provider is deregistered from the IMS network.
+ * Notifies the framework when the IMS Provider is unregistered from the IMS network.
*
* @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
*/
- public void onUnregistered(@Nullable ImsReasonInfo info) {
+ public void onUnregistered(@NonNull ImsReasonInfo info) {
}
/**
@@ -211,7 +211,7 @@ public interface RegistrationManager {
*/
public void onTechnologyChangeFailed(
@AccessNetworkConstants.TransportType int imsTransportType,
- @Nullable ImsReasonInfo info) {
+ @NonNull ImsReasonInfo info) {
}
/**
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index 14a64d2585ed..7069e0ab9b1e 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -175,9 +175,11 @@ public class ImsRegistrationImplBase {
*/
public final void onDeregistered(ImsReasonInfo info) {
updateToDisconnectedState(info);
+ // ImsReasonInfo should never be null.
+ final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo();
mCallbacks.broadcastAction((c) -> {
try {
- c.onDeregistered(info);
+ c.onDeregistered(reasonInfo);
} catch (RemoteException e) {
Log.w(LOG_TAG, e + " " + "onRegistrationDisconnected() - Skipping " +
"callback.");
@@ -194,9 +196,10 @@ public class ImsRegistrationImplBase {
*/
public final void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
ImsReasonInfo info) {
+ final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo();
mCallbacks.broadcastAction((c) -> {
try {
- c.onTechnologyChangeFailed(imsRadioTech, info);
+ c.onTechnologyChangeFailed(imsRadioTech, reasonInfo);
} catch (RemoteException e) {
Log.w(LOG_TAG, e + " " + "onRegistrationChangeFailed() - Skipping " +
"callback.");
diff --git a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
index f13371c1d0fa..8564f7affd6d 100644
--- a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
@@ -44,52 +44,62 @@ import java.lang.annotation.RetentionPolicy;
public class ImsUtImplBase {
/**
* Bar all incoming calls. (See 3GPP TS 24.611)
+ * @hide
*/
public static final int CALL_BARRING_ALL_INCOMING = 1;
/**
* Bar all outgoing calls. (See 3GPP TS 24.611)
+ * @hide
*/
public static final int CALL_BARRING_ALL_OUTGOING = 2;
/**
* Bar all outgoing international calls. (See 3GPP TS 24.611)
+ * @hide
*/
public static final int CALL_BARRING_OUTGOING_INTL = 3;
/**
* Bar all outgoing international calls, excluding those to the home PLMN country
* (See 3GPP TS 24.611)
+ * @hide
*/
public static final int CALL_BARRING_OUTGOING_INTL_EXCL_HOME = 4;
/**
* Bar all incoming calls when roaming (See 3GPP TS 24.611)
+ * @hide
*/
public static final int CALL_BLOCKING_INCOMING_WHEN_ROAMING = 5;
/**
* Enable Anonymous Communication Rejection (See 3GPP TS 24.611)
+ * @hide
*/
public static final int CALL_BARRING_ANONYMOUS_INCOMING = 6;
/**
* Bar all incoming and outgoing calls. (See 3GPP TS 24.611)
+ * @hide
*/
public static final int CALL_BARRING_ALL = 7;
/**
* Bar all outgoing service requests, including calls. (See 3GPP TS 24.611)
+ * @hide
*/
public static final int CALL_BARRING_OUTGOING_ALL_SERVICES = 8;
/**
* Bar all incoming service requests, including calls. (See 3GPP TS 24.611)
+ * @hide
*/
public static final int CALL_BARRING_INCOMING_ALL_SERVICES = 9;
/**
* Bar specific incoming calls. (See 3GPP TS 24.611)
+ * @hide
*/
public static final int CALL_BARRING_SPECIFIC_INCOMING_CALLS = 10;
@@ -104,6 +114,7 @@ public class ImsUtImplBase {
/**
* Constant used to denote an invalid return value.
+ * @hide
*/
public static final int INVALID_RESULT = -1;
@@ -338,6 +349,7 @@ public class ImsUtImplBase {
/**
* Updates the configuration of the call barring for specified service class with password.
+ * @hide
*/
public int updateCallBarringWithPassword(int cbType, int action, @Nullable String[] barrList,
int serviceClass, @NonNull String password) {
diff --git a/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java b/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java
index 504bd1727682..b805744a8387 100644
--- a/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java
+++ b/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java
@@ -38,38 +38,75 @@ import java.util.concurrent.TimeUnit;
public class DummyBlobData {
private static final long DEFAULT_SIZE_BYTES = 10 * 1024L * 1024L;
- private final Context mContext;
private final Random mRandom;
private final File mFile;
private final long mFileSize;
- private final String mLabel;
+ private final CharSequence mLabel;
byte[] mFileDigest;
long mExpiryTimeMs;
- public DummyBlobData(Context context) {
- this(context, new Random(0), "blob_" + System.nanoTime());
+ public DummyBlobData(Builder builder) {
+ mRandom = new Random(builder.getRandomSeed());
+ mFile = new File(builder.getContext().getFilesDir(), builder.getFileName());
+ mFileSize = builder.getFileSize();
+ mLabel = builder.getLabel();
}
- public DummyBlobData(Context context, long fileSize) {
- this(context, fileSize, new Random(0), "blob_" + System.nanoTime(), "Test label");
- }
+ public static class Builder {
+ private final Context mContext;
+ private int mRandomSeed = 0;
+ private long mFileSize = DEFAULT_SIZE_BYTES;
+ private CharSequence mLabel = "Test label";
+ private String mFileName = "blob_" + System.nanoTime();
- public DummyBlobData(Context context, Random random, String fileName) {
- this(context, DEFAULT_SIZE_BYTES, random, fileName, "Test label");
- }
+ public Builder(Context context) {
+ mContext = context;
+ }
- public DummyBlobData(Context context, Random random, String fileName, String label) {
- this(context, DEFAULT_SIZE_BYTES, random, fileName, label);
- }
+ public Context getContext() {
+ return mContext;
+ }
+
+ public Builder setRandomSeed(int randomSeed) {
+ mRandomSeed = randomSeed;
+ return this;
+ }
+
+ public int getRandomSeed() {
+ return mRandomSeed;
+ }
+
+ public Builder setFileSize(int fileSize) {
+ mFileSize = fileSize;
+ return this;
+ }
- public DummyBlobData(Context context, long fileSize, Random random, String fileName,
- String label) {
- mContext = context;
- mRandom = random;
- mFile = new File(mContext.getFilesDir(), fileName);
- mFileSize = fileSize;
- mLabel = label;
+ public long getFileSize() {
+ return mFileSize;
+ }
+
+ public Builder setLabel(CharSequence label) {
+ mLabel = label;
+ return this;
+ }
+
+ public CharSequence getLabel() {
+ return mLabel;
+ }
+
+ public Builder setFileName(String fileName) {
+ mFileName = fileName;
+ return this;
+ }
+
+ public String getFileName() {
+ return mFileName;
+ }
+
+ public DummyBlobData build() {
+ return new DummyBlobData(this);
+ }
}
public void prepare() throws Exception {
diff --git a/tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java b/tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java
index c35385cd0429..654c1e21999d 100644
--- a/tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java
+++ b/tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java
@@ -16,7 +16,13 @@
package com.android.utils.blob;
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.blob.BlobHandle;
import android.app.blob.BlobStoreManager;
+import android.app.blob.LeaseInfo;
+import android.content.Context;
+import android.content.res.Resources;
import android.os.ParcelFileDescriptor;
import java.io.FileInputStream;
@@ -56,4 +62,76 @@ public class Utils {
copy(in, out, lengthBytes);
}
}
+
+ public static void assertLeasedBlobs(BlobStoreManager blobStoreManager,
+ BlobHandle... expectedBlobHandles) throws IOException {
+ assertThat(blobStoreManager.getLeasedBlobs()).containsExactly(expectedBlobHandles);
+ }
+
+ public static void assertNoLeasedBlobs(BlobStoreManager blobStoreManager)
+ throws IOException {
+ assertThat(blobStoreManager.getLeasedBlobs()).isEmpty();
+ }
+
+ public static void acquireLease(Context context,
+ BlobHandle blobHandle, CharSequence description) throws IOException {
+ final BlobStoreManager blobStoreManager = (BlobStoreManager) context.getSystemService(
+ Context.BLOB_STORE_SERVICE);
+ blobStoreManager.acquireLease(blobHandle, description);
+
+ final LeaseInfo leaseInfo = blobStoreManager.getLeaseInfo(blobHandle);
+ assertLeaseInfo(leaseInfo, context.getPackageName(), 0,
+ Resources.ID_NULL, description);
+ }
+
+ public static void acquireLease(Context context,
+ BlobHandle blobHandle, int descriptionResId) throws IOException {
+ final BlobStoreManager blobStoreManager = (BlobStoreManager) context.getSystemService(
+ Context.BLOB_STORE_SERVICE);
+ blobStoreManager.acquireLease(blobHandle, descriptionResId);
+
+ final LeaseInfo leaseInfo = blobStoreManager.getLeaseInfo(blobHandle);
+ assertLeaseInfo(leaseInfo, context.getPackageName(), 0,
+ descriptionResId, context.getString(descriptionResId));
+ }
+
+ public static void acquireLease(Context context,
+ BlobHandle blobHandle, CharSequence description,
+ long expiryTimeMs) throws IOException {
+ final BlobStoreManager blobStoreManager = (BlobStoreManager) context.getSystemService(
+ Context.BLOB_STORE_SERVICE);
+ blobStoreManager.acquireLease(blobHandle, description, expiryTimeMs);
+
+ final LeaseInfo leaseInfo = blobStoreManager.getLeaseInfo(blobHandle);
+ assertLeaseInfo(leaseInfo, context.getPackageName(), expiryTimeMs,
+ Resources.ID_NULL, description);
+ }
+
+ public static void acquireLease(Context context,
+ BlobHandle blobHandle, int descriptionResId,
+ long expiryTimeMs) throws IOException {
+ final BlobStoreManager blobStoreManager = (BlobStoreManager) context.getSystemService(
+ Context.BLOB_STORE_SERVICE);
+ blobStoreManager.acquireLease(blobHandle, descriptionResId, expiryTimeMs);
+
+ final LeaseInfo leaseInfo = blobStoreManager.getLeaseInfo(blobHandle);
+ assertLeaseInfo(leaseInfo, context.getPackageName(), expiryTimeMs,
+ descriptionResId, context.getString(descriptionResId));
+ }
+
+ public static void releaseLease(Context context,
+ BlobHandle blobHandle) throws IOException {
+ final BlobStoreManager blobStoreManager = (BlobStoreManager) context.getSystemService(
+ Context.BLOB_STORE_SERVICE);
+ blobStoreManager.releaseLease(blobHandle);
+ assertThat(blobStoreManager.getLeaseInfo(blobHandle)).isNull();
+ }
+
+ private static void assertLeaseInfo(LeaseInfo leaseInfo, String packageName,
+ long expiryTimeMs, int descriptionResId, CharSequence description) {
+ assertThat(leaseInfo.getPackageName()).isEqualTo(packageName);
+ assertThat(leaseInfo.getExpiryTimeMillis()).isEqualTo(expiryTimeMs);
+ assertThat(leaseInfo.getDescriptionResId()).isEqualTo(descriptionResId);
+ assertThat(leaseInfo.getDescription()).isEqualTo(description);
+ }
}
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index 8cc8cf4d2a97..dfaac2cde91d 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -1063,6 +1063,20 @@ public class PackageWatchdogTest {
assertThat(bootObserver2.mitigatedBootLoop()).isFalse();
}
+ /**
+ * Ensure that passing a null list of failed packages does not cause any mitigation logic to
+ * execute.
+ */
+ @Test
+ public void testNullFailedPackagesList() {
+ PackageWatchdog watchdog = createWatchdog();
+ TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
+ watchdog.startObservingHealth(observer1, List.of(APP_A), LONG_DURATION);
+
+ raiseFatalFailureAndDispatch(watchdog, null, PackageWatchdog.FAILURE_REASON_APP_CRASH);
+ assertThat(observer1.mMitigatedPackages).isEmpty();
+ }
+
private void adoptShellPermissions(String... permissions) {
InstrumentationRegistry
.getInstrumentation()
diff --git a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostTest.java b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostTest.java
index 61696718c76e..3bc530975580 100644
--- a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostTest.java
+++ b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostTest.java
@@ -66,7 +66,7 @@ public class SurfaceControlViewHostTest extends Activity implements SurfaceHolde
WindowManager.LayoutParams lp =
new WindowManager.LayoutParams(500, 500, WindowManager.LayoutParams.TYPE_APPLICATION,
0, PixelFormat.OPAQUE);
- mVr.addView(v, lp);
+ mVr.setView(v, lp);
}
@Override
diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
index e41517085c36..f254e4d3267a 100644
--- a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
+++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
@@ -34,9 +34,7 @@ import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
-import android.view.Window;
import android.view.WindowInsets;
-import android.view.WindowInsets.Type;
import android.view.WindowInsetsAnimation;
import android.view.WindowInsetsAnimation.Callback;
import android.view.WindowInsetsAnimationControlListener;
@@ -101,7 +99,7 @@ public class WindowInsetsActivity extends AppCompatActivity {
&& !mRequestedController) {
mRequestedController = true;
v.getWindowInsetsController().controlWindowInsetsAnimation(ime(),
- 1000, new LinearInterpolator(),
+ 1000, new LinearInterpolator(), null /* cancellationSignal */,
mCurrentRequest = new WindowInsetsAnimationControlListener() {
@Override
public void onReady(
@@ -208,7 +206,7 @@ public class WindowInsetsActivity extends AppCompatActivity {
if ((types & ime()) != 0 && !hasControl) {
hasControl = true;
controller.controlWindowInsetsAnimation(ime(), -1,
- new LinearInterpolator(),
+ new LinearInterpolator(), null /* cancellationSignal */,
new WindowInsetsAnimationControlListener() {
@Override
public void onReady(
diff --git a/tests/net/AndroidManifest.xml b/tests/net/AndroidManifest.xml
index 480b12be91c8..009f817af407 100644
--- a/tests/net/AndroidManifest.xml
+++ b/tests/net/AndroidManifest.xml
@@ -47,6 +47,7 @@
<uses-permission android:name="android.permission.NETWORK_STACK" />
<uses-permission android:name="android.permission.OBSERVE_NETWORK_POLICY" />
<uses-permission android:name="android.permission.NETWORK_FACTORY" />
+ <uses-permission android:name="android.permission.NETWORK_STATS_PROVIDER" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/tests/net/common/java/android/net/LinkAddressTest.java b/tests/net/common/java/android/net/LinkAddressTest.java
index 99dac1439b34..c74c112490f8 100644
--- a/tests/net/common/java/android/net/LinkAddressTest.java
+++ b/tests/net/common/java/android/net/LinkAddressTest.java
@@ -367,6 +367,9 @@ public class LinkAddressTest {
-2, 100000L);
fail("negative deprecation time should cause exception");
} catch (IllegalArgumentException expected) { }
+
+ LinkAddress addr = new LinkAddress(V6_ADDRESS, 64, 0, 456, 100000L, 200000L);
+ assertEquals(100000L, addr.getDeprecationTime());
}
@Test @IgnoreUpTo(Build.VERSION_CODES.Q)
@@ -382,6 +385,9 @@ public class LinkAddressTest {
100000L, -2);
fail("negative expiration time should cause exception");
} catch (IllegalArgumentException expected) { }
+
+ LinkAddress addr = new LinkAddress(V6_ADDRESS, 64, 0, 456, 100000L, 200000L);
+ assertEquals(200000L, addr.getExpirationTime());
}
@Test
diff --git a/tests/net/common/java/android/net/NetworkAgentConfigTest.kt b/tests/net/common/java/android/net/NetworkAgentConfigTest.kt
index 173dbd1271f8..de65ba24972b 100644
--- a/tests/net/common/java/android/net/NetworkAgentConfigTest.kt
+++ b/tests/net/common/java/android/net/NetworkAgentConfigTest.kt
@@ -22,6 +22,9 @@ import androidx.test.runner.AndroidJUnit4
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
import com.android.testutils.assertParcelSane
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -43,4 +46,27 @@ class NetworkAgentConfigTest {
}.build()
assertParcelSane(config, 9)
}
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ fun testBuilder() {
+ val config = NetworkAgentConfig.Builder().apply {
+ setExplicitlySelected(true)
+ setLegacyType(ConnectivityManager.TYPE_ETHERNET)
+ setSubscriberId("MySubId")
+ setPartialConnectivityAcceptable(false)
+ setUnvalidatedConnectivityAcceptable(true)
+ setLegacyTypeName("TEST_NETWORK")
+ disableNat64Detection()
+ disableProvisioningNotification()
+ }.build()
+
+ assertTrue(config.isExplicitlySelected())
+ assertEquals(ConnectivityManager.TYPE_ETHERNET, config.getLegacyType())
+ assertEquals("MySubId", config.getSubscriberId())
+ assertFalse(config.isPartialConnectivityAcceptable())
+ assertTrue(config.isUnvalidatedConnectivityAcceptable())
+ assertEquals("TEST_NETWORK", config.getLegacyTypeName())
+ assertFalse(config.isNat64DetectionEnabled())
+ assertFalse(config.isProvisioningNotificationEnabled())
+ }
}
diff --git a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
index 8eb5cfa48c1a..1d6c10766792 100644
--- a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
@@ -304,12 +304,12 @@ public class ConnectivityDiagnosticsManagerTest {
}
@Test
- public void testConnectivityDiagnosticsCallbackOnConnectivityReport() {
- mBinder.onConnectivityReport(createSampleConnectivityReport());
+ public void testConnectivityDiagnosticsCallbackOnConnectivityReportAvailable() {
+ mBinder.onConnectivityReportAvailable(createSampleConnectivityReport());
// The callback will be invoked synchronously by inline executor. Immediately check the
// latch without waiting.
- verify(mCb).onConnectivityReport(eq(createSampleConnectivityReport()));
+ verify(mCb).onConnectivityReportAvailable(eq(createSampleConnectivityReport()));
}
@Test
diff --git a/tests/net/java/android/net/NetworkStatsTest.java b/tests/net/java/android/net/NetworkStatsTest.java
index 33d77d288e15..e71d5995255d 100644
--- a/tests/net/java/android/net/NetworkStatsTest.java
+++ b/tests/net/java/android/net/NetworkStatsTest.java
@@ -64,15 +64,15 @@ public class NetworkStatsTest {
@Test
public void testFindIndex() throws Exception {
final NetworkStats stats = new NetworkStats(TEST_START, 5)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 1024L, 8L, 0L, 0L, 10)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 0L, 0L, 1024L, 8L, 11)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
DEFAULT_NETWORK_YES, 0L, 0L, 1024L, 8L, 11)
- .addEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 1024L, 8L, 1024L, 8L, 12)
- .addEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
+ .insertEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
DEFAULT_NETWORK_YES, 1024L, 8L, 1024L, 8L, 12);
assertEquals(4, stats.findIndex(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_YES,
@@ -94,21 +94,21 @@ public class NetworkStatsTest {
@Test
public void testFindIndexHinted() {
final NetworkStats stats = new NetworkStats(TEST_START, 3)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 1024L, 8L, 0L, 0L, 10)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 0L, 0L, 1024L, 8L, 11)
- .addEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 1024L, 8L, 1024L, 8L, 12)
- .addEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 1024L, 8L, 0L, 0L, 10)
- .addEntry(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 0L, 0L, 1024L, 8L, 11)
- .addEntry(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO,
+ .insertEntry(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO,
DEFAULT_NETWORK_NO, 0L, 0L, 1024L, 8L, 11)
- .addEntry(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 1024L, 8L, 1024L, 8L, 12)
- .addEntry(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
+ .insertEntry(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
DEFAULT_NETWORK_NO, 1024L, 8L, 1024L, 8L, 12);
// verify that we correctly find across regardless of hinting
@@ -143,27 +143,27 @@ public class NetworkStatsTest {
assertEquals(0, stats.size());
assertEquals(4, stats.internalSize());
- stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 1L, 1L, 2L, 2L, 3);
- stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 2L, 2L, 2L, 2L, 4);
- stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+ stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
DEFAULT_NETWORK_YES, 3L, 3L, 2L, 2L, 5);
- stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
+ stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
DEFAULT_NETWORK_NO, 3L, 3L, 2L, 2L, 5);
assertEquals(4, stats.size());
assertEquals(4, stats.internalSize());
- stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 4L, 40L, 4L, 40L, 7);
- stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 5L, 50L, 4L, 40L, 8);
- stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 6L, 60L, 5L, 50L, 10);
- stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+ stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
DEFAULT_NETWORK_YES, 7L, 70L, 5L, 50L, 11);
- stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
+ stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
DEFAULT_NETWORK_NO, 7L, 70L, 5L, 50L, 11);
assertEquals(9, stats.size());
@@ -193,8 +193,8 @@ public class NetworkStatsTest {
public void testCombineExisting() throws Exception {
final NetworkStats stats = new NetworkStats(TEST_START, 10);
- stats.addEntry(TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 10);
- stats.addEntry(TEST_IFACE, 1001, SET_DEFAULT, 0xff, 128L, 1L, 128L, 1L, 2);
+ stats.insertEntry(TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 10);
+ stats.insertEntry(TEST_IFACE, 1001, SET_DEFAULT, 0xff, 128L, 1L, 128L, 1L, 2);
stats.combineValues(TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, -128L, -1L,
-128L, -1L, -1);
@@ -215,12 +215,12 @@ public class NetworkStatsTest {
@Test
public void testSubtractIdenticalData() throws Exception {
final NetworkStats before = new NetworkStats(TEST_START, 2)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
final NetworkStats after = new NetworkStats(TEST_START, 2)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
final NetworkStats result = after.subtract(before);
@@ -234,12 +234,12 @@ public class NetworkStatsTest {
@Test
public void testSubtractIdenticalRows() throws Exception {
final NetworkStats before = new NetworkStats(TEST_START, 2)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
final NetworkStats after = new NetworkStats(TEST_START, 2)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1025L, 9L, 2L, 1L, 15)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 3L, 1L, 1028L, 9L, 20);
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1025L, 9L, 2L, 1L, 15)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 3L, 1L, 1028L, 9L, 20);
final NetworkStats result = after.subtract(before);
@@ -253,13 +253,13 @@ public class NetworkStatsTest {
@Test
public void testSubtractNewRows() throws Exception {
final NetworkStats before = new NetworkStats(TEST_START, 2)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
final NetworkStats after = new NetworkStats(TEST_START, 3)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12)
- .addEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 20);
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12)
+ .insertEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 20);
final NetworkStats result = after.subtract(before);
@@ -275,11 +275,11 @@ public class NetworkStatsTest {
@Test
public void testSubtractMissingRows() throws Exception {
final NetworkStats before = new NetworkStats(TEST_START, 2)
- .addEntry(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 1024L, 0L, 0L, 0L, 0)
- .addEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 2048L, 0L, 0L, 0L, 0);
+ .insertEntry(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 1024L, 0L, 0L, 0L, 0)
+ .insertEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 2048L, 0L, 0L, 0L, 0);
final NetworkStats after = new NetworkStats(TEST_START, 1)
- .addEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 2049L, 2L, 3L, 4L, 0);
+ .insertEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 2049L, 2L, 3L, 4L, 0);
final NetworkStats result = after.subtract(before);
@@ -293,40 +293,40 @@ public class NetworkStatsTest {
@Test
public void testTotalBytes() throws Exception {
final NetworkStats iface = new NetworkStats(TEST_START, 2)
- .addEntry(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 128L, 0L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 256L, 0L, 0L, 0L, 0L);
+ .insertEntry(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 128L, 0L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 256L, 0L, 0L, 0L, 0L);
assertEquals(384L, iface.getTotalBytes());
final NetworkStats uidSet = new NetworkStats(TEST_START, 3)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 101, SET_FOREGROUND, TAG_NONE, 32L, 0L, 0L, 0L, 0L);
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE, 101, SET_FOREGROUND, TAG_NONE, 32L, 0L, 0L, 0L, 0L);
assertEquals(96L, uidSet.getTotalBytes());
final NetworkStats uidTag = new NetworkStats(TEST_START, 6)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L);
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L);
assertEquals(64L, uidTag.getTotalBytes());
final NetworkStats uidMetered = new NetworkStats(TEST_START, 3)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L);
assertEquals(96L, uidMetered.getTotalBytes());
final NetworkStats uidRoaming = new NetworkStats(TEST_START, 3)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L);
assertEquals(96L, uidRoaming.getTotalBytes());
}
@@ -343,11 +343,11 @@ public class NetworkStatsTest {
@Test
public void testGroupedByIfaceAll() throws Exception {
final NetworkStats uidStats = new NetworkStats(TEST_START, 3)
- .addEntry(IFACE_ALL, 100, SET_ALL, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(IFACE_ALL, 100, SET_ALL, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L)
- .addEntry(IFACE_ALL, 101, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_NO,
+ .insertEntry(IFACE_ALL, 101, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_NO,
DEFAULT_NETWORK_NO, 128L, 8L, 0L, 2L, 20L)
- .addEntry(IFACE_ALL, 101, SET_ALL, TAG_NONE, METERED_NO, ROAMING_YES,
+ .insertEntry(IFACE_ALL, 101, SET_ALL, TAG_NONE, METERED_NO, ROAMING_YES,
DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L);
final NetworkStats grouped = uidStats.groupedByIface();
@@ -361,19 +361,19 @@ public class NetworkStatsTest {
@Test
public void testGroupedByIface() throws Exception {
final NetworkStats uidStats = new NetworkStats(TEST_START, 7)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L)
- .addEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 512L, 32L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 64L, 4L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 512L, 32L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 128L, 8L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO,
DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
DEFAULT_NETWORK_YES, 128L, 8L, 0L, 0L, 0L);
final NetworkStats grouped = uidStats.groupedByIface();
@@ -390,19 +390,19 @@ public class NetworkStatsTest {
@Test
public void testAddAllValues() {
final NetworkStats first = new NetworkStats(TEST_START, 5)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES,
+ .insertEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES,
DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L);
final NetworkStats second = new NetworkStats(TEST_START, 2)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES,
+ .insertEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES,
DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L);
first.combineAllValues(second);
@@ -421,19 +421,19 @@ public class NetworkStatsTest {
@Test
public void testGetTotal() {
final NetworkStats stats = new NetworkStats(TEST_START, 7)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L)
- .addEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 512L, 32L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 64L, 4L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 512L,32L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
DEFAULT_NETWORK_YES, 128L, 8L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L);
assertValues(stats.getTotal(null), 1408L, 88L, 0L, 2L, 20L);
@@ -459,7 +459,7 @@ public class NetworkStatsTest {
assertEquals(0, after.size());
// Test 1 item stats.
- before.addEntry(TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, 1L, 128L, 0L, 2L, 20L);
+ before.insertEntry(TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, 1L, 128L, 0L, 2L, 20L);
after = before.clone();
after.removeUids(new int[0]);
assertEquals(1, after.size());
@@ -469,12 +469,12 @@ public class NetworkStatsTest {
assertEquals(0, after.size());
// Append remaining test items.
- before.addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 2L, 64L, 0L, 2L, 20L)
- .addEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 4L, 32L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 16L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 16L, 8L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 32L, 4L, 0L, 0L, 0L)
- .addEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 64L, 2L, 0L, 0L, 0L);
+ before.insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 2L, 64L, 0L, 2L, 20L)
+ .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 4L, 32L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 16L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 16L, 8L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 32L, 4L, 0L, 0L, 0L)
+ .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 64L, 2L, 0L, 0L, 0L);
assertEquals(7, before.size());
// Test remove with empty uid list.
@@ -505,12 +505,12 @@ public class NetworkStatsTest {
@Test
public void testClone() throws Exception {
final NetworkStats original = new NetworkStats(TEST_START, 5)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
- .addEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L);
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
+ .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L);
// make clone and mutate original
final NetworkStats clone = original.clone();
- original.addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 0L, 0L);
+ original.insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 0L, 0L);
assertEquals(3, original.size());
assertEquals(2, clone.size());
@@ -523,8 +523,8 @@ public class NetworkStatsTest {
public void testAddWhenEmpty() throws Exception {
final NetworkStats red = new NetworkStats(TEST_START, -1);
final NetworkStats blue = new NetworkStats(TEST_START, 5)
- .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
- .addEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L);
+ .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
+ .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L);
// We're mostly checking that we don't crash
red.combineAllValues(blue);
@@ -537,37 +537,37 @@ public class NetworkStatsTest {
final String underlyingIface = "wlan0";
final int testTag1 = 8888;
NetworkStats delta = new NetworkStats(TEST_START, 17)
- .addEntry(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 39605L, 46L, 12259L, 55L, 0L)
- .addEntry(tunIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(tunIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L)
- .addEntry(tunIface, 10120, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(tunIface, 10120, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 72667L, 197L, 43909L, 241L, 0L)
- .addEntry(tunIface, 10120, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(tunIface, 10120, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 9297L, 17L, 4128L, 21L, 0L)
// VPN package also uses some traffic through unprotected network.
- .addEntry(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 4983L, 10L, 1801L, 12L, 0L)
- .addEntry(tunIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(tunIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L)
// Tag entries
- .addEntry(tunIface, 10120, SET_DEFAULT, testTag1, METERED_NO, ROAMING_NO,
+ .insertEntry(tunIface, 10120, SET_DEFAULT, testTag1, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 21691L, 41L, 13820L, 51L, 0L)
- .addEntry(tunIface, 10120, SET_FOREGROUND, testTag1, METERED_NO, ROAMING_NO,
+ .insertEntry(tunIface, 10120, SET_FOREGROUND, testTag1, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 1281L, 2L, 665L, 2L, 0L)
// Irrelevant entries
- .addEntry(TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 1685L, 5L, 2070L, 6L, 0L)
// Underlying Iface entries
- .addEntry(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 5178L, 8L, 2139L, 11L, 0L)
- .addEntry(underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
- DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L)
- .addEntry(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO,
+ ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L)
+ .insertEntry(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 149873L, 287L, 59217L /* smaller than sum(tun0) */,
299L /* smaller than sum(tun0) */, 0L)
- .addEntry(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
- DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L);
+ .insertEntry(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO,
+ ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L);
delta.migrateTun(tunUid, tunIface, new String[]{underlyingIface});
assertEquals(20, delta.size());
@@ -635,19 +635,19 @@ public class NetworkStatsTest {
final String underlyingIface = "wlan0";
NetworkStats delta = new NetworkStats(TEST_START, 9)
// 2 different apps sent/receive data via tun0.
- .addEntry(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L)
- .addEntry(tunIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(tunIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 500L, 2L, 200L, 5L, 0L)
// VPN package resends data through the tunnel (with exaggerated overhead)
- .addEntry(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 240000, 100L, 120000L, 60L, 0L)
// 1 app already has some traffic on the underlying interface, the other doesn't yet
- .addEntry(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 1000L, 10L, 2000L, 20L, 0L)
// Traffic through the underlying interface via the vpn app.
// This test should redistribute this data correctly.
- .addEntry(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 75500L, 37L, 130000L, 70L, 0L);
delta.migrateTun(tunUid, tunIface, new String[]{underlyingIface});
@@ -697,9 +697,9 @@ public class NetworkStatsTest {
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
NetworkStats stats = new NetworkStats(TEST_START, 3)
- .addEntry(entry1)
- .addEntry(entry2)
- .addEntry(entry3);
+ .insertEntry(entry1)
+ .insertEntry(entry2)
+ .insertEntry(entry3);
stats.filter(UID_ALL, INTERFACES_ALL, TAG_ALL);
assertEquals(3, stats.size());
@@ -724,9 +724,9 @@ public class NetworkStatsTest {
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
NetworkStats stats = new NetworkStats(TEST_START, 3)
- .addEntry(entry1)
- .addEntry(entry2)
- .addEntry(entry3);
+ .insertEntry(entry1)
+ .insertEntry(entry2)
+ .insertEntry(entry3);
stats.filter(testUid, INTERFACES_ALL, TAG_ALL);
assertEquals(2, stats.size());
@@ -755,10 +755,10 @@ public class NetworkStatsTest {
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
NetworkStats stats = new NetworkStats(TEST_START, 4)
- .addEntry(entry1)
- .addEntry(entry2)
- .addEntry(entry3)
- .addEntry(entry4);
+ .insertEntry(entry1)
+ .insertEntry(entry2)
+ .insertEntry(entry3)
+ .insertEntry(entry4);
stats.filter(UID_ALL, new String[] { testIf1, testIf2 }, TAG_ALL);
assertEquals(3, stats.size());
@@ -778,8 +778,8 @@ public class NetworkStatsTest {
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
NetworkStats stats = new NetworkStats(TEST_START, 3)
- .addEntry(entry1)
- .addEntry(entry2);
+ .insertEntry(entry1)
+ .insertEntry(entry2);
stats.filter(UID_ALL, new String[] { }, TAG_ALL);
assertEquals(0, stats.size());
@@ -802,9 +802,9 @@ public class NetworkStatsTest {
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
NetworkStats stats = new NetworkStats(TEST_START, 3)
- .addEntry(entry1)
- .addEntry(entry2)
- .addEntry(entry3);
+ .insertEntry(entry1)
+ .insertEntry(entry2)
+ .insertEntry(entry3);
stats.filter(UID_ALL, INTERFACES_ALL, testTag);
assertEquals(2, stats.size());
@@ -831,10 +831,10 @@ public class NetworkStatsTest {
DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
NetworkStats stats = new NetworkStats(TEST_START, 4)
- .addEntry(entry1)
- .addEntry(entry2)
- .addEntry(entry3)
- .addEntry(entry4);
+ .insertEntry(entry1)
+ .insertEntry(entry2)
+ .insertEntry(entry3)
+ .insertEntry(entry4);
stats.filterDebugEntries();
@@ -891,14 +891,14 @@ public class NetworkStatsTest {
0 /* operations */);
final NetworkStats statsXt = new NetworkStats(TEST_START, 3)
- .addEntry(appEntry)
- .addEntry(xtRootUidEntry)
- .addEntry(otherEntry);
+ .insertEntry(appEntry)
+ .insertEntry(xtRootUidEntry)
+ .insertEntry(otherEntry);
final NetworkStats statsEbpf = new NetworkStats(TEST_START, 3)
- .addEntry(appEntry)
- .addEntry(ebpfRootUidEntry)
- .addEntry(otherEntry);
+ .insertEntry(appEntry)
+ .insertEntry(ebpfRootUidEntry)
+ .insertEntry(otherEntry);
statsXt.apply464xlatAdjustments(stackedIface, false);
statsEbpf.apply464xlatAdjustments(stackedIface, true);
@@ -945,8 +945,8 @@ public class NetworkStatsTest {
0 /* operations */);
NetworkStats stats = new NetworkStats(TEST_START, 2)
- .addEntry(firstEntry)
- .addEntry(secondEntry);
+ .insertEntry(firstEntry)
+ .insertEntry(secondEntry);
// Empty map: no adjustment
stats.apply464xlatAdjustments(new ArrayMap<>(), false);
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 6985415a6a37..c21772a5d177 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -2425,7 +2425,7 @@ public class ConnectivityServiceTest {
assertEquals(expectedRequestCount, testFactory.getMyRequestCount());
assertTrue(testFactory.getMyStartRequested());
- testFactory.unregister();
+ testFactory.terminate();
if (networkCallback != null) mCm.unregisterNetworkCallback(networkCallback);
handlerThread.quit();
}
@@ -2451,6 +2451,38 @@ public class ConnectivityServiceTest {
}
@Test
+ public void testNetworkFactoryUnregister() throws Exception {
+ final NetworkCapabilities filter = new NetworkCapabilities();
+ filter.clearAll();
+
+ final HandlerThread handlerThread = new HandlerThread("testNetworkFactoryRequests");
+ handlerThread.start();
+
+ // Checks that calling setScoreFilter on a NetworkFactory immediately before closing it
+ // does not crash.
+ for (int i = 0; i < 100; i++) {
+ final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
+ mServiceContext, "testFactory", filter);
+ // Register the factory and don't be surprised when the default request arrives.
+ testFactory.register();
+ testFactory.expectAddRequestsWithScores(0);
+ testFactory.waitForNetworkRequests(1);
+
+ testFactory.setScoreFilter(42);
+ testFactory.terminate();
+
+ if (i % 2 == 0) {
+ try {
+ testFactory.register();
+ fail("Re-registering terminated NetworkFactory should throw");
+ } catch (IllegalStateException expected) {
+ }
+ }
+ }
+ handlerThread.quit();
+ }
+
+ @Test
public void testNoMutableNetworkRequests() throws Exception {
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, new Intent("a"), 0);
NetworkRequest request1 = new NetworkRequest.Builder()
@@ -3482,7 +3514,7 @@ public class ConnectivityServiceTest {
cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
assertLength(1, mCm.getAllNetworks());
- testFactory.unregister();
+ testFactory.terminate();
mCm.unregisterNetworkCallback(cellNetworkCallback);
handlerThread.quit();
}
@@ -3723,7 +3755,7 @@ public class ConnectivityServiceTest {
mCm.requestNetwork(nr, networkCallback, timeoutMs);
// pass timeout and validate that UNAVAILABLE is called
- networkCallback.expectCallback(CallbackEntry.UNAVAILABLE, null);
+ networkCallback.expectCallback(CallbackEntry.UNAVAILABLE, (Network) null);
// create a network satisfying request - validate that request not triggered
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
@@ -3814,7 +3846,7 @@ public class ConnectivityServiceTest {
// Simulate the factory releasing the request as unfulfillable and expect onUnavailable!
testFactory.triggerUnfulfillable(requests.get(newRequestId));
- networkCallback.expectCallback(CallbackEntry.UNAVAILABLE, null);
+ networkCallback.expectCallback(CallbackEntry.UNAVAILABLE, (Network) null);
testFactory.waitForRequests();
// unregister network callback - a no-op (since already freed by the
@@ -3822,7 +3854,7 @@ public class ConnectivityServiceTest {
mCm.unregisterNetworkCallback(networkCallback);
}
- testFactory.unregister();
+ testFactory.terminate();
handlerThread.quit();
}
@@ -6757,6 +6789,26 @@ public class ConnectivityServiceTest {
}
@Test
+ public void testCheckConnectivityDiagnosticsPermissionsWrongUidPackageName() throws Exception {
+ final NetworkAgentInfo naiWithoutUid =
+ new NetworkAgentInfo(
+ null, null, null, null, null, new NetworkCapabilities(), 0,
+ mServiceContext, null, null, mService, null, null, null, 0);
+
+ mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
+
+ try {
+ assertFalse(
+ "Mismatched uid/package name should not pass the location permission check",
+ mService.checkConnectivityDiagnosticsPermissions(
+ Process.myPid() + 1, Process.myUid() + 1, naiWithoutUid,
+ mContext.getOpPackageName()));
+ } catch (SecurityException e) {
+ fail("checkConnectivityDiagnosticsPermissions shouldn't surface a SecurityException");
+ }
+ }
+
+ @Test
public void testCheckConnectivityDiagnosticsPermissionsNoLocationPermission() throws Exception {
final NetworkAgentInfo naiWithoutUid =
new NetworkAgentInfo(
@@ -6862,14 +6914,15 @@ public class ConnectivityServiceTest {
}
@Test
- public void testConnectivityDiagnosticsCallbackOnConnectivityReport() throws Exception {
+ public void testConnectivityDiagnosticsCallbackOnConnectivityReportAvailable()
+ throws Exception {
setUpConnectivityDiagnosticsCallback();
// Block until all other events are done processing.
HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
// Verify onConnectivityReport fired
- verify(mConnectivityDiagnosticsCallback).onConnectivityReport(
+ verify(mConnectivityDiagnosticsCallback).onConnectivityReportAvailable(
argThat(report -> {
final NetworkCapabilities nc = report.getNetworkCapabilities();
return nc.getUids() == null
diff --git a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
index f0e5774a5dea..a6f7a36ff01b 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
@@ -240,7 +240,7 @@ public class NetworkStatsObserversTest {
// Baseline
NetworkStats xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */)
- .addIfaceValues(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
+ .insertEntry(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
NetworkStats uidSnapshot = null;
mStatsObservers.updateStats(
@@ -264,14 +264,14 @@ public class NetworkStatsObserversTest {
// Baseline
NetworkStats xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */)
- .addIfaceValues(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
+ .insertEntry(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
NetworkStats uidSnapshot = null;
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
// Delta
xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */)
- .addIfaceValues(TEST_IFACE, BASE_BYTES + 1024L, 10L, BASE_BYTES + 2048L, 20L);
+ .insertEntry(TEST_IFACE, BASE_BYTES + 1024L, 10L, BASE_BYTES + 2048L, 20L);
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
waitForObserverToIdle();
@@ -294,14 +294,14 @@ public class NetworkStatsObserversTest {
// Baseline
NetworkStats xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */)
- .addIfaceValues(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
+ .insertEntry(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
NetworkStats uidSnapshot = null;
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
// Delta
xtSnapshot = new NetworkStats(TEST_START + MINUTE_IN_MILLIS, 1 /* initialSize */)
- .addIfaceValues(TEST_IFACE, BASE_BYTES + THRESHOLD_BYTES, 12L,
+ .insertEntry(TEST_IFACE, BASE_BYTES + THRESHOLD_BYTES, 12L,
BASE_BYTES + THRESHOLD_BYTES, 22L);
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
@@ -326,14 +326,14 @@ public class NetworkStatsObserversTest {
// Baseline
NetworkStats xtSnapshot = null;
NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
// Delta
uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L,
BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
mStatsObservers.updateStats(
@@ -359,14 +359,14 @@ public class NetworkStatsObserversTest {
// Baseline
NetworkStats xtSnapshot = null;
NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
// Delta
uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L,
BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
mStatsObservers.updateStats(
@@ -391,14 +391,14 @@ public class NetworkStatsObserversTest {
// Baseline
NetworkStats xtSnapshot = null;
NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
// Delta
uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, BASE_BYTES + THRESHOLD_BYTES, 2L,
BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
mStatsObservers.updateStats(
@@ -424,14 +424,14 @@ public class NetworkStatsObserversTest {
// Baseline
NetworkStats xtSnapshot = null;
NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
- .addEntry(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO,
+ .insertEntry(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO,
ROAMING_NO, DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
// Delta
uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
- .addEntry(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO,
+ .insertEntry(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO,
ROAMING_NO, DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L,
BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
mStatsObservers.updateStats(
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index 36deca3e37b7..b346c923914e 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -263,7 +263,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
incrementCurrentTime(HOUR_IN_MILLIS);
expectDefaultSettings();
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 1024L, 1L, 2048L, 2L));
+ .insertEntry(TEST_IFACE, 1024L, 1L, 2048L, 2L));
expectNetworkStatsUidDetail(buildEmptyStats());
forcePollAndWaitForIdle();
@@ -276,7 +276,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
incrementCurrentTime(DAY_IN_MILLIS);
expectDefaultSettings();
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 4096L, 4L, 8192L, 8L));
+ .insertEntry(TEST_IFACE, 4096L, 4L, 8192L, 8L));
expectNetworkStatsUidDetail(buildEmptyStats());
forcePollAndWaitForIdle();
@@ -306,13 +306,13 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
incrementCurrentTime(HOUR_IN_MILLIS);
expectDefaultSettings();
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 1024L, 8L, 2048L, 16L));
+ .insertEntry(TEST_IFACE, 1024L, 8L, 2048L, 16L));
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 2)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 256L, 2L, 128L, 1L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 256L, 2L, 128L, 1L, 0L)
- .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 128L, 1L, 128L, 1L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 256L, 2L, 128L, 1L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 256L, 2L, 128L, 1L, 0L)
+ .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 128L, 1L, 128L, 1L, 0L));
mService.setUidForeground(UID_RED, false);
mService.incrementOperationCount(UID_RED, 0xFAAD, 4);
mService.setUidForeground(UID_RED, true);
@@ -375,7 +375,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
incrementCurrentTime(2 * HOUR_IN_MILLIS);
expectSettings(0L, HOUR_IN_MILLIS, WEEK_IN_MILLIS);
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 512L, 4L, 512L, 4L));
+ .insertEntry(TEST_IFACE, 512L, 4L, 512L, 4L));
expectNetworkStatsUidDetail(buildEmptyStats());
forcePollAndWaitForIdle();
@@ -415,11 +415,11 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
incrementCurrentTime(HOUR_IN_MILLIS);
expectDefaultSettings();
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 2048L, 16L, 512L, 4L));
+ .insertEntry(TEST_IFACE, 2048L, 16L, 512L, 4L));
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
- .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
+ .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
mService.incrementOperationCount(UID_RED, 0xF00D, 10);
forcePollAndWaitForIdle();
@@ -437,11 +437,11 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
expectDefaultSettings();
states = new NetworkState[] {buildMobile3gState(IMSI_2)};
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 2048L, 16L, 512L, 4L));
+ .insertEntry(TEST_IFACE, 2048L, 16L, 512L, 4L));
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
- .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
+ .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
forcePollAndWaitForIdle();
@@ -451,12 +451,12 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
incrementCurrentTime(HOUR_IN_MILLIS);
expectDefaultSettings();
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 2176L, 17L, 1536L, 12L));
+ .insertEntry(TEST_IFACE, 2176L, 17L, 1536L, 12L));
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
- .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 640L, 5L, 1024L, 8L, 0L)
- .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xFAAD, 128L, 1L, 1024L, 8L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
+ .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 640L, 5L, 1024L, 8L, 0L)
+ .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xFAAD, 128L, 1L, 1024L, 8L, 0L));
mService.incrementOperationCount(UID_BLUE, 0xFAAD, 10);
forcePollAndWaitForIdle();
@@ -488,12 +488,13 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
incrementCurrentTime(HOUR_IN_MILLIS);
expectDefaultSettings();
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 4128L, 258L, 544L, 34L));
+ .insertEntry(TEST_IFACE, 4128L, 258L, 544L, 34L));
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L)
- .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 4096L, 258L, 512L, 32L, 0L)
- .addEntry(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L)
+ .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE,
+ 4096L, 258L, 512L, 32L, 0L)
+ .insertEntry(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L));
mService.incrementOperationCount(UID_RED, 0xFAAD, 10);
forcePollAndWaitForIdle();
@@ -509,12 +510,13 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
// special "removed" bucket.
expectDefaultSettings();
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 4128L, 258L, 544L, 34L));
+ .insertEntry(TEST_IFACE, 4128L, 258L, 544L, 34L));
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L)
- .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 4096L, 258L, 512L, 32L, 0L)
- .addEntry(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L)
+ .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE,
+ 4096L, 258L, 512L, 32L, 0L)
+ .insertEntry(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L));
final Intent intent = new Intent(ACTION_UID_REMOVED);
intent.putExtra(EXTRA_UID, UID_BLUE);
mServiceContext.sendBroadcast(intent);
@@ -546,8 +548,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
expectDefaultSettings();
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
mService.incrementOperationCount(UID_RED, 0xF00D, 5);
forcePollAndWaitForIdle();
@@ -562,8 +564,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
states = new NetworkState[] {buildMobile4gState(TEST_IFACE2)};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
forcePollAndWaitForIdle();
@@ -574,10 +576,10 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
expectDefaultSettings();
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
- .addEntry(TEST_IFACE2, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
- .addEntry(TEST_IFACE2, UID_RED, SET_DEFAULT, 0xFAAD, 512L, 4L, 256L, 2L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
+ .insertEntry(TEST_IFACE2, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
+ .insertEntry(TEST_IFACE2, UID_RED, SET_DEFAULT, 0xFAAD, 512L, 4L, 256L, 2L, 0L));
mService.incrementOperationCount(UID_RED, 0xFAAD, 5);
forcePollAndWaitForIdle();
@@ -601,9 +603,9 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
expectDefaultSettings();
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L)
- .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 1024L, 8L, 512L, 4L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L)
+ .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 1024L, 8L, 512L, 4L, 0L));
mService.incrementOperationCount(UID_RED, 0xF00D, 1);
forcePollAndWaitForIdle();
@@ -618,9 +620,10 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
expectDefaultSettings();
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L)
- .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 2048L, 16L, 1024L, 8L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L)
+ .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE,
+ 2048L, 16L, 1024L, 8L, 0L));
forcePollAndWaitForIdle();
// first verify entire history present
@@ -664,9 +667,9 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
expectDefaultSettings();
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
- .addEntry(entry1)
- .addEntry(entry2)
- .addEntry(entry3));
+ .insertEntry(entry1)
+ .insertEntry(entry2)
+ .insertEntry(entry3));
mService.incrementOperationCount(UID_RED, 0xF00D, 1);
NetworkStats stats = mService.getDetailedUidStats(INTERFACES_ALL);
@@ -714,11 +717,11 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
.thenReturn(augmentedIfaceFilter);
when(mStatsFactory.readNetworkStatsDetail(eq(UID_ALL), any(), eq(TAG_ALL)))
.thenReturn(new NetworkStats(getElapsedRealtime(), 1)
- .addEntry(uidStats));
+ .insertEntry(uidStats));
when(mNetManager.getNetworkStatsTethering(STATS_PER_UID))
.thenReturn(new NetworkStats(getElapsedRealtime(), 2)
- .addEntry(tetheredStats1)
- .addEntry(tetheredStats2));
+ .insertEntry(tetheredStats1)
+ .insertEntry(tetheredStats2));
NetworkStats stats = mService.getDetailedUidStats(ifaceFilter);
@@ -755,8 +758,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
expectDefaultSettings();
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 0L));
mService.incrementOperationCount(UID_RED, 0xF00D, 1);
forcePollAndWaitForIdle();
@@ -770,10 +773,10 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
expectDefaultSettings();
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, 32L, 2L, 32L, 2L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 1L, 1L, 1L, 1L, 0L));
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, 32L, 2L, 32L, 2L, 0L)
+ .insertEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 1L, 1L, 1L, 1L, 0L));
mService.setUidForeground(UID_RED, true);
mService.incrementOperationCount(UID_RED, 0xFAAD, 1);
@@ -814,9 +817,9 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
// and DEFAULT_NETWORK_YES, because these three properties aren't tracked at that layer.
// We layer them on top by inspecting the iface properties.
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 0L));
mService.incrementOperationCount(UID_RED, 0xF00D, 1);
@@ -853,9 +856,9 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
// ROAMING_NO, because metered and roaming isn't tracked at that layer. We layer it
// on top by inspecting the iface properties.
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_ALL, ROAMING_NO,
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_ALL, ROAMING_NO,
DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 0L)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_ALL, ROAMING_NO,
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_ALL, ROAMING_NO,
DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 0L));
forcePollAndWaitForIdle();
@@ -888,17 +891,17 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
// Traffic seen by kernel counters (includes software tethering).
final NetworkStats ifaceStats = new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 1536L, 12L, 384L, 3L);
+ .insertEntry(TEST_IFACE, 1536L, 12L, 384L, 3L);
// Hardware tethering traffic, not seen by kernel counters.
final NetworkStats tetherStatsHardware = new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 512L, 4L, 128L, 1L);
+ .insertEntry(TEST_IFACE, 512L, 4L, 128L, 1L);
// Traffic for UID_RED.
final NetworkStats uidStats = new NetworkStats(getElapsedRealtime(), 1)
- .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L);
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L);
// All tethering traffic, both hardware and software.
final NetworkStats tetherStats = new NetworkStats(getElapsedRealtime(), 1)
- .addEntry(TEST_IFACE, UID_TETHERING, SET_DEFAULT, TAG_NONE, 1920L, 14L, 384L, 2L,
+ .insertEntry(TEST_IFACE, UID_TETHERING, SET_DEFAULT, TAG_NONE, 1920L, 14L, 384L, 2L,
0L);
expectNetworkStatsSummary(ifaceStats, tetherStatsHardware);
@@ -957,7 +960,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
incrementCurrentTime(HOUR_IN_MILLIS);
expectDefaultSettings();
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 1024L, 1L, 2048L, 2L));
+ .insertEntry(TEST_IFACE, 1024L, 1L, 2048L, 2L));
expectNetworkStatsUidDetail(buildEmptyStats());
forcePollAndWaitForIdle();
@@ -972,7 +975,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
incrementCurrentTime(DAY_IN_MILLIS);
expectDefaultSettings();
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 4096000L, 4L, 8192000L, 8L));
+ .insertEntry(TEST_IFACE, 4096000L, 4L, 8192000L, 8L));
expectNetworkStatsUidDetail(buildEmptyStats());
forcePollAndWaitForIdle();
@@ -1026,18 +1029,18 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
// Verifies that one requestStatsUpdate will be called during iface update.
- provider.expectStatsUpdate(0 /* unused */);
+ provider.expectOnRequestStatsUpdate(0 /* unused */);
// Create some initial traffic and report to the service.
incrementCurrentTime(HOUR_IN_MILLIS);
final NetworkStats expectedStats = new NetworkStats(0L, 1)
- .addValues(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT,
+ .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT,
TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES,
128L, 2L, 128L, 2L, 1L))
- .addValues(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT,
+ .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT,
0xF00D, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES,
64L, 1L, 64L, 1L, 1L));
- cb.onStatsUpdated(0 /* unused */, expectedStats, expectedStats);
+ cb.notifyStatsUpdated(0 /* unused */, expectedStats, expectedStats);
// Make another empty mutable stats object. This is necessary since the new NetworkStats
// object will be used to compare with the old one in NetworkStatsRecoder, two of them
@@ -1047,8 +1050,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
forcePollAndWaitForIdle();
// Verifies that one requestStatsUpdate and setAlert will be called during polling.
- provider.expectStatsUpdate(0 /* unused */);
- provider.expectSetAlert(MB_IN_BYTES);
+ provider.expectOnRequestStatsUpdate(0 /* unused */);
+ provider.expectOnSetAlert(MB_IN_BYTES);
// Verifies that service recorded history, does not verify uid tag part.
assertUidTotal(sTemplateWifi, UID_RED, 128L, 2L, 128L, 2L, 1);
@@ -1082,13 +1085,13 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
assertNotNull(cb);
// Simulates alert quota of the provider has been reached.
- cb.onAlertReached();
+ cb.notifyAlertReached();
HandlerUtilsKt.waitForIdle(mHandlerThread, WAIT_TIMEOUT);
// Verifies that polling is triggered by alert reached.
- provider.expectStatsUpdate(0 /* unused */);
+ provider.expectOnRequestStatsUpdate(0 /* unused */);
// Verifies that global alert will be re-armed.
- provider.expectSetAlert(MB_IN_BYTES);
+ provider.expectOnSetAlert(MB_IN_BYTES);
}
private static File getBaseDir(File statsDir) {
diff --git a/wifi/Android.bp b/wifi/Android.bp
index f4d28817a7f2..70c274128266 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -72,8 +72,7 @@ test_access_hidden_api_whitelist = [
// classes before they are renamed.
java_library {
name: "framework-wifi-pre-jarjar",
- // TODO(b/146757305): sdk_version should be "module_lib_current"
- sdk_version: "core_current",
+ sdk_version: "module_current",
static_libs: [
"framework-wifi-util-lib",
"android.hardware.wifi-V1.0-java-constants",
@@ -83,9 +82,6 @@ java_library {
"unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage
"unsupportedappusage-annotation", // for dalvik.annotation.compat.UnsupportedAppUsage
"framework-telephony-stubs",
- // TODO(b/146757305): should be unnecessary once
- // sdk_version="module_lib_current"
- "android_system_stubs_current",
],
srcs: [
":framework-wifi-updatable-sources",
@@ -98,21 +94,12 @@ java_library {
"//frameworks/opt/net/wifi/service",
"//frameworks/opt/net/wifi/tests/wifitests",
],
-
- // TODO(b/146757305): should be unnecessary once
- // sdk_version="module_lib_current"
- aidl: {
- include_dirs: [
- "frameworks/base/core/java",
- ],
- },
}
// post-jarjar version of framework-wifi
java_library {
name: "framework-wifi",
- // TODO(b/146757305): sdk_version should be "module_lib_current"
- sdk_version: "core_current",
+ sdk_version: "module_current",
static_libs: [
"framework-wifi-pre-jarjar",
],
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index 70542b5d3c65..727952c1938c 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -695,31 +695,6 @@ public class ScanResult implements Parcelable {
*/
public AnqpInformationElement[] anqpElements;
- /**
- * Flag indicating if this AP is a carrier AP. The determination is based
- * on the AP's SSID and if AP is using EAP security.
- *
- * @hide
- */
- // TODO(b/144431927): remove once migrated to Suggestions
- public boolean isCarrierAp;
-
- /**
- * The EAP type {@link WifiEnterpriseConfig.Eap} associated with this AP if it is a carrier AP.
- *
- * @hide
- */
- // TODO(b/144431927): remove once migrated to Suggestions
- public int carrierApEapType;
-
- /**
- * The name of the carrier that's associated with this AP if it is a carrier AP.
- *
- * @hide
- */
- // TODO(b/144431927): remove once migrated to Suggestions
- public String carrierName;
-
/** {@hide} */
public ScanResult(WifiSsid wifiSsid, String BSSID, long hessid, int anqpDomainId,
byte[] osuProviders, String caps, int level, int frequency, long tsf) {
@@ -744,9 +719,6 @@ public class ScanResult implements Parcelable {
this.centerFreq0 = UNSPECIFIED;
this.centerFreq1 = UNSPECIFIED;
this.flags = 0;
- this.isCarrierAp = false;
- this.carrierApEapType = UNSPECIFIED;
- this.carrierName = null;
this.radioChainInfos = null;
this.mWifiStandard = WIFI_STANDARD_UNKNOWN;
}
@@ -767,9 +739,6 @@ public class ScanResult implements Parcelable {
this.centerFreq0 = UNSPECIFIED;
this.centerFreq1 = UNSPECIFIED;
this.flags = 0;
- this.isCarrierAp = false;
- this.carrierApEapType = UNSPECIFIED;
- this.carrierName = null;
this.radioChainInfos = null;
this.mWifiStandard = WIFI_STANDARD_UNKNOWN;
}
@@ -797,9 +766,6 @@ public class ScanResult implements Parcelable {
} else {
this.flags = 0;
}
- this.isCarrierAp = false;
- this.carrierApEapType = UNSPECIFIED;
- this.carrierName = null;
this.radioChainInfos = null;
this.mWifiStandard = WIFI_STANDARD_UNKNOWN;
}
@@ -839,9 +805,6 @@ public class ScanResult implements Parcelable {
venueName = source.venueName;
operatorFriendlyName = source.operatorFriendlyName;
flags = source.flags;
- isCarrierAp = source.isCarrierAp;
- carrierApEapType = source.carrierApEapType;
- carrierName = source.carrierName;
radioChainInfos = source.radioChainInfos;
this.mWifiStandard = source.mWifiStandard;
}
@@ -881,9 +844,6 @@ public class ScanResult implements Parcelable {
sb.append(", standard: ").append(wifiStandardToString(mWifiStandard));
sb.append(", 80211mcResponder: ");
sb.append(((flags & FLAG_80211mc_RESPONDER) != 0) ? "is supported" : "is not supported");
- sb.append(", Carrier AP: ").append(isCarrierAp ? "yes" : "no");
- sb.append(", Carrier AP EAP Type: ").append(carrierApEapType);
- sb.append(", Carrier name: ").append(carrierName);
sb.append(", Radio Chain Infos: ").append(Arrays.toString(radioChainInfos));
return sb.toString();
}
@@ -954,9 +914,6 @@ public class ScanResult implements Parcelable {
} else {
dest.writeInt(0);
}
- dest.writeInt(isCarrierAp ? 1 : 0);
- dest.writeInt(carrierApEapType);
- dest.writeString(carrierName);
if (radioChainInfos != null) {
dest.writeInt(radioChainInfos.length);
@@ -1036,9 +993,6 @@ public class ScanResult implements Parcelable {
new AnqpInformationElement(vendorId, elementId, payload);
}
}
- sr.isCarrierAp = in.readInt() != 0;
- sr.carrierApEapType = in.readInt();
- sr.carrierName = in.readString();
n = in.readInt();
if (n != 0) {
sr.radioChainInfos = new RadioChainInfo[n];
diff --git a/wifi/java/android/net/wifi/WifiMigration.java b/wifi/java/android/net/wifi/WifiMigration.java
index a3482d732a1b..008b18bf04c3 100755
--- a/wifi/java/android/net/wifi/WifiMigration.java
+++ b/wifi/java/android/net/wifi/WifiMigration.java
@@ -16,8 +16,6 @@
package android.net.wifi;
-import static com.android.internal.util.Preconditions.checkNotNull;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -26,8 +24,6 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.provider.Settings;
-import java.util.List;
-
/**
* Class used to provide one time hooks for existing OEM devices to migrate their config store
* data and other settings to the wifi mainline module.
@@ -39,166 +35,6 @@ public final class WifiMigration {
private WifiMigration() { }
/**
- * Container for all the wifi config data to migrate.
- */
- public static final class ConfigStoreMigrationData implements Parcelable {
- /**
- * Builder to create instance of {@link ConfigStoreMigrationData}.
- */
- public static final class Builder {
- private List<WifiConfiguration> mUserSavedNetworkConfigurations;
- private SoftApConfiguration mUserSoftApConfiguration;
-
- public Builder() {
- mUserSavedNetworkConfigurations = null;
- mUserSoftApConfiguration = null;
- }
-
- /**
- * Sets the list of all user's saved network configurations parsed from OEM config
- * store files.
- *
- * @param userSavedNetworkConfigurations List of {@link WifiConfiguration} representing
- * the list of user's saved networks
- * @return Instance of {@link Builder} to enable chaining of the builder method.
- */
- public @NonNull Builder setUserSavedNetworkConfigurations(
- @NonNull List<WifiConfiguration> userSavedNetworkConfigurations) {
- checkNotNull(userSavedNetworkConfigurations);
- mUserSavedNetworkConfigurations = userSavedNetworkConfigurations;
- return this;
- }
-
- /**
- * Sets the user's softap configuration parsed from OEM config store files.
- *
- * @param userSoftApConfiguration {@link SoftApConfiguration} representing user's
- * SoftAp configuration
- * @return Instance of {@link Builder} to enable chaining of the builder method.
- */
- public @NonNull Builder setUserSoftApConfiguration(
- @NonNull SoftApConfiguration userSoftApConfiguration) {
- checkNotNull(userSoftApConfiguration);
- mUserSoftApConfiguration = userSoftApConfiguration;
- return this;
- }
-
- /**
- * Build an instance of {@link ConfigStoreMigrationData}.
- *
- * @return Instance of {@link ConfigStoreMigrationData}.
- */
- public @NonNull ConfigStoreMigrationData build() {
- return new ConfigStoreMigrationData(
- mUserSavedNetworkConfigurations, mUserSoftApConfiguration);
- }
- }
-
- private final List<WifiConfiguration> mUserSavedNetworkConfigurations;
- private final SoftApConfiguration mUserSoftApConfiguration;
-
- private ConfigStoreMigrationData(
- @Nullable List<WifiConfiguration> userSavedNetworkConfigurations,
- @Nullable SoftApConfiguration userSoftApConfiguration) {
- mUserSavedNetworkConfigurations = userSavedNetworkConfigurations;
- mUserSoftApConfiguration = userSoftApConfiguration;
- }
-
- public static final @NonNull Parcelable.Creator<ConfigStoreMigrationData> CREATOR =
- new Parcelable.Creator<ConfigStoreMigrationData>() {
- @Override
- public ConfigStoreMigrationData createFromParcel(Parcel in) {
- List<WifiConfiguration> userSavedNetworkConfigurations =
- in.readArrayList(null);
- SoftApConfiguration userSoftApConfiguration = in.readParcelable(null);
- return new ConfigStoreMigrationData(
- userSavedNetworkConfigurations, userSoftApConfiguration);
- }
-
- @Override
- public ConfigStoreMigrationData[] newArray(int size) {
- return new ConfigStoreMigrationData[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeList(mUserSavedNetworkConfigurations);
- dest.writeParcelable(mUserSoftApConfiguration, flags);
- }
-
- /**
- * Returns list of all user's saved network configurations.
- *
- * Note: Only to be returned if there is any format change in how OEM persisted this info.
- * @return List of {@link WifiConfiguration} representing the list of user's saved networks,
- * or null if no migration necessary.
- */
- @Nullable
- public List<WifiConfiguration> getUserSavedNetworkConfigurations() {
- return mUserSavedNetworkConfigurations;
- }
-
- /**
- * Returns user's softap configuration.
- *
- * Note: Only to be returned if there is any format change in how OEM persisted this info.
- * @return {@link SoftApConfiguration} representing user's SoftAp configuration,
- * or null if no migration necessary.
- */
- @Nullable
- public SoftApConfiguration getUserSoftApConfiguration() {
- return mUserSoftApConfiguration;
- }
- }
-
- /**
- * Load data from OEM's config store.
- * <p>
- * Note:
- * <li>OEMs need to implement {@link #loadFromConfigStore()} ()} only if their
- * existing config store format or file locations differs from the vanilla AOSP implementation.
- * </li>
- * <li>The wifi mainline module will invoke {@link #loadFromConfigStore()} method on every
- * bootup, its the responsibility of the OEM implementation to ensure that this method returns
- * non-null data only on the first bootup. Once the migration is done, the OEM can safely delete
- * their config store files when {@link #removeConfigStore()} is invoked.
- * <li>The first & only relevant invocation of {@link #loadFromConfigStore()} occurs when a
- * previously released device upgrades to the wifi mainline module from an OEM implementation
- * of the wifi stack.
- * </li>
- *
- * @return Instance of {@link ConfigStoreMigrationData} for migrating data, null if no
- * migration is necessary.
- */
- @Nullable
- public static ConfigStoreMigrationData loadFromConfigStore() {
- // Note: OEMs should add code to parse data from their config store format here!
- return null;
- }
-
- /**
- * Remove OEM's config store.
- * <p>
- * Note:
- * <li>OEMs need to implement {@link #removeConfigStore()} only if their
- * existing config store format or file locations differs from the vanilla AOSP implementation (
- * which is what the wifi mainline module understands).
- * </li>
- * <li> The wifi mainline module will invoke {@link #removeConfigStore()} after it migrates
- * all the existing data retrieved from {@link #loadFromConfigStore()}.
- * </li>
- */
- public static void removeConfigStore() {
- // Note: OEMs should remove their custom config store files here!
- }
-
- /**
* Container for all the wifi settings data to migrate.
*/
public static final class SettingsMigrationData implements Parcelable {
diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
index 6dbb0bd757d8..72ca900f4ecd 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
@@ -545,6 +545,7 @@ public final class WifiNetworkSuggestion implements Parcelable {
mIsMetered ? WifiConfiguration.METERED_OVERRIDE_METERED
: WifiConfiguration.METERED_OVERRIDE_NONE;
wifiConfiguration.carrierId = mCarrierId;
+ wifiConfiguration.trusted = !mIsNetworkUntrusted;
return wifiConfiguration;
}
@@ -684,8 +685,7 @@ public final class WifiNetworkSuggestion implements Parcelable {
mIsAppInteractionRequired,
mIsUserInteractionRequired,
mIsSharedWithUser,
- mIsInitialAutojoinEnabled,
- mIsNetworkUntrusted);
+ mIsInitialAutojoinEnabled);
}
}
@@ -728,13 +728,6 @@ public final class WifiNetworkSuggestion implements Parcelable {
*/
public final boolean isInitialAutoJoinEnabled;
- /**
- * Whether this network will be brought up as untrusted (TRUSTED capability bit removed).
- * @hide
- */
- public final boolean isNetworkUntrusted;
-
-
/** @hide */
public WifiNetworkSuggestion() {
this.wifiConfiguration = new WifiConfiguration();
@@ -743,7 +736,6 @@ public final class WifiNetworkSuggestion implements Parcelable {
this.isUserInteractionRequired = false;
this.isUserAllowedToManuallyConnect = true;
this.isInitialAutoJoinEnabled = true;
- this.isNetworkUntrusted = false;
}
/** @hide */
@@ -752,8 +744,7 @@ public final class WifiNetworkSuggestion implements Parcelable {
boolean isAppInteractionRequired,
boolean isUserInteractionRequired,
boolean isUserAllowedToManuallyConnect,
- boolean isInitialAutoJoinEnabled,
- boolean isNetworkUntrusted) {
+ boolean isInitialAutoJoinEnabled) {
checkNotNull(networkConfiguration);
this.wifiConfiguration = networkConfiguration;
this.passpointConfiguration = passpointConfiguration;
@@ -762,7 +753,6 @@ public final class WifiNetworkSuggestion implements Parcelable {
this.isUserInteractionRequired = isUserInteractionRequired;
this.isUserAllowedToManuallyConnect = isUserAllowedToManuallyConnect;
this.isInitialAutoJoinEnabled = isInitialAutoJoinEnabled;
- this.isNetworkUntrusted = isNetworkUntrusted;
}
public static final @NonNull Creator<WifiNetworkSuggestion> CREATOR =
@@ -775,8 +765,7 @@ public final class WifiNetworkSuggestion implements Parcelable {
in.readBoolean(), // isAppInteractionRequired
in.readBoolean(), // isUserInteractionRequired
in.readBoolean(), // isSharedCredentialWithUser
- in.readBoolean(), // isAutojoinEnabled
- in.readBoolean()
+ in.readBoolean() // isAutojoinEnabled
);
}
@@ -799,7 +788,6 @@ public final class WifiNetworkSuggestion implements Parcelable {
dest.writeBoolean(isUserInteractionRequired);
dest.writeBoolean(isUserAllowedToManuallyConnect);
dest.writeBoolean(isInitialAutoJoinEnabled);
- dest.writeBoolean(isNetworkUntrusted);
}
@Override
@@ -842,7 +830,7 @@ public final class WifiNetworkSuggestion implements Parcelable {
.append(", isUserInteractionRequired=").append(isUserInteractionRequired)
.append(", isCredentialSharedWithUser=").append(isUserAllowedToManuallyConnect)
.append(", isInitialAutoJoinEnabled=").append(isInitialAutoJoinEnabled)
- .append(", isUnTrusted=").append(isNetworkUntrusted)
+ .append(", isUnTrusted=").append(!wifiConfiguration.trusted)
.append(" ]");
return sb.toString();
}
@@ -933,7 +921,7 @@ public final class WifiNetworkSuggestion implements Parcelable {
/** @see Builder#setUntrusted(boolean) */
public boolean isUntrusted() {
- return isNetworkUntrusted;
+ return !wifiConfiguration.trusted;
}
/**
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
index 65e8b3d9283d..fa806e7797cd 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
+++ b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
@@ -1037,18 +1037,7 @@ public final class Credential implements Parcelable {
* @return a Unique identifier for a Credential object
*/
public int getUniqueId() {
- int usedCredential;
-
- // Initialize usedCredential based on the credential type of the profile
- if (mUserCredential != null) {
- usedCredential = 0;
- } else if (mCertCredential != null) {
- usedCredential = 1;
- } else {
- usedCredential = 2;
- }
-
- return Objects.hash(usedCredential, mRealm);
+ return Objects.hash(mUserCredential, mCertCredential, mSimCredential, mRealm);
}
@Override
diff --git a/wifi/tests/src/android/net/wifi/ScanResultTest.java b/wifi/tests/src/android/net/wifi/ScanResultTest.java
index 4c22d5d6dc7e..6cb832463bc0 100644
--- a/wifi/tests/src/android/net/wifi/ScanResultTest.java
+++ b/wifi/tests/src/android/net/wifi/ScanResultTest.java
@@ -156,8 +156,7 @@ public class ScanResultTest {
+ "distance: 0(cm), distanceSd: 0(cm), "
+ "passpoint: no, ChannelBandwidth: 0, centerFreq0: 0, centerFreq1: 0, "
+ "standard: 11ac, "
- + "80211mcResponder: is not supported, Carrier AP: no, "
- + "Carrier AP EAP Type: 0, Carrier name: null, "
+ + "80211mcResponder: is not supported, "
+ "Radio Chain Infos: null", scanResult.toString());
}
@@ -179,8 +178,7 @@ public class ScanResultTest {
+ "distanceSd: 0(cm), "
+ "passpoint: no, ChannelBandwidth: 0, centerFreq0: 0, centerFreq1: 0, "
+ "standard: 11ac, "
- + "80211mcResponder: is not supported, Carrier AP: no, "
- + "Carrier AP EAP Type: 0, Carrier name: null, "
+ + "80211mcResponder: is not supported, "
+ "Radio Chain Infos: [RadioChainInfo: id=0, level=-45, "
+ "RadioChainInfo: id=1, level=-54]", scanResult.toString());
}
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
index 51bf7380a1bb..01b2a8d4a24c 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
@@ -532,7 +532,7 @@ public class WifiNetworkSuggestionTest {
configuration.BSSID = TEST_BSSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion(
- configuration, null, false, true, true, true, false);
+ configuration, null, false, true, true, true);
Parcel parcelW = Parcel.obtain();
suggestion.writeToParcel(parcelW, 0);
@@ -603,14 +603,14 @@ public class WifiNetworkSuggestionTest {
configuration.BSSID = TEST_BSSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
WifiNetworkSuggestion suggestion =
- new WifiNetworkSuggestion(configuration, null, true, false, true, true, false);
+ new WifiNetworkSuggestion(configuration, null, true, false, true, true);
WifiConfiguration configuration1 = new WifiConfiguration();
configuration1.SSID = TEST_SSID;
configuration1.BSSID = TEST_BSSID;
configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
WifiNetworkSuggestion suggestion1 =
- new WifiNetworkSuggestion(configuration1, null, false, true, true, true, false);
+ new WifiNetworkSuggestion(configuration1, null, false, true, true, true);
assertEquals(suggestion, suggestion1);
assertEquals(suggestion.hashCode(), suggestion1.hashCode());
@@ -626,13 +626,13 @@ public class WifiNetworkSuggestionTest {
configuration.SSID = TEST_SSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion =
- new WifiNetworkSuggestion(configuration, null, false, false, true, true, false);
+ new WifiNetworkSuggestion(configuration, null, false, false, true, true);
WifiConfiguration configuration1 = new WifiConfiguration();
configuration1.SSID = TEST_SSID_1;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion1 =
- new WifiNetworkSuggestion(configuration1, null, false, false, true, true, false);
+ new WifiNetworkSuggestion(configuration1, null, false, false, true, true);
assertNotEquals(suggestion, suggestion1);
}
@@ -648,13 +648,13 @@ public class WifiNetworkSuggestionTest {
configuration.BSSID = TEST_BSSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion =
- new WifiNetworkSuggestion(configuration, null, false, false, true, true, false);
+ new WifiNetworkSuggestion(configuration, null, false, false, true, true);
WifiConfiguration configuration1 = new WifiConfiguration();
configuration1.SSID = TEST_SSID;
configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion1 =
- new WifiNetworkSuggestion(configuration1, null, false, false, true, true, false);
+ new WifiNetworkSuggestion(configuration1, null, false, false, true, true);
assertNotEquals(suggestion, suggestion1);
}
@@ -669,13 +669,13 @@ public class WifiNetworkSuggestionTest {
configuration.SSID = TEST_SSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion =
- new WifiNetworkSuggestion(configuration, null, false, false, true, true, false);
+ new WifiNetworkSuggestion(configuration, null, false, false, true, true);
WifiConfiguration configuration1 = new WifiConfiguration();
configuration1.SSID = TEST_SSID;
configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
WifiNetworkSuggestion suggestion1 =
- new WifiNetworkSuggestion(configuration1, null, false, false, true, true, false);
+ new WifiNetworkSuggestion(configuration1, null, false, false, true, true);
assertNotEquals(suggestion, suggestion1);
}
@@ -770,7 +770,7 @@ public class WifiNetworkSuggestionTest {
.setWpa2Passphrase(TEST_PRESHARED_KEY)
.setUntrusted(true)
.build();
- assertTrue(suggestion.isNetworkUntrusted);
+ assertTrue(suggestion.isUntrusted());
assertFalse(suggestion.isUserAllowedToManuallyConnect);
}
@@ -786,7 +786,7 @@ public class WifiNetworkSuggestionTest {
.setPasspointConfig(passpointConfiguration)
.setUntrusted(true)
.build();
- assertTrue(suggestion.isNetworkUntrusted);
+ assertTrue(suggestion.isUntrusted());
assertFalse(suggestion.isUserAllowedToManuallyConnect);
}
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
index c6825822f4cc..829d8f0a9a3a 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
@@ -18,6 +18,7 @@ package android.net.wifi.hotspot2.pps;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import android.net.wifi.EAPConstants;
@@ -551,4 +552,68 @@ public class CredentialTest {
public void validateTwoCertificateDifferent() {
assertFalse(Credential.isX509CertificateEquals(FakeKeys.CA_CERT0, FakeKeys.CA_CERT1));
}
+
+ /**
+ * Verify that unique identifiers are the same for objects with the same credentials
+ */
+ @Test
+ public void testUniqueIdSameCredentialTypes() throws Exception {
+ assertEquals(createCredentialWithSimCredential().getUniqueId(),
+ createCredentialWithSimCredential().getUniqueId());
+ assertEquals(createCredentialWithCertificateCredential().getUniqueId(),
+ createCredentialWithCertificateCredential().getUniqueId());
+ assertEquals(createCredentialWithUserCredential().getUniqueId(),
+ createCredentialWithUserCredential().getUniqueId());
+ }
+
+ /**
+ * Verify that unique identifiers are different for each credential
+ */
+ @Test
+ public void testUniqueIdDifferentForDifferentCredentialTypes() throws Exception {
+ Credential simCred = createCredentialWithSimCredential();
+ Credential certCred = createCredentialWithCertificateCredential();
+ Credential userCred = createCredentialWithUserCredential();
+
+ assertNotEquals(simCred.getUniqueId(), userCred.getUniqueId());
+ assertNotEquals(simCred.getUniqueId(), certCred.getUniqueId());
+ assertNotEquals(certCred.getUniqueId(), userCred.getUniqueId());
+ }
+
+ /**
+ * Verify that unique identifiers are different for a credential with different values
+ */
+ @Test
+ public void testUniqueIdDifferentForSimCredentialsWithDifferentValues() throws Exception {
+ Credential simCred1 = createCredentialWithSimCredential();
+ Credential simCred2 = createCredentialWithSimCredential();
+ simCred2.getSimCredential().setImsi("567890*");
+
+ assertNotEquals(simCred1.getUniqueId(), simCred2.getUniqueId());
+ }
+
+ /**
+ * Verify that unique identifiers are different for a credential with different values
+ */
+ @Test
+ public void testUniqueIdDifferentForUserCredentialsWithDifferentValues() throws Exception {
+ Credential userCred1 = createCredentialWithUserCredential();
+ Credential userCred2 = createCredentialWithUserCredential();
+ userCred2.getUserCredential().setUsername("anotheruser");
+
+ assertNotEquals(userCred1.getUniqueId(), userCred2.getUniqueId());
+ }
+
+ /**
+ * Verify that unique identifiers are different for a credential with different values
+ */
+ @Test
+ public void testUniqueIdDifferentForCertCredentialsWithDifferentValues() throws Exception {
+ Credential certCred1 = createCredentialWithCertificateCredential();
+ Credential certCred2 = createCredentialWithCertificateCredential();
+ certCred2.getCertCredential().setCertSha256Fingerprint(
+ MessageDigest.getInstance("SHA-256").digest(FakeKeys.CA_CERT0.getEncoded()));
+
+ assertNotEquals(certCred1.getUniqueId(), certCred2.getUniqueId());
+ }
}