summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp21
-rw-r--r--apex/appsearch/framework/Android.bp7
-rw-r--r--apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java15
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java58
-rw-r--r--apex/media/framework/Android.bp7
-rw-r--r--apex/sdkextensions/TEST_MAPPING2
-rw-r--r--apex/sdkextensions/framework/Android.bp5
-rw-r--r--apex/statsd/aidl/android/os/IStatsCompanionService.aidl7
-rw-r--r--apex/statsd/aidl/android/os/IStatsManagerService.aidl10
-rw-r--r--apex/statsd/framework/Android.bp11
-rw-r--r--apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java211
-rw-r--r--apex/statsd/service/java/com/android/server/stats/StatsManagerService.java227
-rw-r--r--api/current.txt64
-rwxr-xr-xapi/system-current.txt65
-rw-r--r--api/test-current.txt5
-rw-r--r--cmds/statsd/Android.bp3
-rw-r--r--cmds/statsd/src/atoms.proto81
-rw-r--r--cmds/statsd/src/external/StatsPullerManager.cpp71
-rw-r--r--cmds/statsd/src/external/SurfaceflingerStatsPuller.cpp99
-rw-r--r--cmds/statsd/src/external/SurfaceflingerStatsPuller.h48
-rw-r--r--cmds/statsd/tests/external/SurfaceflingerStatsPuller_test.cpp96
-rw-r--r--config/boot-image-profile.txt3
-rw-r--r--config/preloaded-classes1
-rw-r--r--core/java/android/app/ActivityManagerInternal.java11
-rw-r--r--core/java/android/app/IActivityTaskManager.aidl4
-rw-r--r--core/java/android/app/INotificationManager.aidl3
-rw-r--r--core/java/android/app/NotificationChannel.java113
-rw-r--r--core/java/android/app/NotificationManager.java19
-rw-r--r--core/java/android/app/PropertyInvalidatedCache.java32
-rw-r--r--core/java/android/app/StatsManager.java18
-rw-r--r--core/java/android/app/StatusBarManager.java2
-rw-r--r--core/java/android/app/admin/DevicePolicyManagerInternal.java22
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java7
-rw-r--r--core/java/android/bluetooth/BluetoothHidDevice.java63
-rw-r--r--core/java/android/bluetooth/BluetoothMap.java49
-rw-r--r--core/java/android/bluetooth/BluetoothPan.java37
-rw-r--r--core/java/android/bluetooth/BluetoothPbap.java6
-rw-r--r--core/java/android/content/Context.java51
-rw-r--r--core/java/android/content/Intent.java11
-rw-r--r--core/java/android/content/pm/CrossProfileApps.java85
-rw-r--r--core/java/android/content/pm/ICrossProfileApps.aidl2
-rw-r--r--core/java/android/content/pm/PackageManager.java7
-rw-r--r--core/java/android/content/res/TEST_MAPPING7
-rw-r--r--core/java/android/content/rollback/PackageRollbackInfo.java19
-rw-r--r--core/java/android/hardware/soundtrigger/ConversionUtil.java4
-rw-r--r--core/java/android/hardware/soundtrigger/SoundTrigger.java93
-rw-r--r--core/java/android/net/DhcpInfo.java8
-rw-r--r--core/java/android/net/IpSecManager.java9
-rw-r--r--core/java/android/net/NetworkCapabilities.java24
-rw-r--r--core/java/android/net/annotations/PolicyDirection.java35
-rw-r--r--core/java/android/os/Process.java12
-rw-r--r--core/java/android/os/UpdateEngine.java10
-rw-r--r--core/java/android/os/UserManager.java3
-rw-r--r--core/java/android/os/storage/StorageManager.java9
-rw-r--r--core/java/android/provider/Settings.java56
-rw-r--r--core/java/android/service/notification/INotificationListener.aidl3
-rw-r--r--core/java/android/service/notification/NotificationAssistantService.java72
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java16
-rw-r--r--core/java/android/service/voice/AlwaysOnHotwordDetector.java181
-rw-r--r--core/java/android/text/SpannableStringInternal.java13
-rw-r--r--core/java/android/view/ITaskOrganizer.aidl38
-rw-r--r--core/java/android/view/InsetsAnimationControlCallbacks.java19
-rw-r--r--core/java/android/view/InsetsAnimationControlImpl.java13
-rw-r--r--core/java/android/view/InsetsController.java122
-rw-r--r--core/java/android/view/InsetsSource.java31
-rw-r--r--core/java/android/view/InsetsSourceConsumer.java28
-rw-r--r--core/java/android/view/InsetsState.java29
-rw-r--r--core/java/android/view/View.java26
-rw-r--r--core/java/android/view/ViewGroup.java22
-rw-r--r--core/java/android/view/ViewRootImpl.java12
-rw-r--r--core/java/android/view/WindowContainerTransaction.java33
-rw-r--r--core/java/android/view/WindowInsets.java14
-rw-r--r--core/java/android/view/WindowInsetsAnimationCallback.java55
-rw-r--r--core/java/android/view/WindowInsetsAnimationControlListener.java7
-rw-r--r--core/java/android/view/WindowInsetsController.java6
-rw-r--r--core/java/android/view/accessibility/AccessibilityManager.java56
-rw-r--r--core/java/android/view/accessibility/IAccessibilityManager.aidl4
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java25
-rw-r--r--core/java/android/widget/Editor.java27
-rw-r--r--core/java/android/widget/EditorTouchState.java14
-rw-r--r--core/java/android/widget/RemoteViews.java3
-rw-r--r--core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java260
-rw-r--r--core/java/com/android/internal/app/ISoundTriggerService.aidl4
-rw-r--r--core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl44
-rw-r--r--core/java/com/android/internal/logging/UiEventLogger.java20
-rw-r--r--core/java/com/android/internal/logging/UiEventLoggerImpl.java10
-rw-r--r--core/java/com/android/internal/logging/testing/UiEventLoggerFake.java17
-rw-r--r--core/java/com/android/internal/util/Preconditions.java24
-rw-r--r--core/proto/android/server/animationadapter.proto10
-rw-r--r--core/proto/android/service/graphicsstats.proto7
-rw-r--r--core/res/AndroidManifest.xml11
-rw-r--r--core/res/res/anim/screen_rotate_0_enter.xml40
-rw-r--r--core/res/res/anim/screen_rotate_0_exit.xml37
-rw-r--r--core/res/res/anim/screen_rotate_0_frame.xml25
-rw-r--r--core/res/res/anim/screen_rotate_180_enter.xml12
-rw-r--r--core/res/res/anim/screen_rotate_180_exit.xml12
-rw-r--r--core/res/res/anim/screen_rotate_alpha.xml4
-rw-r--r--core/res/res/anim/screen_rotate_minus_90_enter.xml26
-rw-r--r--core/res/res/anim/screen_rotate_minus_90_exit.xml32
-rw-r--r--core/res/res/anim/screen_rotate_plus_90_enter.xml25
-rw-r--r--core/res/res/anim/screen_rotate_plus_90_exit.xml32
-rw-r--r--core/res/res/interpolator/screen_rotation_alpha_in.xml22
-rw-r--r--core/res/res/interpolator/screen_rotation_alpha_out.xml22
-rw-r--r--core/res/res/values/config.xml18
-rw-r--r--core/res/res/values/public.xml1
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/WifiConfigurationHelper.java21
-rw-r--r--core/tests/coretests/src/android/app/DownloadManagerBaseTest.java9
-rw-r--r--core/tests/coretests/src/android/net/NetworkKeyTest.java15
-rw-r--r--core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java4
-rw-r--r--core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java8
-rw-r--r--core/tests/coretests/src/android/view/InsetsControllerTest.java125
-rw-r--r--core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java4
-rw-r--r--core/tests/coretests/src/android/view/InsetsSourceTest.java25
-rw-r--r--core/tests/coretests/src/android/view/InsetsStateTest.java38
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java (renamed from services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerTest.java)44
-rw-r--r--core/tests/coretests/src/android/widget/EditorCursorDragTest.java86
-rw-r--r--core/tests/coretests/src/android/widget/EditorTouchStateTest.java54
-rw-r--r--data/etc/OWNERS2
-rw-r--r--data/etc/privapp-permissions-platform.xml1
-rw-r--r--libs/hwui/Android.bp3
-rw-r--r--libs/hwui/GpuMemoryTracker.cpp122
-rw-r--r--libs/hwui/GpuMemoryTracker.h77
-rw-r--r--libs/hwui/ProfileData.cpp3
-rw-r--r--libs/hwui/ProfileData.h5
-rw-r--r--libs/hwui/pipeline/skia/ATraceMemoryDump.cpp175
-rw-r--r--libs/hwui/pipeline/skia/ATraceMemoryDump.h79
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.cpp5
-rw-r--r--libs/hwui/protos/graphicsstats.proto8
-rw-r--r--libs/hwui/renderstate/RenderState.cpp6
-rw-r--r--libs/hwui/renderstate/RenderState.h1
-rw-r--r--libs/hwui/renderthread/CacheManager.cpp14
-rw-r--r--libs/hwui/renderthread/CacheManager.h1
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp3
-rw-r--r--libs/hwui/renderthread/RenderThread.cpp1
-rw-r--r--libs/hwui/service/GraphicsStatsService.cpp167
-rw-r--r--libs/hwui/service/GraphicsStatsService.h6
-rw-r--r--libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp65
-rw-r--r--location/java/android/location/Criteria.java321
-rw-r--r--location/java/com/android/internal/location/ProviderProperties.java162
-rw-r--r--location/java/com/android/internal/location/ProviderRequest.java128
-rw-r--r--media/java/android/media/IMediaRoute2Provider.aidl11
-rw-r--r--media/java/android/media/MediaRoute2Info.java9
-rw-r--r--media/java/android/media/MediaRoute2ProviderService.java60
-rw-r--r--media/java/android/media/MediaRouter2.java42
-rw-r--r--media/java/android/media/MediaRouter2Utils.java100
-rw-r--r--media/java/android/media/RouteSessionInfo.java199
-rw-r--r--media/java/android/media/session/MediaSessionManager.java7
-rw-r--r--media/java/android/media/soundtrigger/SoundTriggerManager.java13
-rw-r--r--media/java/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl8
-rw-r--r--media/java/android/media/soundtrigger_middleware/Status.aidl2
-rw-r--r--media/java/android/media/tv/tuner/DemuxCapabilities.java77
-rw-r--r--media/java/android/media/tv/tuner/Descrambler.java103
-rw-r--r--media/java/android/media/tv/tuner/Dvr.java153
-rw-r--r--media/java/android/media/tv/tuner/DvrSettings.java124
-rw-r--r--media/java/android/media/tv/tuner/FrontendCapabilities.java291
-rw-r--r--media/java/android/media/tv/tuner/FrontendSettings.java2
-rw-r--r--media/java/android/media/tv/tuner/Lnb.java202
-rw-r--r--media/java/android/media/tv/tuner/TimeFilter.java127
-rw-r--r--media/java/android/media/tv/tuner/Tuner.java551
-rw-r--r--media/java/android/media/tv/tuner/TunerConstants.java243
-rw-r--r--media/java/android/media/tv/tuner/TunerUtils.java13
-rw-r--r--media/java/android/media/tv/tuner/dvr/DvrSettings.java180
-rw-r--r--media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java4
-rw-r--r--media/java/android/media/tv/tuner/filter/Filter.java139
-rw-r--r--media/java/android/media/tv/tuner/filter/FilterConfiguration.java44
-rw-r--r--media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java4
-rw-r--r--media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java4
-rw-r--r--media/java/android/media/tv/tuner/filter/PesSettings.java31
-rw-r--r--media/java/android/media/tv/tuner/filter/Settings.java8
-rw-r--r--media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java4
-rw-r--r--media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java33
-rw-r--r--media/java/android/media/tv/tuner/frontend/AnalogFrontendCapabilities.java41
-rw-r--r--media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java150
-rw-r--r--media/java/android/media/tv/tuner/frontend/Atsc3FrontendCapabilities.java65
-rw-r--r--media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java6
-rw-r--r--media/java/android/media/tv/tuner/frontend/AtscFrontendCapabilities.java33
-rw-r--r--media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java5
-rw-r--r--media/java/android/media/tv/tuner/frontend/DvbcFrontendCapabilities.java46
-rw-r--r--media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java5
-rw-r--r--media/java/android/media/tv/tuner/frontend/DvbsFrontendCapabilities.java46
-rw-r--r--media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java5
-rw-r--r--media/java/android/media/tv/tuner/frontend/DvbtFrontendCapabilities.java78
-rw-r--r--media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java6
-rw-r--r--media/java/android/media/tv/tuner/frontend/FrontendCapabilities.java24
-rw-r--r--media/java/android/media/tv/tuner/frontend/FrontendInfo.java5
-rw-r--r--media/java/android/media/tv/tuner/frontend/FrontendSettings.java100
-rw-r--r--media/java/android/media/tv/tuner/frontend/FrontendStatus.java4
-rw-r--r--media/java/android/media/tv/tuner/frontend/IsdbcFrontendCapabilities.java59
-rw-r--r--media/java/android/media/tv/tuner/frontend/Isdbs3FrontendCapabilities.java40
-rw-r--r--media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java5
-rw-r--r--media/java/android/media/tv/tuner/frontend/IsdbsFrontendCapabilities.java40
-rw-r--r--media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java5
-rw-r--r--media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java6
-rw-r--r--media/jni/Android.bp4
-rw-r--r--media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java16
-rw-r--r--media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java20
-rw-r--r--media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java2
-rw-r--r--media/tests/MediaRouter/src/com/android/mediaroutertest/RouteSessionTest.java9
-rw-r--r--mms/OWNERS2
-rw-r--r--mms/java/android/telephony/MmsManager.java29
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java13
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java9
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java9
-rw-r--r--packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java7
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java28
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java22
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java22
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java28
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java22
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java25
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java22
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java14
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java17
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java8
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java21
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java7
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java7
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java7
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java7
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java7
-rw-r--r--packages/Shell/AndroidManifest.xml3
-rw-r--r--packages/SystemUI/docs/broadcasts.md35
-rw-r--r--packages/SystemUI/res/layout/media_carousel.xml35
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/DumpController.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/ScreenDecorations.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java53
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java128
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/PowerUI.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanel.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java47
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarDependenciesModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java69
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java78
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java88
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/GroupCoalescer.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/DerivedMember.java62
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java109
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/IsHighPriorityProvider.java148
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java73
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCache.java75
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImpl.java100
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java168
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/concurrency/ExecutorImpl.java54
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt45
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt37
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java78
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/GroupEntryTest.java148
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java225
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java40
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/provider/IsHighPriorityProviderTest.java184
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImplTest.java112
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java86
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java2
-rw-r--r--packages/Tethering/Android.bp1
-rw-r--r--packages/Tethering/AndroidManifest.xml1
-rw-r--r--packages/Tethering/common/TetheringLib/Android.bp10
-rw-r--r--packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java73
-rw-r--r--packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java7
-rw-r--r--packages/services/PacProcessor/jni/Android.bp6
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java63
-rw-r--r--services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java14
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java145
-rw-r--r--services/backup/java/com/android/server/backup/BackupManagerService.java27
-rw-r--r--services/backup/java/com/android/server/backup/UserBackupManagerService.java33
-rw-r--r--services/core/Android.bp6
-rw-r--r--services/core/java/android/content/pm/PackageManagerInternal.java6
-rw-r--r--services/core/java/com/android/server/GnssManagerService.java9
-rw-r--r--services/core/java/com/android/server/GraphicsStatsService.java94
-rw-r--r--services/core/java/com/android/server/LocationManagerService.java939
-rw-r--r--services/core/java/com/android/server/PackageWatchdog.java3
-rw-r--r--services/core/java/com/android/server/RescueParty.java172
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java14
-rw-r--r--services/core/java/com/android/server/adb/AdbDebuggingManager.java5
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java2
-rw-r--r--services/core/java/com/android/server/am/AppErrors.java25
-rw-r--r--services/core/java/com/android/server/am/UserController.java31
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java20
-rw-r--r--services/core/java/com/android/server/integrity/IntegrityFileManager.java16
-rw-r--r--services/core/java/com/android/server/integrity/model/BitTrackedInputStream.java69
-rw-r--r--services/core/java/com/android/server/integrity/model/ByteTrackedOutputStream.java (renamed from services/core/java/com/android/server/integrity/serializer/ByteTrackedOutputStream.java)2
-rw-r--r--services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java103
-rw-r--r--services/core/java/com/android/server/integrity/parser/RuleParser.java3
-rw-r--r--services/core/java/com/android/server/integrity/parser/RuleXmlParser.java3
-rw-r--r--services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java4
-rw-r--r--services/core/java/com/android/server/location/AbstractLocationProvider.java331
-rw-r--r--services/core/java/com/android/server/location/GnssLocationProvider.java39
-rw-r--r--services/core/java/com/android/server/location/LocationProviderProxy.java129
-rw-r--r--services/core/java/com/android/server/location/LocationSettingsStore.java29
-rw-r--r--services/core/java/com/android/server/location/MockProvider.java35
-rw-r--r--services/core/java/com/android/server/location/MockableLocationProvider.java289
-rw-r--r--services/core/java/com/android/server/location/PassiveProvider.java26
-rw-r--r--services/core/java/com/android/server/location/UserInfoStore.java226
-rw-r--r--services/core/java/com/android/server/locksettings/RebootEscrowManager.java59
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java21
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java50
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java30
-rw-r--r--services/core/java/com/android/server/media/MediaRoute2Provider.java8
-rw-r--r--services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java21
-rw-r--r--services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java60
-rw-r--r--services/core/java/com/android/server/media/SystemMediaRoute2Provider.java22
-rw-r--r--services/core/java/com/android/server/notification/NotificationChannelExtractor.java3
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java114
-rw-r--r--services/core/java/com/android/server/notification/OWNERS4
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java57
-rw-r--r--services/core/java/com/android/server/notification/RankingConfig.java11
-rw-r--r--services/core/java/com/android/server/om/TEST_MAPPING12
-rw-r--r--services/core/java/com/android/server/pm/ApexManager.java145
-rw-r--r--services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java87
-rw-r--r--services/core/java/com/android/server/pm/InstallSource.java102
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java9
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java29
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java78
-rw-r--r--services/core/java/com/android/server/pm/Settings.java19
-rw-r--r--services/core/java/com/android/server/pm/StagingManager.java95
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java148
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java9
-rw-r--r--services/core/java/com/android/server/policy/PermissionPolicyService.java27
-rw-r--r--services/core/java/com/android/server/rollback/Rollback.java76
-rw-r--r--services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java45
-rw-r--r--services/core/java/com/android/server/rollback/RollbackStore.java6
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java8
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/Hw2CompatUtil.java8
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHw2.java6
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java36
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java47
-rw-r--r--services/core/java/com/android/server/stats/StatsPullAtomService.java784
-rw-r--r--services/core/java/com/android/server/wm/ActivityStack.java47
-rw-r--r--services/core/java/com/android/server/wm/ActivityStackSupervisor.java4
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java35
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java7
-rw-r--r--services/core/java/com/android/server/wm/ConfigurationContainer.java4
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java9
-rw-r--r--services/core/java/com/android/server/wm/InputMonitor.java13
-rw-r--r--services/core/java/com/android/server/wm/InsetsSourceProvider.java10
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java14
-rw-r--r--services/core/java/com/android/server/wm/ScreenRotationAnimation.java353
-rw-r--r--services/core/java/com/android/server/wm/SurfaceAnimationRunner.java4
-rw-r--r--services/core/java/com/android/server/wm/Task.java126
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java167
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java10
-rw-r--r--services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java97
-rw-r--r--services/core/jni/Android.bp6
-rw-r--r--services/core/jni/com_android_server_GraphicsStatsService.cpp180
-rw-r--r--services/core/jni/com_android_server_location_GnssLocationProvider.cpp48
-rw-r--r--services/devicepolicy/Android.bp5
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java5
-rw-r--r--services/java/com/android/server/SystemServer.java1
-rw-r--r--services/net/java/android/net/ip/IpClientCallbacks.java13
-rw-r--r--services/net/java/android/net/ip/IpClientUtil.java1
-rw-r--r--services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java41
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java129
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/UserInfoStoreTest.java177
-rw-r--r--services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java128
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java24
-rw-r--r--services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java26
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/model/BitTrackedInputStreamTest.java142
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/model/ByteTrackedOutputStreamTest.java (renamed from services/tests/servicestests/src/com/android/server/integrity/serializer/ByteTrackedOutputStreamTest.java)4
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java102
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java228
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java14
-rw-r--r--services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java40
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java48
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java4
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java99
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java57
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java20
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java170
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/utils/RotationAnimationUtilsTest.java153
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java68
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java110
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerInternal.java62
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java27
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java51
-rw-r--r--telecomm/java/android/telecom/TelecomManager.java36
-rw-r--r--telecomm/java/com/android/internal/telecom/ITelecomService.aidl28
-rw-r--r--telephony/common/android/telephony/LocationAccessPolicy.java2
-rw-r--r--telephony/common/com/android/internal/telephony/CarrierAppUtils.java27
-rw-r--r--telephony/common/com/android/internal/telephony/GsmAlphabet.java2
-rw-r--r--telephony/common/com/android/internal/telephony/HbpcdUtils.java2
-rw-r--r--telephony/common/com/android/internal/telephony/SmsApplication.java2
-rw-r--r--telephony/common/com/android/internal/telephony/SmsNumberUtils.java2
-rw-r--r--telephony/common/com/android/internal/telephony/TelephonyPermissions.java2
-rw-r--r--telephony/java/android/service/carrier/CarrierIdentifier.java2
-rw-r--r--telephony/java/android/telephony/AnomalyReporter.java2
-rwxr-xr-xtelephony/java/android/telephony/CarrierConfigManager.java10
-rw-r--r--telephony/java/android/telephony/CbGeoUtils.java2
-rw-r--r--telephony/java/android/telephony/CellIdentity.java83
-rw-r--r--telephony/java/android/telephony/CellInfoCdma.java2
-rw-r--r--telephony/java/android/telephony/CellInfoGsm.java2
-rw-r--r--telephony/java/android/telephony/CellInfoLte.java2
-rw-r--r--telephony/java/android/telephony/CellInfoTdscdma.java2
-rw-r--r--telephony/java/android/telephony/CellInfoWcdma.java2
-rw-r--r--telephony/java/android/telephony/CellLocation.java3
-rw-r--r--telephony/java/android/telephony/CellSignalStrengthCdma.java2
-rw-r--r--telephony/java/android/telephony/CellSignalStrengthGsm.java2
-rw-r--r--telephony/java/android/telephony/CellSignalStrengthLte.java2
-rw-r--r--telephony/java/android/telephony/CellSignalStrengthNr.java2
-rw-r--r--telephony/java/android/telephony/CellSignalStrengthTdscdma.java2
-rw-r--r--telephony/java/android/telephony/CellSignalStrengthWcdma.java2
-rw-r--r--telephony/java/android/telephony/NetworkScan.java2
-rw-r--r--telephony/java/android/telephony/NetworkService.java2
-rw-r--r--telephony/java/android/telephony/NetworkServiceCallback.java2
-rw-r--r--telephony/java/android/telephony/PhoneNumberUtils.java2
-rw-r--r--telephony/java/android/telephony/ServiceState.java2
-rw-r--r--telephony/java/android/telephony/SignalStrength.java5
-rw-r--r--telephony/java/android/telephony/SmsManager.java22
-rw-r--r--telephony/java/android/telephony/SmsMessage.java2
-rw-r--r--telephony/java/android/telephony/SubscriptionInfo.java2
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java2
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java84
-rw-r--r--telephony/java/android/telephony/TelephonyScanManager.java2
-rw-r--r--telephony/java/android/telephony/UiccAccessRule.java2
-rw-r--r--telephony/java/android/telephony/VoLteServiceState.java5
-rw-r--r--telephony/java/android/telephony/data/ApnSetting.java2
-rw-r--r--telephony/java/android/telephony/data/DataService.java2
-rw-r--r--telephony/java/android/telephony/data/DataServiceCallback.java2
-rw-r--r--telephony/java/android/telephony/data/QualifiedNetworksService.java2
-rw-r--r--telephony/java/android/telephony/emergency/EmergencyNumber.java2
-rw-r--r--telephony/java/android/telephony/ims/ImsConferenceState.java2
-rw-r--r--telephony/java/android/telephony/ims/ImsExternalCallState.java2
-rw-r--r--telephony/java/android/telephony/ims/ImsRcsManager.java71
-rw-r--r--telephony/java/android/telephony/ims/ImsSsData.java2
-rw-r--r--telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl10
-rw-r--r--telephony/java/com/android/ims/ImsConfig.java2
-rw-r--r--telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java2
-rw-r--r--telephony/java/com/android/internal/telephony/TelephonyIntents.java26
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/SmsMessage.java2
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java2
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/SmsMessage.java2
-rw-r--r--telephony/java/com/android/internal/telephony/uicc/IccUtils.java2
-rw-r--r--tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java96
-rw-r--r--tests/RollbackTest/Android.bp45
-rw-r--r--tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java55
-rw-r--r--tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java42
-rw-r--r--tests/RollbackTest/testdata/AndroidManifest.xml8
-rw-r--r--tests/RollbackTest/testdata/manifest_v1.json4
-rw-r--r--tests/RollbackTest/testdata/manifest_v2.json4
-rw-r--r--tests/TaskOrganizerTest/Android.bp22
-rw-r--r--tests/TaskOrganizerTest/AndroidManifest.xml (renamed from packages/SettingsProvider/res/values/overlayable.xml)26
-rw-r--r--tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java127
-rw-r--r--tests/utils/testutils/Android.bp1
-rw-r--r--tests/utils/testutils/java/com/android/server/accessibility/test/MessageCapturingHandler.java (renamed from services/tests/servicestests/src/com/android/server/accessibility/MessageCapturingHandler.java)4
-rwxr-xr-xtools/hiddenapi/generate_hiddenapi_lists.py7
-rw-r--r--tools/lock_agent/Android.bp1
-rw-r--r--wifi/Android.bp6
-rw-r--r--wifi/java/android/net/wifi/IWifiManager.aidl2
-rw-r--r--wifi/java/android/net/wifi/SoftApConfiguration.java87
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java30
-rw-r--r--wifi/java/android/net/wifi/WifiNetworkSuggestion.java26
-rwxr-xr-xwifi/java/android/net/wifi/WifiOemConfigStoreMigrationHook.java180
-rw-r--r--wifi/java/com/android/server/wifi/BaseWifiService.java6
-rw-r--r--wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java8
-rw-r--r--wifi/tests/src/android/net/wifi/WifiManagerTest.java14
-rw-r--r--wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java8
526 files changed, 14532 insertions, 7219 deletions
diff --git a/Android.bp b/Android.bp
index efe530798041..8c8563139083 100644
--- a/Android.bp
+++ b/Android.bp
@@ -416,6 +416,13 @@ filegroup {
}
filegroup {
+ name: "graphicsstats_proto",
+ srcs: [
+ "libs/hwui/protos/graphicsstats.proto",
+ ],
+}
+
+filegroup {
name: "libvibrator_aidl",
srcs: [
"core/java/android/os/IExternalVibrationController.aidl",
@@ -442,8 +449,6 @@ java_library {
"libcore-platform-compat-config",
"services-platform-compat-config",
"media-provider-platform-compat-config",
- "services-devicepolicy-platform-compat-config",
- "services-core-platform-compat-config",
],
static_libs: [
// If MimeMap ever becomes its own APEX, then this dependency would need to be removed
@@ -489,7 +494,8 @@ java_library {
// TODO(b/140299412): should be framework-wifi-stubs
"framework-wifi",
"ike-stubs",
- // TODO(jiyong): add more stubs for APEXes here
+ // TODO(b/147200698): should be the stub of framework-tethering
+ "framework-tethering",
],
sdk_version: "core_platform",
apex_available: ["//apex_available:platform"],
@@ -619,6 +625,15 @@ java_library {
}
filegroup {
+ name: "framework-ike-shared-srcs",
+ visibility: ["//frameworks/opt/net/ike"],
+ srcs: [
+ "core/java/android/net/annotations/PolicyDirection.java",
+ "telephony/java/android/telephony/Annotation.java",
+ ],
+}
+
+filegroup {
name: "framework-networkstack-shared-srcs",
srcs: [
// TODO: remove these annotations as soon as we can use andoid.support.annotations.*
diff --git a/apex/appsearch/framework/Android.bp b/apex/appsearch/framework/Android.bp
index 3dc5a2c10b6b..1f30dda21ef7 100644
--- a/apex/appsearch/framework/Android.bp
+++ b/apex/appsearch/framework/Android.bp
@@ -26,9 +26,16 @@ java_library {
installable: true,
sdk_version: "core_platform", // TODO(b/146218515) should be core_current
srcs: [":framework-appsearch-sources"],
+ hostdex: true, // for hiddenapi check
libs: [
"framework-minus-apex", // TODO(b/146218515) should be framework-system-stubs
],
+ visibility: [
+ "//frameworks/base/apex/appsearch:__subpackages__",
+ // TODO(b/146218515) remove this when framework is built with the stub of appsearch
+ "//frameworks/base",
+ ],
+ apex_available: ["com.android.appsearch"],
}
metalava_appsearch_docs_args =
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 041825c235d0..6109b713de24 100644
--- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
@@ -1,5 +1,6 @@
package com.android.server.usage;
+import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.usage.AppStandbyInfo;
import android.app.usage.UsageEvents;
@@ -99,8 +100,18 @@ public interface AppStandbyInternal {
List<AppStandbyInfo> getAppStandbyBuckets(int userId);
- void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
- int reason, long elapsedRealtime, boolean resetTimeout);
+ /**
+ * Changes an app's standby bucket to the provided value. The caller can only set the standby
+ * bucket for a different app than itself.
+ */
+ void setAppStandbyBucket(@NonNull String packageName, int bucket, int userId, int callingUid,
+ int callingPid);
+
+ /**
+ * Changes the app standby bucket for multiple apps at once.
+ */
+ void setAppStandbyBuckets(@NonNull List<AppStandbyInfo> appBuckets, int userId, int callingUid,
+ int callingPid);
void addActiveDeviceAdmin(String adminPkg, int userId);
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 2f8b5130edb0..58eb58961ac4 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -47,6 +47,7 @@ import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
+import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.AppGlobals;
@@ -101,7 +102,6 @@ import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
import com.android.server.usage.AppIdleHistory.AppUsageHistory;
-import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
import java.io.File;
import java.io.PrintWriter;
@@ -109,6 +109,7 @@ import java.time.Duration;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
@@ -1014,14 +1015,57 @@ public class AppStandbyController implements AppStandbyInternal {
}
}
+ @Override
+ public void setAppStandbyBucket(@NonNull String packageName, int bucket, int userId,
+ int callingUid, int callingPid) {
+ setAppStandbyBuckets(
+ Collections.singletonList(new AppStandbyInfo(packageName, bucket)),
+ userId, callingUid, callingPid);
+ }
+
+ @Override
+ public void setAppStandbyBuckets(@NonNull List<AppStandbyInfo> appBuckets, int userId,
+ int callingUid, int callingPid) {
+ userId = ActivityManager.handleIncomingUser(
+ callingPid, callingUid, userId, false, true, "setAppStandbyBucket", null);
+ final boolean shellCaller = callingUid == Process.ROOT_UID
+ || callingUid == Process.SHELL_UID;
+ final boolean systemCaller = UserHandle.isCore(callingUid);
+ final int reason = systemCaller ? REASON_MAIN_FORCED : REASON_MAIN_PREDICTED;
+ final int packageFlags = PackageManager.MATCH_ANY_USER
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE;
+ final int numApps = appBuckets.size();
+ final long elapsedRealtime = mInjector.elapsedRealtime();
+ for (int i = 0; i < numApps; ++i) {
+ final AppStandbyInfo bucketInfo = appBuckets.get(i);
+ final String packageName = bucketInfo.mPackageName;
+ final int bucket = bucketInfo.mStandbyBucket;
+ if (bucket < STANDBY_BUCKET_ACTIVE || bucket > STANDBY_BUCKET_NEVER) {
+ throw new IllegalArgumentException("Cannot set the standby bucket to " + bucket);
+ }
+ final int packageUid = mInjector.getPackageManagerInternal()
+ .getPackageUid(packageName, packageFlags, userId);
+ // Caller cannot set their own standby state
+ if (packageUid == callingUid) {
+ throw new IllegalArgumentException("Cannot set your own standby bucket");
+ }
+ if (packageUid < 0) {
+ throw new IllegalArgumentException(
+ "Cannot set standby bucket for non existent package (" + packageName + ")");
+ }
+ setAppStandbyBucket(packageName, userId, bucket, reason, elapsedRealtime, shellCaller);
+ }
+ }
+
@VisibleForTesting
void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
- int reason, long elapsedRealtime) {
- setAppStandbyBucket(packageName, userId, newBucket, reason, elapsedRealtime, false);
+ int reason) {
+ setAppStandbyBucket(
+ packageName, userId, newBucket, reason, mInjector.elapsedRealtime(), false);
}
- @Override
- public void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
+ private void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
int reason, long elapsedRealtime, boolean resetTimeout) {
synchronized (mAppIdleLock) {
// If the package is not installed, don't allow the bucket to be set.
@@ -1444,6 +1488,10 @@ public class AppStandbyController implements AppStandbyInternal {
mBatteryStats.noteEvent(event, packageName, uid);
}
+ PackageManagerInternal getPackageManagerInternal() {
+ return mPackageManagerInternal;
+ }
+
boolean isPackageEphemeral(int userId, String packageName) {
return mPackageManagerInternal.isPackageEphemeral(userId, packageName);
}
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
index 6bd0086681a7..18382a488428 100644
--- a/apex/media/framework/Android.bp
+++ b/apex/media/framework/Android.bp
@@ -55,6 +55,13 @@ java_library {
jarjar_rules: "jarjar_rules.txt",
plugins: ["java_api_finder"],
+
+ hostdex: true, // for hiddenapi check
+ visibility: ["//frameworks/av/apex:__subpackages__"],
+ apex_available: [
+ "com.android.media",
+ "test_com.android.media",
+ ],
}
filegroup {
diff --git a/apex/sdkextensions/TEST_MAPPING b/apex/sdkextensions/TEST_MAPPING
index 7e7762337afc..4e1883382e2c 100644
--- a/apex/sdkextensions/TEST_MAPPING
+++ b/apex/sdkextensions/TEST_MAPPING
@@ -1,7 +1,7 @@
{
"presubmit": [
{
- "name": "CtsSdkExtTestCases"
+ "name": "CtsSdkExtensionsTestCases"
},
{
"name": "apiextensions_e2e_tests"
diff --git a/apex/sdkextensions/framework/Android.bp b/apex/sdkextensions/framework/Android.bp
index 5504f4e5dd8e..dd174734df6d 100644
--- a/apex/sdkextensions/framework/Android.bp
+++ b/apex/sdkextensions/framework/Android.bp
@@ -36,6 +36,11 @@ java_library {
"//frameworks/base/apex/sdkextensions",
"//frameworks/base/apex/sdkextensions/testing",
],
+ hostdex: true, // for hiddenapi check
+ apex_available: [
+ "com.android.sdkext",
+ "test_com.android.sdkext",
+ ],
}
droidstubs {
diff --git a/apex/statsd/aidl/android/os/IStatsCompanionService.aidl b/apex/statsd/aidl/android/os/IStatsCompanionService.aidl
index 21b7767e932d..99b9d398e30c 100644
--- a/apex/statsd/aidl/android/os/IStatsCompanionService.aidl
+++ b/apex/statsd/aidl/android/os/IStatsCompanionService.aidl
@@ -67,11 +67,4 @@ interface IStatsCompanionService {
/** Tells StatsCompaionService to grab the uid map snapshot and send it to statsd. */
oneway void triggerUidSnapshot();
-
- /** Tells StatsCompanionService to tell statsd to register a puller for the given atom id */
- oneway void registerPullAtomCallback(int atomTag, long coolDownNs, long timeoutNs,
- in int[] additiveFields, IPullAtomCallback pullerCallback);
-
- /** Tells StatsCompanionService to tell statsd to unregister a puller for the given atom id */
- oneway void unregisterPullAtomCallback(int atomTag);
}
diff --git a/apex/statsd/aidl/android/os/IStatsManagerService.aidl b/apex/statsd/aidl/android/os/IStatsManagerService.aidl
index dec56345ec2f..4a259f50d2f6 100644
--- a/apex/statsd/aidl/android/os/IStatsManagerService.aidl
+++ b/apex/statsd/aidl/android/os/IStatsManagerService.aidl
@@ -17,6 +17,7 @@
package android.os;
import android.app.PendingIntent;
+import android.os.IPullAtomCallback;
/**
* Binder interface to communicate with the Java-based statistics service helper.
@@ -125,4 +126,11 @@ interface IStatsManagerService {
* Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS.
*/
void removeConfiguration(in long configId, in String packageName);
-} \ No newline at end of file
+
+ /** Tell StatsManagerService to register a puller for the given atom tag with statsd. */
+ oneway void registerPullAtomCallback(int atomTag, long coolDownNs, long timeoutNs,
+ in int[] additiveFields, IPullAtomCallback pullerCallback);
+
+ /** Tell StatsManagerService to unregister the pulller for the given atom tag from statsd. */
+ oneway void unregisterPullAtomCallback(int atomTag);
+}
diff --git a/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp
index a2b0577fe001..0b46645ad06f 100644
--- a/apex/statsd/framework/Android.bp
+++ b/apex/statsd/framework/Android.bp
@@ -37,7 +37,16 @@ java_library {
// TODO(b/146230220): Use framework-system-stubs instead.
"android_system_stubs_current",
],
- // TODO:(b/146210774): Add apex_available field.
+ hostdex: true, // for hiddenapi check
+ visibility: [
+ "//frameworks/base/apex/statsd:__subpackages__",
+ //TODO(b/146167933) remove this when framework is built with framework-statsd-stubs
+ "//frameworks/base",
+ ],
+ apex_available: [
+ "com.android.os.statsd",
+ "test_com.android.os.statsd",
+ ],
}
droidstubs {
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
index d57afeeb7157..0f981e25b37a 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
@@ -75,7 +75,6 @@ import android.os.FileUtils;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
-import android.os.IPullAtomCallback;
import android.os.IStatsCompanionService;
import android.os.IStatsd;
import android.os.IStoraged;
@@ -263,71 +262,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
private StatsManagerService mStatsManagerService;
- private static final class PullerKey {
- private final int mUid;
- private final int mAtomTag;
-
- PullerKey(int uid, int atom) {
- mUid = uid;
- mAtomTag = atom;
- }
-
- public int getUid() {
- return mUid;
- }
-
- public int getAtom() {
- return mAtomTag;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mUid, mAtomTag);
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof PullerKey) {
- PullerKey other = (PullerKey) obj;
- return this.mUid == other.getUid() && this.mAtomTag == other.getAtom();
- }
- return false;
- }
- }
-
- private static final class PullerValue {
- private final long mCoolDownNs;
- private final long mTimeoutNs;
- private int[] mAdditiveFields;
- private IPullAtomCallback mCallback;
-
- PullerValue(long coolDownNs, long timeoutNs, int[] additiveFields,
- IPullAtomCallback callback) {
- mCoolDownNs = coolDownNs;
- mTimeoutNs = timeoutNs;
- mAdditiveFields = additiveFields;
- mCallback = callback;
- }
-
- public long getCoolDownNs() {
- return mCoolDownNs;
- }
-
- public long getTimeoutNs() {
- return mTimeoutNs;
- }
-
- public int[] getAdditiveFields() {
- return mAdditiveFields;
- }
-
- public IPullAtomCallback getCallback() {
- return mCallback;
- }
- }
-
- private final HashMap<PullerKey, PullerValue> mPullers = new HashMap<>();
-
private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
private WifiManager mWifiManager = null;
@@ -881,31 +815,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
}
}
- private void pullWifiBytesTransfer(
- int tagId, long elapsedNanos, long wallClockNanos,
- List<StatsLogEventWrapper> pulledData) {
- long token = Binder.clearCallingIdentity();
- try {
- // TODO: Consider caching the following call to get BatteryStatsInternal.
- BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
- String[] ifaces = bs.getWifiIfaces();
- if (ifaces.length == 0) {
- return;
- }
- if (mNetworkStatsService == null) {
- Slog.e(TAG, "NetworkStats Service is not available!");
- return;
- }
- // Combine all the metrics per Uid into one record.
- NetworkStats stats = mNetworkStatsService.getDetailedUidStats(ifaces).groupedByUid();
- addNetworkStats(tagId, pulledData, stats, false);
- } catch (RemoteException e) {
- Slog.e(TAG, "Pulling netstats for wifi bytes has error", e);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
private void pullWifiBytesTransferByFgBg(
int tagId, long elapsedNanos, long wallClockNanos,
List<StatsLogEventWrapper> pulledData) {
@@ -2382,149 +2291,180 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
long elapsedNanos = SystemClock.elapsedRealtimeNanos();
long wallClockNanos = SystemClock.currentTimeMicro() * 1000L;
switch (tagId) {
- case StatsLog.WIFI_BYTES_TRANSFER: {
- pullWifiBytesTransfer(tagId, elapsedNanos, wallClockNanos, ret);
- break;
- }
+
case StatsLog.MOBILE_BYTES_TRANSFER: {
pullMobileBytesTransfer(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG: {
pullWifiBytesTransferByFgBg(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG: {
pullMobileBytesTransferByFgBg(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.BLUETOOTH_BYTES_TRANSFER: {
pullBluetoothBytesTransfer(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.KERNEL_WAKELOCK: {
pullKernelWakelock(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.CPU_TIME_PER_FREQ: {
pullCpuTimePerFreq(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.CPU_TIME_PER_UID: {
pullKernelUidCpuTime(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.CPU_TIME_PER_UID_FREQ: {
pullKernelUidCpuFreqTime(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.CPU_CLUSTER_TIME: {
pullKernelUidCpuClusterTime(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.CPU_ACTIVE_TIME: {
pullKernelUidCpuActiveTime(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.WIFI_ACTIVITY_INFO: {
pullWifiActivityInfo(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.MODEM_ACTIVITY_INFO: {
pullModemActivityInfo(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.BLUETOOTH_ACTIVITY_INFO: {
pullBluetoothActivityInfo(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.SYSTEM_UPTIME: {
pullSystemUpTime(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.SYSTEM_ELAPSED_REALTIME: {
pullSystemElapsedRealtime(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.PROCESS_MEMORY_STATE: {
pullProcessMemoryState(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.PROCESS_MEMORY_HIGH_WATER_MARK: {
pullProcessMemoryHighWaterMark(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.PROCESS_MEMORY_SNAPSHOT: {
pullProcessMemorySnapshot(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.SYSTEM_ION_HEAP_SIZE: {
pullSystemIonHeapSize(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.PROCESS_SYSTEM_ION_HEAP_SIZE: {
pullProcessSystemIonHeapSize(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.BINDER_CALLS: {
pullBinderCallsStats(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.BINDER_CALLS_EXCEPTIONS: {
pullBinderCallsStatsExceptions(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.LOOPER_STATS: {
pullLooperStats(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.DISK_STATS: {
pullDiskStats(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.DIRECTORY_USAGE: {
pullDirectoryUsage(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.APP_SIZE: {
pullAppSize(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.CATEGORY_SIZE: {
pullCategorySize(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.NUM_FINGERPRINTS_ENROLLED: {
pullNumBiometricsEnrolled(BiometricsProtoEnums.MODALITY_FINGERPRINT, tagId,
elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.NUM_FACES_ENROLLED: {
pullNumBiometricsEnrolled(BiometricsProtoEnums.MODALITY_FACE, tagId, elapsedNanos,
wallClockNanos, ret);
break;
}
+
case StatsLog.PROC_STATS: {
pullProcessStats(ProcessStats.REPORT_ALL, tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.PROC_STATS_PKG_PROC: {
pullProcessStats(ProcessStats.REPORT_PKG_PROC_STATS, tagId, elapsedNanos,
wallClockNanos, ret);
break;
}
+
case StatsLog.DISK_IO: {
pullDiskIo(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.POWER_PROFILE: {
pullPowerProfile(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.BUILD_INFORMATION: {
pullBuildInformation(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.PROCESS_CPU_TIME: {
pullProcessCpuTime(tagId, elapsedNanos, wallClockNanos, ret);
break;
@@ -2533,73 +2473,90 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
pullCpuTimePerThreadFreq(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.DEVICE_CALCULATED_POWER_USE: {
pullDeviceCalculatedPowerUse(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.DEVICE_CALCULATED_POWER_BLAME_UID: {
pullDeviceCalculatedPowerBlameUid(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER: {
pullDeviceCalculatedPowerBlameOther(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.TEMPERATURE: {
pullTemperature(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.COOLING_DEVICE: {
pullCoolingDevices(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.DEBUG_ELAPSED_CLOCK: {
pullDebugElapsedClock(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.DEBUG_FAILING_ELAPSED_CLOCK: {
pullDebugFailingElapsedClock(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.ROLE_HOLDER: {
pullRoleHolders(elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.DANGEROUS_PERMISSION_STATE: {
pullDangerousPermissionState(StatsLog.DANGEROUS_PERMISSION_STATE, elapsedNanos,
wallClockNanos, ret);
break;
}
+
case StatsLog.DANGEROUS_PERMISSION_STATE_SAMPLED: {
pullDangerousPermissionState(StatsLog.DANGEROUS_PERMISSION_STATE_SAMPLED,
elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.TIME_ZONE_DATA_INFO: {
pullTimeZoneDataInfo(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.EXTERNAL_STORAGE_INFO: {
pullExternalStorageInfo(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.APPS_ON_EXTERNAL_STORAGE_INFO: {
pullAppsOnExternalStorageInfo(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.FACE_SETTINGS: {
pullFaceSettings(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.APP_OPS: {
pullAppOps(elapsedNanos, wallClockNanos, ret);
break;
}
+
case StatsLog.NOTIFICATION_REMOTE_VIEWS: {
pullNotificationStats(NotificationManagerService.REPORT_REMOTE_VIEWS,
tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+
default:
Slog.w(TAG, "No such tagId data as " + tagId);
return null;
@@ -2634,57 +2591,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
}
}
- @Override
- public void registerPullAtomCallback(int atomTag, long coolDownNs, long timeoutNs,
- int[] additiveFields, IPullAtomCallback pullerCallback) {
- synchronized (sStatsdLock) {
- // Always cache the puller in SCS.
- // If statsd is down, we will register it when it comes back up.
- int callingUid = Binder.getCallingUid();
- final long token = Binder.clearCallingIdentity();
- PullerKey key = new PullerKey(callingUid, atomTag);
- PullerValue val = new PullerValue(
- coolDownNs, timeoutNs, additiveFields, pullerCallback);
- mPullers.put(key, val);
-
- if (sStatsd == null) {
- Slog.w(TAG, "Could not access statsd for registering puller for atom " + atomTag);
- return;
- }
- try {
- sStatsd.registerPullAtomCallback(
- callingUid, atomTag, coolDownNs, timeoutNs, additiveFields, pullerCallback);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to access statsd to register puller for atom " + atomTag);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
- }
-
- @Override
- public void unregisterPullAtomCallback(int atomTag) {
- synchronized (sStatsdLock) {
- // Always remove the puller in SCS.
- // If statsd is down, we will not register it when it comes back up.
- int callingUid = Binder.getCallingUid();
- final long token = Binder.clearCallingIdentity();
- PullerKey key = new PullerKey(callingUid, atomTag);
- mPullers.remove(key);
-
- if (sStatsd == null) {
- Slog.w(TAG, "Could not access statsd for registering puller for atom " + atomTag);
- return;
- }
- try {
- sStatsd.unregisterPullAtomCallback(callingUid, atomTag);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to access statsd to register puller for atom " + atomTag);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
- }
// Statsd related code
@@ -2763,8 +2669,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
// Pull the latest state of UID->app name, version mapping when
// statsd starts.
informAllUidsLocked(mContext);
- // Register all pullers. If SCS has just started, this should be empty.
- registerAllPullersLocked();
} finally {
restoreCallingIdentity(token);
}
@@ -2776,17 +2680,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
}
}
- @GuardedBy("sStatsdLock")
- private void registerAllPullersLocked() throws RemoteException {
- // TODO: pass in one call, using a file descriptor (similar to uidmap).
- for (Map.Entry<PullerKey, PullerValue> entry : mPullers.entrySet()) {
- PullerKey key = entry.getKey();
- PullerValue val = entry.getValue();
- sStatsd.registerPullAtomCallback(key.getUid(), key.getAtom(), val.getCoolDownNs(),
- val.getTimeoutNs(), val.getAdditiveFields(), val.getCallback());
- }
- }
-
private class StatsdDeathRecipient implements IBinder.DeathRecipient {
@Override
public void binderDied() {
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java b/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
index b27d0f7699fc..04d8b006f51d 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
@@ -24,6 +24,7 @@ import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.content.Context;
import android.os.Binder;
+import android.os.IPullAtomCallback;
import android.os.IStatsManagerService;
import android.os.IStatsd;
import android.os.Process;
@@ -60,8 +61,7 @@ public class StatsManagerService extends IStatsManagerService.Stub {
@GuardedBy("mLock")
private ArrayMap<ConfigKey, PendingIntentRef> mDataFetchPirMap = new ArrayMap<>();
@GuardedBy("mLock")
- private ArrayMap<Integer, PendingIntentRef> mActiveConfigsPirMap =
- new ArrayMap<>();
+ private ArrayMap<Integer, PendingIntentRef> mActiveConfigsPirMap = new ArrayMap<>();
@GuardedBy("mLock")
private ArrayMap<ConfigKey, ArrayMap<Long, PendingIntentRef>> mBroadcastSubscriberPirMap =
new ArrayMap<>();
@@ -72,8 +72,8 @@ public class StatsManagerService extends IStatsManagerService.Stub {
}
private static class ConfigKey {
- private int mUid;
- private long mConfigId;
+ private final int mUid;
+ private final long mConfigId;
ConfigKey(int uid, long configId) {
mUid = uid;
@@ -103,6 +103,126 @@ public class StatsManagerService extends IStatsManagerService.Stub {
}
}
+ private static class PullerKey {
+ private final int mUid;
+ private final int mAtomTag;
+
+ PullerKey(int uid, int atom) {
+ mUid = uid;
+ mAtomTag = atom;
+ }
+
+ public int getUid() {
+ return mUid;
+ }
+
+ public int getAtom() {
+ return mAtomTag;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mUid, mAtomTag);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof PullerKey) {
+ PullerKey other = (PullerKey) obj;
+ return this.mUid == other.getUid() && this.mAtomTag == other.getAtom();
+ }
+ return false;
+ }
+ }
+
+ private static class PullerValue {
+ private final long mCoolDownNs;
+ private final long mTimeoutNs;
+ private final int[] mAdditiveFields;
+ private final IPullAtomCallback mCallback;
+
+ PullerValue(long coolDownNs, long timeoutNs, int[] additiveFields,
+ IPullAtomCallback callback) {
+ mCoolDownNs = coolDownNs;
+ mTimeoutNs = timeoutNs;
+ mAdditiveFields = additiveFields;
+ mCallback = callback;
+ }
+
+ public long getCoolDownNs() {
+ return mCoolDownNs;
+ }
+
+ public long getTimeoutNs() {
+ return mTimeoutNs;
+ }
+
+ public int[] getAdditiveFields() {
+ return mAdditiveFields;
+ }
+
+ public IPullAtomCallback getCallback() {
+ return mCallback;
+ }
+ }
+
+ private final ArrayMap<PullerKey, PullerValue> mPullers = new ArrayMap<>();
+
+ @Override
+ public void registerPullAtomCallback(int atomTag, long coolDownNs, long timeoutNs,
+ int[] additiveFields, IPullAtomCallback pullerCallback) {
+ int callingUid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+ PullerKey key = new PullerKey(callingUid, atomTag);
+ PullerValue val = new PullerValue(coolDownNs, timeoutNs, additiveFields, pullerCallback);
+
+ // Always cache the puller in StatsManagerService. If statsd is down, we will register the
+ // puller when statsd comes back up.
+ synchronized (mLock) {
+ mPullers.put(key, val);
+ }
+
+ IStatsd statsd = getStatsdNonblocking();
+ if (statsd == null) {
+ return;
+ }
+
+ try {
+ statsd.registerPullAtomCallback(
+ callingUid, atomTag, coolDownNs, timeoutNs, additiveFields, pullerCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to access statsd to register puller for atom " + atomTag);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void unregisterPullAtomCallback(int atomTag) {
+ int callingUid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+ PullerKey key = new PullerKey(callingUid, atomTag);
+
+ // Always remove the puller from StatsManagerService even if statsd is down. When statsd
+ // comes back up, we will not re-register the removed puller.
+ synchronized (mLock) {
+ mPullers.remove(key);
+ }
+
+ IStatsd statsd = getStatsdNonblocking();
+ if (statsd == null) {
+ return;
+ }
+
+ try {
+ statsd.unregisterPullAtomCallback(callingUid, atomTag);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to access statsd to unregister puller for atom " + atomTag);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
@Override
public void setDataFetchOperation(long configId, PendingIntent pendingIntent,
String packageName) {
@@ -441,46 +561,85 @@ public class StatsManagerService extends IStatsManagerService.Stub {
if (statsd == null) {
return;
}
- // Since we do not want to make an IPC with the a lock held, we first create local deep
- // copies of the data with the lock held before iterating through the maps.
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ registerAllPullers(statsd);
+ registerAllDataFetchOperations(statsd);
+ registerAllActiveConfigsChangedOperations(statsd);
+ registerAllBroadcastSubscribers(statsd);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "StatsManager failed to (re-)register data with statsd");
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ // Pre-condition: the Binder calling identity has already been cleared
+ private void registerAllPullers(IStatsd statsd) throws RemoteException {
+ // Since we do not want to make an IPC with the lock held, we first create a copy of the
+ // data with the lock held before iterating through the map.
+ ArrayMap<PullerKey, PullerValue> pullersCopy;
+ synchronized (mLock) {
+ pullersCopy = new ArrayMap<>(mPullers);
+ }
+
+ for (Map.Entry<PullerKey, PullerValue> entry : pullersCopy.entrySet()) {
+ PullerKey key = entry.getKey();
+ PullerValue value = entry.getValue();
+ statsd.registerPullAtomCallback(key.getUid(), key.getAtom(), value.getCoolDownNs(),
+ value.getTimeoutNs(), value.getAdditiveFields(), value.getCallback());
+ }
+ }
+
+ // Pre-condition: the Binder calling identity has already been cleared
+ private void registerAllDataFetchOperations(IStatsd statsd) throws RemoteException {
+ // Since we do not want to make an IPC with the lock held, we first create a copy of the
+ // data with the lock held before iterating through the map.
ArrayMap<ConfigKey, PendingIntentRef> dataFetchCopy;
- ArrayMap<Integer, PendingIntentRef> activeConfigsChangedCopy;
- ArrayMap<ConfigKey, ArrayMap<Long, PendingIntentRef>> broadcastSubscriberCopy;
synchronized (mLock) {
dataFetchCopy = new ArrayMap<>(mDataFetchPirMap);
- activeConfigsChangedCopy = new ArrayMap<>(mActiveConfigsPirMap);
- broadcastSubscriberCopy = new ArrayMap<>();
- for (Map.Entry<ConfigKey, ArrayMap<Long, PendingIntentRef>> entry
- : mBroadcastSubscriberPirMap.entrySet()) {
- broadcastSubscriberCopy.put(entry.getKey(), new ArrayMap<>(entry.getValue()));
- }
}
+
for (Map.Entry<ConfigKey, PendingIntentRef> entry : dataFetchCopy.entrySet()) {
ConfigKey key = entry.getKey();
- try {
- statsd.setDataFetchOperation(key.getConfigId(), entry.getValue(), key.getUid());
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to setDataFetchOperation from pirMap");
- }
+ statsd.setDataFetchOperation(key.getConfigId(), entry.getValue(), key.getUid());
+ }
+ }
+
+ // Pre-condition: the Binder calling identity has already been cleared
+ private void registerAllActiveConfigsChangedOperations(IStatsd statsd) throws RemoteException {
+ // Since we do not want to make an IPC with the lock held, we first create a copy of the
+ // data with the lock held before iterating through the map.
+ ArrayMap<Integer, PendingIntentRef> activeConfigsChangedCopy;
+ synchronized (mLock) {
+ activeConfigsChangedCopy = new ArrayMap<>(mActiveConfigsPirMap);
+ }
+
+ for (Map.Entry<Integer, PendingIntentRef> entry : activeConfigsChangedCopy.entrySet()) {
+ statsd.setActiveConfigsChangedOperation(entry.getValue(), entry.getKey());
}
- for (Map.Entry<Integer, PendingIntentRef> entry
- : activeConfigsChangedCopy.entrySet()) {
- try {
- statsd.setActiveConfigsChangedOperation(entry.getValue(), entry.getKey());
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to setActiveConfigsChangedOperation from pirMap");
+ }
+
+ // Pre-condition: the Binder calling identity has already been cleared
+ private void registerAllBroadcastSubscribers(IStatsd statsd) throws RemoteException {
+ // Since we do not want to make an IPC with the lock held, we first create a deep copy of
+ // the data with the lock held before iterating through the map.
+ ArrayMap<ConfigKey, ArrayMap<Long, PendingIntentRef>> broadcastSubscriberCopy =
+ new ArrayMap<>();
+ synchronized (mLock) {
+ for (Map.Entry<ConfigKey, ArrayMap<Long, PendingIntentRef>> entry :
+ mBroadcastSubscriberPirMap.entrySet()) {
+ broadcastSubscriberCopy.put(entry.getKey(), new ArrayMap(entry.getValue()));
}
}
- for (Map.Entry<ConfigKey, ArrayMap<Long, PendingIntentRef>> entry
- : broadcastSubscriberCopy.entrySet()) {
+
+ for (Map.Entry<ConfigKey, ArrayMap<Long, PendingIntentRef>> entry :
+ mBroadcastSubscriberPirMap.entrySet()) {
+ ConfigKey configKey = entry.getKey();
for (Map.Entry<Long, PendingIntentRef> subscriberEntry : entry.getValue().entrySet()) {
- ConfigKey configKey = entry.getKey();
- try {
- statsd.setBroadcastSubscriber(configKey.getConfigId(), subscriberEntry.getKey(),
- subscriberEntry.getValue(), configKey.getUid());
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to setBroadcastSubscriber from pirMap");
- }
+ statsd.setBroadcastSubscriber(configKey.getConfigId(), subscriberEntry.getKey(),
+ subscriberEntry.getValue(), configKey.getUid());
}
}
}
diff --git a/api/current.txt b/api/current.txt
index 178a02133d14..5ce5d6107982 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -115,6 +115,7 @@ package android {
field public static final String READ_LOGS = "android.permission.READ_LOGS";
field public static final String READ_PHONE_NUMBERS = "android.permission.READ_PHONE_NUMBERS";
field public static final String READ_PHONE_STATE = "android.permission.READ_PHONE_STATE";
+ field public static final String READ_PRECISE_PHONE_STATE = "android.permission.READ_PRECISE_PHONE_STATE";
field public static final String READ_SMS = "android.permission.READ_SMS";
field public static final String READ_SYNC_SETTINGS = "android.permission.READ_SYNC_SETTINGS";
field public static final String READ_SYNC_STATS = "android.permission.READ_SYNC_STATS";
@@ -1144,6 +1145,7 @@ package android {
field public static final int resizeable = 16843405; // 0x101028d
field public static final int resizeableActivity = 16844022; // 0x10104f6
field public static final int resource = 16842789; // 0x1010025
+ field public static final int resourcesMap = 16844297; // 0x1010609
field public static final int restoreAnyVersion = 16843450; // 0x10102ba
field @Deprecated public static final int restoreNeedsApplication = 16843421; // 0x101029d
field public static final int restrictedAccountType = 16843733; // 0x10103d5
@@ -5837,6 +5839,7 @@ package android.app {
method public void enableLights(boolean);
method public void enableVibration(boolean);
method public android.media.AudioAttributes getAudioAttributes();
+ method @Nullable public String getConversationId();
method public String getDescription();
method public String getGroup();
method public String getId();
@@ -5844,12 +5847,14 @@ package android.app {
method public int getLightColor();
method public int getLockscreenVisibility();
method public CharSequence getName();
+ method @Nullable public String getParentChannelId();
method public android.net.Uri getSound();
method public long[] getVibrationPattern();
method public boolean hasUserSetImportance();
method public boolean hasUserSetSound();
method public void setAllowBubbles(boolean);
method public void setBypassDnd(boolean);
+ method public void setConversationId(@Nullable String, @Nullable String);
method public void setDescription(String);
method public void setGroup(String);
method public void setImportance(int);
@@ -5862,6 +5867,7 @@ package android.app {
method public boolean shouldShowLights();
method public boolean shouldVibrate();
method public void writeToParcel(android.os.Parcel, int);
+ field public static final String CONVERSATION_CHANNEL_ID_FORMAT = "%1$s : %2$s";
field @NonNull public static final android.os.Parcelable.Creator<android.app.NotificationChannel> CREATOR;
field public static final String DEFAULT_CHANNEL_ID = "miscellaneous";
}
@@ -5903,6 +5909,7 @@ package android.app {
method public final int getCurrentInterruptionFilter();
method public int getImportance();
method public android.app.NotificationChannel getNotificationChannel(String);
+ method @Nullable public android.app.NotificationChannel getNotificationChannel(@NonNull String, @NonNull String);
method public android.app.NotificationChannelGroup getNotificationChannelGroup(String);
method public java.util.List<android.app.NotificationChannelGroup> getNotificationChannelGroups();
method public java.util.List<android.app.NotificationChannel> getNotificationChannels();
@@ -9823,6 +9830,7 @@ package android.content {
method public boolean bindIsolatedService(@NonNull @RequiresPermission android.content.Intent, int, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.content.ServiceConnection);
method public abstract boolean bindService(@RequiresPermission android.content.Intent, @NonNull android.content.ServiceConnection, int);
method public boolean bindService(@NonNull @RequiresPermission android.content.Intent, int, @NonNull java.util.concurrent.Executor, @NonNull android.content.ServiceConnection);
+ method @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", android.Manifest.permission.INTERACT_ACROSS_PROFILES}) public boolean bindServiceAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.content.ServiceConnection, int, @NonNull android.os.UserHandle);
method @CheckResult(suggest="#enforceCallingOrSelfPermission(String,String)") public abstract int checkCallingOrSelfPermission(@NonNull String);
method @CheckResult(suggest="#enforceCallingOrSelfUriPermission(Uri,int,String)") public abstract int checkCallingOrSelfUriPermission(android.net.Uri, int);
method @CheckResult(suggest="#enforceCallingPermission(String,String)") public abstract int checkCallingPermission(@NonNull String);
@@ -10000,7 +10008,6 @@ package android.content {
field public static final String MEDIA_ROUTER_SERVICE = "media_router";
field public static final String MEDIA_SESSION_SERVICE = "media_session";
field public static final String MIDI_SERVICE = "midi";
- field public static final String MMS_SERVICE = "mms";
field public static final int MODE_APPEND = 32768; // 0x8000
field public static final int MODE_ENABLE_WRITE_AHEAD_LOGGING = 8; // 0x8
field @Deprecated public static final int MODE_MULTI_PROCESS = 4; // 0x4
@@ -11356,6 +11363,9 @@ package android.content.pm {
}
public class CrossProfileApps {
+ method public boolean canInteractAcrossProfiles();
+ method public boolean canRequestInteractAcrossProfiles();
+ method @Nullable public android.content.Intent createRequestInteractAcrossProfilesIntent();
method @NonNull public android.graphics.drawable.Drawable getProfileSwitchingIconDrawable(@NonNull android.os.UserHandle);
method @NonNull public CharSequence getProfileSwitchingLabel(@NonNull android.os.UserHandle);
method @NonNull public java.util.List<android.os.UserHandle> getTargetUserProfiles();
@@ -11928,6 +11938,7 @@ package android.content.pm {
field public static final String FEATURE_STRONGBOX_KEYSTORE = "android.hardware.strongbox_keystore";
field public static final String FEATURE_TELEPHONY = "android.hardware.telephony";
field public static final String FEATURE_TELEPHONY_CDMA = "android.hardware.telephony.cdma";
+ field public static final String FEATURE_TELEPHONY_DATA = "android.hardware.telephony.data";
field public static final String FEATURE_TELEPHONY_EUICC = "android.hardware.telephony.euicc";
field public static final String FEATURE_TELEPHONY_GSM = "android.hardware.telephony.gsm";
field public static final String FEATURE_TELEPHONY_IMS = "android.hardware.telephony.ims";
@@ -29155,6 +29166,7 @@ package android.net {
ctor public DhcpInfo();
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.DhcpInfo> CREATOR;
field public int dns1;
field public int dns2;
field public int gateway;
@@ -30653,11 +30665,11 @@ package android.net.wifi {
ctor public WifiNetworkSuggestion.Builder();
method @NonNull public android.net.wifi.WifiNetworkSuggestion build();
method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setBssid(@NonNull android.net.MacAddress);
+ method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setCredentialSharedWithUser(boolean);
method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsAppInteractionRequired(boolean);
method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsEnhancedOpen(boolean);
method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsHiddenSsid(boolean);
method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsMetered(boolean);
- method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsUserAllowedToManuallyConnect(boolean);
method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsUserInteractionRequired(boolean);
method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setPasspointConfig(@NonNull android.net.wifi.hotspot2.PasspointConfiguration);
method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setPriority(@IntRange(from=0) int);
@@ -35827,6 +35839,7 @@ package android.os {
field public static final int THREAD_PRIORITY_URGENT_AUDIO = -19; // 0xffffffed
field public static final int THREAD_PRIORITY_URGENT_DISPLAY = -8; // 0xfffffff8
field public static final int THREAD_PRIORITY_VIDEO = -10; // 0xfffffff6
+ field public static final int WIFI_UID = 1010; // 0x3f2
}
public abstract class ProxyFileDescriptorCallback {
@@ -36061,6 +36074,7 @@ package android.os {
method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public android.os.Bundle getUserRestrictions(android.os.UserHandle);
method public boolean hasUserRestriction(String);
method public boolean isDemoUser();
+ method public boolean isManagedProfile();
method public boolean isQuietModeEnabled(android.os.UserHandle);
method public boolean isSystemUser();
method public boolean isUserAGoat();
@@ -39525,7 +39539,9 @@ package android.provider {
field public static final String ACTION_LOCALE_SETTINGS = "android.settings.LOCALE_SETTINGS";
field public static final String ACTION_LOCATION_SOURCE_SETTINGS = "android.settings.LOCATION_SOURCE_SETTINGS";
field public static final String ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS = "android.settings.MANAGE_ALL_APPLICATIONS_SETTINGS";
+ field public static final String ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION = "android.settings.MANAGE_ALL_FILES_ACCESS_PERMISSION";
field public static final String ACTION_MANAGE_APPLICATIONS_SETTINGS = "android.settings.MANAGE_APPLICATIONS_SETTINGS";
+ field public static final String ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION = "android.settings.MANAGE_APP_ALL_FILES_ACCESS_PERMISSION";
field public static final String ACTION_MANAGE_DEFAULT_APPS_SETTINGS = "android.settings.MANAGE_DEFAULT_APPS_SETTINGS";
field public static final String ACTION_MANAGE_OVERLAY_PERMISSION = "android.settings.action.MANAGE_OVERLAY_PERMISSION";
field public static final String ACTION_MANAGE_UNKNOWN_APP_SOURCES = "android.settings.MANAGE_UNKNOWN_APP_SOURCES";
@@ -42669,9 +42685,13 @@ package android.service.voice {
method public android.content.Intent createEnrollIntent();
method public android.content.Intent createReEnrollIntent();
method public android.content.Intent createUnEnrollIntent();
+ method public int getParameter(int);
method public int getSupportedRecognitionModes();
+ method @Nullable public android.service.voice.AlwaysOnHotwordDetector.ModelParamRange queryParameter(int);
+ method public int setParameter(int, int);
method public boolean startRecognition(int);
method public boolean stopRecognition();
+ field public static final int MODEL_PARAM_THRESHOLD_FACTOR = 0; // 0x0
field public static final int RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS = 2; // 0x2
field public static final int RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO = 1; // 0x1
field public static final int RECOGNITION_MODE_USER_IDENTIFICATION = 2; // 0x2
@@ -42696,6 +42716,11 @@ package android.service.voice {
method @Nullable public byte[] getTriggerAudio();
}
+ public static final class AlwaysOnHotwordDetector.ModelParamRange {
+ method public int end();
+ method public int start();
+ }
+
public class VoiceInteractionService extends android.app.Service {
ctor public VoiceInteractionService();
method public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(String, java.util.Locale, android.service.voice.AlwaysOnHotwordDetector.Callback);
@@ -45011,6 +45036,7 @@ package android.telephony {
field public static final String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
field public static final int DATA_CYCLE_THRESHOLD_DISABLED = -2; // 0xfffffffe
field public static final int DATA_CYCLE_USE_PLATFORM_DEFAULT = -1; // 0xffffffff
+ field public static final String ENABLE_EAP_METHOD_PREFIX_BOOL = "enable_eap_method_prefix_bool";
field public static final String EXTRA_SLOT_INDEX = "android.telephony.extra.SLOT_INDEX";
field public static final String EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX";
field public static final String KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY = "5g_nr_ssrsrp_thresholds_int_array";
@@ -45208,6 +45234,7 @@ package android.telephony {
public static final class CarrierConfigManager.Ims {
field public static final String KEY_PREFIX = "ims.";
+ field public static final String KEY_WIFI_OFF_DEFERRING_TIME_INT = "ims.wifi_off_deferring_time_int";
}
public abstract class CellIdentity implements android.os.Parcelable {
@@ -45516,11 +45543,6 @@ package android.telephony {
method @Nullable public android.telephony.mbms.StreamingService startStreaming(android.telephony.mbms.StreamingServiceInfo, @NonNull java.util.concurrent.Executor, android.telephony.mbms.StreamingServiceCallback);
}
- public final class MmsManager {
- method public void downloadMultimediaMessage(int, @NonNull String, @NonNull android.net.Uri, @Nullable android.os.Bundle, @Nullable android.app.PendingIntent);
- method public void sendMultimediaMessage(int, @NonNull android.net.Uri, @Nullable String, @Nullable android.os.Bundle, @Nullable android.app.PendingIntent);
- }
-
@Deprecated public class NeighboringCellInfo implements android.os.Parcelable {
ctor @Deprecated public NeighboringCellInfo();
ctor @Deprecated public NeighboringCellInfo(int, int);
@@ -45758,8 +45780,8 @@ package android.telephony {
method public String createAppSpecificSmsToken(android.app.PendingIntent);
method @Nullable public String createAppSpecificSmsTokenWithPackageInfo(@Nullable String, @NonNull android.app.PendingIntent);
method public java.util.ArrayList<java.lang.String> divideMessage(String);
- method @Deprecated public void downloadMultimediaMessage(android.content.Context, String, android.net.Uri, android.os.Bundle, android.app.PendingIntent);
- method @NonNull public android.os.Bundle getCarrierConfigValues();
+ method public void downloadMultimediaMessage(android.content.Context, String, android.net.Uri, android.os.Bundle, android.app.PendingIntent);
+ method @Nullable public android.os.Bundle getCarrierConfigValues();
method public static android.telephony.SmsManager getDefault();
method public static int getDefaultSmsSubscriptionId();
method public static android.telephony.SmsManager getSmsManagerForSubscriptionId(int);
@@ -45768,7 +45790,7 @@ package android.telephony {
method public int getSubscriptionId();
method public void injectSmsPdu(byte[], String, android.app.PendingIntent);
method public void sendDataMessage(String, String, short, byte[], android.app.PendingIntent, android.app.PendingIntent);
- method @Deprecated public void sendMultimediaMessage(android.content.Context, android.net.Uri, String, android.os.Bundle, android.app.PendingIntent);
+ method 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 sendTextMessage(String, String, String, android.app.PendingIntent, android.app.PendingIntent);
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);
@@ -46135,12 +46157,12 @@ package android.telephony {
method public android.net.Uri getVoicemailRingtoneUri(android.telecom.PhoneAccountHandle);
method public boolean hasCarrierPrivileges();
method public boolean hasIccCard();
- method public boolean iccCloseLogicalChannel(int);
- method public byte[] iccExchangeSimIO(int, int, int, int, int, String);
+ method @Deprecated public boolean iccCloseLogicalChannel(int);
+ method @Deprecated public byte[] iccExchangeSimIO(int, int, int, int, int, String);
method @Deprecated public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannel(String);
- method public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannel(String, int);
- method public String iccTransmitApduBasicChannel(int, int, int, int, int, String);
- method public String iccTransmitApduLogicalChannel(int, int, int, int, int, int, String);
+ method @Deprecated public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannel(String, int);
+ 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 @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();
@@ -46158,7 +46180,7 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void requestCellInfoUpdate(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
method @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback);
method public void sendDialerSpecialCode(String);
- method public String sendEnvelopeWithStatus(String);
+ method @Deprecated public String sendEnvelopeWithStatus(String);
method @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void sendUssdRequest(String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler);
method public void sendVisualVoicemailSms(String, int, String, android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(boolean);
@@ -51600,9 +51622,10 @@ package android.view {
method public boolean dispatchUnhandledMove(android.view.View, int);
method protected void dispatchVisibilityChanged(@NonNull android.view.View, int);
method public void dispatchWindowFocusChanged(boolean);
- method public void dispatchWindowInsetsAnimationFinished(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation);
+ method public void dispatchWindowInsetsAnimationFinish(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation);
+ method public void dispatchWindowInsetsAnimationPrepare(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation);
method @NonNull public android.view.WindowInsets dispatchWindowInsetsAnimationProgress(@NonNull android.view.WindowInsets);
- method @NonNull public android.view.WindowInsetsAnimationCallback.AnimationBounds dispatchWindowInsetsAnimationStarted(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation, @NonNull android.view.WindowInsetsAnimationCallback.AnimationBounds);
+ method @NonNull public android.view.WindowInsetsAnimationCallback.AnimationBounds dispatchWindowInsetsAnimationStart(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation, @NonNull android.view.WindowInsetsAnimationCallback.AnimationBounds);
method public void dispatchWindowSystemUiVisiblityChanged(int);
method public void dispatchWindowVisibilityChanged(int);
method @CallSuper public void draw(android.graphics.Canvas);
@@ -53282,9 +53305,10 @@ package android.view {
}
public interface WindowInsetsAnimationCallback {
- method public default void onFinished(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation);
+ method public default void onFinish(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation);
+ method public default void onPrepare(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation);
method @NonNull public android.view.WindowInsets onProgress(@NonNull android.view.WindowInsets);
- method @NonNull public default android.view.WindowInsetsAnimationCallback.AnimationBounds onStarted(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation, @NonNull android.view.WindowInsetsAnimationCallback.AnimationBounds);
+ method @NonNull public default android.view.WindowInsetsAnimationCallback.AnimationBounds onStart(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation, @NonNull android.view.WindowInsetsAnimationCallback.AnimationBounds);
}
public static final class WindowInsetsAnimationCallback.AnimationBounds {
diff --git a/api/system-current.txt b/api/system-current.txt
index 384dc13ff567..515bf547784c 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -152,6 +152,7 @@ package android {
field public static final String PROVIDE_RESOLVER_RANKER_SERVICE = "android.permission.PROVIDE_RESOLVER_RANKER_SERVICE";
field public static final String PROVIDE_TRUST_AGENT = "android.permission.PROVIDE_TRUST_AGENT";
field public static final String QUERY_TIME_ZONE_RULES = "android.permission.QUERY_TIME_ZONE_RULES";
+ field public static final String RADIO_SCAN_WITHOUT_LOCATION = "android.permission.RADIO_SCAN_WITHOUT_LOCATION";
field public static final String READ_ACTIVE_EMERGENCY_SESSION = "android.permission.READ_ACTIVE_EMERGENCY_SESSION";
field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
field public static final String READ_CONTENT_RATING_SYSTEMS = "android.permission.READ_CONTENT_RATING_SYSTEMS";
@@ -240,7 +241,6 @@ package android {
field public static final int isVrOnly = 16844152; // 0x1010578
field public static final int requiredSystemPropertyName = 16844133; // 0x1010565
field public static final int requiredSystemPropertyValue = 16844134; // 0x1010566
- field public static final int resourcesMap = 16844297; // 0x1010609
field public static final int supportsAmbientMode = 16844173; // 0x101058d
field public static final int userRestriction = 16844164; // 0x1010584
}
@@ -1535,6 +1535,10 @@ package android.bluetooth {
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
}
+ public final class BluetoothHidDevice implements android.bluetooth.BluetoothProfile {
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
+ }
+
public final class BluetoothHidHost implements android.bluetooth.BluetoothProfile {
method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice);
@@ -1543,12 +1547,20 @@ package android.bluetooth {
field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED";
}
+ public final class BluetoothMap implements android.bluetooth.BluetoothProfile {
+ method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice, int);
+ field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED";
+ }
+
public final class BluetoothPan implements android.bluetooth.BluetoothProfile {
method protected void finalize();
method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
method public int getConnectionState(@Nullable android.bluetooth.BluetoothDevice);
method public boolean isTetheringOn();
method public void setBluetoothTethering(boolean);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED";
field public static final String EXTRA_LOCAL_ROLE = "android.bluetooth.pan.extra.LOCAL_ROLE";
field public static final int LOCAL_NAP_ROLE = 1; // 0x1
@@ -1671,7 +1683,6 @@ package android.content {
}
public abstract class Context {
- method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean bindServiceAsUser(@RequiresPermission android.content.Intent, android.content.ServiceConnection, int, android.os.UserHandle);
method @NonNull public android.content.Context createContextAsUser(@NonNull android.os.UserHandle, int);
method public abstract android.content.Context createCredentialProtectedStorageContext();
method @NonNull public android.content.Context createPackageContextAsUser(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -1780,6 +1791,7 @@ package android.content {
field @Deprecated public static final String EXTRA_SIM_STATE = "ss";
field public static final String EXTRA_UNKNOWN_INSTANT_APP = "android.intent.extra.UNKNOWN_INSTANT_APP";
field public static final String EXTRA_VERIFICATION_BUNDLE = "android.intent.extra.VERIFICATION_BUNDLE";
+ field public static final int FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT = 67108864; // 0x4000000
field public static final String METADATA_SETUP_VERSION = "android.SETUP_VERSION";
field @Deprecated public static final String SIM_ABSENT_ON_PERM_DISABLED = "PERM_DISABLED";
field @Deprecated public static final String SIM_LOCKED_NETWORK = "NETWORK";
@@ -3545,7 +3557,6 @@ package android.hardware.soundtrigger {
}
public static final class SoundTrigger.ModelParamRange implements android.os.Parcelable {
- method public int describeContents();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.SoundTrigger.ModelParamRange> CREATOR;
field public final int end;
@@ -3566,6 +3577,7 @@ package android.hardware.soundtrigger {
field public final int powerConsumptionMw;
field public final int recognitionModes;
field public final boolean returnsTriggerInEvent;
+ field @NonNull public final String supportedModelArch;
field public final boolean supportsCaptureTransition;
field public final boolean supportsConcurrentCapture;
field @NonNull public final java.util.UUID uuid;
@@ -4311,7 +4323,7 @@ package android.media.session {
}
public static interface MediaSessionManager.OnMediaKeyEventDispatchedListener {
- method public default void onMediaKeyEventDispatched(@NonNull android.view.KeyEvent, @NonNull String, @NonNull android.media.session.MediaSession.Token);
+ method public default void onMediaKeyEventDispatched(@NonNull android.view.KeyEvent, @NonNull String, @Nullable android.media.session.MediaSession.Token);
}
public static interface MediaSessionManager.OnMediaKeyEventSessionChangedListener {
@@ -4367,9 +4379,9 @@ package android.media.soundtrigger {
method public int getDetectionServiceOperationsTimeout();
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public android.media.soundtrigger.SoundTriggerManager.Model getModel(java.util.UUID);
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public android.hardware.soundtrigger.SoundTrigger.ModuleProperties getModuleProperties();
- method @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public int getParameter(@NonNull java.util.UUID, int) throws java.lang.IllegalArgumentException, java.lang.UnsupportedOperationException;
+ method @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public int getParameter(@NonNull java.util.UUID, int);
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public android.hardware.soundtrigger.SoundTrigger.ModelParamRange queryParameter(@Nullable java.util.UUID, int);
- method @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public int setParameter(@Nullable java.util.UUID, int, int) throws java.lang.IllegalArgumentException, java.lang.UnsupportedOperationException;
+ method @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public int setParameter(@Nullable java.util.UUID, int, int);
method @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public void updateModel(android.media.soundtrigger.SoundTriggerManager.Model);
}
@@ -4855,6 +4867,7 @@ package android.net {
}
public final class NetworkCapabilities implements android.os.Parcelable {
+ method public boolean deduceRestrictedCapability();
method @NonNull public int[] getTransportTypes();
method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities);
method @NonNull public android.net.NetworkCapabilities setSSID(@Nullable String);
@@ -5835,8 +5848,8 @@ package android.net.wifi {
method public int getMaxNumberOfClients();
method @Nullable public String getPassphrase();
method public int getSecurityType();
+ method public int getShutdownTimeoutMillis();
method @Nullable public String getSsid();
- method @Nullable public String getWpa2Passphrase();
method public boolean isHiddenSsid();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field public static final int BAND_2GHZ = 1; // 0x1
@@ -5860,8 +5873,8 @@ package android.net.wifi {
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setHiddenSsid(boolean);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setMaxNumberOfClients(int);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setPassphrase(@Nullable String, int);
+ method @NonNull public android.net.wifi.SoftApConfiguration.Builder setShutdownTimeoutMillis(int);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setSsid(@Nullable String);
- method @NonNull public android.net.wifi.SoftApConfiguration.Builder setWpa2Passphrase(@Nullable String);
}
public final class SoftApInfo implements android.os.Parcelable {
@@ -6024,6 +6037,7 @@ package android.net.wifi {
method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public void getWifiActivityEnergyInfoAsync(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.OnWifiActivityEnergyInfoListener);
method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public android.net.wifi.WifiConfiguration getWifiApConfiguration();
method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public int getWifiApState();
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.List<android.net.wifi.WifiConfiguration> getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(@NonNull java.util.List<android.net.wifi.ScanResult>);
method public boolean isApMacRandomizationSupported();
method public boolean isConnectedMacRandomizationSupported();
method @Deprecated public boolean isDeviceToDeviceRttSupported();
@@ -6168,6 +6182,25 @@ package android.net.wifi {
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_CARRIER_PROVISIONING) public android.net.wifi.WifiNetworkSuggestion.Builder setCarrierId(int);
}
+ public final class WifiOemConfigStoreMigrationHook {
+ method @Nullable public static android.net.wifi.WifiOemConfigStoreMigrationHook.MigrationData load();
+ }
+
+ public static final class WifiOemConfigStoreMigrationHook.MigrationData 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.WifiOemConfigStoreMigrationHook.MigrationData> CREATOR;
+ }
+
+ public static final class WifiOemConfigStoreMigrationHook.MigrationData.Builder {
+ ctor public WifiOemConfigStoreMigrationHook.MigrationData.Builder();
+ method @NonNull public android.net.wifi.WifiOemConfigStoreMigrationHook.MigrationData build();
+ method @NonNull public android.net.wifi.WifiOemConfigStoreMigrationHook.MigrationData.Builder setUserSavedNetworkConfigurations(@NonNull java.util.List<android.net.wifi.WifiConfiguration>);
+ method @NonNull public android.net.wifi.WifiOemConfigStoreMigrationHook.MigrationData.Builder setUserSoftApConfiguration(@NonNull android.net.wifi.SoftApConfiguration);
+ }
+
public class WifiScanner {
method @Deprecated public void configureWifiChange(int, int, int, int, int, android.net.wifi.WifiScanner.BssidInfo[]);
method @Deprecated public void configureWifiChange(android.net.wifi.WifiScanner.WifiChangeSettings);
@@ -7185,7 +7218,7 @@ package android.os {
ctor public UpdateEngine();
method @NonNull public android.os.UpdateEngine.AllocateSpaceResult allocateSpace(@NonNull String, @NonNull String[]);
method public void applyPayload(String, long, long, String[]);
- method public void applyPayload(@NonNull android.os.ParcelFileDescriptor, long, long, @NonNull String[]);
+ method public void applyPayload(@NonNull android.content.res.AssetFileDescriptor, @NonNull String[]);
method public boolean bind(android.os.UpdateEngineCallback, android.os.Handler);
method public boolean bind(android.os.UpdateEngineCallback);
method public void cancel();
@@ -7270,7 +7303,6 @@ package android.os {
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean hasUserRestrictionForUser(@NonNull String, @NonNull android.os.UserHandle);
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isAdminUser();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isGuestUser();
- method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isManagedProfile();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isManagedProfile(int);
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isPrimaryUser();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isRestrictedProfile();
@@ -7860,6 +7892,7 @@ package android.provider {
}
public final class Settings {
+ method public static boolean checkAndNoteWriteSettingsOperation(@NonNull android.content.Context, int, @NonNull String, boolean);
field public static final String ACTION_ACCESSIBILITY_DETAILS_SETTINGS = "android.settings.ACCESSIBILITY_DETAILS_SETTINGS";
field public static final String ACTION_BUGREPORT_HANDLER_SETTINGS = "android.settings.BUGREPORT_HANDLER_SETTINGS";
field public static final String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS";
@@ -8658,7 +8691,10 @@ package android.service.notification {
method @Nullable public android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification, @NonNull android.app.NotificationChannel);
method public void onNotificationExpansionChanged(@NonNull String, boolean, boolean);
method public abstract void onNotificationSnoozedUntilContext(@NonNull android.service.notification.StatusBarNotification, @NonNull String);
+ method public void onNotificationVisibilityChanged(@NonNull String, boolean);
method public void onNotificationsSeen(@NonNull java.util.List<java.lang.String>);
+ method public void onPanelHidden();
+ method public void onPanelRevealed(int);
method public void onSuggestedReplySent(@NonNull String, @NonNull CharSequence, int);
method public final void unsnoozeNotification(@NonNull String);
field public static final String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
@@ -10399,14 +10435,15 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getVoiceActivationState();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handlePinMmi(String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handlePinMmiForSubscriber(int, String);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean iccCloseLogicalChannelBySlot(int, int);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean iccCloseLogicalChannelBySlot(int, int);
method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannelBySlot(int, @Nullable String, int);
- method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduBasicChannelBySlot(int, int, int, int, int, int, @Nullable String);
- method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduLogicalChannelBySlot(int, int, int, int, int, int, int, @Nullable String);
+ method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduBasicChannelBySlot(int, int, int, int, int, int, @Nullable String);
+ method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduLogicalChannelBySlot(int, int, int, int, int, int, int, @Nullable String);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAnyRadioPoweredOn();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApnMetered(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApplicationOnUicc(int);
method public boolean isCurrentSimOperator(@NonNull String, int, @Nullable String);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataConnectionEnabled();
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();
@@ -11833,6 +11870,8 @@ package android.view.accessibility {
public final class AccessibilityManager {
method public int getAccessibilityWindowId(@Nullable android.os.IBinder);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public void performAccessibilityShortcut();
+ method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public void registerSystemAction(@NonNull android.app.RemoteAction, int);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public void unregisterSystemAction(int);
}
}
diff --git a/api/test-current.txt b/api/test-current.txt
index d017dd63bdd3..00a2c29cd06f 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -435,6 +435,8 @@ package android.app {
}
public class StatusBarManager {
+ method public void collapsePanels();
+ method public void expandNotificationsPanel();
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);
@@ -2929,7 +2931,10 @@ package android.service.notification {
method @Nullable public android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification, @NonNull android.app.NotificationChannel);
method public void onNotificationExpansionChanged(@NonNull String, boolean, boolean);
method public abstract void onNotificationSnoozedUntilContext(@NonNull android.service.notification.StatusBarNotification, @NonNull String);
+ method public void onNotificationVisibilityChanged(@NonNull String, boolean);
method public void onNotificationsSeen(@NonNull java.util.List<java.lang.String>);
+ method public void onPanelHidden();
+ method public void onPanelRevealed(int);
method public void onSuggestedReplySent(@NonNull String, @NonNull CharSequence, int);
method public final void unsnoozeNotification(@NonNull String);
field public static final String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 118a508a9071..1c6867c39790 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -78,7 +78,6 @@ cc_defaults {
"src/external/StatsPuller.cpp",
"src/external/StatsPullerManager.cpp",
"src/external/SubsystemSleepStatePuller.cpp",
- "src/external/SurfaceflingerStatsPuller.cpp",
"src/external/TrainInfoPuller.cpp",
"src/FieldValue.cpp",
"src/guardrail/StatsdStats.cpp",
@@ -148,7 +147,6 @@ cc_defaults {
"libincident",
"libservices",
"libstatsmetadata",
- "libtimestats_proto",
"libutils",
],
}
@@ -297,7 +295,6 @@ cc_test {
"tests/external/puller_util_test.cpp",
"tests/external/StatsCallbackPuller_test.cpp",
"tests/external/StatsPuller_test.cpp",
- "tests/external/SurfaceflingerStatsPuller_test.cpp",
"tests/FieldValue_test.cpp",
"tests/guardrail/StatsdStats_test.cpp",
"tests/indexed_priority_queue_test.cpp",
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 19b9709e1d41..2bacfbc57395 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -333,10 +333,11 @@ message Atom {
MediaProviderSchemaChange media_provider_schema_change = 236 [(module) = "mediaprovider"];
MediaProviderIdleMaintenance media_provider_idle_maintenance =
237 [(module) = "mediaprovider"];
+ RebootEscrowRecoveryReported reboot_escrow_recovery_reported = 238;
}
// Pulled events will start at field 10000.
- // Next: 10068
+ // Next: 10069
oneof pulled {
WifiBytesTransfer wifi_bytes_transfer = 10000;
WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001;
@@ -405,6 +406,7 @@ message Atom {
VmsClientStats vms_client_stats = 10065;
NotificationRemoteViews notification_remote_views = 10066;
DangerousPermissionStateSampled dangerous_permission_state_sampled = 10067;
+ GraphicsStats graphics_stats = 10068;
}
// DO NOT USE field numbers above 100,000 in AOSP.
@@ -7338,6 +7340,17 @@ message UpdateEngineSuccessfulUpdateReported {
}
/**
+ * Reported when the RebootEscrow HAL has attempted to recover the escrowed
+ * key to indicate whether it was successful or not.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+ */
+message RebootEscrowRecoveryReported {
+ optional bool successful = 1;
+}
+
+/**
* Global display pipeline metrics reported by SurfaceFlinger.
* Pulled from:
* frameworks/native/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -7553,3 +7566,69 @@ message DangerousPermissionStateSampled {
optional int32 permission_flags = 4;
}
+/**
+ * HWUI renders pipeline type: GL (0) or Vulkan (1).
+ */
+enum PipelineType {
+ GL = 0;
+ VULKAN = 1;
+}
+
+/**
+ * HWUI stats for a given app.
+ */
+message GraphicsStats {
+ // The package name of the app
+ optional string package_name = 1;
+
+ // The version code of the app
+ optional int64 version_code = 2;
+
+ // The start & end timestamps in UTC as
+ // milliseconds since January 1, 1970
+ // Compatible with java.util.Date#setTime()
+ optional int64 stats_start = 3;
+
+ optional int64 stats_end = 4;
+
+ // HWUI renders pipeline type: GL or Vulkan.
+ optional PipelineType pipeline = 5;
+
+ // Distinct frame count.
+ optional int32 total_frames = 6;
+
+ // Number of "missed vsync" events.
+ optional int32 missed_vsync_count = 7;
+
+ // Number of frames in triple-buffering scenario (high input latency)
+ optional int32 high_input_latency_count = 8;
+
+ // Number of "slow UI thread" events.
+ optional int32 slow_ui_thread_count = 9;
+
+ // Number of "slow bitmap upload" events.
+ optional int32 slow_bitmap_upload_count = 10;
+
+ // Number of "slow draw" events.
+ optional int32 slow_draw_count = 11;
+
+ // Number of frames that missed their deadline (aka, visibly janked)
+ optional int32 missed_deadline_count = 12;
+
+ // The frame time histogram for the package
+ optional FrameTimingHistogram cpu_histogram = 13
+ [(android.os.statsd.log_mode) = MODE_BYTES];
+
+ // The gpu frame time histogram for the package
+ optional FrameTimingHistogram gpu_histogram = 14
+ [(android.os.statsd.log_mode) = MODE_BYTES];
+
+ // UI mainline module version.
+ optional int64 version_ui_module = 15;
+
+ // If true, these are HWUI stats for up to a 24h period for a given app from today.
+ // If false, these are HWUI stats for a 24h period for a given app from the last complete
+ // day (yesterday). Stats from yesterday stay constant, while stats from today may change as
+ // more apps are running / rendering.
+ optional bool is_today = 16;
+}
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 50896f84da43..b5680331f63e 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -41,7 +41,6 @@
#include "StatsCallbackPullerDeprecated.h"
#include "StatsCompanionServicePuller.h"
#include "SubsystemSleepStatePuller.h"
-#include "SurfaceflingerStatsPuller.h"
#include "TrainInfoPuller.h"
#include "statslog.h"
@@ -60,161 +59,200 @@ namespace statsd {
const int64_t NO_ALARM_UPDATE = INT64_MAX;
std::map<PullerKey, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = {
- // wifi_bytes_transfer
- {{.atomTag = android::util::WIFI_BYTES_TRANSFER},
- {.additiveFields = {2, 3, 4, 5},
- .puller = new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER)}},
+
// wifi_bytes_transfer_by_fg_bg
{{.atomTag = android::util::WIFI_BYTES_TRANSFER_BY_FG_BG},
{.additiveFields = {3, 4, 5, 6},
.puller = new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER_BY_FG_BG)}},
+
// mobile_bytes_transfer
{{.atomTag = android::util::MOBILE_BYTES_TRANSFER},
{.additiveFields = {2, 3, 4, 5},
.puller = new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER)}},
+
// mobile_bytes_transfer_by_fg_bg
{{.atomTag = android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG},
{.additiveFields = {3, 4, 5, 6},
.puller =
new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG)}},
+
// bluetooth_bytes_transfer
{{.atomTag = android::util::BLUETOOTH_BYTES_TRANSFER},
{.additiveFields = {2, 3},
.puller = new StatsCompanionServicePuller(android::util::BLUETOOTH_BYTES_TRANSFER)}},
+
// kernel_wakelock
{{.atomTag = android::util::KERNEL_WAKELOCK},
{.puller = new StatsCompanionServicePuller(android::util::KERNEL_WAKELOCK)}},
+
// subsystem_sleep_state
{{.atomTag = android::util::SUBSYSTEM_SLEEP_STATE},
{.puller = new SubsystemSleepStatePuller()}},
+
// on_device_power_measurement
{{.atomTag = android::util::ON_DEVICE_POWER_MEASUREMENT},
{.puller = new PowerStatsPuller()}},
+
// cpu_time_per_freq
{{.atomTag = android::util::CPU_TIME_PER_FREQ},
{.additiveFields = {3},
.puller = new StatsCompanionServicePuller(android::util::CPU_TIME_PER_FREQ)}},
+
// cpu_time_per_uid
{{.atomTag = android::util::CPU_TIME_PER_UID},
{.additiveFields = {2, 3},
.puller = new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID)}},
+
// cpu_time_per_uid_freq
// the throttling is 3sec, handled in
// frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
{{.atomTag = android::util::CPU_TIME_PER_UID_FREQ},
{.additiveFields = {4},
.puller = new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID_FREQ)}},
+
// cpu_active_time
// the throttling is 3sec, handled in
// frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
{{.atomTag = android::util::CPU_ACTIVE_TIME},
{.additiveFields = {2},
.puller = new StatsCompanionServicePuller(android::util::CPU_ACTIVE_TIME)}},
+
// cpu_cluster_time
// the throttling is 3sec, handled in
// frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
{{.atomTag = android::util::CPU_CLUSTER_TIME},
{.additiveFields = {3},
.puller = new StatsCompanionServicePuller(android::util::CPU_CLUSTER_TIME)}},
+
// wifi_activity_energy_info
{{.atomTag = android::util::WIFI_ACTIVITY_INFO},
{.puller = new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_INFO)}},
+
// modem_activity_info
{{.atomTag = android::util::MODEM_ACTIVITY_INFO},
{.puller = new StatsCompanionServicePuller(android::util::MODEM_ACTIVITY_INFO)}},
+
// bluetooth_activity_info
{{.atomTag = android::util::BLUETOOTH_ACTIVITY_INFO},
{.puller = new StatsCompanionServicePuller(android::util::BLUETOOTH_ACTIVITY_INFO)}},
+
// system_elapsed_realtime
{{.atomTag = android::util::SYSTEM_ELAPSED_REALTIME},
{.coolDownNs = NS_PER_SEC,
.puller = new StatsCompanionServicePuller(android::util::SYSTEM_ELAPSED_REALTIME),
.pullTimeoutNs = NS_PER_SEC / 2,
}},
+
// system_uptime
{{.atomTag = android::util::SYSTEM_UPTIME},
{.puller = new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}},
+
// remaining_battery_capacity
{{.atomTag = android::util::REMAINING_BATTERY_CAPACITY},
{.puller = new ResourceHealthManagerPuller(android::util::REMAINING_BATTERY_CAPACITY)}},
+
// full_battery_capacity
{{.atomTag = android::util::FULL_BATTERY_CAPACITY},
{.puller = new ResourceHealthManagerPuller(android::util::FULL_BATTERY_CAPACITY)}},
+
// battery_voltage
{{.atomTag = android::util::BATTERY_VOLTAGE},
{.puller = new ResourceHealthManagerPuller(android::util::BATTERY_VOLTAGE)}},
+
// battery_level
{{.atomTag = android::util::BATTERY_LEVEL},
{.puller = new ResourceHealthManagerPuller(android::util::BATTERY_LEVEL)}},
+
// battery_cycle_count
{{.atomTag = android::util::BATTERY_CYCLE_COUNT},
{.puller = new ResourceHealthManagerPuller(android::util::BATTERY_CYCLE_COUNT)}},
+
// process_memory_state
{{.atomTag = android::util::PROCESS_MEMORY_STATE},
{.additiveFields = {4, 5, 6, 7, 8},
.puller = new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}},
+
// process_memory_high_water_mark
{{.atomTag = android::util::PROCESS_MEMORY_HIGH_WATER_MARK},
{.puller =
new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_HIGH_WATER_MARK)}},
+
// process_memory_snapshot
{{.atomTag = android::util::PROCESS_MEMORY_SNAPSHOT},
{.puller = new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_SNAPSHOT)}},
+
// system_ion_heap_size
{{.atomTag = android::util::SYSTEM_ION_HEAP_SIZE},
{.puller = new StatsCompanionServicePuller(android::util::SYSTEM_ION_HEAP_SIZE)}},
+
// process_system_ion_heap_size
{{.atomTag = android::util::PROCESS_SYSTEM_ION_HEAP_SIZE},
{.puller = new StatsCompanionServicePuller(android::util::PROCESS_SYSTEM_ION_HEAP_SIZE)}},
+
// temperature
{{.atomTag = android::util::TEMPERATURE},
{.puller = new StatsCompanionServicePuller(android::util::TEMPERATURE)}},
+
// cooling_device
{{.atomTag = android::util::COOLING_DEVICE},
{.puller = new StatsCompanionServicePuller(android::util::COOLING_DEVICE)}},
+
// binder_calls
{{.atomTag = android::util::BINDER_CALLS},
{.additiveFields = {4, 5, 6, 8, 12},
.puller = new StatsCompanionServicePuller(android::util::BINDER_CALLS)}},
+
// binder_calls_exceptions
{{.atomTag = android::util::BINDER_CALLS_EXCEPTIONS},
{.puller = new StatsCompanionServicePuller(android::util::BINDER_CALLS_EXCEPTIONS)}},
+
// looper_stats
{{.atomTag = android::util::LOOPER_STATS},
{.additiveFields = {5, 6, 7, 8, 9},
.puller = new StatsCompanionServicePuller(android::util::LOOPER_STATS)}},
+
// Disk Stats
{{.atomTag = android::util::DISK_STATS},
{.puller = new StatsCompanionServicePuller(android::util::DISK_STATS)}},
+
// Directory usage
{{.atomTag = android::util::DIRECTORY_USAGE},
{.puller = new StatsCompanionServicePuller(android::util::DIRECTORY_USAGE)}},
+
// Size of app's code, data, and cache
{{.atomTag = android::util::APP_SIZE},
{.puller = new StatsCompanionServicePuller(android::util::APP_SIZE)}},
+
// Size of specific categories of files. Eg. Music.
{{.atomTag = android::util::CATEGORY_SIZE},
{.puller = new StatsCompanionServicePuller(android::util::CATEGORY_SIZE)}},
+
// Number of fingerprints enrolled for each user.
{{.atomTag = android::util::NUM_FINGERPRINTS_ENROLLED},
{.puller = new StatsCompanionServicePuller(android::util::NUM_FINGERPRINTS_ENROLLED)}},
+
// Number of faces enrolled for each user.
{{.atomTag = android::util::NUM_FACES_ENROLLED},
{.puller = new StatsCompanionServicePuller(android::util::NUM_FACES_ENROLLED)}},
+
// ProcStats.
{{.atomTag = android::util::PROC_STATS},
{.puller = new StatsCompanionServicePuller(android::util::PROC_STATS)}},
+
// ProcStatsPkgProc.
{{.atomTag = android::util::PROC_STATS_PKG_PROC},
{.puller = new StatsCompanionServicePuller(android::util::PROC_STATS_PKG_PROC)}},
+
// Disk I/O stats per uid.
{{.atomTag = android::util::DISK_IO},
{.additiveFields = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
.coolDownNs = 3 * NS_PER_SEC,
.puller = new StatsCompanionServicePuller(android::util::DISK_IO)}},
+
// PowerProfile constants for power model calculations.
{{.atomTag = android::util::POWER_PROFILE},
{.puller = new StatsCompanionServicePuller(android::util::POWER_PROFILE)}},
+
// Process cpu stats. Min cool-down is 5 sec, inline with what AcitivityManagerService uses.
{{.atomTag = android::util::PROCESS_CPU_TIME},
{.coolDownNs = 5 * NS_PER_SEC /* min cool-down in seconds*/,
@@ -222,68 +260,83 @@ std::map<PullerKey, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = {
{{.atomTag = android::util::CPU_TIME_PER_THREAD_FREQ},
{.additiveFields = {7, 9, 11, 13, 15, 17, 19, 21},
.puller = new StatsCompanionServicePuller(android::util::CPU_TIME_PER_THREAD_FREQ)}},
+
// DeviceCalculatedPowerUse.
{{.atomTag = android::util::DEVICE_CALCULATED_POWER_USE},
{.puller = new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_USE)}},
+
// DeviceCalculatedPowerBlameUid.
{{.atomTag = android::util::DEVICE_CALCULATED_POWER_BLAME_UID},
{.puller = new StatsCompanionServicePuller(
android::util::DEVICE_CALCULATED_POWER_BLAME_UID)}},
+
// DeviceCalculatedPowerBlameOther.
{{.atomTag = android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER},
{.puller = new StatsCompanionServicePuller(
android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER)}},
+
// DebugElapsedClock.
{{.atomTag = android::util::DEBUG_ELAPSED_CLOCK},
{.additiveFields = {1, 2, 3, 4},
.puller = new StatsCompanionServicePuller(android::util::DEBUG_ELAPSED_CLOCK)}},
+
// DebugFailingElapsedClock.
{{.atomTag = android::util::DEBUG_FAILING_ELAPSED_CLOCK},
{.additiveFields = {1, 2, 3, 4},
.puller = new StatsCompanionServicePuller(android::util::DEBUG_FAILING_ELAPSED_CLOCK)}},
+
// BuildInformation.
{{.atomTag = android::util::BUILD_INFORMATION},
{.puller = new StatsCompanionServicePuller(android::util::BUILD_INFORMATION)}},
+
// RoleHolder.
{{.atomTag = android::util::ROLE_HOLDER},
{.puller = new StatsCompanionServicePuller(android::util::ROLE_HOLDER)}},
+
// PermissionState.
{{.atomTag = android::util::DANGEROUS_PERMISSION_STATE},
{.puller = new StatsCompanionServicePuller(android::util::DANGEROUS_PERMISSION_STATE)}},
+
// TrainInfo.
{{.atomTag = android::util::TRAIN_INFO}, {.puller = new TrainInfoPuller()}},
+
// TimeZoneDataInfo.
{{.atomTag = android::util::TIME_ZONE_DATA_INFO},
{.puller = new StatsCompanionServicePuller(android::util::TIME_ZONE_DATA_INFO)}},
+
// ExternalStorageInfo
{{.atomTag = android::util::EXTERNAL_STORAGE_INFO},
{.puller = new StatsCompanionServicePuller(android::util::EXTERNAL_STORAGE_INFO)}},
+
// GpuStatsGlobalInfo
{{.atomTag = android::util::GPU_STATS_GLOBAL_INFO},
{.puller = new GpuStatsPuller(android::util::GPU_STATS_GLOBAL_INFO)}},
+
// GpuStatsAppInfo
{{.atomTag = android::util::GPU_STATS_APP_INFO},
{.puller = new GpuStatsPuller(android::util::GPU_STATS_APP_INFO)}},
+
// AppsOnExternalStorageInfo
{{.atomTag = android::util::APPS_ON_EXTERNAL_STORAGE_INFO},
{.puller = new StatsCompanionServicePuller(android::util::APPS_ON_EXTERNAL_STORAGE_INFO)}},
+
// Face Settings
{{.atomTag = android::util::FACE_SETTINGS},
{.puller = new StatsCompanionServicePuller(android::util::FACE_SETTINGS)}},
+
// App ops
{{.atomTag = android::util::APP_OPS},
{.puller = new StatsCompanionServicePuller(android::util::APP_OPS)}},
- // SurfaceflingerStatsGlobalInfo
- {{.atomTag = android::util::SURFACEFLINGER_STATS_GLOBAL_INFO},
- {.puller =
- new SurfaceflingerStatsPuller(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO)}},
+
// VmsClientStats
{{.atomTag = android::util::VMS_CLIENT_STATS},
{.additiveFields = {5, 6, 7, 8, 9, 10},
.puller = new CarStatsPuller(android::util::VMS_CLIENT_STATS)}},
+
// NotiifcationRemoteViews.
{{.atomTag = android::util::NOTIFICATION_REMOTE_VIEWS},
{.puller = new StatsCompanionServicePuller(android::util::NOTIFICATION_REMOTE_VIEWS)}},
+
// PermissionStateSampled.
{{.atomTag = android::util::DANGEROUS_PERMISSION_STATE_SAMPLED},
{.puller =
diff --git a/cmds/statsd/src/external/SurfaceflingerStatsPuller.cpp b/cmds/statsd/src/external/SurfaceflingerStatsPuller.cpp
deleted file mode 100644
index 23b2236f35f2..000000000000
--- a/cmds/statsd/src/external/SurfaceflingerStatsPuller.cpp
+++ /dev/null
@@ -1,99 +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.
- */
-
-#include "SurfaceflingerStatsPuller.h"
-
-#include <cutils/compiler.h>
-
-#include <numeric>
-
-#include "logd/LogEvent.h"
-#include "stats_log_util.h"
-#include "statslog.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-SurfaceflingerStatsPuller::SurfaceflingerStatsPuller(const int tagId) : StatsPuller(tagId) {
-}
-
-bool SurfaceflingerStatsPuller::PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) {
- switch (mTagId) {
- case android::util::SURFACEFLINGER_STATS_GLOBAL_INFO:
- return pullGlobalInfo(data);
- default:
- break;
- }
-
- return false;
-}
-
-static int64_t getTotalTime(
- const google::protobuf::RepeatedPtrField<surfaceflinger::SFTimeStatsHistogramBucketProto>&
- buckets) {
- int64_t total = 0;
- for (const auto& bucket : buckets) {
- if (bucket.time_millis() == 1000) {
- continue;
- }
-
- total += bucket.time_millis() * bucket.frame_count();
- }
-
- return total;
-}
-
-bool SurfaceflingerStatsPuller::pullGlobalInfo(std::vector<std::shared_ptr<LogEvent>>* data) {
- std::string protoBytes;
- if (CC_UNLIKELY(mStatsProvider)) {
- protoBytes = mStatsProvider();
- } else {
- std::unique_ptr<FILE, decltype(&pclose)> pipe(popen("dumpsys SurfaceFlinger --timestats -dump --proto", "r"), pclose);
- if (!pipe.get()) {
- return false;
- }
- char buf[1024];
- size_t bytesRead = 0;
- do {
- bytesRead = fread(buf, 1, sizeof(buf), pipe.get());
- protoBytes.append(buf, bytesRead);
- } while (bytesRead > 0);
- }
- surfaceflinger::SFTimeStatsGlobalProto proto;
- proto.ParseFromString(protoBytes);
-
- int64_t totalTime = getTotalTime(proto.present_to_present());
-
- data->clear();
- data->reserve(1);
- std::shared_ptr<LogEvent> event =
- make_shared<LogEvent>(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO, getWallClockNs(),
- getElapsedRealtimeNs());
- if (!event->write(proto.total_frames())) return false;
- if (!event->write(proto.missed_frames())) return false;
- if (!event->write(proto.client_composition_frames())) return false;
- if (!event->write(proto.display_on_time())) return false;
- if (!event->write(totalTime)) return false;
- event->init();
- data->emplace_back(event);
-
- return true;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/SurfaceflingerStatsPuller.h b/cmds/statsd/src/external/SurfaceflingerStatsPuller.h
deleted file mode 100644
index ed7153edf797..000000000000
--- a/cmds/statsd/src/external/SurfaceflingerStatsPuller.h
+++ /dev/null
@@ -1,48 +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.
- */
-
-#pragma once
-
-#include <timestatsproto/TimeStatsProtoHeader.h>
-
-#include "StatsPuller.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-/**
- * Pull metrics from Surfaceflinger
- */
-class SurfaceflingerStatsPuller : public StatsPuller {
-public:
- explicit SurfaceflingerStatsPuller(const int tagId);
-
- // StatsPuller interface
- bool PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) override;
-
-protected:
- // Test-only, for injecting fake data
- using StatsProvider = std::function<std::string()>;
- StatsProvider mStatsProvider;
-
-private:
- bool pullGlobalInfo(std::vector<std::shared_ptr<LogEvent>>* data);
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/tests/external/SurfaceflingerStatsPuller_test.cpp b/cmds/statsd/tests/external/SurfaceflingerStatsPuller_test.cpp
deleted file mode 100644
index 5b7a30d4a5aa..000000000000
--- a/cmds/statsd/tests/external/SurfaceflingerStatsPuller_test.cpp
+++ /dev/null
@@ -1,96 +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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "SurfaceflingerStatsPuller_test"
-
-#include "src/external/SurfaceflingerStatsPuller.h"
-#include "statslog.h"
-
-#include <gtest/gtest.h>
-#include <log/log.h>
-
-#ifdef __ANDROID__
-
-namespace android {
-namespace os {
-namespace statsd {
-
-class TestableSurfaceflingerStatsPuller : public SurfaceflingerStatsPuller {
-public:
- TestableSurfaceflingerStatsPuller(const int tagId) : SurfaceflingerStatsPuller(tagId){};
-
- void injectStats(const StatsProvider& statsProvider) {
- mStatsProvider = statsProvider;
- }
-};
-
-class SurfaceflingerStatsPullerTest : public ::testing::Test {
-public:
- SurfaceflingerStatsPullerTest() {
- const ::testing::TestInfo* const test_info =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
- }
-
- ~SurfaceflingerStatsPullerTest() {
- const ::testing::TestInfo* const test_info =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
- }
-};
-
-TEST_F(SurfaceflingerStatsPullerTest, pullGlobalStats) {
- surfaceflinger::SFTimeStatsGlobalProto proto;
- proto.set_total_frames(1);
- proto.set_missed_frames(2);
- proto.set_client_composition_frames(2);
- proto.set_display_on_time(4);
-
- auto bucketOne = proto.add_present_to_present();
- bucketOne->set_time_millis(2);
- bucketOne->set_frame_count(4);
- auto bucketTwo = proto.add_present_to_present();
- bucketTwo->set_time_millis(4);
- bucketTwo->set_frame_count(1);
- auto bucketThree = proto.add_present_to_present();
- bucketThree->set_time_millis(1000);
- bucketThree->set_frame_count(1);
- static constexpr int64_t expectedAnimationMillis = 12;
- TestableSurfaceflingerStatsPuller puller(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO);
-
- puller.injectStats([&] {
- return proto.SerializeAsString();
- });
- puller.ForceClearCache();
- vector<std::shared_ptr<LogEvent>> outData;
- puller.Pull(&outData);
-
- ASSERT_EQ(1, outData.size());
- EXPECT_EQ(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO, outData[0]->GetTagId());
- EXPECT_EQ(proto.total_frames(), outData[0]->getValues()[0].mValue.long_value);
- EXPECT_EQ(proto.missed_frames(), outData[0]->getValues()[1].mValue.long_value);
- EXPECT_EQ(proto.client_composition_frames(), outData[0]->getValues()[2].mValue.long_value);
- EXPECT_EQ(proto.display_on_time(), outData[0]->getValues()[3].mValue.long_value);
- EXPECT_EQ(expectedAnimationMillis, outData[0]->getValues()[4].mValue.long_value);
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt
index 4d0acb3c2330..4aaf72728cbf 100644
--- a/config/boot-image-profile.txt
+++ b/config/boot-image-profile.txt
@@ -22519,8 +22519,6 @@ HSPLcom/android/internal/telephony/PhoneFactory;->makeDefaultPhone(Landroid/cont
HSPLcom/android/internal/telephony/PhoneFactory;->makeDefaultPhones(Landroid/content/Context;)V
HSPLcom/android/internal/telephony/PhoneInternalInterface$DataActivityState;-><init>(Ljava/lang/String;I)V
HSPLcom/android/internal/telephony/PhoneInternalInterface$DataActivityState;->values()[Lcom/android/internal/telephony/PhoneInternalInterface$DataActivityState;
-HSPLcom/android/internal/telephony/PhoneStateIntentReceiver;-><init>(Landroid/content/Context;Landroid/os/Handler;)V
-HSPLcom/android/internal/telephony/PhoneStateIntentReceiver;->notifyServiceState(I)V
HSPLcom/android/internal/telephony/PhoneSubInfoController;->callPhoneMethodWithPermissionCheck(ILjava/lang/String;Ljava/lang/String;Lcom/android/internal/telephony/PhoneSubInfoController$CallPhoneMethodHelper;Lcom/android/internal/telephony/PhoneSubInfoController$PermissionCheckHelper;)Ljava/lang/Object;
HSPLcom/android/internal/telephony/PhoneSubInfoController;->getCarrierInfoForImsiEncryption(IILjava/lang/String;)Landroid/telephony/ImsiEncryptionInfo;
HSPLcom/android/internal/telephony/PhoneSubInfoController;->getGroupIdLevel1ForSubscriber(ILjava/lang/String;)Ljava/lang/String;
@@ -37729,7 +37727,6 @@ Lcom/android/internal/telephony/PhoneFactory;
Lcom/android/internal/telephony/PhoneInternalInterface$DataActivityState;
Lcom/android/internal/telephony/PhoneInternalInterface;
Lcom/android/internal/telephony/PhoneNotifier;
-Lcom/android/internal/telephony/PhoneStateIntentReceiver;
Lcom/android/internal/telephony/PhoneSubInfoController$CallPhoneMethodHelper;
Lcom/android/internal/telephony/PhoneSubInfoController$PermissionCheckHelper;
Lcom/android/internal/telephony/PhoneSubInfoController;
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 97f009c09217..e53c74bdfc4b 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -4841,7 +4841,6 @@ com.android.internal.telephony.PhoneConstants$State
com.android.internal.telephony.PhoneFactory
com.android.internal.telephony.PhoneInternalInterface
com.android.internal.telephony.PhoneNotifier
-com.android.internal.telephony.PhoneStateIntentReceiver
com.android.internal.telephony.PhoneSubInfoController$CallPhoneMethodHelper
com.android.internal.telephony.PhoneSubInfoController$PermissionCheckHelper
com.android.internal.telephony.PhoneSubInfoController
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 032e8245ca42..4f3e8ec9fb51 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -45,8 +45,19 @@ public abstract class ActivityManagerInternal {
// Access modes for handleIncomingUser.
public static final int ALLOW_NON_FULL = 0;
+ /**
+ * Allows access to a caller with {@link android.Manifest.permission#INTERACT_ACROSS_USERS}
+ * if in the same profile group.
+ * Otherwise, {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} is required.
+ */
public static final int ALLOW_NON_FULL_IN_PROFILE = 1;
public static final int ALLOW_FULL_ONLY = 2;
+ /**
+ * Allows access to a caller with {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES}
+ * or {@link android.Manifest.permission#INTERACT_ACROSS_USERS} if in the same profile group.
+ * Otherwise, {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} is required.
+ */
+ public static final int ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE = 3;
/**
* Verify that calling app has access to the given provider.
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 700b3c1b620e..e5c046c2376c 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -68,6 +68,7 @@ import android.os.StrictMode;
import android.os.WorkSource;
import android.service.voice.IVoiceInteractionSession;
import android.view.IRecentsAnimationRunner;
+import android.view.ITaskOrganizer;
import android.view.RemoteAnimationDefinition;
import android.view.RemoteAnimationAdapter;
import android.view.WindowContainerTransaction;
@@ -121,6 +122,9 @@ interface IActivityTaskManager {
in Intent intent, in String resolvedType, in IBinder resultTo, in String resultWho,
int requestCode, int flags, in ProfilerInfo profilerInfo, in Bundle options,
IBinder permissionToken, boolean ignoreTargetSecurity, int userId);
+
+ void registerTaskOrganizer(in ITaskOrganizer organizer, int windowingMode);
+
boolean isActivityStartAllowedOnDisplay(int displayId, in Intent intent, in String resolvedType,
int userId);
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 86f52af1a13b..fcdb7cc0855d 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -94,8 +94,11 @@ interface INotificationManager
void updateNotificationChannelGroupForPackage(String pkg, int uid, in NotificationChannelGroup group);
void updateNotificationChannelForPackage(String pkg, int uid, in NotificationChannel channel);
NotificationChannel getNotificationChannel(String callingPkg, int userId, String pkg, String channelId);
+ NotificationChannel getConversationNotificationChannel(String callingPkg, int userId, String pkg, String channelId, String conversationId);
+ void createConversationNotificationChannelForPackage(String pkg, int uid, in NotificationChannel parentChannel, String conversationId);
NotificationChannel getNotificationChannelForPackage(String pkg, int uid, String channelId, boolean includeDeleted);
void deleteNotificationChannel(String pkg, String channelId);
+ void deleteConversationNotificationChannels(String pkg, int uid, String conversationId);
ParceledListSlice getNotificationChannels(String callingPkg, String targetPkg, int userId);
ParceledListSlice getNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted);
int getNumNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted);
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 3eee1ae5c00c..a33c2c19d05c 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -23,6 +23,7 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ShortcutInfo;
import android.media.AudioAttributes;
import android.net.Uri;
import android.os.Parcel;
@@ -57,6 +58,22 @@ public final class NotificationChannel implements Parcelable {
public static final String DEFAULT_CHANNEL_ID = "miscellaneous";
/**
+ * The formatter used by the system to create an id for notification
+ * channels when it automatically creates conversation channels on behalf of an app. The format
+ * string takes two arguments, in this order: the
+ * {@link #getId()} of the original notification channel, and the
+ * {@link ShortcutInfo#getId() id} of the conversation.
+ */
+ public static final String CONVERSATION_CHANNEL_ID_FORMAT = "%1$s : %2$s";
+
+ /**
+ * TODO: STOPSHIP remove
+ * Conversation id to use for apps that aren't providing them yet.
+ * @hide
+ */
+ public static final String PLACEHOLDER_CONVERSATION_ID = "placeholder_id";
+
+ /**
* The maximum length for text fields in a NotificationChannel. Fields will be truncated at this
* limit.
*/
@@ -85,6 +102,8 @@ public final class NotificationChannel implements Parcelable {
private static final String ATT_BLOCKABLE_SYSTEM = "blockable_system";
private static final String ATT_ALLOW_BUBBLE = "can_bubble";
private static final String ATT_ORIG_IMP = "orig_imp";
+ private static final String ATT_PARENT_CHANNEL = "parent";
+ private static final String ATT_CONVERSATION_ID = "conv_id";
private static final String DELIMITER = ",";
/**
@@ -147,7 +166,7 @@ public final class NotificationChannel implements Parcelable {
private static final boolean DEFAULT_ALLOW_BUBBLE = true;
@UnsupportedAppUsage
- private final String mId;
+ private String mId;
private String mName;
private String mDesc;
private int mImportance = DEFAULT_IMPORTANCE;
@@ -172,6 +191,8 @@ public final class NotificationChannel implements Parcelable {
private boolean mAllowBubbles = DEFAULT_ALLOW_BUBBLE;
private boolean mImportanceLockedByOEM;
private boolean mImportanceLockedDefaultApp;
+ private String mParentId = null;
+ private String mConversationId = null;
/**
* Creates a notification channel.
@@ -236,6 +257,8 @@ public final class NotificationChannel implements Parcelable {
mAllowBubbles = in.readBoolean();
mImportanceLockedByOEM = in.readBoolean();
mOriginalImportance = in.readInt();
+ mParentId = in.readString();
+ mConversationId = in.readString();
}
@Override
@@ -291,6 +314,8 @@ public final class NotificationChannel implements Parcelable {
dest.writeBoolean(mAllowBubbles);
dest.writeBoolean(mImportanceLockedByOEM);
dest.writeInt(mOriginalImportance);
+ dest.writeString(mParentId);
+ dest.writeString(mConversationId);
}
/**
@@ -363,6 +388,13 @@ public final class NotificationChannel implements Parcelable {
// Modifiable by apps on channel creation.
/**
+ * @hide
+ */
+ public void setId(String id) {
+ mId = id;
+ }
+
+ /**
* Sets what group this channel belongs to.
*
* Group information is only used for presentation, not for behavior.
@@ -502,6 +534,23 @@ public final class NotificationChannel implements Parcelable {
}
/**
+ * Sets this channel as being person-centric. Different settings and functionality may be
+ * exposed for people-centric channels.
+ *
+ * @param parentChannelId The {@link #getId()} id} of the generic channel that notifications of
+ * this type would be posted to in absence of a specific conversation id.
+ * For example, if this channel represents 'Messages from Person A', the
+ * parent channel would be 'Messages.'
+ * @param conversationId The {@link ShortcutInfo#getId()} of the shortcut representing this
+ * channel's conversation.
+ */
+ public void setConversationId(@Nullable String parentChannelId,
+ @Nullable String conversationId) {
+ mParentId = parentChannelId;
+ mConversationId = conversationId;
+ }
+
+ /**
* Returns the id of this channel.
*/
public String getId() {
@@ -622,6 +671,22 @@ public final class NotificationChannel implements Parcelable {
}
/**
+ * Returns the {@link #getId() id} of the parent notification channel to this channel, if it's
+ * a conversation related channel. See {@link #setConversationId(String, String)}.
+ */
+ public @Nullable String getParentChannelId() {
+ return mParentId;
+ }
+
+ /**
+ * Returns the {@link ShortcutInfo#getId() id} of the conversation backing this channel, if it's
+ * associated with a conversation. See {@link #setConversationId(String, String)}.
+ */
+ public @Nullable String getConversationId() {
+ return mConversationId;
+ }
+
+ /**
* @hide
*/
@SystemApi
@@ -761,6 +826,8 @@ public final class NotificationChannel implements Parcelable {
setBlockableSystem(safeBool(parser, ATT_BLOCKABLE_SYSTEM, false));
setAllowBubbles(safeBool(parser, ATT_ALLOW_BUBBLE, DEFAULT_ALLOW_BUBBLE));
setOriginalImportance(safeInt(parser, ATT_ORIG_IMP, DEFAULT_IMPORTANCE));
+ setConversationId(parser.getAttributeValue(ATT_PARENT_CHANNEL, null),
+ parser.getAttributeValue(ATT_CONVERSATION_ID, null));
}
@Nullable
@@ -885,6 +952,12 @@ public final class NotificationChannel implements Parcelable {
if (getOriginalImportance() != DEFAULT_IMPORTANCE) {
out.attribute(null, ATT_ORIG_IMP, Integer.toString(getOriginalImportance()));
}
+ if (getParentChannelId() != null) {
+ out.attribute(null, ATT_PARENT_CHANNEL, getParentChannelId());
+ }
+ if (getConversationId() != null) {
+ out.attribute(null, ATT_CONVERSATION_ID, getConversationId());
+ }
// mImportanceLockedDefaultApp and mImportanceLockedByOEM have a different source of
// truth and so aren't written to this xml file
@@ -1042,7 +1115,9 @@ public final class NotificationChannel implements Parcelable {
&& Objects.equals(getAudioAttributes(), that.getAudioAttributes())
&& mImportanceLockedByOEM == that.mImportanceLockedByOEM
&& mImportanceLockedDefaultApp == that.mImportanceLockedDefaultApp
- && mOriginalImportance == that.mOriginalImportance;
+ && mOriginalImportance == that.mOriginalImportance
+ && Objects.equals(getParentChannelId(), that.getParentChannelId())
+ && Objects.equals(getConversationId(), that.getConversationId());
}
@Override
@@ -1052,7 +1127,8 @@ public final class NotificationChannel implements Parcelable {
getUserLockedFields(),
isFgServiceShown(), mVibrationEnabled, mShowBadge, isDeleted(), getGroup(),
getAudioAttributes(), isBlockableSystem(), mAllowBubbles,
- mImportanceLockedByOEM, mImportanceLockedDefaultApp, mOriginalImportance);
+ mImportanceLockedByOEM, mImportanceLockedDefaultApp, mOriginalImportance,
+ mParentId, mConversationId);
result = 31 * result + Arrays.hashCode(mVibration);
return result;
}
@@ -1063,26 +1139,7 @@ public final class NotificationChannel implements Parcelable {
String output = "NotificationChannel{"
+ "mId='" + mId + '\''
+ ", mName=" + redactedName
- + ", mDescription=" + (!TextUtils.isEmpty(mDesc) ? "hasDescription " : "")
- + ", mImportance=" + mImportance
- + ", mBypassDnd=" + mBypassDnd
- + ", mLockscreenVisibility=" + mLockscreenVisibility
- + ", mSound=" + mSound
- + ", mLights=" + mLights
- + ", mLightColor=" + mLightColor
- + ", mVibration=" + Arrays.toString(mVibration)
- + ", mUserLockedFields=" + Integer.toHexString(mUserLockedFields)
- + ", mFgServiceShown=" + mFgServiceShown
- + ", mVibrationEnabled=" + mVibrationEnabled
- + ", mShowBadge=" + mShowBadge
- + ", mDeleted=" + mDeleted
- + ", mGroup='" + mGroup + '\''
- + ", mAudioAttributes=" + mAudioAttributes
- + ", mBlockableSystem=" + mBlockableSystem
- + ", mAllowBubbles=" + mAllowBubbles
- + ", mImportanceLockedByOEM=" + mImportanceLockedByOEM
- + ", mImportanceLockedDefaultApp=" + mImportanceLockedDefaultApp
- + ", mOriginalImp=" + mOriginalImportance
+ + getFieldsString()
+ '}';
pw.println(prefix + output);
}
@@ -1092,7 +1149,12 @@ public final class NotificationChannel implements Parcelable {
return "NotificationChannel{"
+ "mId='" + mId + '\''
+ ", mName=" + mName
- + ", mDescription=" + (!TextUtils.isEmpty(mDesc) ? "hasDescription " : "")
+ + getFieldsString()
+ + '}';
+ }
+
+ private String getFieldsString() {
+ return ", mDescription=" + (!TextUtils.isEmpty(mDesc) ? "hasDescription " : "")
+ ", mImportance=" + mImportance
+ ", mBypassDnd=" + mBypassDnd
+ ", mLockscreenVisibility=" + mLockscreenVisibility
@@ -1112,7 +1174,8 @@ public final class NotificationChannel implements Parcelable {
+ ", mImportanceLockedByOEM=" + mImportanceLockedByOEM
+ ", mImportanceLockedDefaultApp=" + mImportanceLockedDefaultApp
+ ", mOriginalImp=" + mOriginalImportance
- + '}';
+ + ", mParent=" + mParentId
+ + ", mConversationId" + mConversationId;
}
/** @hide */
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index fdbb8bb0d4c5..61c109885e05 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -843,6 +843,25 @@ public class NotificationManager {
}
/**
+ * Returns the notification channel settings for a given channel and conversation id.
+ *
+ * <p>The channel must belong to your package, or to a package you are an approved notification
+ * delegate for (see {@link #canNotifyAsPackage(String)}), or it will not be returned. To query
+ * a channel as a notification delegate, call this method from a context created for that
+ * package (see {@link Context#createPackageContext(String, int)}).</p>
+ */
+ public @Nullable NotificationChannel getNotificationChannel(@NonNull String channelId,
+ @NonNull String conversationId) {
+ INotificationManager service = getService();
+ try {
+ return service.getConversationNotificationChannel(mContext.getOpPackageName(),
+ mContext.getUserId(), mContext.getPackageName(), channelId, conversationId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns all notification channels belonging to the calling package.
*
* <p>Approved notification delegates (see {@link #canNotifyAsPackage(String)}) can query
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index 844e72ecf07c..736efb6a5e69 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -15,6 +15,7 @@
*/
package android.app;
+
import android.annotation.NonNull;
import android.os.SystemProperties;
import android.util.Log;
@@ -23,6 +24,7 @@ import com.android.internal.annotations.GuardedBy;
import java.util.LinkedHashMap;
import java.util.Map;
+import java.util.Objects;
import java.util.Random;
import java.util.concurrent.atomic.AtomicLong;
@@ -164,6 +166,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
private static final String TAG = "PropertyInvalidatedCache";
private static final boolean DEBUG = false;
private static final boolean ENABLE = true;
+ private static final boolean VERIFY = false;
private final Object mLock = new Object();
@@ -228,6 +231,18 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
protected abstract Result recompute(Query query);
/**
+ * Determines if a pair of responses are considered equal. Used to determine whether
+ * a cache is inadvertently returning stale results when VERIFY is set to true.
+ */
+ protected boolean debugCompareQueryResults(Result cachedResult, Result fetchedResult) {
+ // If a service crashes and returns a null result, the cached value remains valid.
+ if (fetchedResult != null) {
+ return Objects.equals(cachedResult, fetchedResult);
+ }
+ return true;
+ }
+
+ /**
* Make result up-to-date on a cache hit. Called unlocked;
* may block.
*
@@ -334,12 +349,12 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
mCache.put(query, refreshedResult);
}
}
- return refreshedResult;
+ return maybeCheckConsistency(query, refreshedResult);
}
if (DEBUG) {
Log.d(TAG, "cache hit for " + query);
}
- return cachedResult;
+ return maybeCheckConsistency(query, cachedResult);
}
// Cache miss: make the value from scratch.
if (DEBUG) {
@@ -353,7 +368,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
mCache.put(query, result);
}
}
- return result;
+ return maybeCheckConsistency(query, result);
}
}
@@ -425,4 +440,15 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
}
SystemProperties.set(name, newValueString);
}
+
+ private Result maybeCheckConsistency(Query query, Result proposedResult) {
+ if (VERIFY) {
+ Result resultToCompare = recompute(query);
+ boolean nonceChanged = (getCurrentNonce() != mLastSeenNonce);
+ if (!nonceChanged && !debugCompareQueryResults(proposedResult, resultToCompare)) {
+ throw new AssertionError("cache returned out of date response for " + query);
+ }
+ }
+ return proposedResult;
+ }
}
diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java
index 84263749232d..dde6dda8e448 100644
--- a/core/java/android/app/StatsManager.java
+++ b/core/java/android/app/StatsManager.java
@@ -26,7 +26,6 @@ import android.content.Context;
import android.os.IBinder;
import android.os.IPullAtomCallback;
import android.os.IPullAtomResultReceiver;
-import android.os.IStatsCompanionService;
import android.os.IStatsManagerService;
import android.os.IStatsPullerCallback;
import android.os.IStatsd;
@@ -61,9 +60,6 @@ public final class StatsManager {
private IStatsd mService;
@GuardedBy("sLock")
- private IStatsCompanionService mStatsCompanion;
-
- @GuardedBy("sLock")
private IStatsManagerService mStatsManagerService;
/**
@@ -538,7 +534,7 @@ public final class StatsManager {
}
synchronized (sLock) {
try {
- IStatsCompanionService service = getIStatsCompanionServiceLocked();
+ IStatsManagerService service = getIStatsManagerServiceLocked();
PullAtomCallbackInternal rec =
new PullAtomCallbackInternal(atomTag, callback, executor);
service.registerPullAtomCallback(atomTag, coolDownNs, timeoutNs, additiveFields,
@@ -560,7 +556,7 @@ public final class StatsManager {
public void unregisterPullAtomCallback(int atomTag) {
synchronized (sLock) {
try {
- IStatsCompanionService service = getIStatsCompanionServiceLocked();
+ IStatsManagerService service = getIStatsManagerServiceLocked();
service.unregisterPullAtomCallback(atomTag);
} catch (RemoteException e) {
throw new RuntimeException("Unable to unregister pull atom callback");
@@ -746,16 +742,6 @@ public final class StatsManager {
}
@GuardedBy("sLock")
- private IStatsCompanionService getIStatsCompanionServiceLocked() {
- if (mStatsCompanion != null) {
- return mStatsCompanion;
- }
- mStatsCompanion = IStatsCompanionService.Stub.asInterface(
- ServiceManager.getService("statscompanion"));
- return mStatsCompanion;
- }
-
- @GuardedBy("sLock")
private IStatsManagerService getIStatsManagerServiceLocked() {
if (mStatsManagerService != null) {
return mStatsManagerService;
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 078e4538c66b..42563b5a1561 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -267,6 +267,7 @@ public class StatusBarManager {
* @hide
*/
@UnsupportedAppUsage
+ @TestApi
public void expandNotificationsPanel() {
try {
final IStatusBarService svc = getService();
@@ -284,6 +285,7 @@ public class StatusBarManager {
* @hide
*/
@UnsupportedAppUsage
+ @TestApi
public void collapsePanels() {
try {
final IStatusBarService svc = getService();
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index f299d456a18f..e6c89d9071e6 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -17,9 +17,12 @@
package android.app.admin;
import android.annotation.UserIdInt;
+import android.content.ComponentName;
import android.content.Intent;
+import android.os.UserHandle;
import java.util.List;
+import java.util.Set;
/**
* Device policy manager local system service interface.
@@ -165,4 +168,23 @@ public abstract class DevicePolicyManagerInternal {
* Do not call it directly. Use {@link DevicePolicyCache#getInstance()} instead.
*/
protected abstract DeviceStateCache getDeviceStateCache();
+
+ /**
+ * Returns the combined set of the following:
+ * <ul>
+ * <li>The package names that the admin has previously set as allowed to request user consent
+ * for cross-profile communication, via {@link
+ * DevicePolicyManager#setCrossProfilePackages(ComponentName, Set)}.</li>
+ * <li>The default package names that are allowed to request user consent for cross-profile
+ * communication without being explicitly enabled by the admin , via {@link
+ * DevicePolicyManager#setDefaultCrossProfilePackages(ComponentName, UserHandle, Set)}.</li>
+ * </ul>
+ *
+ * @return the combined set of whitelisted package names set via
+ * {@link DevicePolicyManager#setCrossProfilePackages(ComponentName, Set)} and
+ * {@link DevicePolicyManager#setDefaultCrossProfilePackages(ComponentName, UserHandle, Set)}
+ *
+ * @hide
+ */
+ public abstract List<String> getAllCrossProfilePackages();
}
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index b1b6f0d61f9f..cb1f05573d93 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -2670,6 +2670,9 @@ public final class BluetoothAdapter {
} else if (profile == BluetoothProfile.PAN) {
BluetoothPan pan = new BluetoothPan(context, listener);
return true;
+ } else if (profile == BluetoothProfile.PBAP) {
+ BluetoothPbap pbap = new BluetoothPbap(context, listener);
+ return true;
} else if (profile == BluetoothProfile.HEALTH) {
Log.e(TAG, "getProfileProxy(): BluetoothHealth is deprecated");
return false;
@@ -2742,6 +2745,10 @@ public final class BluetoothAdapter {
BluetoothPan pan = (BluetoothPan) proxy;
pan.close();
break;
+ case BluetoothProfile.PBAP:
+ BluetoothPbap pbap = (BluetoothPbap) proxy;
+ pbap.close();
+ break;
case BluetoothProfile.GATT:
BluetoothGatt gatt = (BluetoothGatt) proxy;
gatt.close();
diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java
index e9b0be2c4cd6..a923be62fbce 100644
--- a/core/java/android/bluetooth/BluetoothHidDevice.java
+++ b/core/java/android/bluetooth/BluetoothHidDevice.java
@@ -16,8 +16,12 @@
package android.bluetooth;
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
@@ -36,6 +40,7 @@ import java.util.concurrent.Executor;
*/
public final class BluetoothHidDevice implements BluetoothProfile {
private static final String TAG = BluetoothHidDevice.class.getSimpleName();
+ private static final boolean DBG = false;
/**
* Intent used to broadcast the change in connection state of the Input Host profile.
@@ -682,4 +687,62 @@ public final class BluetoothHidDevice implements BluetoothProfile {
return result;
}
+
+ /**
+ * Connects Hid Device if connectionPolicy is {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED}
+ * and disconnects Hid device if connectionPolicy is
+ * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}.
+ *
+ * <p> The device should already be paired.
+ * Connection policy can be one of:
+ * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED},
+ * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN},
+ * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN}
+ *
+ * @param device Paired bluetooth device
+ * @param connectionPolicy determines whether hid device should be connected or disconnected
+ * @return true if hid device is connected or disconnected, false otherwise
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
+ @ConnectionPolicy int connectionPolicy) {
+ log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
+ try {
+ final IBluetoothHidDevice service = getService();
+ if (service != null && isEnabled()
+ && isValidDevice(device)) {
+ if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+ return false;
+ }
+ return service.setConnectionPolicy(device, connectionPolicy);
+ }
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
+ return false;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return false;
+ }
+ }
+
+ private boolean isEnabled() {
+ if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
+ return false;
+ }
+
+ private boolean isValidDevice(BluetoothDevice device) {
+ if (device == null) return false;
+
+ if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
+ return false;
+ }
+
+ private static void log(String msg) {
+ if (DBG) {
+ Log.d(TAG, msg);
+ }
+ }
}
diff --git a/core/java/android/bluetooth/BluetoothMap.java b/core/java/android/bluetooth/BluetoothMap.java
index 917e7fa04e4c..467470674286 100644
--- a/core/java/android/bluetooth/BluetoothMap.java
+++ b/core/java/android/bluetooth/BluetoothMap.java
@@ -17,7 +17,10 @@
package android.bluetooth;
import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -35,21 +38,35 @@ import java.util.List;
*
* @hide
*/
+@SystemApi
public final class BluetoothMap implements BluetoothProfile {
private static final String TAG = "BluetoothMap";
private static final boolean DBG = true;
private static final boolean VDBG = false;
+ /** @hide */
+ @SuppressLint("ActionValue")
+ @SystemApi
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED";
- /** There was an error trying to obtain the state */
+ /**
+ * There was an error trying to obtain the state
+ *
+ * @hide
+ */
public static final int STATE_ERROR = -1;
+ /** @hide */
public static final int RESULT_FAILURE = 0;
+ /** @hide */
public static final int RESULT_SUCCESS = 1;
- /** Connection canceled before completion. */
+ /**
+ * Connection canceled before completion.
+ *
+ * @hide
+ */
public static final int RESULT_CANCELED = 2;
private BluetoothAdapter mAdapter;
@@ -71,6 +88,7 @@ public final class BluetoothMap implements BluetoothProfile {
mProfileConnector.connect(context, listener);
}
+ @SuppressLint("GenericException")
protected void finalize() throws Throwable {
try {
close();
@@ -84,6 +102,8 @@ public final class BluetoothMap implements BluetoothProfile {
* Other public functions of BluetoothMap will return default error
* results once close() has been called. Multiple invocations of close()
* are ok.
+ *
+ * @hide
*/
public synchronized void close() {
mProfileConnector.disconnect();
@@ -98,6 +118,8 @@ public final class BluetoothMap implements BluetoothProfile {
*
* @return One of the STATE_ return codes, or STATE_ERROR if this proxy object is currently not
* connected to the Map service.
+ *
+ * @hide
*/
public int getState() {
if (VDBG) log("getState()");
@@ -120,6 +142,8 @@ public final class BluetoothMap implements BluetoothProfile {
*
* @return The remote Bluetooth device, or null if not in connected or connecting state, or if
* this proxy object is not connected to the Map service.
+ *
+ * @hide
*/
public BluetoothDevice getClient() {
if (VDBG) log("getClient()");
@@ -141,6 +165,8 @@ public final class BluetoothMap implements BluetoothProfile {
* Returns true if the specified Bluetooth device is connected.
* Returns false if not connected, or if this proxy object is not
* currently connected to the Map service.
+ *
+ * @hide
*/
public boolean isConnected(BluetoothDevice device) {
if (VDBG) log("isConnected(" + device + ")");
@@ -161,6 +187,8 @@ public final class BluetoothMap implements BluetoothProfile {
/**
* Initiate connection. Initiation of outgoing connections is not
* supported for MAP server.
+ *
+ * @hide
*/
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")" + "not supported for MAPS");
@@ -172,6 +200,8 @@ public final class BluetoothMap implements BluetoothProfile {
*
* @param device Remote Bluetooth Device
* @return false on error, true otherwise
+ *
+ * @hide
*/
@UnsupportedAppUsage
public boolean disconnect(BluetoothDevice device) {
@@ -196,6 +226,8 @@ public final class BluetoothMap implements BluetoothProfile {
* devices. It tries to err on the side of false positives.
*
* @return True if this device might support Map.
+ *
+ * @hide
*/
public static boolean doesClassMatchSink(BluetoothClass btClass) {
// TODO optimize the rule
@@ -214,8 +246,11 @@ public final class BluetoothMap implements BluetoothProfile {
* Get the list of connected devices. Currently at most one.
*
* @return list of connected devices
+ *
+ * @hide
*/
- public List<BluetoothDevice> getConnectedDevices() {
+ @SystemApi
+ public @NonNull List<BluetoothDevice> getConnectedDevices() {
if (DBG) log("getConnectedDevices()");
final IBluetoothMap service = getService();
if (service != null && isEnabled()) {
@@ -234,6 +269,8 @@ public final class BluetoothMap implements BluetoothProfile {
* Get the list of devices matching specified states. Currently at most one.
*
* @return list of matching devices
+ *
+ * @hide
*/
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (DBG) log("getDevicesMatchingStates()");
@@ -254,6 +291,8 @@ public final class BluetoothMap implements BluetoothProfile {
* Get connection state of device
*
* @return device connection state
+ *
+ * @hide
*/
public int getConnectionState(BluetoothDevice device) {
if (DBG) log("getConnectionState(" + device + ")");
@@ -301,7 +340,7 @@ public final class BluetoothMap implements BluetoothProfile {
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
- public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
+ public boolean setConnectionPolicy(@Nullable BluetoothDevice device, int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
final IBluetoothMap service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
@@ -349,7 +388,7 @@ public final class BluetoothMap implements BluetoothProfile {
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH)
- public int getConnectionPolicy(BluetoothDevice device) {
+ public int getConnectionPolicy(@Nullable BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothMap service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java
index 42f27f2a9c98..024bb06098ab 100644
--- a/core/java/android/bluetooth/BluetoothPan.java
+++ b/core/java/android/bluetooth/BluetoothPan.java
@@ -16,9 +16,11 @@
package android.bluetooth;
+import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SuppressLint;
@@ -256,6 +258,41 @@ public final class BluetoothPan implements BluetoothProfile {
}
/**
+ * Set connection policy of the profile
+ *
+ * <p> The device should already be paired.
+ * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
+ * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
+ *
+ * @param device Paired bluetooth device
+ * @param connectionPolicy is the connection policy to set to for this profile
+ * @return true if connectionPolicy is set, false on error
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
+ @ConnectionPolicy int connectionPolicy) {
+ if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
+ try {
+ final IBluetoothPan service = getService();
+ if (service != null && isEnabled()
+ && isValidDevice(device)) {
+ if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+ return false;
+ }
+ return service.setConnectionPolicy(device, connectionPolicy);
+ }
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
+ return false;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return false;
+ }
+ }
+
+ /**
* {@inheritDoc}
*/
@Override
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index 948885ed5010..e07ca521e77d 100644
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -274,15 +274,15 @@ public class BluetoothPbap implements BluetoothProfile {
}
/**
- * Pbap does not store connection policy, so this function only disconnects Pbap if
- * connectionPolicy is CONNECTION_POLICY_FORBIDDEN.
+ * Pbap does not store connection policy, so this function only disconnects pbap if
+ * connectionPolicy is {@link #CONNECTION_POLICY_FORBIDDEN}.
*
* <p> The device should already be paired.
* Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
* {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
*
* @param device Paired bluetooth device
- * @param connectionPolicy is the connection policy to set to for this profile
+ * @param connectionPolicy determines whether to disconnect the device
* @return true if pbap is successfully disconnected, false otherwise
* @hide
*/
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 5cb2907dfba4..44b2df691876 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3240,15 +3240,40 @@ public abstract class Context {
}
/**
- * Same as {@link #bindService(Intent, ServiceConnection, int)}, but with an explicit userHandle
- * argument for use by system server and other multi-user aware code.
- * @hide
+ * Binds to a service in the given {@code user} in the same manner as
+ * {@link #bindService(Intent, ServiceConnection, int)}.
+ *
+ * <p>If the given {@code user} is in the same profile group and the target package is the
+ * same as the caller, {@code android.Manifest.permission.INTERACT_ACROSS_PROFILES} is
+ * sufficient. Otherwise, requires {@code android.Manifest.permission.INTERACT_ACROSS_USERS}
+ * for interacting with other users.
+ *
+ * @param service Identifies the service to connect to. The Intent must
+ * specify an explicit component name.
+ * @param conn Receives information as the service is started and stopped.
+ * This must be a valid ServiceConnection object; it must not be null.
+ * @param flags Operation options for the binding. May be 0,
+ * {@link #BIND_AUTO_CREATE}, {@link #BIND_DEBUG_UNBIND},
+ * {@link #BIND_NOT_FOREGROUND}, {@link #BIND_ABOVE_CLIENT},
+ * {@link #BIND_ALLOW_OOM_MANAGEMENT}, {@link #BIND_WAIVE_PRIORITY}.
+ * {@link #BIND_IMPORTANT}, or
+ * {@link #BIND_ADJUST_WITH_ACTIVITY}.
+ * @return {@code true} if the system is in the process of bringing up a
+ * service that your client has permission to bind to; {@code false}
+ * if the system couldn't find the service. If this value is {@code true}, you
+ * should later call {@link #unbindService} to release the
+ * connection.
+ *
+ * @throws SecurityException if the client does not have the required permission to bind.
*/
- @SystemApi
@SuppressWarnings("unused")
- @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
- public boolean bindServiceAsUser(@RequiresPermission Intent service, ServiceConnection conn,
- int flags, UserHandle user) {
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.INTERACT_ACROSS_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_PROFILES
+ })
+ public boolean bindServiceAsUser(
+ @NonNull @RequiresPermission Intent service, @NonNull ServiceConnection conn, int flags,
+ @NonNull UserHandle user) {
throw new RuntimeException("Not implemented. Must override in a subclass.");
}
@@ -3391,7 +3416,6 @@ public abstract class Context {
TELEPHONY_SUBSCRIPTION_SERVICE,
CARRIER_CONFIG_SERVICE,
EUICC_SERVICE,
- MMS_SERVICE,
TELECOM_SERVICE,
CLIPBOARD_SERVICE,
INPUT_METHOD_SERVICE,
@@ -3588,8 +3612,6 @@ public abstract class Context {
* @see android.telephony.CarrierConfigManager
* @see #EUICC_SERVICE
* @see android.telephony.euicc.EuiccManager
- * @see #MMS_SERVICE
- * @see android.telephony.MmsManager
* @see #INPUT_METHOD_SERVICE
* @see android.view.inputmethod.InputMethodManager
* @see #UI_MODE_SERVICE
@@ -4266,15 +4288,6 @@ public abstract class Context {
/**
* Use with {@link #getSystemService(String)} to retrieve a
- * {@link android.telephony.MmsManager} to send/receive MMS messages.
- *
- * @see #getSystemService(String)
- * @see android.telephony.MmsManager
- */
- public static final String MMS_SERVICE = "mms";
-
- /**
- * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.content.ClipboardManager} for accessing and modifying
* the contents of the global clipboard.
*
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 3bb0f9222237..c8f587f71bca 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -6440,19 +6440,22 @@ public class Intent implements Parcelable, Cloneable {
*/
public static final int FLAG_RECEIVER_NO_ABORT = 0x08000000;
/**
- * If set, when sending a broadcast <i>before boot has completed</i> only
+ * If set, when sending a broadcast <i>before the system has fully booted up
+ * (which is even before {@link #ACTION_LOCKED_BOOT_COMPLETED} has been sent)"</i> only
* registered receivers will be called -- no BroadcastReceiver components
* will be launched. Sticky intent state will be recorded properly even
* if no receivers wind up being called. If {@link #FLAG_RECEIVER_REGISTERED_ONLY}
* is specified in the broadcast intent, this flag is unnecessary.
*
- * <p>This flag is only for use by system sevices as a convenience to
- * avoid having to implement a more complex mechanism around detection
+ * <p>This flag is only for use by system services (even services from mainline modules) as a
+ * convenience to avoid having to implement a more complex mechanism around detection
* of boot completion.
*
+ * <p>This is useful to system server mainline modules
+ *
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public static final int FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT = 0x04000000;
/**
* Set when this broadcast is for a boot upgrade, a special mode that
diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
index e897b917fcc2..9d5751480a80 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -16,20 +16,25 @@
package android.content.pm;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
+import android.net.Uri;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.Settings;
import com.android.internal.R;
import com.android.internal.util.UserIcons;
import java.util.List;
+import java.util.Set;
/**
* Class for handling cross profile operations. Apps can use this class to interact with its
@@ -169,6 +174,86 @@ public class CrossProfileApps {
}
}
+ /**
+ * Returns whether the calling package can request to interact across profiles.
+ *
+ * <p>The package's current ability to interact across profiles can be checked with
+ * {@link #canInteractAcrossProfiles()}.
+ *
+ * <p>Specifically, returns whether the following are all true:
+ * <ul>
+ * <li>{@link #getTargetUserProfiles()} returns a non-empty list for the calling user.</li>
+ * <li>The calling app has requested</li>
+ * {@code android.Manifest.permission.INTERACT_ACROSS_PROFILES} in its manifest.
+ * <li>The calling package has either been whitelisted by default by the OEM or has been
+ * explicitly whitelisted by the admin via
+ * {@link android.app.admin.DevicePolicyManager#setCrossProfilePackages(ComponentName, Set)}.
+ * </li>
+ * </ul>
+ *
+ * @return true if the calling package can request to interact across profiles.
+ */
+ public boolean canRequestInteractAcrossProfiles() {
+ try {
+ return mService.canRequestInteractAcrossProfiles(mContext.getPackageName());
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns whether the calling package can interact across profiles.
+ *
+ * <p>The package's current ability to request to interact across profiles can be checked with
+ * {@link #canRequestInteractAcrossProfiles()}.
+ *
+ * <p>Specifically, returns whether the following are all true:
+ * <ul>
+ * <li>{@link #getTargetUserProfiles()} returns a non-empty list for the calling user.</li>
+ * <li>The user has previously consented to cross-profile communication for the calling
+ * package.</li>
+ * <li>The calling package has either been whitelisted by default by the OEM or has been
+ * explicitly whitelisted by the admin via
+ * {@link android.app.admin.DevicePolicyManager#setCrossProfilePackages(ComponentName, Set)}.
+ * </li>
+ * </ul>
+ *
+ * @return true if the calling package can interact across profiles.
+ * @throws SecurityException if {@code mContext.getPackageName()} does not belong to the
+ * calling UID.
+ */
+ public boolean canInteractAcrossProfiles() {
+ try {
+ return mService.canInteractAcrossProfiles(mContext.getPackageName());
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns an {@link Intent} to open the settings page that allows the user to decide whether
+ * the calling app can interact across profiles. The current state is given by
+ * {@link #canInteractAcrossProfiles()}.
+ *
+ * <p>Returns {@code null} if {@link #canRequestInteractAcrossProfiles()} is {@code false}.
+ *
+ * @return an {@link Intent} to open the settings page that allows the user to decide whether
+ * the app can interact across profiles
+ *
+ * @throws SecurityException if {@code mContext.getPackageName()} does not belong to the
+ * calling UID.
+ */
+ public @Nullable Intent createRequestInteractAcrossProfilesIntent() {
+ if (!canRequestInteractAcrossProfiles()) {
+ return null;
+ }
+ final Intent settingsIntent = new Intent();
+ settingsIntent.setAction(Settings.ACTION_MANAGE_CROSS_PROFILE_ACCESS);
+ final Uri packageUri = Uri.parse("package:" + mContext.getPackageName());
+ settingsIntent.setData(packageUri);
+ return settingsIntent;
+ }
+
private void verifyCanAccessUser(UserHandle userHandle) {
if (!getTargetUserProfiles().contains(userHandle)) {
throw new SecurityException("Not allowed to access " + userHandle);
diff --git a/core/java/android/content/pm/ICrossProfileApps.aidl b/core/java/android/content/pm/ICrossProfileApps.aidl
index d2d66cbda610..c5db0ccebf52 100644
--- a/core/java/android/content/pm/ICrossProfileApps.aidl
+++ b/core/java/android/content/pm/ICrossProfileApps.aidl
@@ -30,4 +30,6 @@ interface ICrossProfileApps {
void startActivityAsUser(in IApplicationThread caller, in String callingPackage,
in ComponentName component, int userId, boolean launchMainActivity);
List<UserHandle> getTargetUserProfiles(in String callingPackage);
+ boolean canInteractAcrossProfiles(in String callingPackage);
+ boolean canRequestInteractAcrossProfiles(in String callingPackage);
} \ No newline at end of file
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index b85c58ad0e2f..4bfc40e698b9 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2323,6 +2323,13 @@ public abstract class PackageManager {
public static final String FEATURE_TELEPHONY_GSM = "android.hardware.telephony.gsm";
/**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device
+ * has a telephony radio that support data.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_TELEPHONY_DATA = "android.hardware.telephony.data";
+
+ /**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
* The device supports telephony carrier restriction mechanism.
*
diff --git a/core/java/android/content/res/TEST_MAPPING b/core/java/android/content/res/TEST_MAPPING
new file mode 100644
index 000000000000..daf9a1491cf9
--- /dev/null
+++ b/core/java/android/content/res/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksResourceLoaderTests"
+ }
+ ]
+}
diff --git a/core/java/android/content/rollback/PackageRollbackInfo.java b/core/java/android/content/rollback/PackageRollbackInfo.java
index 6378db0ebbbd..b273cd67479c 100644
--- a/core/java/android/content/rollback/PackageRollbackInfo.java
+++ b/core/java/android/content/rollback/PackageRollbackInfo.java
@@ -76,6 +76,11 @@ public final class PackageRollbackInfo implements Parcelable {
*/
private final boolean mIsApex;
+ /**
+ * Whether this instance represents the PackageRollbackInfo for an APK in APEX.
+ */
+ private final boolean mIsApkInApex;
+
/*
* The list of users for which snapshots have been saved.
*/
@@ -157,6 +162,10 @@ public final class PackageRollbackInfo implements Parcelable {
public @PackageManager.RollbackDataPolicy int getRollbackDataPolicy() {
return mRollbackDataPolicy;
}
+ /** @hide */
+ public boolean isApkInApex() {
+ return mIsApkInApex;
+ }
/** @hide */
public IntArray getSnapshottedUsers() {
@@ -190,17 +199,18 @@ public final class PackageRollbackInfo implements Parcelable {
public PackageRollbackInfo(VersionedPackage packageRolledBackFrom,
VersionedPackage packageRolledBackTo,
@NonNull IntArray pendingBackups, @NonNull ArrayList<RestoreInfo> pendingRestores,
- boolean isApex, @NonNull IntArray snapshottedUsers,
+ boolean isApex, boolean isApkInApex, @NonNull IntArray snapshottedUsers,
@NonNull SparseLongArray ceSnapshotInodes) {
this(packageRolledBackFrom, packageRolledBackTo, pendingBackups, pendingRestores, isApex,
- snapshottedUsers, ceSnapshotInodes, PackageManager.RollbackDataPolicy.RESTORE);
+ isApkInApex, snapshottedUsers, ceSnapshotInodes,
+ PackageManager.RollbackDataPolicy.RESTORE);
}
/** @hide */
public PackageRollbackInfo(VersionedPackage packageRolledBackFrom,
VersionedPackage packageRolledBackTo,
@NonNull IntArray pendingBackups, @NonNull ArrayList<RestoreInfo> pendingRestores,
- boolean isApex, @NonNull IntArray snapshottedUsers,
+ boolean isApex, boolean isApkInApex, @NonNull IntArray snapshottedUsers,
@NonNull SparseLongArray ceSnapshotInodes,
@PackageManager.RollbackDataPolicy int rollbackDataPolicy) {
this.mVersionRolledBackFrom = packageRolledBackFrom;
@@ -209,6 +219,7 @@ public final class PackageRollbackInfo implements Parcelable {
this.mPendingRestores = pendingRestores;
this.mIsApex = isApex;
this.mRollbackDataPolicy = rollbackDataPolicy;
+ this.mIsApkInApex = isApkInApex;
this.mSnapshottedUsers = snapshottedUsers;
this.mCeSnapshotInodes = ceSnapshotInodes;
}
@@ -217,6 +228,7 @@ public final class PackageRollbackInfo implements Parcelable {
this.mVersionRolledBackFrom = VersionedPackage.CREATOR.createFromParcel(in);
this.mVersionRolledBackTo = VersionedPackage.CREATOR.createFromParcel(in);
this.mIsApex = in.readBoolean();
+ this.mIsApkInApex = in.readBoolean();
this.mPendingRestores = null;
this.mPendingBackups = null;
this.mSnapshottedUsers = null;
@@ -234,6 +246,7 @@ public final class PackageRollbackInfo implements Parcelable {
mVersionRolledBackFrom.writeToParcel(out, flags);
mVersionRolledBackTo.writeToParcel(out, flags);
out.writeBoolean(mIsApex);
+ out.writeBoolean(mIsApkInApex);
}
public static final @NonNull Parcelable.Creator<PackageRollbackInfo> CREATOR =
diff --git a/core/java/android/hardware/soundtrigger/ConversionUtil.java b/core/java/android/hardware/soundtrigger/ConversionUtil.java
index 8231c58a105e..43f3787e15da 100644
--- a/core/java/android/hardware/soundtrigger/ConversionUtil.java
+++ b/core/java/android/hardware/soundtrigger/ConversionUtil.java
@@ -16,6 +16,7 @@
package android.hardware.soundtrigger;
+import android.annotation.Nullable;
import android.hardware.soundtrigger.ModelParams;
import android.media.AudioFormat;
import android.media.audio.common.AudioConfig;
@@ -32,8 +33,6 @@ import android.media.soundtrigger_middleware.SoundModel;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
-import android.annotation.Nullable;
-
import java.util.Arrays;
import java.util.UUID;
@@ -48,6 +47,7 @@ class ConversionUtil {
properties.description,
properties.uuid,
properties.version,
+ properties.supportedModelArch,
properties.maxSoundModels,
properties.maxKeyPhrases,
properties.maxUsers,
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index 55505ba2e278..d87200931830 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -101,6 +101,14 @@ public class SoundTrigger {
/** Voice detection engine version */
public final int version;
+ /**
+ * String naming the architecture used for running the supported models.
+ * (eg. a platform running models on a DSP could implement this string to convey the DSP
+ * architecture used)
+ */
+ @NonNull
+ public final String supportedModelArch;
+
/** Maximum number of active sound models */
public final int maxSoundModels;
@@ -130,15 +138,17 @@ public class SoundTrigger {
public final boolean returnsTriggerInEvent;
ModuleProperties(int id, @NonNull String implementor, @NonNull String description,
- @NonNull String uuid, int version, int maxSoundModels, int maxKeyphrases,
- int maxUsers, int recognitionModes, boolean supportsCaptureTransition,
- int maxBufferMs, boolean supportsConcurrentCapture,
- int powerConsumptionMw, boolean returnsTriggerInEvent) {
+ @NonNull String uuid, int version, @NonNull String supportedModelArch,
+ int maxSoundModels, int maxKeyphrases, int maxUsers, int recognitionModes,
+ boolean supportsCaptureTransition, int maxBufferMs,
+ boolean supportsConcurrentCapture, int powerConsumptionMw,
+ boolean returnsTriggerInEvent) {
this.id = id;
this.implementor = requireNonNull(implementor);
this.description = requireNonNull(description);
this.uuid = UUID.fromString(requireNonNull(uuid));
this.version = version;
+ this.supportedModelArch = requireNonNull(supportedModelArch);
this.maxSoundModels = maxSoundModels;
this.maxKeyphrases = maxKeyphrases;
this.maxUsers = maxUsers;
@@ -167,6 +177,7 @@ public class SoundTrigger {
String description = in.readString();
String uuid = in.readString();
int version = in.readInt();
+ String supportedModelArch = in.readString();
int maxSoundModels = in.readInt();
int maxKeyphrases = in.readInt();
int maxUsers = in.readInt();
@@ -177,7 +188,7 @@ public class SoundTrigger {
int powerConsumptionMw = in.readInt();
boolean returnsTriggerInEvent = in.readByte() == 1;
return new ModuleProperties(id, implementor, description, uuid, version,
- maxSoundModels, maxKeyphrases, maxUsers, recognitionModes,
+ supportedModelArch, maxSoundModels, maxKeyphrases, maxUsers, recognitionModes,
supportsCaptureTransition, maxBufferMs, supportsConcurrentCapture,
powerConsumptionMw, returnsTriggerInEvent);
}
@@ -189,6 +200,7 @@ public class SoundTrigger {
dest.writeString(description);
dest.writeString(uuid.toString());
dest.writeInt(version);
+ dest.writeString(supportedModelArch);
dest.writeInt(maxSoundModels);
dest.writeInt(maxKeyphrases);
dest.writeInt(maxUsers);
@@ -208,7 +220,8 @@ public class SoundTrigger {
@Override
public String toString() {
return "ModuleProperties [id=" + id + ", implementor=" + implementor + ", description="
- + description + ", uuid=" + uuid + ", version=" + version + ", maxSoundModels="
+ + description + ", uuid=" + uuid + ", version=" + version
+ + " , supportedModelArch=" + supportedModelArch + ", maxSoundModels="
+ maxSoundModels + ", maxKeyphrases=" + maxKeyphrases + ", maxUsers="
+ maxUsers + ", recognitionModes=" + recognitionModes
+ ", supportsCaptureTransition=" + supportsCaptureTransition + ", maxBufferMs="
@@ -588,19 +601,19 @@ public class SoundTrigger {
}
}
- /*****************************************************************************
+ /**
* A ModelParamRange is a representation of supported parameter range for a
* given loaded model.
- ****************************************************************************/
+ */
public static final class ModelParamRange implements Parcelable {
/**
- * start of supported range inclusive
+ * The inclusive start of supported range.
*/
public final int start;
/**
- * end of supported range inclusive
+ * The inclusive end of supported range.
*/
public final int end;
@@ -609,31 +622,65 @@ public class SoundTrigger {
this.end = end;
}
+ /** @hide */
private ModelParamRange(@NonNull Parcel in) {
this.start = in.readInt();
this.end = in.readInt();
}
@NonNull
- public static final Creator<ModelParamRange> CREATOR = new Creator<ModelParamRange>() {
- @Override
- @NonNull
- public ModelParamRange createFromParcel(@NonNull Parcel in) {
- return new ModelParamRange(in);
- }
-
- @Override
- @NonNull
- public ModelParamRange[] newArray(int size) {
- return new ModelParamRange[size];
- }
- };
+ public static final Creator<ModelParamRange> CREATOR =
+ new Creator<ModelParamRange>() {
+ @Override
+ @NonNull
+ public ModelParamRange createFromParcel(@NonNull Parcel in) {
+ return new ModelParamRange(in);
+ }
+
+ @Override
+ @NonNull
+ public ModelParamRange[] newArray(int size) {
+ return new ModelParamRange[size];
+ }
+ };
+ /** @hide */
@Override
public int describeContents() {
return 0;
}
+ /** @hide */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + (start);
+ result = prime * result + (end);
+ return result;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ ModelParamRange other = (ModelParamRange) obj;
+ if (start != other.start) {
+ return false;
+ }
+ if (end != other.end) {
+ return false;
+ }
+ return true;
+ }
+
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(start);
diff --git a/core/java/android/net/DhcpInfo.java b/core/java/android/net/DhcpInfo.java
index 98bab44e19fb..912df67a0b45 100644
--- a/core/java/android/net/DhcpInfo.java
+++ b/core/java/android/net/DhcpInfo.java
@@ -16,8 +16,8 @@
package android.net;
-import android.os.Parcelable;
import android.os.Parcel;
+import android.os.Parcelable;
/**
* A simple object for retrieving the results of a DHCP request.
@@ -67,12 +67,12 @@ public class DhcpInfo implements Parcelable {
buf.append(NetworkUtils.intToInetAddress(addr).getHostAddress());
}
- /** Implement the Parcelable interface {@hide} */
+ /** Implement the Parcelable interface */
public int describeContents() {
return 0;
}
- /** Implement the Parcelable interface {@hide} */
+ /** Implement the Parcelable interface */
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(ipAddress);
dest.writeInt(gateway);
@@ -83,7 +83,7 @@ public class DhcpInfo implements Parcelable {
dest.writeInt(leaseDuration);
}
- /** Implement the Parcelable interface {@hide} */
+ /** Implement the Parcelable interface */
public static final @android.annotation.NonNull Creator<DhcpInfo> CREATOR =
new Creator<DhcpInfo>() {
public DhcpInfo createFromParcel(Parcel in) {
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index 45d0c7313fca..09ec6c35fcb9 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -17,7 +17,6 @@ package android.net;
import static com.android.internal.util.Preconditions.checkNotNull;
-import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
@@ -26,6 +25,7 @@ import android.annotation.SystemService;
import android.annotation.TestApi;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.net.annotations.PolicyDirection;
import android.os.Binder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
@@ -41,8 +41,6 @@ import dalvik.system.CloseGuard;
import java.io.FileDescriptor;
import java.io.IOException;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.Socket;
@@ -78,11 +76,6 @@ public final class IpSecManager {
*/
public static final int DIRECTION_OUT = 1;
- /** @hide */
- @IntDef(value = {DIRECTION_IN, DIRECTION_OUT})
- @Retention(RetentionPolicy.SOURCE)
- public @interface PolicyDirection {}
-
/**
* The Security Parameter Index (SPI) 0 indicates an unknown or invalid index.
*
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 33c39d489835..739e8178e68e 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -587,15 +587,14 @@ public final class NetworkCapabilities implements Parcelable {
}
/**
- * Removes the NET_CAPABILITY_NOT_RESTRICTED capability if all the capabilities it provides are
- * typically provided by restricted networks.
+ * Deduces that all the capabilities it provides are typically provided by restricted networks
+ * or not.
*
- * TODO: consider:
- * - Renaming it to guessRestrictedCapability and make it set the
- * restricted capability bit in addition to clearing it.
+ * @return {@code true} if the network should be restricted.
* @hide
*/
- public void maybeMarkCapabilitiesRestricted() {
+ @SystemApi
+ public boolean deduceRestrictedCapability() {
// Check if we have any capability that forces the network to be restricted.
final boolean forceRestrictedCapability =
(mNetworkCapabilities & FORCE_RESTRICTED_CAPABILITIES) != 0;
@@ -609,8 +608,17 @@ public final class NetworkCapabilities implements Parcelable {
final boolean hasRestrictedCapabilities =
(mNetworkCapabilities & RESTRICTED_CAPABILITIES) != 0;
- if (forceRestrictedCapability
- || (hasRestrictedCapabilities && !hasUnrestrictedCapabilities)) {
+ return forceRestrictedCapability
+ || (hasRestrictedCapabilities && !hasUnrestrictedCapabilities);
+ }
+
+ /**
+ * Removes the NET_CAPABILITY_NOT_RESTRICTED capability if deducing the network is restricted.
+ *
+ * @hide
+ */
+ public void maybeMarkCapabilitiesRestricted() {
+ if (deduceRestrictedCapability()) {
removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
}
}
diff --git a/core/java/android/net/annotations/PolicyDirection.java b/core/java/android/net/annotations/PolicyDirection.java
new file mode 100644
index 000000000000..febd9b406111
--- /dev/null
+++ b/core/java/android/net/annotations/PolicyDirection.java
@@ -0,0 +1,35 @@
+/*
+ * 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.net.annotations;
+
+import android.annotation.IntDef;
+import android.net.IpSecManager;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * IPsec traffic direction.
+ *
+ * <p>Mainline modules cannot reference hidden @IntDef. Moving this annotation to a separate class
+ * to allow others to statically include it.
+ *
+ * @hide
+ */
+@IntDef(value = {IpSecManager.DIRECTION_IN, IpSecManager.DIRECTION_OUT})
+@Retention(RetentionPolicy.SOURCE)
+public @interface PolicyDirection {}
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 3ef86ed9f994..5d80ab6453cc 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -68,10 +68,9 @@ public class Process {
public static final int LOG_UID = 1007;
/**
- * Defines the UID/GID for the WIFI supplicant process.
- * @hide
+ * Defines the UID/GID for the WIFI native processes like wificond, supplicant, hostapd,
+ * vendor HAL, etc.
*/
- @UnsupportedAppUsage
public static final int WIFI_UID = 1010;
/**
@@ -758,11 +757,12 @@ public class Process {
/**
* Set the priority of a thread, based on Linux priorities.
- *
- * @param tid The identifier of the thread/process to change.
+ *
+ * @param tid The identifier of the thread/process to change. It should be
+ * the native thread id but not the managed id of {@link java.lang.Thread}.
* @param priority A Linux priority level, from -20 for highest scheduling
* priority to 19 for lowest scheduling priority.
- *
+ *
* @throws IllegalArgumentException Throws IllegalArgumentException if
* <var>tid</var> does not exist.
* @throws SecurityException Throws SecurityException if your process does
diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java
index 16f7b398731c..73e1adf134f2 100644
--- a/core/java/android/os/UpdateEngine.java
+++ b/core/java/android/os/UpdateEngine.java
@@ -19,6 +19,7 @@ package android.os;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.content.res.AssetFileDescriptor;
import android.os.IUpdateEngine;
import android.os.IUpdateEngineCallback;
import android.os.RemoteException;
@@ -349,16 +350,17 @@ public class UpdateEngine {
}
/**
- * Applies the payload passed as ParcelFileDescriptor {@code pfd} instead of
- * using the {@code file://} scheme.
+ * Applies the payload passed as AssetFileDescriptor {@code assetFd}
+ * instead of using the {@code file://} scheme.
*
* <p>See {@link #applyPayload(String)} for {@code offset}, {@code size} and
* {@code headerKeyValuePairs} parameters.
*/
- public void applyPayload(@NonNull ParcelFileDescriptor pfd, long offset, long size,
+ public void applyPayload(@NonNull AssetFileDescriptor assetFd,
@NonNull String[] headerKeyValuePairs) {
try {
- mUpdateEngine.applyPayloadFd(pfd, offset, size, headerKeyValuePairs);
+ mUpdateEngine.applyPayloadFd(assetFd.getParcelFileDescriptor(),
+ assetFd.getStartOffset(), assetFd.getLength(), headerKeyValuePairs);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 6e199ce3a73f..d8fadfb41189 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1850,10 +1850,7 @@ public class UserManager {
* Checks if the calling app is running in a managed profile.
*
* @return whether the caller is in a managed profile.
- * @hide
*/
- @SystemApi
- @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
public boolean isManagedProfile() {
// No need for synchronization. Once it becomes non-null, it'll be non-null forever.
// Worst case we might end up calling the AIDL method multiple times but that's fine.
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 3ea64f13fe6b..f0a11748fbd6 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -876,7 +876,14 @@ public class StorageManager {
*/
public @NonNull UUID getUuidForPath(@NonNull File path) throws IOException {
Preconditions.checkNotNull(path);
- final String pathString = path.getCanonicalPath();
+ String pathString = path.getCanonicalPath();
+ if (path.getPath().startsWith("/sdcard")) {
+ // On FUSE enabled devices, realpath(2) /sdcard is /mnt/user/<userid>/emulated/<userid>
+ // as opposed to /storage/emulated/<userid>.
+ // And vol.path below expects to match with a path starting with /storage
+ pathString = pathString.replaceFirst("^/mnt/user/[0-9]+/", "/storage/");
+ }
+
if (FileUtils.contains(Environment.getDataDirectory().getAbsolutePath(), pathString)) {
return UUID_DEFAULT;
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 9ee4cc30949d..a31c3d1749c6 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -389,6 +389,21 @@ public final class Settings {
"android.settings.MANAGE_UNKNOWN_APP_SOURCES";
/**
+ * Activity Action: Show settings to allow configuration of cross-profile access for apps
+ *
+ * Input: Optionally, the Intent's data URI can specify the application package name to
+ * directly invoke the management GUI specific to the package name. For example
+ * "package:com.my.app".
+ * <p>
+ * Output: Nothing.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_MANAGE_CROSS_PROFILE_ACCESS =
+ "android.settings.MANAGE_CROSS_PROFILE_ACCESS";
+
+ /**
* Activity Action: Show the "Open by Default" page in a particular application's details page.
* <p>
* In some cases, a matching Activity may not exist, so ensure you safeguard against this.
@@ -14404,6 +14419,42 @@ public final class Settings {
};
/**
+ * Activity Action: Show screen for controlling which apps have access to manage external
+ * storage.
+ * <p>
+ * In some cases, a matching Activity may not exist, so ensure you safeguard against this.
+ * <p>
+ * If you want to control a specific app's access to manage external storage, use
+ * {@link #ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION} instead.
+ * <p>
+ * Output: Nothing.
+ * @see #ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION =
+ "android.settings.MANAGE_ALL_FILES_ACCESS_PERMISSION";
+
+ /**
+ * Activity Action: Show screen for controlling if the app specified in the data URI of the
+ * intent can manage external storage.
+ * <p>
+ * Launching the corresponding activity requires the permission
+ * {@link Manifest.permission#MANAGE_EXTERNAL_STORAGE}.
+ * <p>
+ * In some cases, a matching Activity may not exist, so ensure you safeguard against this.
+ * <p>
+ * Input: The Intent's data URI MUST specify the application package name whose ability of
+ * managing external storage you want to control.
+ * For example "package:com.my.app".
+ * <p>
+ * Output: Nothing.
+ * @see #ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION =
+ "android.settings.MANAGE_APP_ALL_FILES_ACCESS_PERMISSION";
+
+ /**
* Performs a strict and comprehensive check of whether a calling package is allowed to
* write/modify system settings, as the condition differs for pre-M, M+, and
* privileged/preinstalled apps. If the provided uid does not match the
@@ -14429,8 +14480,9 @@ public final class Settings {
* current time.
* @hide
*/
- public static boolean checkAndNoteWriteSettingsOperation(Context context, int uid,
- String callingPackage, boolean throwException) {
+ @SystemApi
+ public static boolean checkAndNoteWriteSettingsOperation(@NonNull Context context, int uid,
+ @NonNull String callingPackage, boolean throwException) {
return isCallingPackageAllowedToPerformAppOpsProtectedOperation(context, uid,
callingPackage, throwException, AppOpsManager.OP_WRITE_SETTINGS,
PM_WRITE_SETTINGS, true);
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index 5977bafd7cf1..4ead3fc67260 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -49,6 +49,9 @@ oneway interface INotificationListener
void onNotificationEnqueuedWithChannel(in IStatusBarNotificationHolder notificationHolder, in NotificationChannel channel);
void onNotificationSnoozedUntilContext(in IStatusBarNotificationHolder notificationHolder, String snoozeCriterionId);
void onNotificationsSeen(in List<String> keys);
+ void onPanelRevealed(int items);
+ void onPanelHidden();
+ void onNotificationVisibilityChanged(String key, boolean isVisible);
void onNotificationExpansionChanged(String key, boolean userAction, boolean expanded);
void onNotificationDirectReply(String key);
void onSuggestedReplySent(String key, in CharSequence reply, int source);
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index da4025419656..e976e18602c1 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -182,6 +182,32 @@ public abstract class NotificationAssistantService extends NotificationListenerS
}
/**
+ * Implement this to know when the notification panel is revealed
+ *
+ * @param items Number of items on the panel at time of opening
+ */
+ public void onPanelRevealed(int items) {
+
+ }
+
+ /**
+ * Implement this to know when the notification panel is hidden
+ */
+ public void onPanelHidden() {
+
+ }
+
+ /**
+ * Implement this to know when a notification becomes visible or hidden from the user.
+ *
+ * @param key the notification key
+ * @param isVisible whether the notification is visible.
+ */
+ public void onNotificationVisibilityChanged(@NonNull String key, boolean isVisible) {
+
+ }
+
+ /**
* Implement this to know when a notification change (expanded / collapsed) is visible to user.
*
* @param key the notification key
@@ -337,6 +363,30 @@ public abstract class NotificationAssistantService extends NotificationListenerS
}
@Override
+ public void onPanelRevealed(int items) {
+ SomeArgs args = SomeArgs.obtain();
+ args.argi1 = items;
+ mHandler.obtainMessage(MyHandler.MSG_ON_PANEL_REVEALED,
+ args).sendToTarget();
+ }
+
+ @Override
+ public void onPanelHidden() {
+ SomeArgs args = SomeArgs.obtain();
+ mHandler.obtainMessage(MyHandler.MSG_ON_PANEL_HIDDEN,
+ args).sendToTarget();
+ }
+
+ @Override
+ public void onNotificationVisibilityChanged(String key, boolean isVisible) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = key;
+ args.argi1 = isVisible ? 1 : 0;
+ mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_VISIBILITY_CHANGED,
+ args).sendToTarget();
+ }
+
+ @Override
public void onNotificationExpansionChanged(String key, boolean isUserAction,
boolean isExpanded) {
SomeArgs args = SomeArgs.obtain();
@@ -394,6 +444,9 @@ public abstract class NotificationAssistantService extends NotificationListenerS
public static final int MSG_ON_SUGGESTED_REPLY_SENT = 6;
public static final int MSG_ON_ACTION_INVOKED = 7;
public static final int MSG_ON_ALLOWED_ADJUSTMENTS_CHANGED = 8;
+ public static final int MSG_ON_PANEL_REVEALED = 9;
+ public static final int MSG_ON_PANEL_HIDDEN = 10;
+ public static final int MSG_ON_NOTIFICATION_VISIBILITY_CHANGED = 11;
public MyHandler(Looper looper) {
super(looper, null, false);
@@ -480,6 +533,25 @@ public abstract class NotificationAssistantService extends NotificationListenerS
onAllowedAdjustmentsChanged();
break;
}
+ case MSG_ON_PANEL_REVEALED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ int items = args.argi1;
+ args.recycle();
+ onPanelRevealed(items);
+ break;
+ }
+ case MSG_ON_PANEL_HIDDEN: {
+ onPanelHidden();
+ break;
+ }
+ case MSG_ON_NOTIFICATION_VISIBILITY_CHANGED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ String key = (String) args.arg1;
+ boolean isVisible = args.argi1 == 1;
+ args.recycle();
+ onNotificationVisibilityChanged(key, isVisible);
+ break;
+ }
}
}
}
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 80d054b700f8..fd04f499a432 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1383,6 +1383,22 @@ public abstract class NotificationListenerService extends Service {
}
@Override
+ public void onPanelRevealed(int items) throws RemoteException {
+ // no-op in the listener
+ }
+
+ @Override
+ public void onPanelHidden() throws RemoteException {
+ // no-op in the listener
+ }
+
+ @Override
+ public void onNotificationVisibilityChanged(
+ String key, boolean isVisible) {
+ // no-op in the listener
+ }
+
+ @Override
public void onNotificationSnoozedUntilContext(
IStatusBarNotificationHolder notificationHolder, String snoozeCriterionId)
throws RemoteException {
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 67925bfea2c2..d7c6d0f265c6 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -168,6 +168,22 @@ public class AlwaysOnHotwordDetector {
public static final int RECOGNITION_MODE_USER_IDENTIFICATION
= SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, prefix = { "MODEL_PARAM_" }, value = {
+ MODEL_PARAM_THRESHOLD_FACTOR,
+ })
+ public @interface ModelParams {}
+
+ /**
+ * Controls the sensitivity threshold adjustment factor for a given model.
+ * Negative value corresponds to less sensitive model (high threshold) and
+ * a positive value corresponds to a more sensitive model (low threshold).
+ * Default value is 0.
+ */
+ public static final int MODEL_PARAM_THRESHOLD_FACTOR =
+ android.hardware.soundtrigger.ModelParams.THRESHOLD_FACTOR;
+
static final String TAG = "AlwaysOnHotwordDetector";
static final boolean DBG = false;
@@ -198,6 +214,53 @@ public class AlwaysOnHotwordDetector {
private int mAvailability = STATE_NOT_READY;
/**
+ * A ModelParamRange is a representation of supported parameter range for a
+ * given loaded model.
+ */
+ public static final class ModelParamRange {
+ private final SoundTrigger.ModelParamRange mModelParamRange;
+
+ /** @hide */
+ ModelParamRange(SoundTrigger.ModelParamRange modelParamRange) {
+ mModelParamRange = modelParamRange;
+ }
+
+ /**
+ * The inclusive start of supported range.
+ *
+ * @return start of range
+ */
+ public int start() {
+ return mModelParamRange.start;
+ }
+
+ /**
+ * The inclusive end of supported range.
+ *
+ * @return end of range
+ */
+ public int end() {
+ return mModelParamRange.end;
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ return mModelParamRange.toString();
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ return mModelParamRange.equals(obj);
+ }
+
+ @Override
+ public int hashCode() {
+ return mModelParamRange.hashCode();
+ }
+ }
+
+ /**
* Additional payload for {@link Callback#onDetected}.
*/
public static class EventPayload {
@@ -445,6 +508,83 @@ public class AlwaysOnHotwordDetector {
}
/**
+ * Set a model specific {@link ModelParams} with the given value. This
+ * parameter will keep its value for the duration the model is loaded regardless of starting and
+ * stopping recognition. Once the model is unloaded, the value will be lost.
+ * {@link AlwaysOnHotwordDetector#queryParameter} should be checked first before calling this
+ * method.
+ *
+ * @param modelParam {@link ModelParams}
+ * @param value Value to set
+ * @return - {@link SoundTrigger#STATUS_OK} in case of success
+ * - {@link SoundTrigger#STATUS_NO_INIT} if the native service cannot be reached
+ * - {@link SoundTrigger#STATUS_BAD_VALUE} invalid input parameter
+ * - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence or
+ * if API is not supported by HAL
+ */
+ public int setParameter(@ModelParams int modelParam, int value) {
+ if (DBG) {
+ Slog.d(TAG, "setParameter(" + modelParam + ", " + value + ")");
+ }
+
+ synchronized (mLock) {
+ if (mAvailability == STATE_INVALID) {
+ throw new IllegalStateException("setParameter called on an invalid detector");
+ }
+
+ return setParameterLocked(modelParam, value);
+ }
+ }
+
+ /**
+ * Get a model specific {@link ModelParams}. This parameter will keep its value
+ * for the duration the model is loaded regardless of starting and stopping recognition.
+ * Once the model is unloaded, the value will be lost. If the value is not set, a default
+ * value is returned. See {@link ModelParams} for parameter default values.
+ * {@link AlwaysOnHotwordDetector#queryParameter} should be checked first before
+ * calling this method.
+ *
+ * @param modelParam {@link ModelParams}
+ * @return value of parameter
+ */
+ public int getParameter(@ModelParams int modelParam) {
+ if (DBG) {
+ Slog.d(TAG, "getParameter(" + modelParam + ")");
+ }
+
+ synchronized (mLock) {
+ if (mAvailability == STATE_INVALID) {
+ throw new IllegalStateException("getParameter called on an invalid detector");
+ }
+
+ return getParameterLocked(modelParam);
+ }
+ }
+
+ /**
+ * Determine if parameter control is supported for the given model handle.
+ * This method should be checked prior to calling {@link AlwaysOnHotwordDetector#setParameter}
+ * or {@link AlwaysOnHotwordDetector#getParameter}.
+ *
+ * @param modelParam {@link ModelParams}
+ * @return supported range of parameter, null if not supported
+ */
+ @Nullable
+ public ModelParamRange queryParameter(@ModelParams int modelParam) {
+ if (DBG) {
+ Slog.d(TAG, "queryParameter(" + modelParam + ")");
+ }
+
+ synchronized (mLock) {
+ if (mAvailability == STATE_INVALID) {
+ throw new IllegalStateException("queryParameter called on an invalid detector");
+ }
+
+ return queryParameterLocked(modelParam);
+ }
+ }
+
+ /**
* Creates an intent to start the enrollment for the associated keyphrase.
* This intent must be invoked using {@link Context#startForegroundService(Intent)}.
* Starting re-enrollment is only valid if the keyphrase is un-enrolled,
@@ -601,6 +741,47 @@ public class AlwaysOnHotwordDetector {
return code;
}
+ private int setParameterLocked(@ModelParams int modelParam, int value) {
+ try {
+ int code = mModelManagementService.setParameter(mVoiceInteractionService,
+ mKeyphraseMetadata.id, modelParam, value);
+
+ if (code != STATUS_OK) {
+ Slog.w(TAG, "setParameter failed with error code " + code);
+ }
+
+ return code;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private int getParameterLocked(@ModelParams int modelParam) {
+ try {
+ return mModelManagementService.getParameter(mVoiceInteractionService,
+ mKeyphraseMetadata.id, modelParam);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Nullable
+ private ModelParamRange queryParameterLocked(@ModelParams int modelParam) {
+ try {
+ SoundTrigger.ModelParamRange modelParamRange =
+ mModelManagementService.queryParameter(mVoiceInteractionService,
+ mKeyphraseMetadata.id, modelParam);
+
+ if (modelParamRange == null) {
+ return null;
+ }
+
+ return new ModelParamRange(modelParamRange);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
private void notifyStateChangedLocked() {
Message message = Message.obtain(mHandler, MSG_AVAILABILITY_CHANGED);
message.arg1 = mAvailability;
diff --git a/core/java/android/text/SpannableStringInternal.java b/core/java/android/text/SpannableStringInternal.java
index b85ef76bd2d5..4c9328a1c372 100644
--- a/core/java/android/text/SpannableStringInternal.java
+++ b/core/java/android/text/SpannableStringInternal.java
@@ -40,9 +40,10 @@ import java.lang.reflect.Array;
if (source instanceof Spanned) {
if (source instanceof SpannableStringInternal) {
- copySpans((SpannableStringInternal) source, start, end, ignoreNoCopySpan);
+ copySpansFromInternal(
+ (SpannableStringInternal) source, start, end, ignoreNoCopySpan);
} else {
- copySpans((Spanned) source, start, end, ignoreNoCopySpan);
+ copySpansFromSpanned((Spanned) source, start, end, ignoreNoCopySpan);
}
}
}
@@ -65,7 +66,7 @@ import java.lang.reflect.Array;
* @param end End index in the source object.
* @param ignoreNoCopySpan whether to copy NoCopySpans in the {@code source}
*/
- private void copySpans(Spanned src, int start, int end, boolean ignoreNoCopySpan) {
+ private void copySpansFromSpanned(Spanned src, int start, int end, boolean ignoreNoCopySpan) {
Object[] spans = src.getSpans(start, end, Object.class);
for (int i = 0; i < spans.length; i++) {
@@ -94,7 +95,7 @@ import java.lang.reflect.Array;
* @param end End index in the source object.
* @param ignoreNoCopySpan copy NoCopySpan for backward compatible reasons.
*/
- private void copySpans(SpannableStringInternal src, int start, int end,
+ private void copySpansFromInternal(SpannableStringInternal src, int start, int end,
boolean ignoreNoCopySpan) {
int count = 0;
final int[] srcData = src.mSpanData;
@@ -555,12 +556,12 @@ import java.lang.reflect.Array;
*/
@UnsupportedAppUsage
private void copySpans(Spanned src, int start, int end) {
- copySpans(src, start, end, false);
+ copySpansFromSpanned(src, start, end, false);
}
@UnsupportedAppUsage
private void copySpans(SpannableStringInternal src, int start, int end) {
- copySpans(src, start, end, false);
+ copySpansFromInternal(src, start, end, false);
}
diff --git a/core/java/android/view/ITaskOrganizer.aidl b/core/java/android/view/ITaskOrganizer.aidl
new file mode 100644
index 000000000000..e92aafed6f22
--- /dev/null
+++ b/core/java/android/view/ITaskOrganizer.aidl
@@ -0,0 +1,38 @@
+/* //device/java/android/android/view/ITaskOrganizer.aidl
+**
+** 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.view;
+
+import android.view.IWindowContainer;
+import android.view.SurfaceControl;
+import android.app.ActivityManager;
+
+/**
+ * Interface for ActivityTaskManager/WindowManager to delegate control of tasks.
+ * {@hide}
+ */
+oneway interface ITaskOrganizer {
+ void taskAppeared(in IWindowContainer container,
+ in ActivityManager.RunningTaskInfo taskInfo);
+ void taskVanished(in IWindowContainer container);
+
+ /**
+ * Called upon completion of
+ * ActivityTaskManagerService#applyTaskOrganizerTransaction
+ */
+ void transactionReady(int id, in SurfaceControl.Transaction t);
+} \ No newline at end of file
diff --git a/core/java/android/view/InsetsAnimationControlCallbacks.java b/core/java/android/view/InsetsAnimationControlCallbacks.java
index 6fdadc60afea..27edb0b69bdd 100644
--- a/core/java/android/view/InsetsAnimationControlCallbacks.java
+++ b/core/java/android/view/InsetsAnimationControlCallbacks.java
@@ -16,17 +16,28 @@
package android.view;
+import android.view.InsetsController.LayoutInsetsDuringAnimation;
+import android.view.WindowInsetsAnimationCallback.AnimationBounds;
+import android.view.WindowInsetsAnimationCallback.InsetsAnimation;
+
/**
* Provide an interface to let InsetsAnimationControlImpl call back into its owner.
* @hide
*/
public interface InsetsAnimationControlCallbacks {
+
/**
- * Dispatch the animation started event to all listeners.
- * @param animation
+ * Executes the necessary code to start the animation in the correct order, including:
+ * <ul>
+ * <li>Dispatch {@link WindowInsetsAnimationCallback#onPrepare}</li>
+ * <li>Update insets state and run layout according to {@code layoutDuringAnimation}</li>
+ * <li>Dispatch {@link WindowInsetsAnimationCallback#onStart}</li>
+ * <li>Dispatch {@link WindowInsetsAnimationControlListener#onReady}</li>
+ * </ul>
*/
- void dispatchAnimationStarted(WindowInsetsAnimationCallback.InsetsAnimation animation,
- WindowInsetsAnimationCallback.AnimationBounds bounds);
+ void startAnimation(InsetsAnimationControlImpl controller,
+ WindowInsetsAnimationControlListener listener, int types, InsetsAnimation animation,
+ AnimationBounds bounds, @LayoutInsetsDuringAnimation int layoutDuringAnimation);
/**
* Schedule the apply by posting the animation callback.
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index a3245b9916ec..6589e75c7bc2 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -16,6 +16,8 @@
package android.view;
+import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_HIDDEN;
+import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
import static android.view.InsetsState.ISIDE_BOTTOM;
import static android.view.InsetsState.ISIDE_FLOATING;
import static android.view.InsetsState.ISIDE_LEFT;
@@ -30,6 +32,7 @@ import android.util.ArraySet;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.SparseSetArray;
+import android.view.InsetsController.LayoutInsetsDuringAnimation;
import android.view.InsetsState.InternalInsetsSide;
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import android.view.WindowInsets.Type.InsetsType;
@@ -80,7 +83,8 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
public InsetsAnimationControlImpl(SparseArray<InsetsSourceControl> controls, Rect frame,
InsetsState state, WindowInsetsAnimationControlListener listener,
@InsetsType int types,
- InsetsAnimationControlCallbacks controller, long durationMs, boolean fade) {
+ InsetsAnimationControlCallbacks controller, long durationMs, boolean fade,
+ @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) {
mControls = controls;
mListener = listener;
mTypes = types;
@@ -95,14 +99,11 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
mFrame = new Rect(frame);
buildTypeSourcesMap(mTypeSideMap, mSideSourceMap, mControls);
- // TODO: Check for controllability first and wait for IME if needed.
- listener.onReady(this, types);
-
mAnimation = new WindowInsetsAnimationCallback.InsetsAnimation(mTypes,
InsetsController.INTERPOLATOR, durationMs);
mAnimation.setAlpha(getCurrentAlpha());
- mController.dispatchAnimationStarted(mAnimation,
- new AnimationBounds(mHiddenInsets, mShownInsets));
+ mController.startAnimation(this, listener, types, mAnimation,
+ new AnimationBounds(mHiddenInsets, mShownInsets), layoutInsetsDuringAnimation);
}
@Override
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 2a7a4e3a922c..775490c757d4 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -37,16 +37,20 @@ import android.util.SparseArray;
import android.view.InsetsSourceConsumer.ShowResult;
import android.view.InsetsState.InternalInsetsType;
import android.view.SurfaceControl.Transaction;
+import android.view.ViewTreeObserver.OnPreDrawListener;
import android.view.WindowInsets.Type;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsAnimationCallback.AnimationBounds;
import android.view.WindowInsetsAnimationCallback.InsetsAnimation;
+import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
/**
@@ -67,6 +71,37 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
private @interface AnimationDirection{}
/**
+ * Layout mode during insets animation: The views should be laid out as if the changing inset
+ * types are fully shown. Before starting the animation, {@link View#onApplyWindowInsets} will
+ * be called as if the changing insets types are shown, which will result in the views being
+ * laid out as if the insets are fully shown.
+ */
+ static final int LAYOUT_INSETS_DURING_ANIMATION_SHOWN = 0;
+
+ /**
+ * Layout mode during insets animation: The views should be laid out as if the changing inset
+ * types are fully hidden. Before starting the animation, {@link View#onApplyWindowInsets} will
+ * be called as if the changing insets types are hidden, which will result in the views being
+ * laid out as if the insets are fully hidden.
+ */
+ static final int LAYOUT_INSETS_DURING_ANIMATION_HIDDEN = 1;
+
+ /**
+ * Determines the behavior of how the views should be laid out during an insets animation that
+ * is controlled by the application by calling {@link #controlWindowInsetsAnimation}.
+ * <p>
+ * When the animation is system-initiated, the layout mode is always chosen such that the
+ * pre-animation layout will represent the opposite of the starting state, i.e. when insets
+ * are appearing, {@link #LAYOUT_INSETS_DURING_ANIMATION_SHOWN} will be used. When insets
+ * are disappearing, {@link #LAYOUT_INSETS_DURING_ANIMATION_HIDDEN} will be used.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {LAYOUT_INSETS_DURING_ANIMATION_SHOWN,
+ LAYOUT_INSETS_DURING_ANIMATION_HIDDEN})
+ @interface LayoutInsetsDuringAnimation {
+ }
+
+ /**
* Translation animation evaluator.
*/
private static TypeEvaluator<Insets> sEvaluator = (fraction, startValue, endValue) -> Insets.of(
@@ -109,11 +144,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
@Override
public void onReady(WindowInsetsAnimationController controller, int types) {
mController = controller;
- if (mShow) {
- showDirectly(types);
- } else {
- hideDirectly(types);
- }
+
mAnimationDirection = mShow ? DIRECTION_SHOW : DIRECTION_HIDE;
mAnimator = ObjectAnimator.ofObject(
controller,
@@ -131,7 +162,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
onAnimationFinish();
}
});
+ mStartingAnimation = true;
mAnimator.start();
+ mStartingAnimation = false;
}
@Override
@@ -185,6 +218,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
private int mPendingTypesToShow;
private int mLastLegacySoftInputMode;
+ private boolean mStartingAnimation;
private SyncRtSurfaceTransactionApplier mApplier;
@@ -266,6 +300,14 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
}
/**
+ * @see InsetsState#calculateVisibleInsets(Rect, Rect, int)
+ */
+ public Rect calculateVisibleInsets(Rect legacyVisibleInsets,
+ @SoftInputModeFlags int softInputMode) {
+ return mState.calculateVisibleInsets(mFrame, legacyVisibleInsets, softInputMode);
+ }
+
+ /**
* Called when the server has dispatched us a new set of inset controls.
*/
public void onControlsChanged(InsetsSourceControl[] activeControls) {
@@ -312,7 +354,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
// Only one animator (with multiple InsetsType) can run at a time.
// previous one should be cancelled for simplicity.
cancelExistingAnimation();
- } else if (consumer.isVisible()
+ } else if (consumer.isRequestedVisible()
&& (mAnimationDirection == DIRECTION_NONE
|| mAnimationDirection == DIRECTION_HIDE)) {
// no-op: already shown or animating in (because window visibility is
@@ -338,7 +380,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
if (mAnimationDirection == DIRECTION_SHOW) {
cancelExistingAnimation();
- } else if (!consumer.isVisible()
+ } else if (!consumer.isRequestedVisible()
&& (mAnimationDirection == DIRECTION_NONE
|| mAnimationDirection == DIRECTION_HIDE)) {
// no-op: already hidden or animating out.
@@ -363,12 +405,14 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
listener.onCancelled();
return;
}
- controlAnimationUnchecked(types, listener, mFrame, fromIme, durationMs, false /* fade */);
+ controlAnimationUnchecked(types, listener, mFrame, fromIme, durationMs, false /* fade */,
+ getLayoutInsetsDuringAnimationMode(types));
}
private void controlAnimationUnchecked(@InsetsType int types,
WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme,
- long durationMs, boolean fade) {
+ long durationMs, boolean fade,
+ @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) {
if (types == 0) {
// nothing to animate.
return;
@@ -398,7 +442,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
}
final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(controls,
- frame, mState, listener, typesReady, this, durationMs, fade);
+ frame, mState, listener, typesReady, this, durationMs, fade,
+ layoutInsetsDuringAnimation);
mAnimationControls.add(controller);
}
@@ -412,7 +457,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
boolean isReady = true;
for (int i = internalTypes.size() - 1; i >= 0; i--) {
InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
- boolean setVisible = !consumer.isVisible();
+ boolean setVisible = !consumer.isRequestedVisible();
if (setVisible) {
// Show request
switch(consumer.requestShow(fromIme)) {
@@ -454,6 +499,29 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
return typesReady;
}
+ private @LayoutInsetsDuringAnimation int getLayoutInsetsDuringAnimationMode(
+ @InsetsType int types) {
+
+ final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
+
+ // Generally, we want to layout the opposite of the current state. This is to make animation
+ // callbacks easy to use: The can capture the layout values and then treat that as end-state
+ // during the animation.
+ //
+ // However, if controlling multiple sources, we want to treat it as shown if any of the
+ // types is currently hidden.
+ for (int i = internalTypes.size() - 1; i >= 0; i--) {
+ InsetsSourceConsumer consumer = mSourceConsumers.get(internalTypes.valueAt(i));
+ if (consumer == null) {
+ continue;
+ }
+ if (!consumer.isRequestedVisible()) {
+ return LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
+ }
+ }
+ return LAYOUT_INSETS_DURING_ANIMATION_HIDDEN;
+ }
+
private void cancelExistingControllers(@InsetsType int types) {
for (int i = mAnimationControls.size() - 1; i >= 0; i--) {
InsetsAnimationControlImpl control = mAnimationControls.get(i);
@@ -597,7 +665,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
// and hidden state insets are correct.
controlAnimationUnchecked(
types, listener, mState.getDisplayFrame(), fromIme, listener.getDurationMs(),
- true /* fade */);
+ true /* fade */, show
+ ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN
+ : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN);
}
private void hideDirectly(@InsetsType int types) {
@@ -629,18 +699,40 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
@VisibleForTesting
@Override
- public void dispatchAnimationStarted(InsetsAnimation animation, AnimationBounds bounds) {
- mViewRoot.mView.dispatchWindowInsetsAnimationStarted(animation, bounds);
+ public void startAnimation(InsetsAnimationControlImpl controller,
+ WindowInsetsAnimationControlListener listener, int types, InsetsAnimation animation,
+ AnimationBounds bounds, int layoutDuringAnimation) {
+ if (layoutDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN) {
+ showDirectly(types);
+ } else {
+ hideDirectly(types);
+ }
+ mViewRoot.mView.dispatchWindowInsetsAnimationPrepare(animation);
+ mViewRoot.mView.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ mViewRoot.mView.getViewTreeObserver().removeOnPreDrawListener(this);
+ mViewRoot.mView.dispatchWindowInsetsAnimationStart(animation, bounds);
+ listener.onReady(controller, types);
+ return true;
+ }
+ });
+ mViewRoot.mView.invalidate();
}
@VisibleForTesting
public void dispatchAnimationFinished(InsetsAnimation animation) {
- mViewRoot.mView.dispatchWindowInsetsAnimationFinished(animation);
+ mViewRoot.mView.dispatchWindowInsetsAnimationFinish(animation);
}
@VisibleForTesting
@Override
public void scheduleApplyChangeInsets() {
+ if (mStartingAnimation) {
+ mAnimCallback.run();
+ mAnimCallbackScheduled = false;
+ return;
+ }
if (!mAnimCallbackScheduled) {
mViewRoot.mChoreographer.postCallback(Choreographer.CALLBACK_INSETS_ANIMATION,
mAnimCallback, null /* token*/);
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index 324d562bfcfb..67ccfd6707f4 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.Nullable;
import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Parcel;
@@ -23,6 +24,7 @@ import android.os.Parcelable;
import android.view.InsetsState.InternalInsetsType;
import java.io.PrintWriter;
+import java.util.Objects;
/**
* Represents the state of a single window generating insets for clients.
@@ -34,6 +36,7 @@ public class InsetsSource implements Parcelable {
/** Frame of the source in screen coordinate space */
private final Rect mFrame;
+ private @Nullable Rect mVisibleFrame;
private boolean mVisible;
private final Rect mTmpFrame = new Rect();
@@ -54,6 +57,10 @@ public class InsetsSource implements Parcelable {
mFrame.set(frame);
}
+ public void setVisibleFrame(@Nullable Rect visibleFrame) {
+ mVisibleFrame = visibleFrame != null ? new Rect(visibleFrame) : visibleFrame;
+ }
+
public void setVisible(boolean visible) {
mVisible = visible;
}
@@ -66,6 +73,10 @@ public class InsetsSource implements Parcelable {
return mFrame;
}
+ public @Nullable Rect getVisibleFrame() {
+ return mVisibleFrame;
+ }
+
public boolean isVisible() {
return mVisible;
}
@@ -79,10 +90,22 @@ public class InsetsSource implements Parcelable {
* source.
*/
public Insets calculateInsets(Rect relativeFrame, boolean ignoreVisibility) {
+ return calculateInsets(relativeFrame, mFrame, ignoreVisibility);
+ }
+
+ /**
+ * Like {@link #calculateInsets(Rect, boolean)}, but will return visible insets.
+ */
+ public Insets calculateVisibleInsets(Rect relativeFrame) {
+ return calculateInsets(relativeFrame, mVisibleFrame != null ? mVisibleFrame : mFrame,
+ false /* ignoreVisibility */);
+ }
+
+ private Insets calculateInsets(Rect relativeFrame, Rect frame, boolean ignoreVisibility) {
if (!ignoreVisibility && !mVisible) {
return Insets.NONE;
}
- if (!mTmpFrame.setIntersect(mFrame, relativeFrame)) {
+ if (!mTmpFrame.setIntersect(frame, relativeFrame)) {
return Insets.NONE;
}
@@ -110,6 +133,9 @@ public class InsetsSource implements Parcelable {
pw.print(prefix);
pw.print("InsetsSource type="); pw.print(InsetsState.typeToString(mType));
pw.print(" frame="); pw.print(mFrame.toShortString());
+ if (mVisibleFrame != null) {
+ pw.print(" visibleFrmae="); pw.print(mVisibleFrame.toShortString());
+ }
pw.print(" visible="); pw.print(mVisible);
pw.println();
}
@@ -123,6 +149,7 @@ public class InsetsSource implements Parcelable {
if (mType != that.mType) return false;
if (mVisible != that.mVisible) return false;
+ if (!Objects.equals(mVisibleFrame, that.mVisibleFrame)) return false;
return mFrame.equals(that.mFrame);
}
@@ -137,6 +164,7 @@ public class InsetsSource implements Parcelable {
public InsetsSource(Parcel in) {
mType = in.readInt();
mFrame = in.readParcelable(null /* loader */);
+ mVisibleFrame = in.readParcelable(null /* loader */);
mVisible = in.readBoolean();
}
@@ -149,6 +177,7 @@ public class InsetsSource implements Parcelable {
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mType);
dest.writeParcelable(mFrame, 0 /* flags*/);
+ dest.writeParcelable(mVisibleFrame, 0 /* flags */);
dest.writeBoolean(mVisible);
}
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index c6d9898a425c..b2a5d915c2a6 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -53,7 +53,7 @@ public class InsetsSourceConsumer {
}
protected final InsetsController mController;
- protected boolean mVisible;
+ protected boolean mRequestedVisible;
private final Supplier<Transaction> mTransactionSupplier;
private final @InternalInsetsType int mType;
private final InsetsState mState;
@@ -66,7 +66,7 @@ public class InsetsSourceConsumer {
mState = state;
mTransactionSupplier = transactionSupplier;
mController = controller;
- mVisible = InsetsState.getDefaultVisibility(type);
+ mRequestedVisible = InsetsState.getDefaultVisibility(type);
}
public void setControl(@Nullable InsetsSourceControl control) {
@@ -94,12 +94,12 @@ public class InsetsSourceConsumer {
@VisibleForTesting
public void show() {
- setVisible(true);
+ setRequestedVisible(true);
}
@VisibleForTesting
public void hide() {
- setVisible(false);
+ setRequestedVisible(false);
}
/**
@@ -126,16 +126,16 @@ public class InsetsSourceConsumer {
if (mSourceControl == null) {
return false;
}
- if (mState.getSource(mType).isVisible() == mVisible) {
+ if (mState.getSource(mType).isVisible() == mRequestedVisible) {
return false;
}
- mState.getSource(mType).setVisible(mVisible);
+ mState.getSource(mType).setVisible(mRequestedVisible);
return true;
}
@VisibleForTesting
- public boolean isVisible() {
- return mVisible;
+ public boolean isRequestedVisible() {
+ return mRequestedVisible;
}
/**
@@ -157,11 +157,15 @@ public class InsetsSourceConsumer {
// no-op for types that always return ShowResult#SHOW_IMMEDIATELY.
}
- private void setVisible(boolean visible) {
- if (mVisible == visible) {
+ /**
+ * Sets requested visibility from the client, regardless of whether we are able to control it at
+ * the moment.
+ */
+ private void setRequestedVisible(boolean requestedVisible) {
+ if (mRequestedVisible == requestedVisible) {
return;
}
- mVisible = visible;
+ mRequestedVisible = requestedVisible;
applyLocalVisibilityOverride();
mController.notifyVisibilityChanged();
}
@@ -173,7 +177,7 @@ public class InsetsSourceConsumer {
}
final Transaction t = mTransactionSupplier.get();
- if (mVisible) {
+ if (mRequestedVisible) {
t.show(mSourceControl.getLeash());
} else {
t.hide(mSourceControl.getLeash());
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index ae1e579da8f6..e33ca70c222e 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -19,12 +19,14 @@ package android.view;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_IME;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
+import static android.view.ViewRootImpl.sNewInsetsMode;
import static android.view.WindowInsets.Type.IME;
import static android.view.WindowInsets.Type.MANDATORY_SYSTEM_GESTURES;
import static android.view.WindowInsets.Type.SIZE;
import static android.view.WindowInsets.Type.SYSTEM_GESTURES;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.indexOf;
+import static android.view.WindowInsets.Type.isVisibleInsetsType;
import static android.view.WindowInsets.Type.systemBars;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
@@ -41,6 +43,7 @@ import android.util.SparseIntArray;
import android.view.WindowInsets.Type;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowManager.LayoutParams;
+import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -186,6 +189,32 @@ public class InsetsState implements Parcelable {
: systemBars());
}
+ public Rect calculateVisibleInsets(Rect frame, Rect legacyVisibleInsets,
+ @SoftInputModeFlags int softInputMode) {
+ if (sNewInsetsMode == NEW_INSETS_MODE_NONE) {
+ return legacyVisibleInsets;
+ }
+
+ Insets insets = Insets.NONE;
+ for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
+ InsetsSource source = mSources.get(type);
+ if (source == null) {
+ continue;
+ }
+ if (sNewInsetsMode != NEW_INSETS_MODE_FULL && type != ITYPE_IME) {
+ continue;
+ }
+
+ // Ignore everything that's not a system bar or IME.
+ int publicType = InsetsState.toPublicType(type);
+ if (!isVisibleInsetsType(publicType, softInputMode)) {
+ continue;
+ }
+ insets = Insets.max(source.calculateVisibleInsets(frame), insets);
+ }
+ return insets.toRect();
+ }
+
private void processSource(InsetsSource source, Rect relativeFrame, boolean ignoreVisibility,
Insets[] typeInsetsMap, @Nullable @InternalInsetsSide SparseIntArray typeSideMap,
@Nullable boolean[] typeVisibilityMap) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 0db80e2749c3..13d609b16541 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -11117,7 +11117,21 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
- * Dispatches {@link WindowInsetsAnimationCallback#onStarted(InsetsAnimation, AnimationBounds)}
+ * Dispatches {@link WindowInsetsAnimationCallback#onPrepare(InsetsAnimation)}
+ * when Window Insets animation is being prepared.
+ * @param animation current animation
+ *
+ * @see WindowInsetsAnimationCallback#onPrepare(InsetsAnimation)
+ */
+ public void dispatchWindowInsetsAnimationPrepare(
+ @NonNull InsetsAnimation animation) {
+ if (mListenerInfo != null && mListenerInfo.mWindowInsetsAnimationCallback != null) {
+ mListenerInfo.mWindowInsetsAnimationCallback.onPrepare(animation);
+ }
+ }
+
+ /**
+ * Dispatches {@link WindowInsetsAnimationCallback#onStart(InsetsAnimation, AnimationBounds)}
* when Window Insets animation is started.
* @param animation current animation
* @param bounds the upper and lower {@link AnimationBounds} that provides range of
@@ -11125,10 +11139,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @return the upper and lower {@link AnimationBounds}.
*/
@NonNull
- public AnimationBounds dispatchWindowInsetsAnimationStarted(
+ public AnimationBounds dispatchWindowInsetsAnimationStart(
@NonNull InsetsAnimation animation, @NonNull AnimationBounds bounds) {
if (mListenerInfo != null && mListenerInfo.mWindowInsetsAnimationCallback != null) {
- return mListenerInfo.mWindowInsetsAnimationCallback.onStarted(animation, bounds);
+ return mListenerInfo.mWindowInsetsAnimationCallback.onStart(animation, bounds);
}
return bounds;
}
@@ -11149,13 +11163,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
- * Dispatches {@link WindowInsetsAnimationCallback#onFinished(InsetsAnimation)}
+ * Dispatches {@link WindowInsetsAnimationCallback#onFinish(InsetsAnimation)}
* when Window Insets animation finishes.
* @param animation The current ongoing {@link InsetsAnimation}.
*/
- public void dispatchWindowInsetsAnimationFinished(@NonNull InsetsAnimation animation) {
+ public void dispatchWindowInsetsAnimationFinish(@NonNull InsetsAnimation animation) {
if (mListenerInfo != null && mListenerInfo.mWindowInsetsAnimationCallback != null) {
- mListenerInfo.mWindowInsetsAnimationCallback.onFinished(animation);
+ mListenerInfo.mWindowInsetsAnimationCallback.onFinish(animation);
}
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 5fb71773db8f..047d7da7536f 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -7199,13 +7199,23 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
@Override
+ public void dispatchWindowInsetsAnimationPrepare(
+ @NonNull InsetsAnimation animation) {
+ super.dispatchWindowInsetsAnimationPrepare(animation);
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ getChildAt(i).dispatchWindowInsetsAnimationPrepare(animation);
+ }
+ }
+
+ @Override
@NonNull
- public AnimationBounds dispatchWindowInsetsAnimationStarted(
+ public AnimationBounds dispatchWindowInsetsAnimationStart(
@NonNull InsetsAnimation animation, @NonNull AnimationBounds bounds) {
- super.dispatchWindowInsetsAnimationStarted(animation, bounds);
+ super.dispatchWindowInsetsAnimationStart(animation, bounds);
final int count = getChildCount();
for (int i = 0; i < count; i++) {
- getChildAt(i).dispatchWindowInsetsAnimationStarted(animation, bounds);
+ getChildAt(i).dispatchWindowInsetsAnimationStart(animation, bounds);
}
return bounds;
}
@@ -7222,11 +7232,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
@Override
- public void dispatchWindowInsetsAnimationFinished(@NonNull InsetsAnimation animation) {
- super.dispatchWindowInsetsAnimationFinished(animation);
+ public void dispatchWindowInsetsAnimationFinish(@NonNull InsetsAnimation animation) {
+ super.dispatchWindowInsetsAnimationFinish(animation);
final int count = getChildCount();
for (int i = 0; i < count; i++) {
- getChildAt(i).dispatchWindowInsetsAnimationFinished(animation);
+ getChildAt(i).dispatchWindowInsetsAnimationFinish(animation);
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index bf8dc65abe28..ca8ba4ca1b8a 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1464,6 +1464,7 @@ public final class ViewRootImpl implements ViewParent,
return;
}
mApplyInsetsRequested = true;
+ requestLayout();
// If this changes during traversal, no need to schedule another one as it will dispatch it
// during the current traversal.
@@ -2102,6 +2103,12 @@ public final class ViewRootImpl implements ViewParent,
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
+ private void updateVisibleInsets() {
+ Rect visibleInsets = mInsetsController.calculateVisibleInsets(mPendingVisibleInsets,
+ mWindowAttributes.softInputMode);
+ mAttachInfo.mVisibleInsets.set(visibleInsets);
+ }
+
InsetsController getInsetsController() {
return mInsetsController;
}
@@ -2250,7 +2257,7 @@ public final class ViewRootImpl implements ViewParent,
insetsChanged = true;
}
if (!mPendingVisibleInsets.equals(mAttachInfo.mVisibleInsets)) {
- mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
+ updateVisibleInsets();
if (DEBUG_LAYOUT) Log.v(mTag, "Visible insets changing to: "
+ mAttachInfo.mVisibleInsets);
}
@@ -2316,6 +2323,7 @@ public final class ViewRootImpl implements ViewParent,
if (mApplyInsetsRequested) {
mApplyInsetsRequested = false;
+ updateVisibleInsets();
dispatchApplyInsets(host);
if (mLayoutRequested) {
// Short-circuit catching a new layout request here, so
@@ -2500,7 +2508,7 @@ public final class ViewRootImpl implements ViewParent,
contentInsetsChanged = true;
}
if (visibleInsetsChanged) {
- mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
+ updateVisibleInsets();
if (DEBUG_LAYOUT) Log.v(mTag, "Visible insets changing to: "
+ mAttachInfo.mVisibleInsets);
}
diff --git a/core/java/android/view/WindowContainerTransaction.java b/core/java/android/view/WindowContainerTransaction.java
index 607a87047733..253794f70ef2 100644
--- a/core/java/android/view/WindowContainerTransaction.java
+++ b/core/java/android/view/WindowContainerTransaction.java
@@ -62,6 +62,18 @@ public class WindowContainerTransaction implements Parcelable {
return this;
}
+ /**
+ * Notify activies within the hiearchy of a container that they have entered picture-in-picture
+ * mode with the given bounds.
+ */
+ public WindowContainerTransaction scheduleFinishEnterPip(IWindowContainer container,
+ Rect bounds) {
+ Change chg = getOrCreateChange(container.asBinder());
+ chg.mSchedulePipCallback = true;
+ chg.mPinnedBounds = new Rect(bounds);
+ return this;
+ }
+
public Map<IBinder, Change> getChanges() {
return mChanges;
}
@@ -104,12 +116,20 @@ public class WindowContainerTransaction implements Parcelable {
private @ActivityInfo.Config int mConfigSetMask = 0;
private @WindowConfiguration.WindowConfig int mWindowSetMask = 0;
+ private boolean mSchedulePipCallback = false;
+ private Rect mPinnedBounds = null;
+
public Change() {}
protected Change(Parcel in) {
mConfiguration.readFromParcel(in);
mConfigSetMask = in.readInt();
mWindowSetMask = in.readInt();
+ mSchedulePipCallback = (in.readInt() != 0);
+ if (mSchedulePipCallback ) {
+ mPinnedBounds = new Rect();
+ mPinnedBounds.readFromParcel(in);
+ }
}
public Configuration getConfiguration() {
@@ -126,6 +146,14 @@ public class WindowContainerTransaction implements Parcelable {
return mWindowSetMask;
}
+ /**
+ * Returns the bounds to be used for scheduling the enter pip callback
+ * or null if no callback is to be scheduled.
+ */
+ public Rect getEnterPipBounds() {
+ return mPinnedBounds;
+ }
+
@Override
public String toString() {
final boolean changesBounds =
@@ -151,6 +179,11 @@ public class WindowContainerTransaction implements Parcelable {
mConfiguration.writeToParcel(dest, flags);
dest.writeInt(mConfigSetMask);
dest.writeInt(mWindowSetMask);
+
+ dest.writeInt(mSchedulePipCallback ? 1 : 0);
+ if (mSchedulePipCallback ) {
+ mPinnedBounds.writeToParcel(dest, flags);
+ }
}
@Override
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 9df131de6754..9291b5652425 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -29,6 +29,8 @@ import static android.view.WindowInsets.Type.TAPPABLE_ELEMENT;
import static android.view.WindowInsets.Type.all;
import static android.view.WindowInsets.Type.indexOf;
import static android.view.WindowInsets.Type.systemBars;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
import android.annotation.IntDef;
import android.annotation.IntRange;
@@ -40,6 +42,7 @@ import android.graphics.Insets;
import android.graphics.Rect;
import android.util.SparseArray;
import android.view.WindowInsets.Type.InsetsType;
+import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethod;
@@ -1289,6 +1292,17 @@ public final class WindowInsets {
public static @InsetsType int all() {
return 0xFFFFFFFF;
}
+
+ /**
+ * Checks whether the specified type is considered to be part of visible insets.
+ * @hide
+ */
+ public static boolean isVisibleInsetsType(int type,
+ @SoftInputModeFlags int softInputModeFlags) {
+ int softInputMode = softInputModeFlags & SOFT_INPUT_MASK_ADJUST;
+ return (type & Type.systemBars()) != 0
+ || (softInputMode != SOFT_INPUT_ADJUST_NOTHING && (type & Type.ime()) != 0);
+ }
}
/**
diff --git a/core/java/android/view/WindowInsetsAnimationCallback.java b/core/java/android/view/WindowInsetsAnimationCallback.java
index 5e71f271f1d4..e84c3e33c000 100644
--- a/core/java/android/view/WindowInsetsAnimationCallback.java
+++ b/core/java/android/view/WindowInsetsAnimationCallback.java
@@ -20,6 +20,7 @@ import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Insets;
+import android.view.WindowInsets.Type;
import android.view.WindowInsets.Type.InsetsType;
import android.view.animation.Interpolator;
@@ -30,7 +31,47 @@ import android.view.animation.Interpolator;
public interface WindowInsetsAnimationCallback {
/**
- * Called when an inset animation gets started.
+ * Called when an insets animation is about to start and before the views have been laid out in
+ * the end state of the animation. The ordering of events during an insets animation is the
+ * following:
+ * <p>
+ * <ul>
+ * <li>Application calls {@link WindowInsetsController#hideInputMethod()},
+ * {@link WindowInsetsController#showInputMethod()},
+ * {@link WindowInsetsController#controlInputMethodAnimation(long, WindowInsetsAnimationControlListener)}</li>
+ * <li>onPrepare is called on the view hierarchy listeners</li>
+ * <li>{@link View#onApplyWindowInsets} will be called with the end state of the
+ * animation</li>
+ * <li>View hierarchy gets laid out according to the changes the application has requested
+ * due to the new insets being dispatched</li>
+ * <li>{@link #onStart} is called <em>before</em> the view
+ * hierarchy gets drawn in the new laid out state</li>
+ * <li>{@link #onProgress} is called immediately after with the animation start state</li>
+ * <li>The frame gets drawn.</li>
+ * </ul>
+ * <p>
+ * This ordering allows the application to inspect the end state after the animation has
+ * finished, and then revert to the starting state of the animation in the first
+ * {@link #onProgress} callback by using post-layout view properties like {@link View#setX} and
+ * related methods.
+ * <p>
+ * Note: If the animation is application controlled by using
+ * {@link WindowInsetsController#controlInputMethodAnimation}, the end state of the animation
+ * is undefined as the application may decide on the end state only by passing in the
+ * {@code shown} parameter when calling {@link WindowInsetsAnimationController#finish}. In this
+ * situation, the system will dispatch the insets in the opposite visibility state before the
+ * animation starts. Example: When controlling the input method with
+ * {@link WindowInsetsController#controlInputMethodAnimation} and the input method is currently
+ * showing, {@link View#onApplyWindowInsets} will receive a {@link WindowInsets} instance for
+ * which {@link WindowInsets#isVisible} will return {@code false} for {@link Type#ime}.
+ *
+ * @param animation The animation that is about to start.
+ */
+ default void onPrepare(@NonNull InsetsAnimation animation) {
+ }
+
+ /**
+ * Called when an insets animation gets started.
* <p>
* Note that, like {@link #onProgress}, dispatch of the animation start event is hierarchical:
* It will starts at the root of the view hierarchy and then traverse it and invoke the callback
@@ -45,7 +86,7 @@ public interface WindowInsetsAnimationCallback {
* subtree of the hierarchy.
*/
@NonNull
- default AnimationBounds onStarted(
+ default AnimationBounds onStart(
@NonNull InsetsAnimation animation, @NonNull AnimationBounds bounds) {
return bounds;
}
@@ -72,12 +113,12 @@ public interface WindowInsetsAnimationCallback {
WindowInsets onProgress(@NonNull WindowInsets insets);
/**
- * Called when an inset animation has finished.
+ * Called when an insets animation has finished.
*
* @param animation The animation that has finished running. This will be the same instance as
- * passed into {@link #onStarted}
+ * passed into {@link #onStart}
*/
- default void onFinished(@NonNull InsetsAnimation animation) {
+ default void onFinish(@NonNull InsetsAnimation animation) {
}
/**
@@ -253,14 +294,14 @@ public interface WindowInsetsAnimationCallback {
/**
* Insets both the lower and upper bound by the specified insets. This is to be used in
- * {@link WindowInsetsAnimationCallback#onStarted} to indicate that a part of the insets has
+ * {@link WindowInsetsAnimationCallback#onStart} to indicate that a part of the insets has
* been used to offset or clip its children, and the children shouldn't worry about that
* part anymore.
*
* @param insets The amount to inset.
* @return A copy of this instance inset in the given directions.
* @see WindowInsets#inset
- * @see WindowInsetsAnimationCallback#onStarted
+ * @see WindowInsetsAnimationCallback#onStart
*/
@NonNull
public AnimationBounds inset(@NonNull Insets insets) {
diff --git a/core/java/android/view/WindowInsetsAnimationControlListener.java b/core/java/android/view/WindowInsetsAnimationControlListener.java
index 8a226c1bbe23..f91254de33ff 100644
--- a/core/java/android/view/WindowInsetsAnimationControlListener.java
+++ b/core/java/android/view/WindowInsetsAnimationControlListener.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.Hide;
import android.annotation.NonNull;
import android.view.WindowInsets.Type.InsetsType;
import android.view.inputmethod.EditorInfo;
@@ -26,6 +27,12 @@ import android.view.inputmethod.EditorInfo;
public interface WindowInsetsAnimationControlListener {
/**
+ * @hide
+ */
+ default void onPrepare(int types) {
+ }
+
+ /**
* Called when the animation is ready to be controlled. This may be delayed when the IME needs
* to redraw because of an {@link EditorInfo} change, or when the window is starting up.
*
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
index 6de56be2f3c5..9d7f292dbdf5 100644
--- a/core/java/android/view/WindowInsetsController.java
+++ b/core/java/android/view/WindowInsetsController.java
@@ -149,7 +149,8 @@ public interface WindowInsetsController {
*
* @param types The {@link InsetsType}s the application has requested to control.
* @param durationMillis duration of animation in
- * {@link java.util.concurrent.TimeUnit#MILLISECONDS}
+ * {@link java.util.concurrent.TimeUnit#MILLISECONDS}, or -1 if the
+ * animation doesn't have a predetermined duration.
* @param listener The {@link WindowInsetsAnimationControlListener} that gets called when the
* windows are ready to be controlled, among other callbacks.
* @hide
@@ -162,7 +163,8 @@ public interface WindowInsetsController {
* modifying the position of the IME when it's causing insets.
*
* @param durationMillis duration of the animation in
- * {@link java.util.concurrent.TimeUnit#MILLISECONDS}
+ * {@link java.util.concurrent.TimeUnit#MILLISECONDS}, or -1 if the
+ * animation doesn't have a predetermined duration.
* @param listener The {@link WindowInsetsAnimationControlListener} that gets called when the
* IME are ready to be controlled, among other callbacks.
*/
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index ff31bccfa648..2e5a4b57da18 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -31,6 +31,7 @@ import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.annotation.UserIdInt;
+import android.app.RemoteAction;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
@@ -1209,6 +1210,61 @@ public final class AccessibilityManager {
}
/**
+ * Register the provided {@link RemoteAction} with the given actionId
+ *
+ * @param action The remote action to be registered with the given actionId as system action.
+ * @param actionId The id uniquely identify the system action.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
+ public void registerSystemAction(@NonNull RemoteAction action, int actionId) {
+ final IAccessibilityManager service;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return;
+ }
+ }
+ try {
+ service.registerSystemAction(action, actionId);
+
+ if (DEBUG) {
+ Log.i(LOG_TAG, "System action " + action.getTitle() + " is registered.");
+ }
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error registering system action " + action.getTitle() + " ", re);
+ }
+ }
+
+ /**
+ * Unregister a system action with the given actionId
+ *
+ * @param actionId The id uniquely identify the system action to be unregistered.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
+ public void unregisterSystemAction(int actionId) {
+ final IAccessibilityManager service;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return;
+ }
+ }
+ try {
+ service.unregisterSystemAction(actionId);
+
+ if (DEBUG) {
+ Log.i(LOG_TAG, "System action with actionId " + actionId + " is unregistered.");
+ }
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error unregistering system action with actionId " + actionId + " ", re);
+ }
+ }
+
+ /**
* Notifies that the accessibility button in the system's navigation area has been clicked
*
* @param displayId The logical display id.
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 36515b3ba094..392db574d988 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -16,6 +16,7 @@
package android.view.accessibility;
+import android.app.RemoteAction;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.IAccessibilityServiceConnection;
import android.accessibilityservice.IAccessibilityServiceClient;
@@ -82,4 +83,7 @@ interface IAccessibilityManager {
int getAccessibilityWindowId(IBinder windowToken);
long getRecommendedTimeoutMillis();
+
+ oneway void registerSystemAction(in RemoteAction action, int actionId);
+ oneway void unregisterSystemAction(int actionId);
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 67ce8d2e49b5..f3007a794344 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -93,9 +93,12 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CancellationException;
-import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
/**
@@ -429,7 +432,10 @@ public final class InputMethodManager {
* in a background thread. Later, if there is an actual startInput it will wait on
* main thread till the background thread completes.
*/
- private CompletableFuture<Void> mWindowFocusGainFuture;
+ private Future<?> mWindowFocusGainFuture;
+
+ private ExecutorService mStartInputWorker = Executors.newSingleThreadExecutor(
+ new ImeThreadFactory("StartInputWorker"));
/**
* The instance that has previously been sent to the input method.
@@ -790,6 +796,19 @@ public final class InputMethodManager {
}
}
+ private static class ImeThreadFactory implements ThreadFactory {
+ private final String mThreadName;
+
+ ImeThreadFactory(String name) {
+ mThreadName = name;
+ }
+
+ @Override
+ public Thread newThread(Runnable r) {
+ return new Thread(r, mThreadName);
+ }
+ }
+
final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() {
@Override
protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
@@ -1978,7 +1997,7 @@ public final class InputMethodManager {
if (mWindowFocusGainFuture != null) {
mWindowFocusGainFuture.cancel(false/* mayInterruptIfRunning */);
}
- mWindowFocusGainFuture = CompletableFuture.runAsync(() -> {
+ mWindowFocusGainFuture = mStartInputWorker.submit(() -> {
if (checkFocusNoStartInput(forceNewFocus1)) {
// We need to restart input on the current focus view. This
// should be done in conjunction with telling the system service
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index a0cf53437a50..20af76b0d5ca 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -5921,8 +5921,6 @@ public class Editor {
// The offsets of that last touch down event. Remembered to start selection there.
private int mMinTouchOffset, mMaxTouchOffset;
- private boolean mGestureStayedInTapRegion;
-
// Where the user first starts the drag motion.
private int mStartOffset = -1;
@@ -6029,8 +6027,7 @@ public class Editor {
eventX, eventY);
// Double tap detection
- if (mGestureStayedInTapRegion
- && mTouchState.isMultiTapInSameArea()
+ if (mTouchState.isMultiTapInSameArea()
&& (isMouse || isPositionOnText(eventX, eventY))) {
if (TextView.DEBUG_CURSOR) {
logCursor("SelectionModifierCursorController: onTouchEvent",
@@ -6043,7 +6040,6 @@ public class Editor {
}
mDiscardNextActionUp = true;
}
- mGestureStayedInTapRegion = true;
mHaventMovedEnoughToStartDrag = true;
}
break;
@@ -6059,25 +6055,8 @@ public class Editor {
break;
case MotionEvent.ACTION_MOVE:
- final ViewConfiguration viewConfig = ViewConfiguration.get(
- mTextView.getContext());
-
- if (mGestureStayedInTapRegion || mHaventMovedEnoughToStartDrag) {
- final float deltaX = eventX - mTouchState.getLastDownX();
- final float deltaY = eventY - mTouchState.getLastDownY();
- final float distanceSquared = deltaX * deltaX + deltaY * deltaY;
-
- if (mGestureStayedInTapRegion) {
- int doubleTapTouchSlop = viewConfig.getScaledDoubleTapTouchSlop();
- mGestureStayedInTapRegion =
- distanceSquared <= doubleTapTouchSlop * doubleTapTouchSlop;
- }
- if (mHaventMovedEnoughToStartDrag) {
- // We don't start dragging until the user has moved enough.
- int touchSlop = viewConfig.getScaledTouchSlop();
- mHaventMovedEnoughToStartDrag =
- distanceSquared <= touchSlop * touchSlop;
- }
+ if (mHaventMovedEnoughToStartDrag) {
+ mHaventMovedEnoughToStartDrag = !mTouchState.isMovedEnoughForDrag();
}
if (isMouse && !isDragAcceleratorActive()) {
diff --git a/core/java/android/widget/EditorTouchState.java b/core/java/android/widget/EditorTouchState.java
index d53099d44f6f..6277afe2f613 100644
--- a/core/java/android/widget/EditorTouchState.java
+++ b/core/java/android/widget/EditorTouchState.java
@@ -31,13 +31,15 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
- * Helper class used by {@link Editor} to track state for touch events.
+ * Helper class used by {@link Editor} to track state for touch events. Ideally the logic here
+ * should be replaced with {@link android.view.GestureDetector}.
*
* @hide
*/
@VisibleForTesting(visibility = PACKAGE)
public class EditorTouchState {
private float mLastDownX, mLastDownY;
+ private long mLastDownMillis;
private float mLastUpX, mLastUpY;
private long mLastUpMillis;
@@ -106,9 +108,18 @@ public class EditorTouchState {
final int action = event.getActionMasked();
if (action == MotionEvent.ACTION_DOWN) {
final boolean isMouse = event.isFromSource(InputDevice.SOURCE_MOUSE);
+
+ // We check both the time between the last up and current down event, as well as the
+ // time between the first down and up events. The latter check is necessary to handle
+ // the case when the user taps, drags/holds for some time, and then lifts up and
+ // quickly taps in the same area. This scenario should not be treated as a double-tap.
+ // This follows the behavior in GestureDetector.
final long millisSinceLastUp = event.getEventTime() - mLastUpMillis;
+ final long millisBetweenLastDownAndLastUp = mLastUpMillis - mLastDownMillis;
+
// Detect double tap and triple click.
if (millisSinceLastUp <= ViewConfiguration.getDoubleTapTimeout()
+ && millisBetweenLastDownAndLastUp <= ViewConfiguration.getDoubleTapTimeout()
&& (mMultiTapStatus == MultiTapStatus.FIRST_TAP
|| (mMultiTapStatus == MultiTapStatus.DOUBLE_TAP && isMouse))) {
if (mMultiTapStatus == MultiTapStatus.FIRST_TAP) {
@@ -133,6 +144,7 @@ public class EditorTouchState {
}
mLastDownX = event.getX();
mLastDownY = event.getY();
+ mLastDownMillis = event.getEventTime();
mMovedEnoughForDrag = false;
mIsDragCloseToVertical = false;
} else if (action == MotionEvent.ACTION_UP) {
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index d86766ebdf58..01a0e9b15463 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -565,7 +565,8 @@ public class RemoteViews implements Parcelable, Filter {
}
private static void visitIconUri(Icon icon, @NonNull Consumer<Uri> visitor) {
- if (icon != null && icon.getType() == Icon.TYPE_URI) {
+ if (icon != null && (icon.getType() == Icon.TYPE_URI
+ || icon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP)) {
visitor.accept(icon.getUri());
}
}
diff --git a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
index 0a0d05ce8fc8..de204badfd0d 100644
--- a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
+++ b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
@@ -16,6 +16,7 @@
package com.android.internal.app;
import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
+import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
import static android.view.accessibility.AccessibilityManager.ShortcutType;
import android.accessibilityservice.AccessibilityServiceInfo;
@@ -24,13 +25,17 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.AlertDialog;
+import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
+import android.os.UserHandle;
import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.ArraySet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -49,7 +54,10 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
+import java.util.StringJoiner;
/**
* Activity used to display and persist a service or feature target for the Accessibility button.
@@ -59,12 +67,46 @@ public class AccessibilityButtonChooserActivity extends Activity {
private static final String MAGNIFICATION_COMPONENT_ID =
"com.android.server.accessibility.MagnificationController";
+ private static final char SERVICES_SEPARATOR = ':';
+ private static final TextUtils.SimpleStringSplitter sStringColonSplitter =
+ new TextUtils.SimpleStringSplitter(SERVICES_SEPARATOR);
+ private static final int ACCESSIBILITY_BUTTON_USER_TYPE = convertToUserType(
+ ACCESSIBILITY_BUTTON);
+ private static final int ACCESSIBILITY_SHORTCUT_KEY_USER_TYPE = convertToUserType(
+ ACCESSIBILITY_SHORTCUT_KEY);
+
private int mShortcutType;
private List<AccessibilityButtonTarget> mTargets = new ArrayList<>();
private AlertDialog mAlertDialog;
private TargetAdapter mTargetAdapter;
/**
+ * Annotation for different user shortcut type UI type.
+ *
+ * {@code DEFAULT} for displaying default value.
+ * {@code SOFTWARE} for displaying specifying the accessibility services or features which
+ * choose accessibility button in the navigation bar as preferred shortcut.
+ * {@code HARDWARE} for displaying specifying the accessibility services or features which
+ * choose accessibility shortcut as preferred shortcut.
+ * {@code TRIPLETAP} for displaying specifying magnification to be toggled via quickly
+ * tapping screen 3 times as preferred shortcut.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ UserShortcutType.DEFAULT,
+ UserShortcutType.SOFTWARE,
+ UserShortcutType.HARDWARE,
+ UserShortcutType.TRIPLETAP,
+ })
+ /** Denotes the user shortcut type. */
+ public @interface UserShortcutType {
+ int DEFAULT = 0;
+ int SOFTWARE = 1; // 1 << 0
+ int HARDWARE = 2; // 1 << 1
+ int TRIPLETAP = 4; // 1 << 2
+ }
+
+ /**
* Annotation for different accessibilityService fragment UI type.
*
* {@code LEGACY} for displaying appearance aligned with sdk version Q accessibility service
@@ -395,19 +437,63 @@ public class AccessibilityButtonChooserActivity extends Activity {
}
private void onTargetDeleted(AdapterView<?> parent, View view, int position, long id) {
- // TODO(b/146967898): disable service when deleting the target and the target only have
- // last one shortcut item, only remove it from shortcut list otherwise.
- if ((mShortcutType == ACCESSIBILITY_BUTTON) && (mTargets.get(position).mFragmentType
- != AccessibilityServiceFragmentType.LEGACY)) {
- mTargets.remove(position);
- mTargetAdapter.notifyDataSetChanged();
+ final AccessibilityButtonTarget target = mTargets.get(position);
+ final ComponentName targetComponentName =
+ ComponentName.unflattenFromString(target.getId());
+
+ switch (target.getFragmentType()) {
+ case AccessibilityServiceFragmentType.INVISIBLE:
+ onInvisibleTargetDeleted(targetComponentName);
+ break;
+ case AccessibilityServiceFragmentType.INTUITIVE:
+ onIntuitiveTargetDeleted(targetComponentName);
+ break;
+ case AccessibilityServiceFragmentType.LEGACY:
+ case AccessibilityServiceFragmentType.BOUNCE:
+ // Do nothing
+ break;
+ default:
+ throw new IllegalStateException("Unexpected fragment type");
}
+ mTargets.remove(position);
+ mTargetAdapter.notifyDataSetChanged();
+
if (mTargets.isEmpty()) {
mAlertDialog.dismiss();
}
}
+ private void onInvisibleTargetDeleted(ComponentName componentName) {
+ if (mShortcutType == ACCESSIBILITY_BUTTON) {
+ optOutValueFromSettings(this, ACCESSIBILITY_BUTTON_USER_TYPE, componentName);
+
+ if (!hasValueInSettings(this,
+ ACCESSIBILITY_SHORTCUT_KEY_USER_TYPE, componentName)) {
+ setAccessibilityServiceState(this, componentName, /* enabled= */ false);
+ }
+ } else if (mShortcutType == ACCESSIBILITY_SHORTCUT_KEY) {
+ optOutValueFromSettings(this, ACCESSIBILITY_SHORTCUT_KEY_USER_TYPE, componentName);
+
+ if (!hasValueInSettings(this,
+ ACCESSIBILITY_BUTTON_USER_TYPE, componentName)) {
+ setAccessibilityServiceState(this, componentName, /* enabled= */ false);
+ }
+ } else {
+ throw new IllegalArgumentException("Unsupported shortcut type:" + mShortcutType);
+ }
+ }
+
+ private void onIntuitiveTargetDeleted(ComponentName componentName) {
+ if (mShortcutType == ACCESSIBILITY_BUTTON) {
+ optOutValueFromSettings(this, ACCESSIBILITY_BUTTON_USER_TYPE, componentName);
+ } else if (mShortcutType == ACCESSIBILITY_SHORTCUT_KEY) {
+ optOutValueFromSettings(this, ACCESSIBILITY_SHORTCUT_KEY_USER_TYPE, componentName);
+ } else {
+ throw new IllegalArgumentException("Unsupported shortcut type:" + mShortcutType);
+ }
+ }
+
private void onCancelButtonClicked() {
mTargetAdapter.setShortcutMenuMode(ShortcutMenuMode.LAUNCH);
mTargetAdapter.notifyDataSetChanged();
@@ -437,4 +523,166 @@ public class AccessibilityButtonChooserActivity extends Activity {
mAlertDialog.getListView().setOnItemClickListener(
isEditMenuMode ? this::onTargetDeleted : this::onTargetSelected);
}
+
+ /**
+ * @return the set of enabled accessibility services for {@param userId}. If there are no
+ * services, it returns the unmodifiable {@link Collections#emptySet()}.
+ */
+ private Set<ComponentName> getEnabledServicesFromSettings(Context context, int userId) {
+ final String enabledServicesSetting = Settings.Secure.getStringForUser(
+ context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+ userId);
+ if (TextUtils.isEmpty(enabledServicesSetting)) {
+ return Collections.emptySet();
+ }
+
+ final Set<ComponentName> enabledServices = new HashSet<>();
+ final TextUtils.StringSplitter colonSplitter =
+ new TextUtils.SimpleStringSplitter(SERVICES_SEPARATOR);
+ colonSplitter.setString(enabledServicesSetting);
+
+ for (String componentNameString : colonSplitter) {
+ final ComponentName enabledService = ComponentName.unflattenFromString(
+ componentNameString);
+ if (enabledService != null) {
+ enabledServices.add(enabledService);
+ }
+ }
+
+ return enabledServices;
+ }
+
+ /**
+ * Changes an accessibility component's state.
+ */
+ private void setAccessibilityServiceState(Context context, ComponentName componentName,
+ boolean enabled) {
+ setAccessibilityServiceState(context, componentName, enabled, UserHandle.myUserId());
+ }
+
+ /**
+ * Changes an accessibility component's state for {@param userId}.
+ */
+ private void setAccessibilityServiceState(Context context, ComponentName componentName,
+ boolean enabled, int userId) {
+ Set<ComponentName> enabledServices = getEnabledServicesFromSettings(
+ context, userId);
+
+ if (enabledServices.isEmpty()) {
+ enabledServices = new ArraySet<>(/* capacity= */ 1);
+ }
+
+ if (enabled) {
+ enabledServices.add(componentName);
+ } else {
+ enabledServices.remove(componentName);
+ }
+
+ final StringBuilder enabledServicesBuilder = new StringBuilder();
+ for (ComponentName enabledService : enabledServices) {
+ enabledServicesBuilder.append(enabledService.flattenToString());
+ enabledServicesBuilder.append(
+ SERVICES_SEPARATOR);
+ }
+
+ final int enabledServicesBuilderLength = enabledServicesBuilder.length();
+ if (enabledServicesBuilderLength > 0) {
+ enabledServicesBuilder.deleteCharAt(enabledServicesBuilderLength - 1);
+ }
+
+ Settings.Secure.putStringForUser(context.getContentResolver(),
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+ enabledServicesBuilder.toString(), userId);
+ }
+
+ /**
+ * Opts out component name into colon-separated {@code shortcutType} key's string in Settings.
+ *
+ * @param context The current context.
+ * @param shortcutType The preferred shortcut type user selected.
+ * @param componentName The component name that need to be opted out from Settings.
+ */
+ private void optOutValueFromSettings(
+ Context context, int shortcutType, ComponentName componentName) {
+ final StringJoiner joiner = new StringJoiner(String.valueOf(SERVICES_SEPARATOR));
+ final String targetsKey = convertToKey(shortcutType);
+ final String targetsValue = Settings.Secure.getString(context.getContentResolver(),
+ targetsKey);
+
+ if (TextUtils.isEmpty(targetsValue)) {
+ return;
+ }
+
+ sStringColonSplitter.setString(targetsValue);
+ while (sStringColonSplitter.hasNext()) {
+ final String name = sStringColonSplitter.next();
+ if (TextUtils.isEmpty(name) || (componentName.flattenToString()).equals(name)) {
+ continue;
+ }
+ joiner.add(name);
+ }
+
+ Settings.Secure.putString(context.getContentResolver(), targetsKey, joiner.toString());
+ }
+
+ /**
+ * Returns if component name existed in Settings.
+ *
+ * @param context The current context.
+ * @param shortcutType The preferred shortcut type user selected.
+ * @param componentName The component name that need to be checked existed in Settings.
+ * @return {@code true} if componentName existed in Settings.
+ */
+ private boolean hasValueInSettings(Context context, @UserShortcutType int shortcutType,
+ @NonNull ComponentName componentName) {
+ final String targetKey = convertToKey(shortcutType);
+ final String targetString = Settings.Secure.getString(context.getContentResolver(),
+ targetKey);
+
+ if (TextUtils.isEmpty(targetString)) {
+ return false;
+ }
+
+ sStringColonSplitter.setString(targetString);
+ while (sStringColonSplitter.hasNext()) {
+ final String name = sStringColonSplitter.next();
+ if ((componentName.flattenToString()).equals(name)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Converts {@link UserShortcutType} to key in Settings.
+ *
+ * @param type The shortcut type.
+ * @return Mapping key in Settings.
+ */
+ private String convertToKey(@UserShortcutType int type) {
+ switch (type) {
+ case UserShortcutType.SOFTWARE:
+ return Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT;
+ case UserShortcutType.HARDWARE:
+ return Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
+ case UserShortcutType.TRIPLETAP:
+ return Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED;
+ default:
+ throw new IllegalArgumentException(
+ "Unsupported user shortcut type: " + type);
+ }
+ }
+
+ private static @UserShortcutType int convertToUserType(@ShortcutType int type) {
+ switch (type) {
+ case ACCESSIBILITY_BUTTON:
+ return UserShortcutType.SOFTWARE;
+ case ACCESSIBILITY_SHORTCUT_KEY:
+ return UserShortcutType.HARDWARE;
+ default:
+ throw new IllegalArgumentException(
+ "Unsupported shortcut type:" + type);
+ }
+ }
}
diff --git a/core/java/com/android/internal/app/ISoundTriggerService.aidl b/core/java/com/android/internal/app/ISoundTriggerService.aidl
index d94294f0aa22..74bfb963c6b0 100644
--- a/core/java/com/android/internal/app/ISoundTriggerService.aidl
+++ b/core/java/com/android/internal/app/ISoundTriggerService.aidl
@@ -61,10 +61,6 @@ interface ISoundTriggerService {
int setParameter(in ParcelUuid soundModelId, in ModelParams modelParam,
int value);
- /**
- * @throws UnsupportedOperationException if hal or model do not support this API.
- * @throws IllegalArgumentException if invalid model handle or parameter is passed.
- */
int getParameter(in ParcelUuid soundModelId, in ModelParams modelParam);
@nullable SoundTrigger.ModelParamRange queryParameter(in ParcelUuid soundModelId,
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index f462f5d2571d..be2d1d60e9a2 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -26,6 +26,7 @@ import com.android.internal.app.IVoiceInteractionSessionShowCallback;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.app.IVoiceInteractionSessionListener;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
+import android.hardware.soundtrigger.ModelParams;
import android.hardware.soundtrigger.SoundTrigger;
import android.service.voice.IVoiceInteractionService;
import android.service.voice.IVoiceInteractionSession;
@@ -91,6 +92,49 @@ interface IVoiceInteractionManagerService {
*/
int stopRecognition(in IVoiceInteractionService service, int keyphraseId,
in IRecognitionStatusCallback callback);
+ /**
+ * Set a model specific ModelParams with the given value. This
+ * parameter will keep its value for the duration the model is loaded regardless of starting and
+ * stopping recognition. Once the model is unloaded, the value will be lost.
+ * queryParameter should be checked first before calling this method.
+ *
+ * @param service The current VoiceInteractionService.
+ * @param keyphraseId The unique identifier for the keyphrase.
+ * @param modelParam ModelParams
+ * @param value Value to set
+ * @return - {@link SoundTrigger#STATUS_OK} in case of success
+ * - {@link SoundTrigger#STATUS_NO_INIT} if the native service cannot be reached
+ * - {@link SoundTrigger#STATUS_BAD_VALUE} invalid input parameter
+ * - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence or
+ * if API is not supported by HAL
+ */
+ int setParameter(in IVoiceInteractionService service, int keyphraseId,
+ in ModelParams modelParam, int value);
+ /**
+ * Get a model specific ModelParams. This parameter will keep its value
+ * for the duration the model is loaded regardless of starting and stopping recognition.
+ * Once the model is unloaded, the value will be lost. If the value is not set, a default
+ * value is returned. See ModelParams for parameter default values.
+ * queryParameter should be checked first before calling this method.
+ *
+ * @param service The current VoiceInteractionService.
+ * @param keyphraseId The unique identifier for the keyphrase.
+ * @param modelParam ModelParams
+ * @return value of parameter
+ */
+ int getParameter(in IVoiceInteractionService service, int keyphraseId,
+ in ModelParams modelParam);
+ /**
+ * Determine if parameter control is supported for the given model handle.
+ * This method should be checked prior to calling setParameter or getParameter.
+ *
+ * @param service The current VoiceInteractionService.
+ * @param keyphraseId The unique identifier for the keyphrase.
+ * @param modelParam ModelParams
+ * @return supported range of parameter, null if not supported
+ */
+ @nullable SoundTrigger.ModelParamRange queryParameter(in IVoiceInteractionService service,
+ int keyphraseId, in ModelParams modelParam);
/**
* @return the component name for the currently active voice interaction service
diff --git a/core/java/com/android/internal/logging/UiEventLogger.java b/core/java/com/android/internal/logging/UiEventLogger.java
index cca97f689f4e..3a450de6c8e6 100644
--- a/core/java/com/android/internal/logging/UiEventLogger.java
+++ b/core/java/com/android/internal/logging/UiEventLogger.java
@@ -16,6 +16,9 @@
package com.android.internal.logging;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
/**
* Logging interface for UI events. Normal implementation is UiEventLoggerImpl.
* For testing, use fake implementation UiEventLoggerFake.
@@ -26,13 +29,24 @@ public interface UiEventLogger {
/** Put your Event IDs in enums that implement this interface, and document them using the
* UiEventEnum annotation.
* Event IDs must be globally unique. This will be enforced by tooling (forthcoming).
- * OEMs should use event IDs above 100000.
+ * OEMs should use event IDs above 100000 and below 1000000 (1 million).
*/
interface UiEventEnum {
int getId();
}
+
+ /**
+ * Log a simple event, with no package information. Does nothing if event.getId() <= 0.
+ * @param event an enum implementing UiEventEnum interface.
+ */
+ void log(@NonNull UiEventEnum event);
+
/**
- * Log a simple event, with no package or instance ID.
+ * Log an event with package information. Does nothing if event.getId() <= 0.
+ * Give both uid and packageName if both are known, but one may be omitted if unknown.
+ * @param event an enum implementing UiEventEnum interface.
+ * @param uid the uid of the relevant app, if known (0 otherwise).
+ * @param packageName the package name of the relevant app, if known (null otherwise).
*/
- void log(UiEventEnum eventID);
+ void log(@NonNull UiEventEnum event, int uid, @Nullable String packageName);
}
diff --git a/core/java/com/android/internal/logging/UiEventLoggerImpl.java b/core/java/com/android/internal/logging/UiEventLoggerImpl.java
index e64fba2d2a31..bdf460c710c3 100644
--- a/core/java/com/android/internal/logging/UiEventLoggerImpl.java
+++ b/core/java/com/android/internal/logging/UiEventLoggerImpl.java
@@ -24,14 +24,16 @@ import android.util.StatsLog;
* See UiEventReported atom in atoms.proto for more context.
*/
public class UiEventLoggerImpl implements UiEventLogger {
- /**
- * Log a simple event, with no package or instance ID.
- */
@Override
public void log(UiEventEnum event) {
+ log(event, 0, null);
+ }
+
+ @Override
+ public void log(UiEventEnum event, int uid, String packageName) {
final int eventID = event.getId();
if (eventID > 0) {
- StatsLog.write(StatsLog.UI_EVENT_REPORTED, eventID, 0, null);
+ StatsLog.write(StatsLog.UI_EVENT_REPORTED, eventID, uid, packageName);
}
}
}
diff --git a/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java b/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java
index 92e9bbb77bd3..6be5b81afee2 100644
--- a/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java
+++ b/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java
@@ -30,7 +30,7 @@ public class UiEventLoggerFake implements UiEventLogger {
/**
* Immutable data class used to record fake log events.
*/
- public class FakeUiEvent {
+ public static class FakeUiEvent {
public final int eventId;
public final int uid;
public final String packageName;
@@ -44,15 +44,20 @@ public class UiEventLoggerFake implements UiEventLogger {
private Queue<FakeUiEvent> mLogs = new LinkedList<FakeUiEvent>();
+ public Queue<FakeUiEvent> getLogs() {
+ return mLogs;
+ }
+
@Override
public void log(UiEventEnum event) {
+ log(event, 0, null);
+ }
+
+ @Override
+ public void log(UiEventEnum event, int uid, String packageName) {
final int eventId = event.getId();
if (eventId > 0) {
- mLogs.offer(new FakeUiEvent(eventId, 0, null));
+ mLogs.offer(new FakeUiEvent(eventId, uid, packageName));
}
}
-
- public Queue<FakeUiEvent> getLogs() {
- return mLogs;
- }
}
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
index 5bc96d8ee1d3..408a7a8e139a 100644
--- a/core/java/com/android/internal/util/Preconditions.java
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -126,7 +126,9 @@ public class Preconditions {
* @param reference an object reference
* @return the non-null reference that was validated
* @throws NullPointerException if {@code reference} is null
+ * @deprecated - use {@link java.util.Objects.requireNonNull} instead.
*/
+ @Deprecated
@UnsupportedAppUsage
public static @NonNull <T> T checkNotNull(final T reference) {
if (reference == null) {
@@ -144,7 +146,9 @@ public class Preconditions {
* be converted to a string using {@link String#valueOf(Object)}
* @return the non-null reference that was validated
* @throws NullPointerException if {@code reference} is null
+ * @deprecated - use {@link java.util.Objects.requireNonNull} instead.
*/
+ @Deprecated
@UnsupportedAppUsage
public static @NonNull <T> T checkNotNull(final T reference, final Object errorMessage) {
if (reference == null) {
@@ -154,26 +158,6 @@ public class Preconditions {
}
/**
- * Ensures that an object reference passed as a parameter to the calling
- * method is not null.
- *
- * @param reference an object reference
- * @param messageTemplate a printf-style message template to use if the check fails; will
- * be converted to a string using {@link String#format(String, Object...)}
- * @param messageArgs arguments for {@code messageTemplate}
- * @return the non-null reference that was validated
- * @throws NullPointerException if {@code reference} is null
- */
- public static @NonNull <T> T checkNotNull(final T reference,
- final String messageTemplate,
- final Object... messageArgs) {
- if (reference == null) {
- throw new NullPointerException(String.format(messageTemplate, messageArgs));
- }
- return reference;
- }
-
- /**
* Ensures the truth of an expression involving the state of the calling
* instance, but not involving any parameters to the calling method.
*
diff --git a/core/proto/android/server/animationadapter.proto b/core/proto/android/server/animationadapter.proto
index c6925f448a58..70627edf2cb3 100644
--- a/core/proto/android/server/animationadapter.proto
+++ b/core/proto/android/server/animationadapter.proto
@@ -50,7 +50,6 @@ message AnimationSpecProto {
optional WindowAnimationSpecProto window = 1;
optional MoveAnimationSpecProto move = 2;
optional AlphaAnimationSpecProto alpha = 3;
- optional RotationAnimationSpecProto rotate = 4;
}
/* represents WindowAnimationSpec */
@@ -77,12 +76,3 @@ message AlphaAnimationSpecProto {
optional float to = 2;
optional int64 duration_ms = 3;
}
-
-/* represents RotationAnimationSpec */
-message RotationAnimationSpecProto {
- option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
- optional float start_luma = 1;
- optional float end_luma = 2;
- optional int64 duration_ms = 3;
-}
diff --git a/core/proto/android/service/graphicsstats.proto b/core/proto/android/service/graphicsstats.proto
index 557075cc5bfa..2de5b7fd45f6 100644
--- a/core/proto/android/service/graphicsstats.proto
+++ b/core/proto/android/service/graphicsstats.proto
@@ -32,6 +32,10 @@ message GraphicsStatsServiceDumpProto {
}
message GraphicsStatsProto {
+ enum PipelineType {
+ GL = 0;
+ VULKAN = 1;
+ }
option (android.msg_privacy).dest = DEST_AUTOMATIC;
// The package name of the app
@@ -54,6 +58,9 @@ message GraphicsStatsProto {
// The gpu frame time histogram for the package
repeated GraphicsStatsHistogramBucketProto gpu_histogram = 7;
+
+ // HWUI renders pipeline type: GL or Vulkan
+ optional PipelineType pipeline = 8;
}
message GraphicsStatsJankSummaryProto {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index a808ed8fcc81..c050146c0e1a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -370,6 +370,7 @@
<protected-broadcast android:name="android.net.wifi.STATE_CHANGE" />
<protected-broadcast android:name="android.net.wifi.LINK_CONFIGURATION_CHANGED" />
<protected-broadcast android:name="android.net.wifi.CONFIGURED_NETWORKS_CHANGE" />
+ <protected-broadcast android:name="android.net.wifi.action.NETWORK_SETTINGS_RESET" />
<protected-broadcast android:name="android.net.wifi.action.PASSPOINT_DEAUTH_IMMINENT" />
<protected-broadcast android:name="android.net.wifi.action.PASSPOINT_ICON" />
<protected-broadcast android:name="android.net.wifi.action.PASSPOINT_OSU_PROVIDERS_LIST" />
@@ -933,11 +934,10 @@
<!-- Allows an application a broad access to external storage in scoped storage.
Intended to be used by few apps that need to manage files on behalf of the users.
- <p>Protection level: signature|appop
- <p>This protection level is temporary and will most likely be changed to |preinstalled -->
+ <p>Protection level: signature|appop|preinstalled -->
<permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
android:permissionGroup="android.permission-group.UNDEFINED"
- android:protectionLevel="signature|appop" />
+ android:protectionLevel="signature|appop|preinstalled" />
<!-- ====================================================================== -->
<!-- Permissions for accessing the device location -->
@@ -1655,6 +1655,7 @@
<!-- Allows holder to request bluetooth/wifi scan bypassing global "use location" setting and
location permissions.
<p>Not for use by third-party or privileged applications.
+ @SystemApi
@hide
-->
<permission android:name="android.permission.RADIO_SCAN_WITHOUT_LOCATION"
@@ -2070,8 +2071,10 @@
android:protectionLevel="signature|privileged" />
<!-- Allows read only access to precise phone state.
- @hide Pending API council approval -->
+ Allows reading of detailed information about phone state for special-use applications
+ such as dialers, carrier applications, or ims applications. -->
<permission android:name="android.permission.READ_PRECISE_PHONE_STATE"
+ android:permissionGroup="android.permission-group.UNDEFINED"
android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows read access to privileged phone state.
diff --git a/core/res/res/anim/screen_rotate_0_enter.xml b/core/res/res/anim/screen_rotate_0_enter.xml
index 629be7ea2d30..93cf3652d185 100644
--- a/core/res/res/anim/screen_rotate_0_enter.xml
+++ b/core/res/res/anim/screen_rotate_0_enter.xml
@@ -1,25 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ 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.
- -->
+/*
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false">
- <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
- android:interpolator="@interpolator/screen_rotation_alpha_in"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:duration="@android:integer/config_screen_rotation_fade_in" />
+ android:shareInterpolator="false">
+ <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:duration="@android:integer/config_shortAnimTime" />
</set>
diff --git a/core/res/res/anim/screen_rotate_0_exit.xml b/core/res/res/anim/screen_rotate_0_exit.xml
index fa046a036855..37d5a4115621 100644
--- a/core/res/res/anim/screen_rotate_0_exit.xml
+++ b/core/res/res/anim/screen_rotate_0_exit.xml
@@ -1,25 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ 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.
- -->
+/*
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false">
- <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
- android:interpolator="@interpolator/screen_rotation_alpha_out"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:duration="@android:integer/config_screen_rotation_fade_out" />
+ android:shareInterpolator="false">
</set>
diff --git a/core/res/res/anim/screen_rotate_0_frame.xml b/core/res/res/anim/screen_rotate_0_frame.xml
new file mode 100644
index 000000000000..5ea9bf8205e3
--- /dev/null
+++ b/core/res/res/anim/screen_rotate_0_frame.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false">
+ <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:duration="@android:integer/config_shortAnimTime" />
+</set>
diff --git a/core/res/res/anim/screen_rotate_180_enter.xml b/core/res/res/anim/screen_rotate_180_enter.xml
index 889a615e07f4..688a8d5bb2aa 100644
--- a/core/res/res/anim/screen_rotate_180_enter.xml
+++ b/core/res/res/anim/screen_rotate_180_enter.xml
@@ -18,11 +18,11 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false">
+ android:shareInterpolator="false">
<rotate android:fromDegrees="180" android:toDegrees="0"
- android:pivotX="50%" android:pivotY="50%"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/fast_out_slow_in"
- android:duration="@android:integer/config_screen_rotation_total_180" />
+ android:pivotX="50%" android:pivotY="50%"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:duration="@android:integer/config_mediumAnimTime" />
</set>
diff --git a/core/res/res/anim/screen_rotate_180_exit.xml b/core/res/res/anim/screen_rotate_180_exit.xml
index 766fcfae1f91..58a1868bd398 100644
--- a/core/res/res/anim/screen_rotate_180_exit.xml
+++ b/core/res/res/anim/screen_rotate_180_exit.xml
@@ -18,11 +18,11 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false">
+ android:shareInterpolator="false">
<rotate android:fromDegrees="0" android:toDegrees="-180"
- android:pivotX="50%" android:pivotY="50%"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/fast_out_slow_in"
- android:duration="@android:integer/config_screen_rotation_total_180" />
+ android:pivotX="50%" android:pivotY="50%"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:duration="@android:integer/config_mediumAnimTime" />
</set> \ No newline at end of file
diff --git a/core/res/res/anim/screen_rotate_alpha.xml b/core/res/res/anim/screen_rotate_alpha.xml
index 2cac982e24b4..c49ef9cafd39 100644
--- a/core/res/res/anim/screen_rotate_alpha.xml
+++ b/core/res/res/anim/screen_rotate_alpha.xml
@@ -20,8 +20,8 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
- android:interpolator="@interpolator/screen_rotation_alpha_out"
+ android:interpolator="@interpolator/decelerate_quint"
android:fillEnabled="true"
android:fillBefore="true" android:fillAfter="true"
- android:duration="@android:integer/config_screen_rotation_fade_out" />
+ android:duration="@android:integer/config_mediumAnimTime" />
</set>
diff --git a/core/res/res/anim/screen_rotate_minus_90_enter.xml b/core/res/res/anim/screen_rotate_minus_90_enter.xml
index 87fd25ea4603..b16d5fc761ee 100644
--- a/core/res/res/anim/screen_rotate_minus_90_enter.xml
+++ b/core/res/res/anim/screen_rotate_minus_90_enter.xml
@@ -18,17 +18,19 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false">
+ android:shareInterpolator="false">
+ <!-- Version for two-phase anim
<rotate android:fromDegrees="-90" android:toDegrees="0"
- android:pivotX="50%" android:pivotY="50%"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/fast_out_slow_in"
- android:duration="@android:integer/config_screen_rotation_total_90" />
- <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/screen_rotation_alpha_in"
- android:startOffset="@android:integer/config_screen_rotation_fade_in_delay"
- android:duration="@android:integer/config_screen_rotation_fade_in" />
+ android:pivotX="50%" android:pivotY="50%"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:duration="@android:integer/config_longAnimTime" />
+ -->
+ <rotate android:fromDegrees="-90" android:toDegrees="0"
+ android:pivotX="50%" android:pivotY="50%"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:duration="@android:integer/config_mediumAnimTime" />
</set>
diff --git a/core/res/res/anim/screen_rotate_minus_90_exit.xml b/core/res/res/anim/screen_rotate_minus_90_exit.xml
index c3aee14dc235..0927dd30ceb3 100644
--- a/core/res/res/anim/screen_rotate_minus_90_exit.xml
+++ b/core/res/res/anim/screen_rotate_minus_90_exit.xml
@@ -18,16 +18,26 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false">
+ android:shareInterpolator="false">
+ <!-- Version for two-phase animation
<rotate android:fromDegrees="0" android:toDegrees="90"
- android:pivotX="50%" android:pivotY="50%"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/fast_out_slow_in"
- android:duration="@android:integer/config_screen_rotation_total_90" />
- <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/screen_rotation_alpha_out"
- android:duration="@android:integer/config_screen_rotation_fade_out" />
+ android:pivotX="50%" android:pivotY="50%"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:duration="@android:integer/config_longAnimTime" />
+ -->
+ <scale android:fromXScale="100%" android:toXScale="100%p"
+ android:fromYScale="100%" android:toYScale="100%p"
+ android:pivotX="50%" android:pivotY="50%"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:duration="@android:integer/config_mediumAnimTime" />
+ <rotate android:fromDegrees="0" android:toDegrees="90"
+ android:pivotX="50%" android:pivotY="50%"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:duration="@android:integer/config_mediumAnimTime" />
</set>
diff --git a/core/res/res/anim/screen_rotate_plus_90_enter.xml b/core/res/res/anim/screen_rotate_plus_90_enter.xml
index 8849db421e75..86a8d24cbbcc 100644
--- a/core/res/res/anim/screen_rotate_plus_90_enter.xml
+++ b/core/res/res/anim/screen_rotate_plus_90_enter.xml
@@ -18,16 +18,19 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false">
+ android:shareInterpolator="false">
+ <!-- Version for two-phase animation
<rotate android:fromDegrees="90" android:toDegrees="0"
- android:pivotX="50%" android:pivotY="50%"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/fast_out_slow_in"
- android:duration="@android:integer/config_screen_rotation_total_90" />
- <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
- android:fillEnabled="true"
- android:interpolator="@interpolator/screen_rotation_alpha_in"
- android:startOffset="@android:integer/config_screen_rotation_fade_in_delay"
- android:duration="@android:integer/config_screen_rotation_fade_in" />
+ android:pivotX="50%" android:pivotY="50%"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:duration="@android:integer/config_longAnimTime" />
+ -->
+ <rotate android:fromDegrees="90" android:toDegrees="0"
+ android:pivotX="50%" android:pivotY="50%"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:duration="@android:integer/config_mediumAnimTime" />
</set>
diff --git a/core/res/res/anim/screen_rotate_plus_90_exit.xml b/core/res/res/anim/screen_rotate_plus_90_exit.xml
index de84c3bd08fc..fd786f9afce0 100644
--- a/core/res/res/anim/screen_rotate_plus_90_exit.xml
+++ b/core/res/res/anim/screen_rotate_plus_90_exit.xml
@@ -18,16 +18,26 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false">
+ android:shareInterpolator="false">
+ <!-- Version for two-phase animation
<rotate android:fromDegrees="0" android:toDegrees="-90"
- android:pivotX="50%" android:pivotY="50%"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/fast_out_slow_in"
- android:duration="@android:integer/config_screen_rotation_total_90" />
- <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
- android:interpolator="@interpolator/screen_rotation_alpha_out"
- android:fillEnabled="true"
- android:fillBefore="true" android:fillAfter="true"
- android:duration="@android:integer/config_screen_rotation_fade_out" />
+ android:pivotX="50%" android:pivotY="50%"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:duration="@android:integer/config_longAnimTime" />
+ -->
+ <scale android:fromXScale="100%" android:toXScale="100%p"
+ android:fromYScale="100%" android:toYScale="100%p"
+ android:pivotX="50%" android:pivotY="50%"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:duration="@android:integer/config_mediumAnimTime" />
+ <rotate android:fromDegrees="0" android:toDegrees="-90"
+ android:pivotX="50%" android:pivotY="50%"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:duration="@android:integer/config_mediumAnimTime" />
</set>
diff --git a/core/res/res/interpolator/screen_rotation_alpha_in.xml b/core/res/res/interpolator/screen_rotation_alpha_in.xml
deleted file mode 100644
index 9c566a7c8f23..000000000000
--- a/core/res/res/interpolator/screen_rotation_alpha_in.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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.
- -->
-
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:controlX1="0.15"
- android:controlY1="0.45"
- android:controlX2="0.33"
- android:controlY2="1"/>
diff --git a/core/res/res/interpolator/screen_rotation_alpha_out.xml b/core/res/res/interpolator/screen_rotation_alpha_out.xml
deleted file mode 100644
index 73a37d4f1aa5..000000000000
--- a/core/res/res/interpolator/screen_rotation_alpha_out.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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.
- -->
-
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:controlX1="0.57"
- android:controlY1="0"
- android:controlX2="0.71"
- android:controlY2=".43"/>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 245aed1484f7..6691d4c5955b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -147,24 +147,6 @@
<integer name="config_activityShortDur">150</integer>
<integer name="config_activityDefaultDur">220</integer>
- <!-- Fade out time for screen rotation -->
- <integer name="config_screen_rotation_fade_out">116</integer>
-
- <!-- Fade in time for screen rotation -->
- <integer name="config_screen_rotation_fade_in">233</integer>
-
- <!-- Fade in delay time for screen rotation -->
- <integer name="config_screen_rotation_fade_in_delay">100</integer>
-
- <!-- Total time for 90 degree screen rotation animations -->
- <integer name="config_screen_rotation_total_90">333</integer>
-
- <!-- Total time for 180 degree screen rotation animation -->
- <integer name="config_screen_rotation_total_180">433</integer>
-
- <!-- Total time for the rotation background color transition -->
- <integer name="config_screen_rotation_color_transition">200</integer>
-
<!-- The duration (in milliseconds) of the tooltip show/hide animations. -->
<integer name="config_tooltipAnimTime">150</integer>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 817ccde98504..37c971021b2e 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3000,7 +3000,6 @@
<public-group type="attr" first-id="0x01010607">
<public name="importantForContentCapture" />
<public name="forceQueryable" />
- <!-- @hide @SystemApi -->
<public name="resourcesMap" />
<public name="animatedImageDrawable"/>
<public name="htmlDescription"/>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 973d5f6392f4..9e117498eb10 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1805,6 +1805,7 @@
<!-- From services -->
<java-symbol type="anim" name="screen_rotate_0_enter" />
<java-symbol type="anim" name="screen_rotate_0_exit" />
+ <java-symbol type="anim" name="screen_rotate_0_frame" />
<java-symbol type="anim" name="screen_rotate_180_enter" />
<java-symbol type="anim" name="screen_rotate_180_exit" />
<java-symbol type="anim" name="screen_rotate_180_frame" />
@@ -1980,7 +1981,6 @@
<java-symbol type="integer" name="config_virtualKeyQuietTimeMillis" />
<java-symbol type="integer" name="config_brightness_ramp_rate_fast" />
<java-symbol type="integer" name="config_brightness_ramp_rate_slow" />
- <java-symbol type="integer" name="config_screen_rotation_color_transition" />
<java-symbol type="layout" name="am_compat_mode_dialog" />
<java-symbol type="layout" name="launch_warning" />
<java-symbol type="layout" name="safe_mode" />
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/WifiConfigurationHelper.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/WifiConfigurationHelper.java
index f0a83678f70b..a296ca27e268 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/WifiConfigurationHelper.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/WifiConfigurationHelper.java
@@ -16,6 +16,7 @@
package com.android.connectivitymanagertest;
+import android.net.IpConfiguration;
import android.net.IpConfiguration.IpAssignment;
import android.net.IpConfiguration.ProxySettings;
import android.net.LinkAddress;
@@ -136,7 +137,7 @@ public class WifiConfigurationHelper {
config.enterpriseConfig.setPhase2Method(phase2);
config.enterpriseConfig.setIdentity(identity);
config.enterpriseConfig.setAnonymousIdentity(anonymousIdentity);
- config.enterpriseConfig.setCaCertificateAlias(caCert);
+ config.enterpriseConfig.setCaCertificateAliases(new String[] {caCert});
config.enterpriseConfig.setClientCertificateAlias(clientCert);
return config;
}
@@ -147,8 +148,12 @@ public class WifiConfigurationHelper {
private static WifiConfiguration createGenericConfig(String ssid) {
WifiConfiguration config = new WifiConfiguration();
config.SSID = quotedString(ssid);
- config.setIpAssignment(IpAssignment.DHCP);
- config.setProxySettings(ProxySettings.NONE);
+
+ IpConfiguration ipConfiguration = config.getIpConfiguration();
+ ipConfiguration.setIpAssignment(IpAssignment.DHCP);
+ ipConfiguration.setProxySettings(ProxySettings.NONE);
+ config.setIpConfiguration(ipConfiguration);
+
return config;
}
@@ -237,6 +242,7 @@ public class WifiConfigurationHelper {
throw new IllegalArgumentException();
}
+ IpConfiguration ipConfiguration = config.getIpConfiguration();
if (jsonConfig.has("ip")) {
StaticIpConfiguration staticIpConfig = new StaticIpConfiguration();
@@ -247,13 +253,14 @@ public class WifiConfigurationHelper {
staticIpConfig.dnsServers.add(getInetAddress(jsonConfig.getString("dns1")));
staticIpConfig.dnsServers.add(getInetAddress(jsonConfig.getString("dns2")));
- config.setIpAssignment(IpAssignment.STATIC);
- config.setStaticIpConfiguration(staticIpConfig);
+ ipConfiguration.setIpAssignment(IpAssignment.STATIC);
+ ipConfiguration.setStaticIpConfiguration(staticIpConfig);
} else {
- config.setIpAssignment(IpAssignment.DHCP);
+ ipConfiguration.setIpAssignment(IpAssignment.DHCP);
}
+ ipConfiguration.setProxySettings(ProxySettings.NONE);
+ config.setIpConfiguration(ipConfiguration);
- config.setProxySettings(ProxySettings.NONE);
return config;
}
diff --git a/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java b/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
index 68b9b0079761..f4709ff0bc00 100644
--- a/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
@@ -33,14 +33,15 @@ import android.os.ParcelFileDescriptor.AutoCloseInputStream;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
+import android.support.test.uiautomator.UiDevice;
import android.test.InstrumentationTestCase;
import android.util.Log;
-import libcore.io.Streams;
-
import com.google.mockwebserver.MockResponse;
import com.google.mockwebserver.MockWebServer;
+import libcore.io.Streams;
+
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
@@ -63,6 +64,7 @@ public class DownloadManagerBaseTest extends InstrumentationTestCase {
private static final String TAG = "DownloadManagerBaseTest";
protected DownloadManager mDownloadManager = null;
private MockWebServer mServer = null;
+ private UiDevice mUiDevice = null;
protected String mFileType = "text/plain";
protected Context mContext = null;
protected MultipleDownloadsCompletedReceiver mReceiver = null;
@@ -234,6 +236,7 @@ public class DownloadManagerBaseTest extends InstrumentationTestCase {
@Override
public void setUp() throws Exception {
mContext = getInstrumentation().getContext();
+ mUiDevice = UiDevice.getInstance(getInstrumentation());
mDownloadManager = (DownloadManager)mContext.getSystemService(Context.DOWNLOAD_SERVICE);
mServer = new MockWebServer();
mServer.play();
@@ -512,7 +515,7 @@ public class DownloadManagerBaseTest extends InstrumentationTestCase {
Log.i(LOG_TAG, "Setting WiFi State to: " + enable);
WifiManager manager = (WifiManager)mContext.getSystemService(Context.WIFI_SERVICE);
- manager.setWifiEnabled(enable);
+ mUiDevice.executeShellCommand("svc wifi " + (enable ? "enable" : "disable"));
String timeoutMessage = "Timed out waiting for Wifi to be "
+ (enable ? "enabled!" : "disabled!");
diff --git a/core/tests/coretests/src/android/net/NetworkKeyTest.java b/core/tests/coretests/src/android/net/NetworkKeyTest.java
index c6c0b46d0505..b13bcd1311f6 100644
--- a/core/tests/coretests/src/android/net/NetworkKeyTest.java
+++ b/core/tests/coretests/src/android/net/NetworkKeyTest.java
@@ -18,6 +18,7 @@ package android.net;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.net.wifi.ScanResult;
@@ -107,7 +108,7 @@ public class NetworkKeyTest {
@Test
public void createFromScanResult_nullSsid() {
- ScanResult scanResult = new ScanResult();
+ ScanResult scanResult = mock(ScanResult.class);
scanResult.BSSID = VALID_BSSID;
assertNull(NetworkKey.createFromScanResult(scanResult));
@@ -115,7 +116,7 @@ public class NetworkKeyTest {
@Test
public void createFromScanResult_emptySsid() {
- ScanResult scanResult = new ScanResult();
+ ScanResult scanResult = mock(ScanResult.class);
scanResult.SSID = "";
scanResult.BSSID = VALID_BSSID;
@@ -124,7 +125,7 @@ public class NetworkKeyTest {
@Test
public void createFromScanResult_noneSsid() {
- ScanResult scanResult = new ScanResult();
+ ScanResult scanResult = mock(ScanResult.class);
scanResult.SSID = WifiManager.UNKNOWN_SSID;
scanResult.BSSID = VALID_BSSID;
@@ -133,7 +134,7 @@ public class NetworkKeyTest {
@Test
public void createFromScanResult_nullBssid() {
- ScanResult scanResult = new ScanResult();
+ ScanResult scanResult = mock(ScanResult.class);
scanResult.SSID = VALID_UNQUOTED_SSID;
assertNull(NetworkKey.createFromScanResult(scanResult));
@@ -141,7 +142,7 @@ public class NetworkKeyTest {
@Test
public void createFromScanResult_emptyBssid() {
- ScanResult scanResult = new ScanResult();
+ ScanResult scanResult = mock(ScanResult.class);
scanResult.SSID = VALID_UNQUOTED_SSID;
scanResult.BSSID = "";
@@ -150,7 +151,7 @@ public class NetworkKeyTest {
@Test
public void createFromScanResult_invalidBssid() {
- ScanResult scanResult = new ScanResult();
+ ScanResult scanResult = mock(ScanResult.class);
scanResult.SSID = VALID_UNQUOTED_SSID;
scanResult.BSSID = INVALID_BSSID;
@@ -159,7 +160,7 @@ public class NetworkKeyTest {
@Test
public void createFromScanResult_validSsid() {
- ScanResult scanResult = new ScanResult();
+ ScanResult scanResult = mock(ScanResult.class);
scanResult.SSID = VALID_UNQUOTED_SSID;
scanResult.BSSID = VALID_BSSID;
diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
index 0e19ca84d433..d0fd92a838c9 100644
--- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
@@ -90,12 +90,12 @@ public class ImeInsetsSourceConsumerTest {
mImeConsumer.onWindowFocusGained();
mImeConsumer.applyImeVisibility(true);
mController.cancelExistingAnimation();
- assertTrue(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertTrue(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
// test if setVisibility can hide IME
mImeConsumer.applyImeVisibility(false);
mController.cancelExistingAnimation();
- assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
});
}
diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
index 179929f2aae0..fa61a0a0250b 100644
--- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
+++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
@@ -16,11 +16,11 @@
package android.view;
+import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
import static android.view.WindowInsets.Type.systemBars;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -39,8 +39,6 @@ import android.view.SurfaceControl.Transaction;
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import android.view.test.InsetsModeSession;
-import androidx.test.runner.AndroidJUnit4;
-
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
@@ -52,6 +50,8 @@ import org.mockito.MockitoAnnotations;
import java.util.List;
+import androidx.test.runner.AndroidJUnit4;
+
/**
* Tests for {@link InsetsAnimationControlImpl}.
*
@@ -116,7 +116,7 @@ public class InsetsAnimationControlImplTest {
mController = new InsetsAnimationControlImpl(controls,
new Rect(0, 0, 500, 500), mInsetsState, mMockListener, systemBars(),
mMockController, 10 /* durationMs */,
- false /* fade */);
+ false /* fade */, LAYOUT_INSETS_DURING_ANIMATION_SHOWN);
}
@Test
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index a89fc1e6315f..1db96b15f83a 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -68,6 +68,7 @@ public class InsetsControllerTest {
private InsetsController mController;
private SurfaceSession mSession = new SurfaceSession();
private SurfaceControl mLeash;
+ private ViewRootImpl mViewRoot;
@Before
public void setup() {
@@ -77,13 +78,13 @@ public class InsetsControllerTest {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
Context context = InstrumentationRegistry.getTargetContext();
// cannot mock ViewRootImpl since it's final.
- ViewRootImpl viewRootImpl = new ViewRootImpl(context, context.getDisplay());
+ mViewRoot = new ViewRootImpl(context, context.getDisplay());
try {
- viewRootImpl.setView(new TextView(context), new LayoutParams(), null);
+ mViewRoot.setView(new TextView(context), new LayoutParams(), null);
} catch (BadTokenException e) {
// activity isn't running, we will ignore BadTokenException.
}
- mController = new InsetsController(viewRootImpl);
+ mController = new InsetsController(mViewRoot);
final Rect rect = new Rect(5, 5, 5, 5);
mController.calculateInsets(
false,
@@ -117,16 +118,22 @@ public class InsetsControllerTest {
@Test
public void testControlsRevoked_duringAnim() {
- InsetsSourceControl control =
- new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point());
- mController.onControlsChanged(new InsetsSourceControl[] { control });
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ InsetsSourceControl control =
+ new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point());
+ mController.onControlsChanged(new InsetsSourceControl[] { control });
- WindowInsetsAnimationControlListener mockListener =
- mock(WindowInsetsAnimationControlListener.class);
- mController.controlWindowInsetsAnimation(statusBars(), 10 /* durationMs */, mockListener);
- verify(mockListener).onReady(any(), anyInt());
- mController.onControlsChanged(new InsetsSourceControl[0]);
- verify(mockListener).onCancelled();
+ WindowInsetsAnimationControlListener mockListener =
+ mock(WindowInsetsAnimationControlListener.class);
+ mController.controlWindowInsetsAnimation(statusBars(), 10 /* durationMs */,
+ mockListener);
+
+ // Ready gets deferred until next predraw
+ mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
+ verify(mockListener).onReady(any(), anyInt());
+ mController.onControlsChanged(new InsetsSourceControl[0]);
+ verify(mockListener).onCancelled();
+ });
}
@Test
@@ -154,16 +161,16 @@ public class InsetsControllerTest {
mController.show(Type.all());
// quickly jump to final state by cancelling it.
mController.cancelExistingAnimation();
- assertTrue(mController.getSourceConsumer(navBar.getType()).isVisible());
- assertTrue(mController.getSourceConsumer(statusBar.getType()).isVisible());
- assertTrue(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
+ assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
+ assertTrue(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
mController.applyImeVisibility(false /* setVisible */);
mController.hide(Type.all());
mController.cancelExistingAnimation();
- assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(statusBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
mController.getSourceConsumer(ITYPE_IME).onWindowFocusLost();
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -180,10 +187,10 @@ public class InsetsControllerTest {
mController.getSourceConsumer(ITYPE_IME).onWindowFocusGained();
mController.applyImeVisibility(true);
mController.cancelExistingAnimation();
- assertTrue(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertTrue(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
mController.applyImeVisibility(false);
mController.cancelExistingAnimation();
- assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
mController.getSourceConsumer(ITYPE_IME).onWindowFocusLost();
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -201,16 +208,16 @@ public class InsetsControllerTest {
// test show select types.
mController.show(types);
mController.cancelExistingAnimation();
- assertTrue(mController.getSourceConsumer(navBar.getType()).isVisible());
- assertTrue(mController.getSourceConsumer(statusBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
+ assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
// test hide all
mController.hide(types);
mController.cancelExistingAnimation();
- assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(statusBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
@@ -227,29 +234,29 @@ public class InsetsControllerTest {
// test show select types.
mController.show(types);
mController.cancelExistingAnimation();
- assertTrue(mController.getSourceConsumer(navBar.getType()).isVisible());
- assertTrue(mController.getSourceConsumer(statusBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
+ assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
// test hide all
mController.hide(Type.all());
mController.cancelExistingAnimation();
- assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(statusBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
// test single show
mController.show(Type.navigationBars());
mController.cancelExistingAnimation();
- assertTrue(mController.getSourceConsumer(navBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(statusBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
// test single hide
mController.hide(Type.navigationBars());
- assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(statusBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -267,31 +274,31 @@ public class InsetsControllerTest {
mController.show(Type.navigationBars());
mController.show(Type.systemBars());
mController.cancelExistingAnimation();
- assertTrue(mController.getSourceConsumer(navBar.getType()).isVisible());
- assertTrue(mController.getSourceConsumer(statusBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
+ assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
mController.hide(Type.navigationBars());
mController.hide(Type.systemBars());
mController.cancelExistingAnimation();
- assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(statusBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
int types = Type.navigationBars() | Type.systemBars();
// show two at a time and hide one by one.
mController.show(types);
mController.hide(Type.navigationBars());
mController.cancelExistingAnimation();
- assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
- assertTrue(mController.getSourceConsumer(statusBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
+ assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
mController.hide(Type.systemBars());
mController.cancelExistingAnimation();
- assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(statusBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
@@ -309,15 +316,15 @@ public class InsetsControllerTest {
mController.show(types);
mController.hide(Type.navigationBars());
mController.cancelExistingAnimation();
- assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
- assertTrue(mController.getSourceConsumer(statusBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
+ assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
mController.hide(Type.systemBars());
mController.cancelExistingAnimation();
- assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(statusBar.getType()).isVisible());
- assertFalse(mController.getSourceConsumer(ime.getType()).isVisible());
+ assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
+ assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
@@ -336,12 +343,16 @@ public class InsetsControllerTest {
ArgumentCaptor<WindowInsetsAnimationController> controllerCaptor =
ArgumentCaptor.forClass(WindowInsetsAnimationController.class);
+
+ // Ready gets deferred until next predraw
+ mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
+
verify(mockListener).onReady(controllerCaptor.capture(), anyInt());
controllerCaptor.getValue().finish(false /* shown */);
});
waitUntilNextFrame();
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
- assertFalse(mController.getSourceConsumer(ITYPE_STATUS_BAR).isVisible());
+ assertFalse(mController.getSourceConsumer(ITYPE_STATUS_BAR).isRequestedVisible());
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
index 7af833bfcba4..492c03653990 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
@@ -96,7 +96,7 @@ public class InsetsSourceConsumerTest {
@Test
public void testHide() {
mConsumer.hide();
- assertFalse("Consumer should not be visible", mConsumer.isVisible());
+ assertFalse("Consumer should not be visible", mConsumer.isRequestedVisible());
verify(mSpyInsetsSource).setVisible(eq(false));
}
@@ -106,7 +106,7 @@ public class InsetsSourceConsumerTest {
// Insets source starts out visible
mConsumer.hide();
mConsumer.show();
- assertTrue("Consumer should be visible", mConsumer.isVisible());
+ assertTrue("Consumer should be visible", mConsumer.isRequestedVisible());
verify(mSpyInsetsSource).setVisible(eq(false));
verify(mSpyInsetsSource).setVisible(eq(true));
}
diff --git a/core/tests/coretests/src/android/view/InsetsSourceTest.java b/core/tests/coretests/src/android/view/InsetsSourceTest.java
index d7f50bafdc0a..e3b08bb14e46 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceTest.java
@@ -108,5 +108,30 @@ public class InsetsSourceTest {
assertEquals(Insets.of(0, 100, 0, 0), insets);
}
+ @Test
+ public void testCalculateVisibleInsets_default() {
+ mSource.setFrame(new Rect(0, 0, 500, 100));
+ Insets insets = mSource.calculateVisibleInsets(new Rect(100, 0, 500, 500));
+ assertEquals(Insets.of(0, 100, 0, 0), insets);
+ }
+
+ @Test
+ public void testCalculateVisibleInsets_override() {
+ mSource.setFrame(new Rect(0, 0, 500, 100));
+ mSource.setVisibleFrame(new Rect(0, 0, 500, 200));
+ Insets insets = mSource.calculateVisibleInsets(new Rect(100, 0, 500, 500));
+ assertEquals(Insets.of(0, 200, 0, 0), insets);
+ }
+
+ @Test
+ public void testCalculateVisibleInsets_invisible() {
+ mSource.setFrame(new Rect(0, 0, 500, 100));
+ mSource.setVisibleFrame(new Rect(0, 0, 500, 200));
+ mSource.setVisible(false);
+ Insets insets = mSource.calculateVisibleInsets(new Rect(100, 0, 500, 500));
+ assertEquals(Insets.of(0, 0, 0, 0), insets);
+ }
+
+
// Parcel and equals already tested via InsetsStateTest
}
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index fa2ffccaaa63..4b76fee00496 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -18,12 +18,14 @@ package android.view;
import static android.view.InsetsState.ISIDE_BOTTOM;
import static android.view.InsetsState.ISIDE_TOP;
+import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES;
import static android.view.InsetsState.ITYPE_CAPTION_BAR;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static org.junit.Assert.assertEquals;
@@ -209,6 +211,42 @@ public class InsetsStateTest {
assertFalse(InsetsState.getDefaultVisibility(ITYPE_IME));
}
+ @Test
+ public void testCalculateVisibleInsets() throws Exception {
+ try (final InsetsModeSession session =
+ new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) {
+ mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(ITYPE_STATUS_BAR).setVisible(true);
+ mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300));
+ mState.getSource(ITYPE_IME).setVisible(true);
+
+ // Make sure bottom gestures are ignored
+ mState.getSource(ITYPE_BOTTOM_GESTURES).setFrame(new Rect(0, 100, 100, 300));
+ mState.getSource(ITYPE_BOTTOM_GESTURES).setVisible(true);
+ Rect visibleInsets = mState.calculateVisibleInsets(
+ new Rect(0, 0, 100, 300), new Rect(), SOFT_INPUT_ADJUST_PAN);
+ assertEquals(new Rect(0, 100, 0, 100), visibleInsets);
+ }
+ }
+
+ @Test
+ public void testCalculateVisibleInsets_adjustNothing() throws Exception {
+ try (final InsetsModeSession session =
+ new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) {
+ mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(ITYPE_STATUS_BAR).setVisible(true);
+ mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300));
+ mState.getSource(ITYPE_IME).setVisible(true);
+
+ // Make sure bottom gestures are ignored
+ mState.getSource(ITYPE_BOTTOM_GESTURES).setFrame(new Rect(0, 100, 100, 300));
+ mState.getSource(ITYPE_BOTTOM_GESTURES).setVisible(true);
+ Rect visibleInsets = mState.calculateVisibleInsets(
+ new Rect(0, 0, 100, 300), new Rect(), SOFT_INPUT_ADJUST_NOTHING);
+ assertEquals(new Rect(0, 100, 0, 0), visibleInsets);
+ }
+ }
+
private void assertEqualsAndHashCode() {
assertEquals(mState, mState2);
assertEquals(mState.hashCode(), mState2.hashCode());
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
index b9b6d55b52d6..e23c51e66a02 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.accessibility;
+package android.view.accessibility;
import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertSame;
@@ -29,16 +29,17 @@ import static org.mockito.Mockito.when;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.Instrumentation;
+import android.app.PendingIntent;
+import android.app.RemoteAction;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
import android.os.UserHandle;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.IAccessibilityManager;
-import android.view.accessibility.IAccessibilityManagerClient;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.IntPair;
+import com.android.server.accessibility.test.MessageCapturingHandler;
import org.junit.After;
import org.junit.Before;
@@ -57,6 +58,16 @@ import java.util.List;
public class AccessibilityManagerTest {
private static final boolean WITH_A11Y_ENABLED = true;
private static final boolean WITH_A11Y_DISABLED = false;
+ private static final String LABEL = "label";
+ private static final String INTENT_ACTION = "TESTACTION";
+ private static final String DESCRIPTION = "description";
+ private static final PendingIntent TEST_PENDING_INTENT = PendingIntent.getBroadcast(
+ InstrumentationRegistry.getTargetContext(), 0, new Intent(INTENT_ACTION), 0);
+ private static final RemoteAction TEST_ACTION = new RemoteAction(
+ Icon.createWithContentUri("content://test"),
+ LABEL,
+ DESCRIPTION,
+ TEST_PENDING_INTENT);
@Mock private IAccessibilityManager mMockService;
private MessageCapturingHandler mHandler;
@@ -122,6 +133,29 @@ public class AccessibilityManagerTest {
}
@Test
+ public void testRegisterSystemAction() throws Exception {
+ AccessibilityManager manager = createManager(WITH_A11Y_ENABLED);
+ RemoteAction action = new RemoteAction(
+ Icon.createWithContentUri("content://test"),
+ LABEL,
+ DESCRIPTION,
+ TEST_PENDING_INTENT);
+ final int actionId = 0;
+ manager.registerSystemAction(TEST_ACTION, actionId);
+
+ verify(mMockService).registerSystemAction(TEST_ACTION, actionId);
+ }
+
+ @Test
+ public void testUnregisterSystemAction() throws Exception {
+ AccessibilityManager manager = createManager(WITH_A11Y_ENABLED);
+ final int actionId = 0;
+ manager.unregisterSystemAction(actionId);
+
+ verify(mMockService).unregisterSystemAction(actionId);
+ }
+
+ @Test
public void testIsEnabled() throws Exception {
// Create manager with a11y enabled
AccessibilityManager manager = createManager(WITH_A11Y_ENABLED);
diff --git a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
index f497db2256dd..89c237498e5c 100644
--- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
+++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
@@ -226,6 +226,61 @@ public class EditorCursorDragTest {
}
@Test
+ public void testEditor_onTouchEvent_quickTapAfterDrag() throws Throwable {
+ String text = "Hi world!";
+ onView(withId(R.id.textview)).perform(replaceText(text));
+ onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));
+
+ TextView tv = mActivity.findViewById(R.id.textview);
+ Editor editor = tv.getEditorForTesting();
+
+ // Simulate a tap-and-drag gesture.
+ long event1Time = 1001;
+ MotionEvent event1 = downEvent(event1Time, event1Time, 5f, 10f);
+ mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event1));
+ assertFalse(editor.getInsertionController().isCursorBeingModified());
+ assertFalse(editor.getSelectionController().isCursorBeingModified());
+
+ long event2Time = 1002;
+ MotionEvent event2 = moveEvent(event1Time, event2Time, 50f, 10f);
+ mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event2));
+ assertTrue(editor.getInsertionController().isCursorBeingModified());
+ assertFalse(editor.getSelectionController().isCursorBeingModified());
+
+ long event3Time = 1003;
+ MotionEvent event3 = moveEvent(event1Time, event3Time, 100f, 10f);
+ mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event3));
+ assertTrue(editor.getInsertionController().isCursorBeingModified());
+ assertFalse(editor.getSelectionController().isCursorBeingModified());
+
+ long event4Time = 2004;
+ MotionEvent event4 = upEvent(event1Time, event4Time, 100f, 10f);
+ mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event4));
+ assertFalse(editor.getInsertionController().isCursorBeingModified());
+ assertFalse(editor.getSelectionController().isCursorBeingModified());
+
+ // Simulate a quick tap after the drag, near the location where the drag ended.
+ long event5Time = 2005;
+ MotionEvent event5 = downEvent(event5Time, event5Time, 90f, 10f);
+ mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event5));
+ assertFalse(editor.getInsertionController().isCursorBeingModified());
+ assertFalse(editor.getSelectionController().isCursorBeingModified());
+
+ long event6Time = 2006;
+ MotionEvent event6 = upEvent(event5Time, event6Time, 90f, 10f);
+ mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event6));
+ assertFalse(editor.getInsertionController().isCursorBeingModified());
+ assertFalse(editor.getSelectionController().isCursorBeingModified());
+
+ // Simulate another quick tap in the same location; now selection should be triggered.
+ long event7Time = 2007;
+ MotionEvent event7 = downEvent(event7Time, event7Time, 90f, 10f);
+ mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event7));
+ assertFalse(editor.getInsertionController().isCursorBeingModified());
+ assertTrue(editor.getSelectionController().isCursorBeingModified());
+ }
+
+ @Test
public void testEditor_onTouchEvent_cursorDrag() throws Throwable {
String text = "testEditor_onTouchEvent_cursorDrag";
onView(withId(R.id.textview)).perform(replaceText(text));
@@ -237,29 +292,25 @@ public class EditorCursorDragTest {
// Simulate a tap-and-drag gesture. This should trigger a cursor drag.
long event1Time = 1001;
MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
- mActivity.runOnUiThread(() -> editor.onTouchEvent(event1));
- mInstrumentation.waitForIdleSync();
+ mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event1));
assertFalse(editor.getInsertionController().isCursorBeingModified());
assertFalse(editor.getSelectionController().isCursorBeingModified());
long event2Time = 1002;
MotionEvent event2 = moveEvent(event1Time, event2Time, 21f, 30f);
- mActivity.runOnUiThread(() -> editor.onTouchEvent(event2));
- mInstrumentation.waitForIdleSync();
+ mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event2));
assertFalse(editor.getInsertionController().isCursorBeingModified());
assertFalse(editor.getSelectionController().isCursorBeingModified());
long event3Time = 1003;
- MotionEvent event3 = moveEvent(event3Time, event3Time, 120f, 30f);
- mActivity.runOnUiThread(() -> editor.onTouchEvent(event3));
- mInstrumentation.waitForIdleSync();
+ MotionEvent event3 = moveEvent(event1Time, event3Time, 120f, 30f);
+ mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event3));
assertTrue(editor.getInsertionController().isCursorBeingModified());
assertFalse(editor.getSelectionController().isCursorBeingModified());
long event4Time = 1004;
- MotionEvent event4 = upEvent(event3Time, event4Time, 120f, 30f);
- mActivity.runOnUiThread(() -> editor.onTouchEvent(event4));
- mInstrumentation.waitForIdleSync();
+ MotionEvent event4 = upEvent(event1Time, event4Time, 120f, 30f);
+ mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event4));
assertFalse(editor.getInsertionController().isCursorBeingModified());
assertFalse(editor.getSelectionController().isCursorBeingModified());
}
@@ -276,36 +327,31 @@ public class EditorCursorDragTest {
// Simulate a double-tap followed by a drag. This should trigger a selection drag.
long event1Time = 1001;
MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
- mActivity.runOnUiThread(() -> editor.onTouchEvent(event1));
- mInstrumentation.waitForIdleSync();
+ mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event1));
assertFalse(editor.getInsertionController().isCursorBeingModified());
assertFalse(editor.getSelectionController().isCursorBeingModified());
long event2Time = 1002;
MotionEvent event2 = upEvent(event1Time, event2Time, 20f, 30f);
- mActivity.runOnUiThread(() -> editor.onTouchEvent(event2));
- mInstrumentation.waitForIdleSync();
+ mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event2));
assertFalse(editor.getInsertionController().isCursorBeingModified());
assertFalse(editor.getSelectionController().isCursorBeingModified());
long event3Time = 1003;
MotionEvent event3 = downEvent(event3Time, event3Time, 20f, 30f);
- mActivity.runOnUiThread(() -> editor.onTouchEvent(event3));
- mInstrumentation.waitForIdleSync();
+ mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event3));
assertFalse(editor.getInsertionController().isCursorBeingModified());
assertTrue(editor.getSelectionController().isCursorBeingModified());
long event4Time = 1004;
MotionEvent event4 = moveEvent(event3Time, event4Time, 120f, 30f);
- mActivity.runOnUiThread(() -> editor.onTouchEvent(event4));
- mInstrumentation.waitForIdleSync();
+ mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event4));
assertFalse(editor.getInsertionController().isCursorBeingModified());
assertTrue(editor.getSelectionController().isCursorBeingModified());
long event5Time = 1005;
MotionEvent event5 = upEvent(event3Time, event5Time, 120f, 30f);
- mActivity.runOnUiThread(() -> editor.onTouchEvent(event5));
- mInstrumentation.waitForIdleSync();
+ mInstrumentation.runOnMainSync(() -> editor.onTouchEvent(event5));
assertFalse(editor.getInsertionController().isCursorBeingModified());
assertFalse(editor.getSelectionController().isCursorBeingModified());
}
diff --git a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
index 6adb1b8fa0d6..215d0b800074 100644
--- a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
+++ b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
@@ -120,6 +120,60 @@ public class EditorTouchStateTest {
}
@Test
+ public void testUpdate_doubleTap_delayAfterFirstDownEvent() throws Exception {
+ // Simulate an ACTION_DOWN event.
+ long event1Time = 1000;
+ MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
+ mTouchState.update(event1, mConfig);
+ assertSingleTap(mTouchState, 20f, 30f, 0, 0, false);
+
+ // Simulate an ACTION_UP event with a delay that's longer than the double-tap timeout.
+ long event2Time = 1000 + ViewConfiguration.getDoubleTapTimeout() + 1;
+ MotionEvent event2 = upEvent(event1Time, event2Time, 20f, 30f);
+ mTouchState.update(event2, mConfig);
+ assertSingleTap(mTouchState, 20f, 30f, 20f, 30f, false);
+
+ // Generate an ACTION_DOWN event whose time is within the double-tap timeout when
+ // calculated from the last ACTION_UP event time. Even though the time between the last up
+ // and this down event is within the double-tap timeout, this should not be considered a
+ // double-tap (since the first down event had a longer delay).
+ long event3Time = event2Time + 1;
+ MotionEvent event3 = downEvent(event3Time, event3Time, 22f, 33f);
+ mTouchState.update(event3, mConfig);
+ assertSingleTap(mTouchState, 22f, 33f, 20f, 30f, false);
+ }
+
+ @Test
+ public void testUpdate_quickTapAfterDrag() throws Exception {
+ // Simulate an ACTION_DOWN event.
+ long event1Time = 1000;
+ MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
+ mTouchState.update(event1, mConfig);
+ assertSingleTap(mTouchState, 20f, 30f, 0, 0, false);
+
+ // Simulate an ACTION_MOVE event.
+ long event2Time = 1001;
+ MotionEvent event2 = moveEvent(event1Time, event2Time, 200f, 31f);
+ mTouchState.update(event2, mConfig);
+ assertSingleTap(mTouchState, 20f, 30f, 0, 0, true);
+
+ // Simulate an ACTION_UP event with a delay that's longer than the double-tap timeout.
+ long event3Time = 5000;
+ MotionEvent event3 = upEvent(event1Time, event3Time, 200f, 31f);
+ mTouchState.update(event3, mConfig);
+ assertSingleTap(mTouchState, 20f, 30f, 200f, 31f, false);
+
+ // Generate an ACTION_DOWN event whose time is within the double-tap timeout when
+ // calculated from the last ACTION_UP event time. Even though the time between the last up
+ // and this down event is within the double-tap timeout, this should not be considered a
+ // double-tap (since the first down event had a longer delay).
+ long event4Time = event3Time + 1;
+ MotionEvent event4 = downEvent(event4Time, event4Time, 200f, 31f);
+ mTouchState.update(event4, mConfig);
+ assertSingleTap(mTouchState, 200f, 31f, 200f, 31f, false);
+ }
+
+ @Test
public void testUpdate_tripleClick_mouse() throws Exception {
// Simulate an ACTION_DOWN event.
long event1Time = 1000;
diff --git a/data/etc/OWNERS b/data/etc/OWNERS
index ea66ee373785..70d467829269 100644
--- a/data/etc/OWNERS
+++ b/data/etc/OWNERS
@@ -1 +1 @@
-per-file privapp-permissions-platform.xml = hackbod@android.com, jsharkey@android.com, svetoslavganov@google.com, toddke@google.com, yamasani@google.com, cbrubaker@google.com, jeffv@google.com, moltmann@google.com
+per-file privapp-permissions-platform.xml = hackbod@android.com, jsharkey@android.com, svetoslavganov@google.com, toddke@google.com, yamasani@google.com, cbrubaker@google.com, jeffv@google.com, moltmann@google.com, lorenzo@google.com
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index eb1d1ab1089c..9930ea262b32 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -243,6 +243,7 @@ applications that come with the platform
<permission name="android.permission.MANAGE_USB"/>
<permission name="android.permission.MODIFY_PHONE_STATE"/>
<permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/>
+ <permission name="android.permission.TETHER_PRIVILEGED"/>
<permission name="android.permission.UPDATE_APP_OPS_STATS"/>
</privapp-permissions>
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index d945fc49ec88..a7f8cc4688ef 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -215,6 +215,7 @@ cc_defaults {
android: {
srcs: [
+ "pipeline/skia/ATraceMemoryDump.cpp",
"pipeline/skia/GLFunctorDrawable.cpp",
"pipeline/skia/LayerDrawable.cpp",
"pipeline/skia/ShaderCache.cpp",
@@ -244,7 +245,6 @@ cc_defaults {
"DeviceInfo.cpp",
"FrameInfo.cpp",
"FrameInfoVisualizer.cpp",
- "GpuMemoryTracker.cpp",
"HardwareBitmapUploader.cpp",
"HWUIProperties.sysprop",
"JankTracker.cpp",
@@ -325,7 +325,6 @@ cc_test {
"tests/unit/DamageAccumulatorTests.cpp",
"tests/unit/DeferredLayerUpdaterTests.cpp",
"tests/unit/FatVectorTests.cpp",
- "tests/unit/GpuMemoryTrackerTests.cpp",
"tests/unit/GraphicsStatsServiceTests.cpp",
"tests/unit/LayerUpdateQueueTests.cpp",
"tests/unit/LinearAllocatorTests.cpp",
diff --git a/libs/hwui/GpuMemoryTracker.cpp b/libs/hwui/GpuMemoryTracker.cpp
deleted file mode 100644
index a9a7af8f22f3..000000000000
--- a/libs/hwui/GpuMemoryTracker.cpp
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "utils/StringUtils.h"
-
-#include <GpuMemoryTracker.h>
-#include <cutils/compiler.h>
-#include <utils/Trace.h>
-#include <array>
-#include <sstream>
-#include <unordered_set>
-#include <vector>
-
-namespace android {
-namespace uirenderer {
-
-pthread_t gGpuThread = 0;
-
-#define NUM_TYPES static_cast<int>(GpuObjectType::TypeCount)
-
-const char* TYPE_NAMES[] = {
- "Texture", "OffscreenBuffer", "Layer",
-};
-
-struct TypeStats {
- int totalSize = 0;
- int count = 0;
-};
-
-static std::array<TypeStats, NUM_TYPES> gObjectStats;
-static std::unordered_set<GpuMemoryTracker*> gObjectSet;
-
-void GpuMemoryTracker::notifySizeChanged(int newSize) {
- int delta = newSize - mSize;
- mSize = newSize;
- gObjectStats[static_cast<int>(mType)].totalSize += delta;
-}
-
-void GpuMemoryTracker::startTrackingObject() {
- auto result = gObjectSet.insert(this);
- LOG_ALWAYS_FATAL_IF(!result.second,
- "startTrackingObject() on %p failed, already being tracked!", this);
- gObjectStats[static_cast<int>(mType)].count++;
-}
-
-void GpuMemoryTracker::stopTrackingObject() {
- size_t removed = gObjectSet.erase(this);
- LOG_ALWAYS_FATAL_IF(removed != 1, "stopTrackingObject removed %zd, is %p not being tracked?",
- removed, this);
- gObjectStats[static_cast<int>(mType)].count--;
-}
-
-void GpuMemoryTracker::onGpuContextCreated() {
- LOG_ALWAYS_FATAL_IF(gGpuThread != 0,
- "We already have a gpu thread? "
- "current = %lu, gpu thread = %lu",
- pthread_self(), gGpuThread);
- gGpuThread = pthread_self();
-}
-
-void GpuMemoryTracker::onGpuContextDestroyed() {
- gGpuThread = 0;
- if (CC_UNLIKELY(gObjectSet.size() > 0)) {
- std::stringstream os;
- dump(os);
- ALOGE("%s", os.str().c_str());
- LOG_ALWAYS_FATAL("Leaked %zd GPU objects!", gObjectSet.size());
- }
-}
-
-void GpuMemoryTracker::dump() {
- std::stringstream strout;
- dump(strout);
- ALOGD("%s", strout.str().c_str());
-}
-
-void GpuMemoryTracker::dump(std::ostream& stream) {
- for (int type = 0; type < NUM_TYPES; type++) {
- const TypeStats& stats = gObjectStats[type];
- stream << TYPE_NAMES[type];
- stream << " is using " << SizePrinter{stats.totalSize};
- stream << ", count = " << stats.count;
- stream << std::endl;
- }
-}
-
-int GpuMemoryTracker::getInstanceCount(GpuObjectType type) {
- return gObjectStats[static_cast<int>(type)].count;
-}
-
-int GpuMemoryTracker::getTotalSize(GpuObjectType type) {
- return gObjectStats[static_cast<int>(type)].totalSize;
-}
-
-void GpuMemoryTracker::onFrameCompleted() {
- if (ATRACE_ENABLED()) {
- char buf[128];
- for (int type = 0; type < NUM_TYPES; type++) {
- snprintf(buf, 128, "hwui_%s", TYPE_NAMES[type]);
- const TypeStats& stats = gObjectStats[type];
- ATRACE_INT(buf, stats.totalSize);
- snprintf(buf, 128, "hwui_%s_count", TYPE_NAMES[type]);
- ATRACE_INT(buf, stats.count);
- }
- }
-}
-
-} // namespace uirenderer
-} // namespace android;
diff --git a/libs/hwui/GpuMemoryTracker.h b/libs/hwui/GpuMemoryTracker.h
deleted file mode 100644
index de3ca99ef14b..000000000000
--- a/libs/hwui/GpuMemoryTracker.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#pragma once
-
-#include <pthread.h>
-#include <ostream>
-
-#include <log/log.h>
-
-namespace android {
-namespace uirenderer {
-
-extern pthread_t gGpuThread;
-
-#define ASSERT_GPU_THREAD() \
- LOG_ALWAYS_FATAL_IF(!pthread_equal(gGpuThread, pthread_self()), \
- "Error, %p of type %d (size=%d) used on wrong thread! cur thread %lu " \
- "!= gpu thread %lu", \
- this, static_cast<int>(mType), mSize, pthread_self(), gGpuThread)
-
-enum class GpuObjectType {
- Texture = 0,
- OffscreenBuffer,
- Layer,
-
- TypeCount,
-};
-
-class GpuMemoryTracker {
-public:
- GpuObjectType objectType() { return mType; }
- int objectSize() { return mSize; }
-
- static void onGpuContextCreated();
- static void onGpuContextDestroyed();
- static void dump();
- static void dump(std::ostream& stream);
- static int getInstanceCount(GpuObjectType type);
- static int getTotalSize(GpuObjectType type);
- static void onFrameCompleted();
-
-protected:
- explicit GpuMemoryTracker(GpuObjectType type) : mType(type) {
- ASSERT_GPU_THREAD();
- startTrackingObject();
- }
-
- ~GpuMemoryTracker() {
- notifySizeChanged(0);
- stopTrackingObject();
- }
-
- void notifySizeChanged(int newSize);
-
-private:
- void startTrackingObject();
- void stopTrackingObject();
-
- int mSize = 0;
- GpuObjectType mType;
-};
-
-} // namespace uirenderer
-} // namespace android;
diff --git a/libs/hwui/ProfileData.cpp b/libs/hwui/ProfileData.cpp
index 7921662b213c..a8e36e37905d 100644
--- a/libs/hwui/ProfileData.cpp
+++ b/libs/hwui/ProfileData.cpp
@@ -15,6 +15,7 @@
*/
#include "ProfileData.h"
+#include "Properties.h"
#include <cinttypes>
@@ -102,6 +103,7 @@ void ProfileData::mergeWith(const ProfileData& other) {
mGPUFrameCounts[i] >>= divider;
mGPUFrameCounts[i] += other.mGPUFrameCounts[i];
}
+ mPipelineType = other.mPipelineType;
}
void ProfileData::dump(int fd) const {
@@ -157,6 +159,7 @@ void ProfileData::reset() {
mTotalFrameCount = 0;
mJankFrameCount = 0;
mStatStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ mPipelineType = Properties::getRenderPipelineType();
}
void ProfileData::reportFrame(int64_t duration) {
diff --git a/libs/hwui/ProfileData.h b/libs/hwui/ProfileData.h
index ccbffc6f136e..dd3ba661dd29 100644
--- a/libs/hwui/ProfileData.h
+++ b/libs/hwui/ProfileData.h
@@ -16,6 +16,7 @@
#pragma once
+#include "Properties.h"
#include "utils/Macros.h"
#include <utils/Timers.h>
@@ -65,6 +66,7 @@ public:
uint32_t jankFrameCount() const { return mJankFrameCount; }
nsecs_t statsStartTime() const { return mStatStartTime; }
uint32_t jankTypeCount(JankType type) const { return mJankTypeCounts[static_cast<int>(type)]; }
+ RenderPipelineType pipelineType() const { return mPipelineType; }
struct HistogramEntry {
uint32_t renderTimeMs;
@@ -103,6 +105,9 @@ private:
uint32_t mTotalFrameCount;
uint32_t mJankFrameCount;
nsecs_t mStatStartTime;
+
+ // true if HWUI renders with Vulkan pipeline
+ RenderPipelineType mPipelineType;
};
// For testing
diff --git a/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp b/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp
new file mode 100644
index 000000000000..2c78b024536b
--- /dev/null
+++ b/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp
@@ -0,0 +1,175 @@
+/*
+ * 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.
+ */
+
+#include "ATraceMemoryDump.h"
+
+#include <utils/Trace.h>
+
+#include <cstring>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+// When purgeable is INVALID_TIME it won't be logged at all.
+#define INVALID_TIME -1
+
+/**
+ * Skia invokes the following SkTraceMemoryDump functions:
+ * 1. dumpNumericValue (dumpName, units="bytes", valueName="size")
+ * 2. dumpStringValue (dumpName, valueName="type") [optional -> for example CPU memory does not
+ * invoke dumpStringValue]
+ * 3. dumpNumericValue (dumpName, units="bytes", valueName="purgeable_size") [optional]
+ * 4. setMemoryBacking(dumpName, backingType) [optional -> for example Vulkan GPU resources do not
+ * invoke setMemoryBacking]
+ *
+ * ATraceMemoryDump calculates memory category first by looking at the "type" string passed to
+ * dumpStringValue and then by looking at "backingType" passed to setMemoryBacking.
+ * Only GPU Texture memory is tracked separately and everything else is grouped as one
+ * "GPU Memory" category.
+ */
+static std::unordered_map<const char*, const char*> sResourceMap = {
+ {"malloc", "Graphics CPU Memory"}, // taken from setMemoryBacking(backingType)
+ {"gl_texture", "Graphics Texture Memory"}, // taken from setMemoryBacking(backingType)
+ {"Texture",
+ "Graphics Texture Memory"}, // taken from dumpStringValue(value, valueName="type")
+ // Uncomment categories below to split "GPU Memory" into more brackets for debugging.
+ /*{"vk_buffer", "vk_buffer"},
+ {"gl_renderbuffer", "gl_renderbuffer"},
+ {"gl_buffer", "gl_buffer"},
+ {"RenderTarget", "RenderTarget"},
+ {"Stencil", "Stencil"},
+ {"Path Data", "Path Data"},
+ {"Buffer Object", "Buffer Object"},
+ {"Surface", "Surface"},*/
+};
+
+ATraceMemoryDump::ATraceMemoryDump() {
+ mLastDumpName.reserve(100);
+ mCategory.reserve(100);
+}
+
+void ATraceMemoryDump::dumpNumericValue(const char* dumpName, const char* valueName,
+ const char* units, uint64_t value) {
+ if (!strcmp(units, "bytes")) {
+ recordAndResetCountersIfNeeded(dumpName);
+ if (!strcmp(valueName, "size")) {
+ mLastDumpValue = value;
+ } else if (!strcmp(valueName, "purgeable_size")) {
+ mLastPurgeableDumpValue = value;
+ }
+ }
+}
+
+void ATraceMemoryDump::dumpStringValue(const char* dumpName, const char* valueName,
+ const char* value) {
+ if (!strcmp(valueName, "type")) {
+ recordAndResetCountersIfNeeded(dumpName);
+ auto categoryIt = sResourceMap.find(value);
+ if (categoryIt != sResourceMap.end()) {
+ mCategory = categoryIt->second;
+ }
+ }
+}
+
+void ATraceMemoryDump::setMemoryBacking(const char* dumpName, const char* backingType,
+ const char* backingObjectId) {
+ recordAndResetCountersIfNeeded(dumpName);
+ auto categoryIt = sResourceMap.find(backingType);
+ if (categoryIt != sResourceMap.end()) {
+ mCategory = categoryIt->second;
+ }
+}
+
+/**
+ * startFrame is invoked before dumping anything. It resets counters from the previous frame.
+ * This is important, because if there is no new data for a given category trace would assume
+ * usage has not changed (instead of reporting 0).
+ */
+void ATraceMemoryDump::startFrame() {
+ resetCurrentCounter("");
+ for (auto& it : mCurrentValues) {
+ // Once a category is observed in at least one frame, it is always reported in subsequent
+ // frames (even if it is 0). Not logging a category to ATRACE would mean its value has not
+ // changed since the previous frame, which is not what we want.
+ it.second.time = 0;
+ // If purgeableTime is INVALID_TIME, then logTraces won't log it at all.
+ if (it.second.purgeableTime != INVALID_TIME) {
+ it.second.purgeableTime = 0;
+ }
+ }
+}
+
+/**
+ * logTraces reads from mCurrentValues and logs the counters with ATRACE.
+ */
+void ATraceMemoryDump::logTraces() {
+ // Accumulate data from last dumpName
+ recordAndResetCountersIfNeeded("");
+ for (auto& it : mCurrentValues) {
+ ATRACE_INT64(it.first.c_str(), it.second.time);
+ if (it.second.purgeableTime != INVALID_TIME) {
+ ATRACE_INT64((std::string("Purgeable ") + it.first).c_str(), it.second.purgeableTime);
+ }
+ }
+}
+
+/**
+ * recordAndResetCountersIfNeeded reads memory usage from mLastDumpValue/mLastPurgeableDumpValue and
+ * accumulates in mCurrentValues[category]. It makes provision to create a new category and track
+ * purgeable memory only if there is at least one observation.
+ * recordAndResetCountersIfNeeded won't do anything until all the information for a given dumpName
+ * is received.
+ */
+void ATraceMemoryDump::recordAndResetCountersIfNeeded(const char* dumpName) {
+ if (!mLastDumpName.compare(dumpName)) {
+ // Still waiting for more data for current dumpName.
+ return;
+ }
+
+ // First invocation will have an empty mLastDumpName.
+ if (!mLastDumpName.empty()) {
+ // A new dumpName observed -> store the data already collected.
+ auto memoryCounter = mCurrentValues.find(mCategory);
+ if (memoryCounter != mCurrentValues.end()) {
+ memoryCounter->second.time += mLastDumpValue;
+ if (mLastPurgeableDumpValue != INVALID_TIME) {
+ if (memoryCounter->second.purgeableTime == INVALID_TIME) {
+ memoryCounter->second.purgeableTime = mLastPurgeableDumpValue;
+ } else {
+ memoryCounter->second.purgeableTime += mLastPurgeableDumpValue;
+ }
+ }
+ } else {
+ mCurrentValues[mCategory] = {mLastDumpValue, mLastPurgeableDumpValue};
+ }
+ }
+
+ // Reset counters and default category for the newly observed "dumpName".
+ resetCurrentCounter(dumpName);
+}
+
+void ATraceMemoryDump::resetCurrentCounter(const char* dumpName) {
+ mLastDumpValue = 0;
+ mLastPurgeableDumpValue = INVALID_TIME;
+ mLastDumpName = dumpName;
+ // Categories not listed in sResourceMap are reported as "GPU memory"
+ mCategory = "GPU Memory";
+}
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/ATraceMemoryDump.h b/libs/hwui/pipeline/skia/ATraceMemoryDump.h
new file mode 100644
index 000000000000..aa5c401ad1ca
--- /dev/null
+++ b/libs/hwui/pipeline/skia/ATraceMemoryDump.h
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <SkString.h>
+#include <SkTraceMemoryDump.h>
+
+#include <string>
+#include <unordered_map>
+#include <utility>
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+class ATraceMemoryDump : public SkTraceMemoryDump {
+public:
+ ATraceMemoryDump();
+ ~ATraceMemoryDump() override {}
+
+ void dumpNumericValue(const char* dumpName, const char* valueName, const char* units,
+ uint64_t value) override;
+
+ void dumpStringValue(const char* dumpName, const char* valueName, const char* value) override;
+
+ LevelOfDetail getRequestedDetails() const override {
+ return SkTraceMemoryDump::kLight_LevelOfDetail;
+ }
+
+ bool shouldDumpWrappedObjects() const override { return false; }
+
+ void setMemoryBacking(const char* dumpName, const char* backingType,
+ const char* backingObjectId) override;
+
+ void setDiscardableMemoryBacking(const char*, const SkDiscardableMemory&) override {}
+
+ void startFrame();
+
+ void logTraces();
+
+private:
+ std::string mLastDumpName;
+
+ uint64_t mLastDumpValue;
+
+ uint64_t mLastPurgeableDumpValue;
+
+ std::string mCategory;
+
+ struct TraceValue {
+ uint64_t time;
+ uint64_t purgeableTime;
+ };
+
+ // keys are define in sResourceMap
+ std::unordered_map<std::string, TraceValue> mCurrentValues;
+
+ void recordAndResetCountersIfNeeded(const char* dumpName);
+
+ void resetCurrentCounter(const char* dumpName);
+};
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */ \ No newline at end of file
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 11dc013af6bc..35a885f46919 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -457,7 +457,10 @@ void SkiaPipeline::renderFrameImpl(const SkRect& clip,
const Rect& contentDrawBounds, SkCanvas* canvas,
const SkMatrix& preTransform) {
SkAutoCanvasRestore saver(canvas, true);
- canvas->androidFramework_setDeviceClipRestriction(preTransform.mapRect(clip).roundOut());
+ auto clipRestriction = preTransform.mapRect(clip).roundOut();
+ canvas->androidFramework_setDeviceClipRestriction(clipRestriction);
+ canvas->drawAnnotation(SkRect::Make(clipRestriction), "AndroidDeviceClipRestriction",
+ nullptr);
canvas->concat(preTransform);
// STOPSHIP: Revert, temporary workaround to clear always F16 frame buffer for b/74976293
diff --git a/libs/hwui/protos/graphicsstats.proto b/libs/hwui/protos/graphicsstats.proto
index 0cd5c6228504..dd5676c9c961 100644
--- a/libs/hwui/protos/graphicsstats.proto
+++ b/libs/hwui/protos/graphicsstats.proto
@@ -29,6 +29,11 @@ message GraphicsStatsServiceDumpProto {
}
message GraphicsStatsProto {
+ enum PipelineType {
+ GL = 0;
+ VULKAN = 1;
+ }
+
// The package name of the app
optional string package_name = 1;
@@ -49,6 +54,9 @@ message GraphicsStatsProto {
// The gpu frame time histogram for the package
repeated GraphicsStatsHistogramBucketProto gpu_histogram = 7;
+
+ // HWUI renders pipeline type: GL or Vulkan
+ optional PipelineType pipeline = 8;
}
message GraphicsStatsJankSummaryProto {
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
index fad9440be73f..7e8c96d96860 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -16,7 +16,6 @@
#include "renderstate/RenderState.h"
#include "renderthread/RenderThread.h"
-#include "GpuMemoryTracker.h"
namespace android {
namespace uirenderer {
@@ -25,15 +24,10 @@ RenderState::RenderState(renderthread::RenderThread& thread) : mRenderThread(thr
mThreadId = pthread_self();
}
-void RenderState::onContextCreated() {
- GpuMemoryTracker::onGpuContextCreated();
-}
-
void RenderState::onContextDestroyed() {
for(auto callback : mContextCallbacks) {
callback->onContextDestroyed();
}
- GpuMemoryTracker::onGpuContextDestroyed();
}
void RenderState::postDecStrong(VirtualLightRefBase* object) {
diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h
index ff5d02fe359a..e08d32a7735c 100644
--- a/libs/hwui/renderstate/RenderState.h
+++ b/libs/hwui/renderstate/RenderState.h
@@ -62,7 +62,6 @@ private:
~RenderState() {}
// Context notifications are only to be triggered by renderthread::RenderThread
- void onContextCreated();
void onContextDestroyed();
std::set<IGpuContextCallback*> mContextCallbacks;
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index eaed46c44e5d..d177855e5a7d 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -20,10 +20,12 @@
#include "Layer.h"
#include "Properties.h"
#include "RenderThread.h"
+#include "pipeline/skia/ATraceMemoryDump.h"
#include "pipeline/skia/ShaderCache.h"
#include "pipeline/skia/SkiaMemoryTracer.h"
#include "renderstate/RenderState.h"
#include "thread/CommonPool.h"
+#include <utils/Trace.h>
#include <GrContextOptions.h>
#include <SkExecutor.h>
@@ -184,6 +186,18 @@ void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState)
gpuTracer.logTotals(log);
}
+void CacheManager::onFrameCompleted() {
+ if (ATRACE_ENABLED()) {
+ static skiapipeline::ATraceMemoryDump tracer;
+ tracer.startFrame();
+ SkGraphics::DumpMemoryStatistics(&tracer);
+ if (mGrContext) {
+ mGrContext->dumpMemoryStatistics(&tracer);
+ }
+ tracer.logTraces();
+ }
+}
+
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h
index 968251e9f467..b009cc4f48f2 100644
--- a/libs/hwui/renderthread/CacheManager.h
+++ b/libs/hwui/renderthread/CacheManager.h
@@ -50,6 +50,7 @@ public:
size_t getCacheSize() const { return mMaxResourceBytes; }
size_t getBackgroundCacheSize() const { return mBackgroundResourceBytes; }
+ void onFrameCompleted();
private:
friend class RenderThread;
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 84902210a751..5993e176f0b8 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -16,7 +16,6 @@
#include "CanvasContext.h"
-#include <GpuMemoryTracker.h>
#include <apex/window.h>
#include <fcntl.h>
#include <strings.h>
@@ -558,7 +557,7 @@ void CanvasContext::draw() {
mJankTracker.finishGpuDraw(*forthBehind);
}
- GpuMemoryTracker::onFrameCompleted();
+ mRenderThread.cacheManager().onFrameCompleted();
}
// Called by choreographer to do an RT-driven animation
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index d78f641d45b9..cae3e3b5188c 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -270,7 +270,6 @@ void RenderThread::setGrContext(sk_sp<GrContext> context) {
}
mGrContext = std::move(context);
if (mGrContext) {
- mRenderState->onContextCreated();
DeviceInfo::setMaxTextureSize(mGrContext->maxRenderTargetSize());
}
}
diff --git a/libs/hwui/service/GraphicsStatsService.cpp b/libs/hwui/service/GraphicsStatsService.cpp
index 12c5b836f711..c4186174b637 100644
--- a/libs/hwui/service/GraphicsStatsService.cpp
+++ b/libs/hwui/service/GraphicsStatsService.cpp
@@ -16,24 +16,28 @@
#include "GraphicsStatsService.h"
-#include "JankTracker.h"
-#include "protos/graphicsstats.pb.h"
-
-#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
-#include <log/log.h>
-
#include <errno.h>
#include <fcntl.h>
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
#include <inttypes.h>
+#include <log/log.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
+#include <algorithm>
+#include <map>
+#include <vector>
+
+#include "JankTracker.h"
+#include "protos/graphicsstats.pb.h"
+
namespace android {
namespace uirenderer {
using namespace google::protobuf;
+using namespace uirenderer::protos;
constexpr int32_t sCurrentFileVersion = 1;
constexpr int32_t sHeaderSize = 4;
@@ -42,9 +46,9 @@ static_assert(sizeof(sCurrentFileVersion) == sHeaderSize, "Header size is wrong"
constexpr int sHistogramSize = ProfileData::HistogramSize();
constexpr int sGPUHistogramSize = ProfileData::GPUHistogramSize();
-static bool mergeProfileDataIntoProto(protos::GraphicsStatsProto* proto,
- const std::string& package, int64_t versionCode,
- int64_t startTime, int64_t endTime, const ProfileData* data);
+static bool mergeProfileDataIntoProto(protos::GraphicsStatsProto* proto, const std::string& package,
+ int64_t versionCode, int64_t startTime, int64_t endTime,
+ const ProfileData* data);
static void dumpAsTextToFd(protos::GraphicsStatsProto* proto, int outFd);
class FileDescriptor {
@@ -57,7 +61,7 @@ public:
}
}
bool valid() { return mFd != -1; }
- operator int() { return mFd; } // NOLINT(google-explicit-constructor)
+ operator int() { return mFd; } // NOLINT(google-explicit-constructor)
private:
int mFd;
@@ -167,6 +171,8 @@ bool mergeProfileDataIntoProto(protos::GraphicsStatsProto* proto, const std::str
}
proto->set_package_name(package);
proto->set_version_code(versionCode);
+ proto->set_pipeline(data->pipelineType() == RenderPipelineType::SkiaGL ?
+ GraphicsStatsProto_PipelineType_GL : GraphicsStatsProto_PipelineType_VULKAN);
auto summary = proto->mutable_summary();
summary->set_total_frames(summary->total_frames() + data->totalFrameCount());
summary->set_janky_frames(summary->janky_frames() + data->jankFrameCount());
@@ -179,8 +185,8 @@ bool mergeProfileDataIntoProto(protos::GraphicsStatsProto* proto, const std::str
summary->set_slow_bitmap_upload_count(summary->slow_bitmap_upload_count() +
data->jankTypeCount(kSlowSync));
summary->set_slow_draw_count(summary->slow_draw_count() + data->jankTypeCount(kSlowRT));
- summary->set_missed_deadline_count(summary->missed_deadline_count()
- + data->jankTypeCount(kMissedDeadline));
+ summary->set_missed_deadline_count(summary->missed_deadline_count() +
+ data->jankTypeCount(kMissedDeadline));
bool creatingHistogram = false;
if (proto->histogram_size() == 0) {
@@ -365,17 +371,69 @@ void GraphicsStatsService::saveBuffer(const std::string& path, const std::string
class GraphicsStatsService::Dump {
public:
- Dump(int outFd, DumpType type) : mFd(outFd), mType(type) {}
+ Dump(int outFd, DumpType type) : mFd(outFd), mType(type) {
+ if (mFd == -1 && mType == DumpType::Protobuf) {
+ mType = DumpType::ProtobufStatsd;
+ }
+ }
int fd() { return mFd; }
DumpType type() { return mType; }
protos::GraphicsStatsServiceDumpProto& proto() { return mProto; }
+ void mergeStat(const protos::GraphicsStatsProto& stat);
+ void updateProto();
private:
+ // use package name and app version for a key
+ typedef std::pair<std::string, int64_t> DumpKey;
+
+ std::map<DumpKey, protos::GraphicsStatsProto> mStats;
int mFd;
DumpType mType;
protos::GraphicsStatsServiceDumpProto mProto;
};
+void GraphicsStatsService::Dump::mergeStat(const protos::GraphicsStatsProto& stat) {
+ auto dumpKey = std::make_pair(stat.package_name(), stat.version_code());
+ auto findIt = mStats.find(dumpKey);
+ if (findIt == mStats.end()) {
+ mStats[dumpKey] = stat;
+ } else {
+ auto summary = findIt->second.mutable_summary();
+ summary->set_total_frames(summary->total_frames() + stat.summary().total_frames());
+ summary->set_janky_frames(summary->janky_frames() + stat.summary().janky_frames());
+ summary->set_missed_vsync_count(summary->missed_vsync_count() +
+ stat.summary().missed_vsync_count());
+ summary->set_high_input_latency_count(summary->high_input_latency_count() +
+ stat.summary().high_input_latency_count());
+ summary->set_slow_ui_thread_count(summary->slow_ui_thread_count() +
+ stat.summary().slow_ui_thread_count());
+ summary->set_slow_bitmap_upload_count(summary->slow_bitmap_upload_count() +
+ stat.summary().slow_bitmap_upload_count());
+ summary->set_slow_draw_count(summary->slow_draw_count() + stat.summary().slow_draw_count());
+ summary->set_missed_deadline_count(summary->missed_deadline_count() +
+ stat.summary().missed_deadline_count());
+ for (int bucketIndex = 0; bucketIndex < findIt->second.histogram_size(); bucketIndex++) {
+ auto bucket = findIt->second.mutable_histogram(bucketIndex);
+ bucket->set_frame_count(bucket->frame_count() +
+ stat.histogram(bucketIndex).frame_count());
+ }
+ for (int bucketIndex = 0; bucketIndex < findIt->second.gpu_histogram_size();
+ bucketIndex++) {
+ auto bucket = findIt->second.mutable_gpu_histogram(bucketIndex);
+ bucket->set_frame_count(bucket->frame_count() +
+ stat.gpu_histogram(bucketIndex).frame_count());
+ }
+ findIt->second.set_stats_start(std::min(findIt->second.stats_start(), stat.stats_start()));
+ findIt->second.set_stats_end(std::max(findIt->second.stats_end(), stat.stats_end()));
+ }
+}
+
+void GraphicsStatsService::Dump::updateProto() {
+ for (auto& stat : mStats) {
+ mProto.add_stats()->CopyFrom(stat.second);
+ }
+}
+
GraphicsStatsService::Dump* GraphicsStatsService::createDump(int outFd, DumpType type) {
return new Dump(outFd, type);
}
@@ -396,8 +454,9 @@ void GraphicsStatsService::addToDump(Dump* dump, const std::string& path,
path.empty() ? "<empty>" : path.c_str(), data);
return;
}
-
- if (dump->type() == DumpType::Protobuf) {
+ if (dump->type() == DumpType::ProtobufStatsd) {
+ dump->mergeStat(statsProto);
+ } else if (dump->type() == DumpType::Protobuf) {
dump->proto().add_stats()->CopyFrom(statsProto);
} else {
dumpAsTextToFd(&statsProto, dump->fd());
@@ -409,7 +468,9 @@ void GraphicsStatsService::addToDump(Dump* dump, const std::string& path) {
if (!parseFromFile(path, &statsProto)) {
return;
}
- if (dump->type() == DumpType::Protobuf) {
+ if (dump->type() == DumpType::ProtobufStatsd) {
+ dump->mergeStat(statsProto);
+ } else if (dump->type() == DumpType::Protobuf) {
dump->proto().add_stats()->CopyFrom(statsProto);
} else {
dumpAsTextToFd(&statsProto, dump->fd());
@@ -424,5 +485,79 @@ void GraphicsStatsService::finishDump(Dump* dump) {
delete dump;
}
+class MemOutputStreamLite : public io::ZeroCopyOutputStream {
+public:
+ explicit MemOutputStreamLite() : mCopyAdapter(), mImpl(&mCopyAdapter) {}
+ virtual ~MemOutputStreamLite() {}
+
+ virtual bool Next(void** data, int* size) override { return mImpl.Next(data, size); }
+
+ virtual void BackUp(int count) override { mImpl.BackUp(count); }
+
+ virtual int64 ByteCount() const override { return mImpl.ByteCount(); }
+
+ bool Flush() { return mImpl.Flush(); }
+
+ void copyData(const DumpMemoryFn& reader, void* param1, void* param2) {
+ int bufferOffset = 0;
+ int totalSize = mCopyAdapter.mBuffersSize - mCopyAdapter.mCurrentBufferUnusedSize;
+ int totalDataLeft = totalSize;
+ for (auto& it : mCopyAdapter.mBuffers) {
+ int bufferSize = std::min(totalDataLeft, (int)it.size()); // last buffer is not full
+ reader(it.data(), bufferOffset, bufferSize, totalSize, param1, param2);
+ bufferOffset += bufferSize;
+ totalDataLeft -= bufferSize;
+ }
+ }
+
+private:
+ struct MemAdapter : public io::CopyingOutputStream {
+ // Data is stored in an array of buffers.
+ // JNI SetByteArrayRegion assembles data in one continuous Java byte[] buffer.
+ std::vector<std::vector<unsigned char>> mBuffers;
+ int mBuffersSize = 0; // total bytes allocated in mBuffers
+ int mCurrentBufferUnusedSize = 0; // unused bytes in the last buffer mBuffers.back()
+ unsigned char* mCurrentBuffer = nullptr; // pointer to next free byte in mBuffers.back()
+
+ explicit MemAdapter() {}
+ virtual ~MemAdapter() {}
+
+ virtual bool Write(const void* buffer, int size) override {
+ while (size > 0) {
+ if (0 == mCurrentBufferUnusedSize) {
+ mCurrentBufferUnusedSize =
+ std::max(size, mBuffersSize ? 2 * mBuffersSize : 10000);
+ mBuffers.emplace_back();
+ mBuffers.back().resize(mCurrentBufferUnusedSize);
+ mCurrentBuffer = mBuffers.back().data();
+ mBuffersSize += mCurrentBufferUnusedSize;
+ }
+ int dataMoved = std::min(mCurrentBufferUnusedSize, size);
+ memcpy(mCurrentBuffer, buffer, dataMoved);
+ mCurrentBufferUnusedSize -= dataMoved;
+ mCurrentBuffer += dataMoved;
+ buffer = reinterpret_cast<const unsigned char*>(buffer) + dataMoved;
+ size -= dataMoved;
+ }
+ return true;
+ }
+ };
+
+ MemOutputStreamLite::MemAdapter mCopyAdapter;
+ io::CopyingOutputStreamAdaptor mImpl;
+};
+
+void GraphicsStatsService::finishDumpInMemory(Dump* dump, const DumpMemoryFn& reader, void* param1,
+ void* param2) {
+ MemOutputStreamLite stream;
+ dump->updateProto();
+ bool success = dump->proto().SerializeToZeroCopyStream(&stream) && stream.Flush();
+ delete dump;
+ if (!success) {
+ return;
+ }
+ stream.copyData(reader, param1, param2);
+}
+
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/service/GraphicsStatsService.h b/libs/hwui/service/GraphicsStatsService.h
index 389f599f439f..4bed96330a52 100644
--- a/libs/hwui/service/GraphicsStatsService.h
+++ b/libs/hwui/service/GraphicsStatsService.h
@@ -27,6 +27,9 @@ namespace protos {
class GraphicsStatsProto;
}
+typedef void (*DumpMemoryFn)(void* buffer, int bufferOffset, int bufferSize, int totalSize,
+ void* param1, void* param2);
+
/*
* The exported entry points used by GraphicsStatsService.java in f/b/services/core
*
@@ -40,6 +43,7 @@ public:
enum class DumpType {
Text,
Protobuf,
+ ProtobufStatsd,
};
ANDROID_API static void saveBuffer(const std::string& path, const std::string& package,
@@ -52,6 +56,8 @@ public:
int64_t startTime, int64_t endTime, const ProfileData* data);
ANDROID_API static void addToDump(Dump* dump, const std::string& path);
ANDROID_API static void finishDump(Dump* dump);
+ ANDROID_API static void finishDumpInMemory(Dump* dump, const DumpMemoryFn& reader, void* param1,
+ void* param2);
// Visible for testing
static bool parseFromFile(const std::string& path, protos::GraphicsStatsProto* output);
diff --git a/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp b/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp
deleted file mode 100644
index dac888cd79ca..000000000000
--- a/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <GpuMemoryTracker.h>
-#include <gtest/gtest.h>
-
-#include "renderthread/EglManager.h"
-#include "renderthread/RenderThread.h"
-#include "tests/common/TestUtils.h"
-
-#include <utils/StrongPointer.h>
-
-using namespace android;
-using namespace android::uirenderer;
-using namespace android::uirenderer::renderthread;
-
-class TestGPUObject : public GpuMemoryTracker {
-public:
- TestGPUObject() : GpuMemoryTracker(GpuObjectType::Texture) {}
-
- void changeSize(int newSize) { notifySizeChanged(newSize); }
-};
-
-// Other tests may have created a renderthread and EGL context.
-// This will destroy the EGLContext on RenderThread if it exists so that the
-// current thread can spoof being a GPU thread
-static void destroyEglContext() {
- if (TestUtils::isRenderThreadRunning()) {
- TestUtils::runOnRenderThread([](RenderThread& thread) { thread.destroyRenderingContext(); });
- }
-}
-
-TEST(GpuMemoryTracker, sizeCheck) {
- destroyEglContext();
-
- GpuMemoryTracker::onGpuContextCreated();
- ASSERT_EQ(0, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture));
- ASSERT_EQ(0, GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture));
- {
- TestGPUObject myObj;
- ASSERT_EQ(1, GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture));
- myObj.changeSize(500);
- ASSERT_EQ(500, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture));
- myObj.changeSize(1000);
- ASSERT_EQ(1000, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture));
- myObj.changeSize(300);
- ASSERT_EQ(300, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture));
- }
- ASSERT_EQ(0, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture));
- ASSERT_EQ(0, GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture));
- GpuMemoryTracker::onGpuContextDestroyed();
-}
diff --git a/location/java/android/location/Criteria.java b/location/java/android/location/Criteria.java
index 1370b1095ae1..26f73f784879 100644
--- a/location/java/android/location/Criteria.java
+++ b/location/java/android/location/Criteria.java
@@ -16,9 +16,16 @@
package android.location;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* A class indicating the application criteria for selecting a
* location provider. Providers may be ordered according to accuracy,
@@ -26,6 +33,25 @@ import android.os.Parcelable;
* cost.
*/
public class Criteria implements Parcelable {
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({NO_REQUIREMENT, POWER_LOW, POWER_MEDIUM, POWER_HIGH})
+ public @interface PowerRequirement {
+ }
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({NO_REQUIREMENT, ACCURACY_LOW, ACCURACY_MEDIUM, ACCURACY_HIGH})
+ public @interface AccuracyRequirement {
+ }
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({NO_REQUIREMENT, ACCURACY_FINE, ACCURACY_COARSE})
+ public @interface LocationAccuracyRequirement {
+ }
+
/**
* A constant indicating that the application does not choose to
* place requirement on a particular feature.
@@ -81,15 +107,15 @@ public class Criteria implements Parcelable {
*/
public static final int ACCURACY_HIGH = 3;
- private int mHorizontalAccuracy = NO_REQUIREMENT;
- private int mVerticalAccuracy = NO_REQUIREMENT;
- private int mSpeedAccuracy = NO_REQUIREMENT;
- private int mBearingAccuracy = NO_REQUIREMENT;
- private int mPowerRequirement = NO_REQUIREMENT;
- private boolean mAltitudeRequired = false;
- private boolean mBearingRequired = false;
- private boolean mSpeedRequired = false;
- private boolean mCostAllowed = false;
+ private int mHorizontalAccuracy = NO_REQUIREMENT;
+ private int mVerticalAccuracy = NO_REQUIREMENT;
+ private int mSpeedAccuracy = NO_REQUIREMENT;
+ private int mBearingAccuracy = NO_REQUIREMENT;
+ private int mPowerRequirement = NO_REQUIREMENT;
+ private boolean mAltitudeRequired = false;
+ private boolean mBearingRequired = false;
+ private boolean mSpeedRequired = false;
+ private boolean mCostAllowed = false;
/**
* Constructs a new Criteria object. The new object will have no
@@ -97,7 +123,8 @@ public class Criteria implements Parcelable {
* require altitude, speed, or bearing; and will not allow monetary
* cost.
*/
- public Criteria() {}
+ public Criteria() {
+ }
/**
* Constructs a new Criteria object that is a copy of the given criteria.
@@ -115,125 +142,121 @@ public class Criteria implements Parcelable {
}
/**
- * Indicates the desired horizontal accuracy (latitude and longitude).
- * Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_MEDIUM},
- * {@link #ACCURACY_HIGH} or {@link #NO_REQUIREMENT}.
- * More accurate location may consume more power and may take longer.
+ * Indicates the desired horizontal accuracy (latitude and longitude). Accuracy may be
+ * {@link #ACCURACY_LOW}, {@link #ACCURACY_MEDIUM}, {@link #ACCURACY_HIGH} or
+ * {@link #NO_REQUIREMENT}. More accurate location may consume more power and may take longer.
*
* @throws IllegalArgumentException if accuracy is not one of the supported constants
*/
- public void setHorizontalAccuracy(int accuracy) {
- if (accuracy < NO_REQUIREMENT || accuracy > ACCURACY_HIGH) {
- throw new IllegalArgumentException("accuracy=" + accuracy);
- }
- mHorizontalAccuracy = accuracy;
+ public void setHorizontalAccuracy(@AccuracyRequirement int accuracy) {
+ mHorizontalAccuracy = Preconditions.checkArgumentInRange(accuracy, NO_REQUIREMENT,
+ ACCURACY_HIGH, "accuracy");
}
/**
* Returns a constant indicating the desired horizontal accuracy (latitude and longitude).
- * Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_MEDIUM},
- * {@link #ACCURACY_HIGH} or {@link #NO_REQUIREMENT}.
+ *
+ * @see #setHorizontalAccuracy(int)
*/
+ @AccuracyRequirement
public int getHorizontalAccuracy() {
return mHorizontalAccuracy;
}
/**
- * Indicates the desired vertical accuracy (altitude).
- * Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_MEDIUM},
- * {@link #ACCURACY_HIGH} or {@link #NO_REQUIREMENT}.
- * More accurate location may consume more power and may take longer.
+ * Indicates the desired vertical accuracy (altitude). Accuracy may be {@link #ACCURACY_LOW},
+ * {@link #ACCURACY_MEDIUM}, {@link #ACCURACY_HIGH} or {@link #NO_REQUIREMENT}. More accurate
+ * location may consume more power and may take longer.
*
* @throws IllegalArgumentException if accuracy is not one of the supported constants
*/
- public void setVerticalAccuracy(int accuracy) {
- if (accuracy < NO_REQUIREMENT || accuracy > ACCURACY_HIGH) {
- throw new IllegalArgumentException("accuracy=" + accuracy);
- }
- mVerticalAccuracy = accuracy;
+ public void setVerticalAccuracy(@AccuracyRequirement int accuracy) {
+ mVerticalAccuracy = Preconditions.checkArgumentInRange(accuracy, NO_REQUIREMENT,
+ ACCURACY_HIGH, "accuracy");
}
/**
* Returns a constant indicating the desired vertical accuracy (altitude).
- * Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_HIGH},
- * or {@link #NO_REQUIREMENT}.
+ *
+ * @see #setVerticalAccuracy(int)
*/
+ @AccuracyRequirement
public int getVerticalAccuracy() {
return mVerticalAccuracy;
}
/**
- * Indicates the desired speed accuracy.
- * Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_HIGH},
- * or {@link #NO_REQUIREMENT}.
- * More accurate location may consume more power and may take longer.
+ * Indicates the desired speed accuracy. Accuracy may be {@link #ACCURACY_LOW},
+ * {@link #ACCURACY_MEDIUM}, {@link #ACCURACY_HIGH}, or {@link #NO_REQUIREMENT}. More accurate
+ * location may consume more power and may take longer.
*
* @throws IllegalArgumentException if accuracy is not one of the supported constants
*/
- public void setSpeedAccuracy(int accuracy) {
- if (accuracy < NO_REQUIREMENT || accuracy > ACCURACY_HIGH) {
- throw new IllegalArgumentException("accuracy=" + accuracy);
- }
- mSpeedAccuracy = accuracy;
+ public void setSpeedAccuracy(@AccuracyRequirement int accuracy) {
+ mSpeedAccuracy = Preconditions.checkArgumentInRange(accuracy, NO_REQUIREMENT, ACCURACY_HIGH,
+ "accuracy");
}
/**
- * Returns a constant indicating the desired speed accuracy
- * Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_HIGH},
- * or {@link #NO_REQUIREMENT}.
+ * Returns a constant indicating the desired speed accuracy.
+ *
+ * @see #setSpeedAccuracy(int)
*/
+ @AccuracyRequirement
public int getSpeedAccuracy() {
return mSpeedAccuracy;
}
/**
- * Indicates the desired bearing accuracy.
- * Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_HIGH},
- * or {@link #NO_REQUIREMENT}.
- * More accurate location may consume more power and may take longer.
+ * Indicates the desired bearing accuracy. Accuracy may be {@link #ACCURACY_LOW},
+ * {@link #ACCURACY_MEDIUM}, {@link #ACCURACY_HIGH}, or {@link #NO_REQUIREMENT}. More accurate
+ * location may consume more power and may take longer.
*
* @throws IllegalArgumentException if accuracy is not one of the supported constants
*/
- public void setBearingAccuracy(int accuracy) {
- if (accuracy < NO_REQUIREMENT || accuracy > ACCURACY_HIGH) {
- throw new IllegalArgumentException("accuracy=" + accuracy);
- }
- mBearingAccuracy = accuracy;
+ public void setBearingAccuracy(@AccuracyRequirement int accuracy) {
+ mBearingAccuracy = Preconditions.checkArgumentInRange(accuracy, NO_REQUIREMENT,
+ ACCURACY_HIGH, "accuracy");
}
/**
* Returns a constant indicating the desired bearing accuracy.
- * Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_HIGH},
- * or {@link #NO_REQUIREMENT}.
+ *
+ * @see #setBearingAccuracy(int)
*/
+ @AccuracyRequirement
public int getBearingAccuracy() {
return mBearingAccuracy;
}
/**
- * Indicates the desired accuracy for latitude and longitude. Accuracy
- * may be {@link #ACCURACY_FINE} if desired location
- * is fine, else it can be {@link #ACCURACY_COARSE}.
- * More accurate location may consume more power and may take longer.
+ * Indicates the desired accuracy for latitude and longitude. Accuracy may be
+ * {@link #ACCURACY_FINE} or {@link #ACCURACY_COARSE}. More accurate location may consume more
+ * power and may take longer.
*
* @throws IllegalArgumentException if accuracy is not one of the supported constants
*/
- public void setAccuracy(int accuracy) {
- if (accuracy < NO_REQUIREMENT || accuracy > ACCURACY_COARSE) {
- throw new IllegalArgumentException("accuracy=" + accuracy);
- }
- if (accuracy == ACCURACY_FINE) {
- mHorizontalAccuracy = ACCURACY_HIGH;
- } else {
- mHorizontalAccuracy = ACCURACY_LOW;
+ public void setAccuracy(@LocationAccuracyRequirement int accuracy) {
+ Preconditions.checkArgumentInRange(accuracy, NO_REQUIREMENT, ACCURACY_COARSE, "accuracy");
+ switch (accuracy) {
+ case NO_REQUIREMENT:
+ setHorizontalAccuracy(NO_REQUIREMENT);
+ break;
+ case ACCURACY_FINE:
+ setHorizontalAccuracy(ACCURACY_HIGH);
+ break;
+ case ACCURACY_COARSE:
+ setHorizontalAccuracy(ACCURACY_LOW);
+ break;
}
}
/**
- * Returns a constant indicating desired accuracy of location
- * Accuracy may be {@link #ACCURACY_FINE} if desired location
- * is fine, else it can be {@link #ACCURACY_COARSE}.
+ * Returns a constant indicating desired accuracy of location.
+ *
+ * @see #setAccuracy(int)
*/
+ @LocationAccuracyRequirement
public int getAccuracy() {
if (mHorizontalAccuracy >= ACCURACY_HIGH) {
return ACCURACY_FINE;
@@ -243,21 +266,20 @@ public class Criteria implements Parcelable {
}
/**
- * Indicates the desired maximum power level. The level parameter
- * must be one of NO_REQUIREMENT, POWER_LOW, POWER_MEDIUM, or
- * POWER_HIGH.
+ * Indicates the desired maximum power requirement. The power requirement parameter may be
+ * {@link #NO_REQUIREMENT}, {@link #POWER_LOW}, {@link #POWER_MEDIUM}, or {@link #POWER_HIGH}.
*/
- public void setPowerRequirement(int level) {
- if (level < NO_REQUIREMENT || level > POWER_HIGH) {
- throw new IllegalArgumentException("level=" + level);
- }
- mPowerRequirement = level;
+ public void setPowerRequirement(@PowerRequirement int powerRequirement) {
+ mPowerRequirement = Preconditions.checkArgumentInRange(powerRequirement, NO_REQUIREMENT,
+ POWER_HIGH, "powerRequirement");
}
/**
- * Returns a constant indicating the desired power requirement. The
- * returned
+ * Returns a constant indicating the desired maximum power requirement.
+ *
+ * @see #setPowerRequirement(int)
*/
+ @PowerRequirement
public int getPowerRequirement() {
return mPowerRequirement;
}
@@ -277,8 +299,8 @@ public class Criteria implements Parcelable {
}
/**
- * Indicates whether the provider must provide altitude information.
- * Not all fixes are guaranteed to contain such information.
+ * Indicates whether the provider must provide altitude information. Not all fixes are
+ * guaranteed to contain such information.
*/
public void setAltitudeRequired(boolean altitudeRequired) {
mAltitudeRequired = altitudeRequired;
@@ -286,15 +308,16 @@ public class Criteria implements Parcelable {
/**
* Returns whether the provider must provide altitude information.
- * Not all fixes are guaranteed to contain such information.
+ *
+ * @see #setAltitudeRequired(boolean)
*/
public boolean isAltitudeRequired() {
return mAltitudeRequired;
}
/**
- * Indicates whether the provider must provide speed information.
- * Not all fixes are guaranteed to contain such information.
+ * Indicates whether the provider must provide speed information. Not all fixes are guaranteed
+ * to contain such information.
*/
public void setSpeedRequired(boolean speedRequired) {
mSpeedRequired = speedRequired;
@@ -302,15 +325,16 @@ public class Criteria implements Parcelable {
/**
* Returns whether the provider must provide speed information.
- * Not all fixes are guaranteed to contain such information.
+ *
+ * @see #setSpeedRequired(boolean)
*/
public boolean isSpeedRequired() {
return mSpeedRequired;
}
/**
- * Indicates whether the provider must provide bearing information.
- * Not all fixes are guaranteed to contain such information.
+ * Indicates whether the provider must provide bearing information. Not all fixes are guaranteed
+ * to contain such information.
*/
public void setBearingRequired(boolean bearingRequired) {
mBearingRequired = bearingRequired;
@@ -318,34 +342,36 @@ public class Criteria implements Parcelable {
/**
* Returns whether the provider must provide bearing information.
- * Not all fixes are guaranteed to contain such information.
+ *
+ * @see #setBearingRequired(boolean)
*/
public boolean isBearingRequired() {
return mBearingRequired;
}
- public static final @android.annotation.NonNull Parcelable.Creator<Criteria> CREATOR =
- new Parcelable.Creator<Criteria>() {
- @Override
- public Criteria createFromParcel(Parcel in) {
- Criteria c = new Criteria();
- c.mHorizontalAccuracy = in.readInt();
- c.mVerticalAccuracy = in.readInt();
- c.mSpeedAccuracy = in.readInt();
- c.mBearingAccuracy = in.readInt();
- c.mPowerRequirement = in.readInt();
- c.mAltitudeRequired = in.readInt() != 0;
- c.mBearingRequired = in.readInt() != 0;
- c.mSpeedRequired = in.readInt() != 0;
- c.mCostAllowed = in.readInt() != 0;
- return c;
- }
-
- @Override
- public Criteria[] newArray(int size) {
- return new Criteria[size];
- }
- };
+ @NonNull
+ public static final Parcelable.Creator<Criteria> CREATOR =
+ new Parcelable.Creator<Criteria>() {
+ @Override
+ public Criteria createFromParcel(Parcel in) {
+ Criteria c = new Criteria();
+ c.mHorizontalAccuracy = in.readInt();
+ c.mVerticalAccuracy = in.readInt();
+ c.mSpeedAccuracy = in.readInt();
+ c.mBearingAccuracy = in.readInt();
+ c.mPowerRequirement = in.readInt();
+ c.mAltitudeRequired = in.readInt() != 0;
+ c.mBearingRequired = in.readInt() != 0;
+ c.mSpeedRequired = in.readInt() != 0;
+ c.mCostAllowed = in.readInt() != 0;
+ return c;
+ }
+
+ @Override
+ public Criteria[] newArray(int size) {
+ return new Criteria[size];
+ }
+ };
@Override
public int describeContents() {
@@ -365,42 +391,57 @@ public class Criteria implements Parcelable {
parcel.writeInt(mCostAllowed ? 1 : 0);
}
- private static String powerToString(int power) {
+ @Override
+ public String toString() {
+ StringBuilder s = new StringBuilder();
+ s.append("Criteria[");
+ s.append("power=").append(requirementToString(mPowerRequirement)).append(", ");
+ s.append("accuracy=").append(requirementToString(mHorizontalAccuracy));
+ if (mVerticalAccuracy != NO_REQUIREMENT) {
+ s.append(", verticalAccuracy=").append(requirementToString(mVerticalAccuracy));
+ }
+ if (mSpeedAccuracy != NO_REQUIREMENT) {
+ s.append(", speedAccuracy=").append(requirementToString(mSpeedAccuracy));
+ }
+ if (mBearingAccuracy != NO_REQUIREMENT) {
+ s.append(", bearingAccuracy=").append(requirementToString(mBearingAccuracy));
+ }
+ if (mAltitudeRequired || mBearingRequired || mSpeedRequired) {
+ s.append(", required=[");
+ if (mAltitudeRequired) {
+ s.append("altitude, ");
+ }
+ if (mBearingRequired) {
+ s.append("bearing, ");
+ }
+ if (mSpeedRequired) {
+ s.append("speed, ");
+ }
+ s.setLength(s.length() - 2);
+ s.append("]");
+ }
+ if (mCostAllowed) {
+ s.append(", costAllowed");
+ }
+ s.append(']');
+ return s.toString();
+ }
+
+ private static String requirementToString(int power) {
switch (power) {
case NO_REQUIREMENT:
- return "NO_REQ";
+ return "None";
+ //case ACCURACY_LOW:
case POWER_LOW:
- return "LOW";
+ return "Low";
+ //case ACCURACY_MEDIUM:
case POWER_MEDIUM:
- return "MEDIUM";
+ return "Medium";
+ //case ACCURACY_HIGH:
case POWER_HIGH:
- return "HIGH";
+ return "High";
default:
return "???";
}
}
-
- private static String accuracyToString(int accuracy) {
- switch (accuracy) {
- case NO_REQUIREMENT:
- return "---";
- case ACCURACY_HIGH:
- return "HIGH";
- case ACCURACY_MEDIUM:
- return "MEDIUM";
- case ACCURACY_LOW:
- return "LOW";
- default:
- return "???";
- }
- }
-
- @Override
- public String toString() {
- StringBuilder s = new StringBuilder();
- s.append("Criteria[power=").append(powerToString(mPowerRequirement));
- s.append(" acc=").append(accuracyToString(mHorizontalAccuracy));
- s.append(']');
- return s.toString();
- }
}
diff --git a/location/java/com/android/internal/location/ProviderProperties.java b/location/java/com/android/internal/location/ProviderProperties.java
index def96f0fb674..68f9ec3c530b 100644
--- a/location/java/com/android/internal/location/ProviderProperties.java
+++ b/location/java/com/android/internal/location/ProviderProperties.java
@@ -16,15 +16,36 @@
package com.android.internal.location;
+import android.annotation.IntDef;
+import android.location.Criteria;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* A Parcelable containing (legacy) location provider properties.
* This object is just used inside the framework and system services.
+ *
* @hide
*/
public final class ProviderProperties implements Parcelable {
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({Criteria.POWER_LOW, Criteria.POWER_MEDIUM, Criteria.POWER_HIGH})
+ public @interface PowerRequirement {
+ }
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({Criteria.ACCURACY_FINE, Criteria.ACCURACY_COARSE})
+ public @interface Accuracy {
+ }
+
/**
* True if provider requires access to a
* data network (e.g., the Internet), false otherwise.
@@ -79,58 +100,58 @@ public final class ProviderProperties implements Parcelable {
/**
* Power requirement for this provider.
- *
- * @return the power requirement for this provider, as one of the
- * constants Criteria.POWER_*.
*/
+ @PowerRequirement
public final int mPowerRequirement;
/**
* Constant describing the horizontal accuracy returned
* by this provider.
- *
- * @return the horizontal accuracy for this provider, as one of the
- * constants Criteria.ACCURACY_COARSE or Criteria.ACCURACY_FINE
*/
+ @Accuracy
public final int mAccuracy;
- public ProviderProperties(boolean mRequiresNetwork,
- boolean mRequiresSatellite, boolean mRequiresCell, boolean mHasMonetaryCost,
- boolean mSupportsAltitude, boolean mSupportsSpeed, boolean mSupportsBearing,
- int mPowerRequirement, int mAccuracy) {
- this.mRequiresNetwork = mRequiresNetwork;
- this.mRequiresSatellite = mRequiresSatellite;
- this.mRequiresCell = mRequiresCell;
- this.mHasMonetaryCost = mHasMonetaryCost;
- this.mSupportsAltitude = mSupportsAltitude;
- this.mSupportsSpeed = mSupportsSpeed;
- this.mSupportsBearing = mSupportsBearing;
- this.mPowerRequirement = mPowerRequirement;
- this.mAccuracy = mAccuracy;
+ public ProviderProperties(boolean requiresNetwork, boolean requiresSatellite,
+ boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
+ boolean supportsSpeed, boolean supportsBearing, @PowerRequirement int powerRequirement,
+ @Accuracy int accuracy) {
+ mRequiresNetwork = requiresNetwork;
+ mRequiresSatellite = requiresSatellite;
+ mRequiresCell = requiresCell;
+ mHasMonetaryCost = hasMonetaryCost;
+ mSupportsAltitude = supportsAltitude;
+ mSupportsSpeed = supportsSpeed;
+ mSupportsBearing = supportsBearing;
+ mPowerRequirement = Preconditions.checkArgumentInRange(powerRequirement, Criteria.POWER_LOW,
+ Criteria.POWER_HIGH, "powerRequirement");
+ mAccuracy = Preconditions.checkArgumentInRange(accuracy, Criteria.ACCURACY_FINE,
+ Criteria.ACCURACY_COARSE, "accuracy");
}
public static final Parcelable.Creator<ProviderProperties> CREATOR =
new Parcelable.Creator<ProviderProperties>() {
- @Override
- public ProviderProperties createFromParcel(Parcel in) {
- boolean requiresNetwork = in.readInt() == 1;
- boolean requiresSatellite = in.readInt() == 1;
- boolean requiresCell = in.readInt() == 1;
- boolean hasMonetaryCost = in.readInt() == 1;
- boolean supportsAltitude = in.readInt() == 1;
- boolean supportsSpeed = in.readInt() == 1;
- boolean supportsBearing = in.readInt() == 1;
- int powerRequirement = in.readInt();
- int accuracy = in.readInt();
- return new ProviderProperties(requiresNetwork, requiresSatellite,
- requiresCell, hasMonetaryCost, supportsAltitude, supportsSpeed, supportsBearing,
- powerRequirement, accuracy);
- }
- @Override
- public ProviderProperties[] newArray(int size) {
- return new ProviderProperties[size];
- }
- };
+ @Override
+ public ProviderProperties createFromParcel(Parcel in) {
+ boolean requiresNetwork = in.readInt() == 1;
+ boolean requiresSatellite = in.readInt() == 1;
+ boolean requiresCell = in.readInt() == 1;
+ boolean hasMonetaryCost = in.readInt() == 1;
+ boolean supportsAltitude = in.readInt() == 1;
+ boolean supportsSpeed = in.readInt() == 1;
+ boolean supportsBearing = in.readInt() == 1;
+ int powerRequirement = in.readInt();
+ int accuracy = in.readInt();
+ return new ProviderProperties(requiresNetwork, requiresSatellite,
+ requiresCell, hasMonetaryCost, supportsAltitude, supportsSpeed,
+ supportsBearing,
+ powerRequirement, accuracy);
+ }
+
+ @Override
+ public ProviderProperties[] newArray(int size) {
+ return new ProviderProperties[size];
+ }
+ };
@Override
public int describeContents() {
@@ -149,4 +170,67 @@ public final class ProviderProperties implements Parcelable {
parcel.writeInt(mPowerRequirement);
parcel.writeInt(mAccuracy);
}
+
+ @Override
+ public String toString() {
+ StringBuilder b = new StringBuilder("ProviderProperties[");
+ b.append("power=").append(powerToString(mPowerRequirement)).append(", ");
+ b.append("accuracy=").append(accuracyToString(mAccuracy));
+ if (mRequiresNetwork || mRequiresSatellite || mRequiresCell) {
+ b.append(", requires=");
+ if (mRequiresNetwork) {
+ b.append("network,");
+ }
+ if (mRequiresSatellite) {
+ b.append("satellite,");
+ }
+ if (mRequiresCell) {
+ b.append("cell,");
+ }
+ b.setLength(b.length() - 1);
+ }
+ if (mHasMonetaryCost) {
+ b.append(", hasMonetaryCost");
+ }
+ if (mSupportsBearing || mSupportsSpeed || mSupportsAltitude) {
+ b.append(", supports=[");
+ if (mSupportsBearing) {
+ b.append("bearing, ");
+ }
+ if (mSupportsSpeed) {
+ b.append("speed, ");
+ }
+ if (mSupportsAltitude) {
+ b.append("altitude, ");
+ }
+ b.setLength(b.length() - 2);
+ b.append("]");
+ }
+ b.append("]");
+ return b.toString();
+ }
+
+ private static String powerToString(@PowerRequirement int power) {
+ switch (power) {
+ case Criteria.POWER_LOW:
+ return "Low";
+ case Criteria.POWER_MEDIUM:
+ return "Medium";
+ case Criteria.POWER_HIGH:
+ return "High";
+ default:
+ return "???";
+ }
+ }
+
+ private static String accuracyToString(@Accuracy int accuracy) {
+ switch (accuracy) {
+ case Criteria.ACCURACY_COARSE:
+ return "Coarse";
+ case Criteria.ACCURACY_FINE:
+ return "Fine";
+ default:
+ return "???";
+ }
+ }
}
diff --git a/location/java/com/android/internal/location/ProviderRequest.java b/location/java/com/android/internal/location/ProviderRequest.java
index c23f49976799..8d8df4533ebe 100644
--- a/location/java/com/android/internal/location/ProviderRequest.java
+++ b/location/java/com/android/internal/location/ProviderRequest.java
@@ -20,33 +20,42 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.location.LocationRequest;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.WorkSource;
import android.util.TimeUtils;
+import com.android.internal.util.Preconditions;
+
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/** @hide */
public final class ProviderRequest implements Parcelable {
+
+ public static final ProviderRequest EMPTY_REQUEST = new ProviderRequest(false, Long.MAX_VALUE,
+ false, false,
+ Collections.emptyList(), new WorkSource());
+
/** Location reporting is requested (true) */
@UnsupportedAppUsage
- public boolean reportLocation = false;
+ public final boolean reportLocation;
/** The smallest requested interval */
@UnsupportedAppUsage
- public long interval = Long.MAX_VALUE;
+ public final long interval;
/**
- * When this flag is true, providers should ignore all location settings, user consents, power
- * restrictions or any other restricting factors and always satisfy this request to the best of
- * their ability. This flag should only be used in event of an emergency.
+ * Whether provider shall make stronger than normal tradeoffs to substantially restrict power
+ * use.
*/
- public boolean locationSettingsIgnored = false;
+ public final boolean lowPowerMode;
/**
- * Whether provider shall make stronger than normal tradeoffs to substantially restrict power
- * use.
+ * When this flag is true, providers should ignore all location settings, user consents, power
+ * restrictions or any other restricting factors and always satisfy this request to the best of
+ * their ability. This flag should only be used in event of an emergency.
*/
- public boolean lowPowerMode = false;
+ public final boolean locationSettingsIgnored;
/**
* A more detailed set of requests.
@@ -56,26 +65,37 @@ public final class ProviderRequest implements Parcelable {
* low power fast interval request.
*/
@UnsupportedAppUsage
- public final List<LocationRequest> locationRequests = new ArrayList<>();
+ public final List<LocationRequest> locationRequests;
- @UnsupportedAppUsage
- public ProviderRequest() {
+ public final WorkSource workSource;
+
+ private ProviderRequest(boolean reportLocation, long interval, boolean lowPowerMode,
+ boolean locationSettingsIgnored, List<LocationRequest> locationRequests,
+ WorkSource workSource) {
+ this.reportLocation = reportLocation;
+ this.interval = interval;
+ this.lowPowerMode = lowPowerMode;
+ this.locationSettingsIgnored = locationSettingsIgnored;
+ this.locationRequests = Preconditions.checkNotNull(locationRequests);
+ this.workSource = Preconditions.checkNotNull(workSource);
}
public static final Parcelable.Creator<ProviderRequest> CREATOR =
new Parcelable.Creator<ProviderRequest>() {
@Override
public ProviderRequest createFromParcel(Parcel in) {
- ProviderRequest request = new ProviderRequest();
- request.reportLocation = in.readInt() == 1;
- request.interval = in.readLong();
- request.lowPowerMode = in.readBoolean();
- request.locationSettingsIgnored = in.readBoolean();
+ boolean reportLocation = in.readInt() == 1;
+ long interval = in.readLong();
+ boolean lowPowerMode = in.readBoolean();
+ boolean locationSettingsIgnored = in.readBoolean();
int count = in.readInt();
+ ArrayList<LocationRequest> locationRequests = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
- request.locationRequests.add(LocationRequest.CREATOR.createFromParcel(in));
+ locationRequests.add(LocationRequest.CREATOR.createFromParcel(in));
}
- return request;
+ WorkSource workSource = in.readParcelable(null);
+ return new ProviderRequest(reportLocation, interval, lowPowerMode,
+ locationSettingsIgnored, locationRequests, workSource);
}
@Override
@@ -106,14 +126,13 @@ public final class ProviderRequest implements Parcelable {
StringBuilder s = new StringBuilder();
s.append("ProviderRequest[");
if (reportLocation) {
- s.append("ON");
- s.append(" interval=");
+ s.append("interval=");
TimeUtils.formatDuration(interval, s);
if (lowPowerMode) {
- s.append(" lowPowerMode");
+ s.append(", lowPowerMode");
}
if (locationSettingsIgnored) {
- s.append(" locationSettingsIgnored");
+ s.append(", locationSettingsIgnored");
}
} else {
s.append("OFF");
@@ -121,4 +140,67 @@ public final class ProviderRequest implements Parcelable {
s.append(']');
return s.toString();
}
+
+ /**
+ * A Builder for {@link ProviderRequest}s.
+ */
+ public static class Builder {
+ private long mInterval = Long.MAX_VALUE;
+ private boolean mLowPowerMode;
+ private boolean mLocationSettingsIgnored;
+ private List<LocationRequest> mLocationRequests = Collections.emptyList();
+ private WorkSource mWorkSource = new WorkSource();
+
+ public long getInterval() {
+ return mInterval;
+ }
+
+ public void setInterval(long interval) {
+ this.mInterval = interval;
+ }
+
+ public boolean isLowPowerMode() {
+ return mLowPowerMode;
+ }
+
+ public void setLowPowerMode(boolean lowPowerMode) {
+ this.mLowPowerMode = lowPowerMode;
+ }
+
+ public boolean isLocationSettingsIgnored() {
+ return mLocationSettingsIgnored;
+ }
+
+ public void setLocationSettingsIgnored(boolean locationSettingsIgnored) {
+ this.mLocationSettingsIgnored = locationSettingsIgnored;
+ }
+
+ public List<LocationRequest> getLocationRequests() {
+ return mLocationRequests;
+ }
+
+ public void setLocationRequests(List<LocationRequest> locationRequests) {
+ this.mLocationRequests = Preconditions.checkNotNull(locationRequests);
+ }
+
+ public WorkSource getWorkSource() {
+ return mWorkSource;
+ }
+
+ public void setWorkSource(WorkSource workSource) {
+ mWorkSource = Preconditions.checkNotNull(workSource);
+ }
+
+ /**
+ * Builds a ProviderRequest object with the set information.
+ */
+ public ProviderRequest build() {
+ if (mInterval == Long.MAX_VALUE) {
+ return EMPTY_REQUEST;
+ } else {
+ return new ProviderRequest(true, mInterval, mLowPowerMode,
+ mLocationSettingsIgnored, mLocationRequests, mWorkSource);
+ }
+ }
+ }
}
diff --git a/media/java/android/media/IMediaRoute2Provider.aidl b/media/java/android/media/IMediaRoute2Provider.aidl
index 51fa4eeaf4d8..28bf84d6e079 100644
--- a/media/java/android/media/IMediaRoute2Provider.aidl
+++ b/media/java/android/media/IMediaRoute2Provider.aidl
@@ -24,13 +24,12 @@ import android.media.IMediaRoute2ProviderClient;
*/
oneway interface IMediaRoute2Provider {
void setClient(IMediaRoute2ProviderClient client);
- void requestCreateSession(String packageName, String routeId,
- String routeType, long requestId);
- void releaseSession(int sessionId);
+ void requestCreateSession(String packageName, String routeId, String routeType, long requestId);
+ void releaseSession(String sessionId);
- void selectRoute(int sessionId, String routeId);
- void deselectRoute(int sessionId, String routeId);
- void transferToRoute(int sessionId, String routeId);
+ void selectRoute(String sessionId, String routeId);
+ void deselectRoute(String sessionId, String routeId);
+ void transferToRoute(String sessionId, String routeId);
void notifyControlRequestSent(String id, in Intent request);
void requestSetVolume(String id, int volume);
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index 13640a438e7b..1ed53d942b63 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -16,6 +16,8 @@
package android.media;
+import static android.media.MediaRouter2Utils.toUniqueId;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -191,13 +193,6 @@ public final class MediaRoute2Info implements Parcelable {
}
/**
- * @hide
- */
- public static String toUniqueId(String providerId, String routeId) {
- return providerId + ":" + routeId;
- }
-
- /**
* Returns true if the route info has all of the required field.
* A route info only obtained from {@link com.android.server.media.MediaRouterService}
* is valid.
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index 91cc44807a73..24b65baebcbd 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -29,6 +29,7 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
@@ -55,7 +56,7 @@ public abstract class MediaRoute2ProviderService extends Service {
private MediaRoute2ProviderInfo mProviderInfo;
@GuardedBy("mSessionLock")
- private ArrayMap<Integer, RouteSessionInfo> mSessionInfo = new ArrayMap<>();
+ private ArrayMap<String, RouteSessionInfo> mSessionInfo = new ArrayMap<>();
public MediaRoute2ProviderService() {
mHandler = new Handler(Looper.getMainLooper());
@@ -106,7 +107,10 @@ public abstract class MediaRoute2ProviderService extends Service {
* null if the session is destroyed or id is not valid.
*/
@Nullable
- public final RouteSessionInfo getSessionInfo(int sessionId) {
+ public final RouteSessionInfo getSessionInfo(@NonNull String sessionId) {
+ if (TextUtils.isEmpty(sessionId)) {
+ throw new IllegalArgumentException("sessionId must not be empty");
+ }
synchronized (mSessionLock) {
return mSessionInfo.get(sessionId);
}
@@ -134,7 +138,7 @@ public abstract class MediaRoute2ProviderService extends Service {
*/
public final void updateSessionInfo(@NonNull RouteSessionInfo sessionInfo) {
Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
- int sessionId = sessionInfo.getSessionId();
+ String sessionId = sessionInfo.getId();
if (sessionInfo.getSelectedRoutes().isEmpty()) {
releaseSession(sessionId);
return;
@@ -160,7 +164,7 @@ public abstract class MediaRoute2ProviderService extends Service {
public final void notifySessionInfoChanged(@NonNull RouteSessionInfo sessionInfo) {
Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
- int sessionId = sessionInfo.getSessionId();
+ String sessionId = sessionInfo.getId();
synchronized (mSessionLock) {
if (mSessionInfo.containsKey(sessionId)) {
mSessionInfo.put(sessionId, sessionInfo);
@@ -185,7 +189,7 @@ public abstract class MediaRoute2ProviderService extends Service {
* controlled, pass a {@link Bundle} that contains how to control it.
*
* @param sessionInfo information of the new session.
- * The {@link RouteSessionInfo#getSessionId() id} of the session must be
+ * The {@link RouteSessionInfo#getId() id} of the session must be
* unique. Pass {@code null} to reject the request or inform clients that
* session creation is failed.
* @param requestId id of the previous request to create this session
@@ -194,13 +198,13 @@ public abstract class MediaRoute2ProviderService extends Service {
// TODO: Maybe better to create notifySessionCreationFailed?
public final void notifySessionCreated(@Nullable RouteSessionInfo sessionInfo, long requestId) {
if (sessionInfo != null) {
- int sessionId = sessionInfo.getSessionId();
+ String sessionId = sessionInfo.getId();
synchronized (mSessionLock) {
if (mSessionInfo.containsKey(sessionId)) {
Log.w(TAG, "Ignoring duplicate session id.");
return;
}
- mSessionInfo.put(sessionInfo.getSessionId(), sessionInfo);
+ mSessionInfo.put(sessionInfo.getId(), sessionInfo);
}
schedulePublishState();
}
@@ -220,9 +224,12 @@ public abstract class MediaRoute2ProviderService extends Service {
* {@link #onDestroySession} is called if the session is released.
*
* @param sessionId id of the session to be released
- * @see #onDestroySession(int, RouteSessionInfo)
+ * @see #onDestroySession(String, RouteSessionInfo)
*/
- public final void releaseSession(int sessionId) {
+ public final void releaseSession(@NonNull String sessionId) {
+ if (TextUtils.isEmpty(sessionId)) {
+ throw new IllegalArgumentException("sessionId must not be empty");
+ }
//TODO: notify media router service of release.
RouteSessionInfo sessionInfo;
synchronized (mSessionLock) {
@@ -259,9 +266,10 @@ public abstract class MediaRoute2ProviderService extends Service {
*
* @param sessionId id of the session being destroyed.
* @param lastSessionInfo information of the session being destroyed.
- * @see #releaseSession(int)
+ * @see #releaseSession(String)
*/
- public abstract void onDestroySession(int sessionId, @NonNull RouteSessionInfo lastSessionInfo);
+ public abstract void onDestroySession(@NonNull String sessionId,
+ @NonNull RouteSessionInfo lastSessionInfo);
//TODO: make a way to reject the request
/**
@@ -274,7 +282,7 @@ public abstract class MediaRoute2ProviderService extends Service {
* @param routeId id of the route
* @see #updateSessionInfo(RouteSessionInfo)
*/
- public abstract void onSelectRoute(int sessionId, @NonNull String routeId);
+ public abstract void onSelectRoute(@NonNull String sessionId, @NonNull String routeId);
//TODO: make a way to reject the request
/**
@@ -286,7 +294,7 @@ public abstract class MediaRoute2ProviderService extends Service {
* @param sessionId id of the session
* @param routeId id of the route
*/
- public abstract void onDeselectRoute(int sessionId, @NonNull String routeId);
+ public abstract void onDeselectRoute(@NonNull String sessionId, @NonNull String routeId);
//TODO: make a way to reject the request
/**
@@ -298,7 +306,7 @@ public abstract class MediaRoute2ProviderService extends Service {
* @param sessionId id of the session
* @param routeId id of the route
*/
- public abstract void onTransferToRoute(int sessionId, @NonNull String routeId);
+ public abstract void onTransferToRoute(@NonNull String sessionId, @NonNull String routeId);
/**
* Called when the {@link RouteDiscoveryRequest discovery request} has changed.
@@ -385,37 +393,53 @@ public abstract class MediaRoute2ProviderService extends Service {
requestId));
}
@Override
- public void releaseSession(int sessionId) {
+ public void releaseSession(@NonNull String sessionId) {
if (!checkCallerisSystem()) {
return;
}
+ if (TextUtils.isEmpty(sessionId)) {
+ Log.w(TAG, "releaseSession: Ignoring empty sessionId from system service.");
+ return;
+ }
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::releaseSession,
MediaRoute2ProviderService.this, sessionId));
}
@Override
- public void selectRoute(int sessionId, String routeId) {
+ public void selectRoute(@NonNull String sessionId, String routeId) {
if (!checkCallerisSystem()) {
return;
}
+ if (TextUtils.isEmpty(sessionId)) {
+ Log.w(TAG, "selectRoute: Ignoring empty sessionId from system service.");
+ return;
+ }
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSelectRoute,
MediaRoute2ProviderService.this, sessionId, routeId));
}
@Override
- public void deselectRoute(int sessionId, String routeId) {
+ public void deselectRoute(@NonNull String sessionId, String routeId) {
if (!checkCallerisSystem()) {
return;
}
+ if (TextUtils.isEmpty(sessionId)) {
+ Log.w(TAG, "deselectRoute: Ignoring empty sessionId from system service.");
+ return;
+ }
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onDeselectRoute,
MediaRoute2ProviderService.this, sessionId, routeId));
}
@Override
- public void transferToRoute(int sessionId, String routeId) {
+ public void transferToRoute(@NonNull String sessionId, String routeId) {
if (!checkCallerisSystem()) {
return;
}
+ if (TextUtils.isEmpty(sessionId)) {
+ Log.w(TAG, "transferToRoute: Ignoring empty sessionId from system service.");
+ return;
+ }
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onTransferToRoute,
MediaRoute2ProviderService.this, sessionId, routeId));
}
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index dea8b045e72a..8ebf6174cabf 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -342,8 +342,7 @@ public class MediaRouter2 {
final int requestId;
requestId = mSessionCreationRequestCnt.getAndIncrement();
- SessionCreationRequest request = new SessionCreationRequest(
- requestId, route, routeType);
+ SessionCreationRequest request = new SessionCreationRequest(requestId, route, routeType);
mSessionCreationRequests.add(request);
Client2 client;
@@ -352,8 +351,7 @@ public class MediaRouter2 {
}
if (client != null) {
try {
- mMediaRouterService.requestCreateSession(
- client, route, routeType, requestId);
+ mMediaRouterService.requestCreateSession(client, route, routeType, requestId);
} catch (RemoteException ex) {
Log.e(TAG, "Unable to request to create session.", ex);
mHandler.sendMessage(obtainMessage(MediaRouter2::createControllerOnHandler,
@@ -542,7 +540,7 @@ public class MediaRouter2 {
if (sessionInfo != null) {
RouteSessionController controller = new RouteSessionController(sessionInfo);
synchronized (sRouterLock) {
- mSessionControllers.put(controller.getUniqueSessionId(), controller);
+ mSessionControllers.put(controller.getSessionId(), controller);
}
notifySessionCreated(controller);
}
@@ -556,12 +554,12 @@ public class MediaRouter2 {
RouteSessionController matchingController;
synchronized (sRouterLock) {
- matchingController = mSessionControllers.get(sessionInfo.getUniqueSessionId());
+ matchingController = mSessionControllers.get(sessionInfo.getId());
}
if (matchingController == null) {
Log.w(TAG, "changeSessionInfoOnHandler: Matching controller not found. uniqueSessionId="
- + sessionInfo.getUniqueSessionId());
+ + sessionInfo.getId());
return;
}
@@ -582,7 +580,7 @@ public class MediaRouter2 {
return;
}
- final String uniqueSessionId = sessionInfo.getUniqueSessionId();
+ final String uniqueSessionId = sessionInfo.getId();
RouteSessionController matchingController;
synchronized (sRouterLock) {
matchingController = mSessionControllers.get(uniqueSessionId);
@@ -591,7 +589,7 @@ public class MediaRouter2 {
if (matchingController == null) {
if (DEBUG) {
Log.d(TAG, "releaseControllerOnHandler: Matching controller not found. "
- + "uniqueSessionId=" + sessionInfo.getUniqueSessionId());
+ + "uniqueSessionId=" + sessionInfo.getId());
}
return;
}
@@ -783,20 +781,9 @@ public class MediaRouter2 {
/**
* @return the ID of the session
*/
- public int getSessionId() {
+ public String getSessionId() {
synchronized (mControllerLock) {
- return mSessionInfo.getSessionId();
- }
- }
-
- /**
- * @return the unique ID of the session
- * @hide
- */
- @NonNull
- public String getUniqueSessionId() {
- synchronized (mControllerLock) {
- return mSessionInfo.getUniqueSessionId();
+ return mSessionInfo.getId();
}
}
@@ -913,7 +900,7 @@ public class MediaRouter2 {
}
if (client != null) {
try {
- mMediaRouterService.selectRoute(client, getUniqueSessionId(), route);
+ mMediaRouterService.selectRoute(client, getSessionId(), route);
} catch (RemoteException ex) {
Log.e(TAG, "Unable to select route for session.", ex);
}
@@ -960,7 +947,7 @@ public class MediaRouter2 {
}
if (client != null) {
try {
- mMediaRouterService.deselectRoute(client, getUniqueSessionId(), route);
+ mMediaRouterService.deselectRoute(client, getSessionId(), route);
} catch (RemoteException ex) {
Log.e(TAG, "Unable to remove route from session.", ex);
}
@@ -1008,7 +995,7 @@ public class MediaRouter2 {
}
if (client != null) {
try {
- mMediaRouterService.transferToRoute(client, getUniqueSessionId(), route);
+ mMediaRouterService.transferToRoute(client, getSessionId(), route);
} catch (RemoteException ex) {
Log.e(TAG, "Unable to transfer to route for session.", ex);
}
@@ -1033,12 +1020,12 @@ public class MediaRouter2 {
Client2 client;
synchronized (sRouterLock) {
- mSessionControllers.remove(getUniqueSessionId(), this);
+ mSessionControllers.remove(getSessionId(), this);
client = mClient;
}
if (client != null) {
try {
- mMediaRouterService.releaseSession(client, getUniqueSessionId());
+ mMediaRouterService.releaseSession(client, getSessionId());
} catch (RemoteException ex) {
Log.e(TAG, "Unable to notify of controller release", ex);
}
@@ -1068,6 +1055,7 @@ public class MediaRouter2 {
List<MediaRoute2Info> routes = new ArrayList<>();
synchronized (sRouterLock) {
+ // TODO: Maybe able to change using Collection.stream()?
for (String routeId : routeIds) {
MediaRoute2Info route = mRoutes.get(routeId);
if (route != null) {
diff --git a/media/java/android/media/MediaRouter2Utils.java b/media/java/android/media/MediaRouter2Utils.java
new file mode 100644
index 000000000000..49045828dbe8
--- /dev/null
+++ b/media/java/android/media/MediaRouter2Utils.java
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.text.TextUtils;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class MediaRouter2Utils {
+
+ static final String TAG = "MR2Utils";
+ static final String SEPARATOR = ":";
+
+ /**
+ * @hide
+ */
+ @NonNull
+ public static String toUniqueId(@NonNull String providerId, @NonNull String id) {
+ if (TextUtils.isEmpty(providerId)) {
+ Log.w(TAG, "toUniqueId: providerId shouldn't be empty");
+ return null;
+ }
+ if (TextUtils.isEmpty(id)) {
+ Log.w(TAG, "toUniqueId: id shouldn't be null");
+ return null;
+ }
+
+ return providerId + SEPARATOR + id;
+ }
+
+ /**
+ * Gets provider ID from unique ID.
+ * If the corresponding provider ID could not be generated, it will return null.
+ *
+ * @hide
+ */
+ @Nullable
+ public static String getProviderId(@NonNull String uniqueId) {
+ if (TextUtils.isEmpty(uniqueId)) {
+ Log.w(TAG, "getProviderId: uniqueId shouldn't be empty");
+ return null;
+ }
+
+ int firstIndexOfSeparator = uniqueId.indexOf(SEPARATOR);
+ if (firstIndexOfSeparator == -1) {
+ return null;
+ }
+
+ String providerId = uniqueId.substring(0, firstIndexOfSeparator);
+ if (TextUtils.isEmpty(providerId)) {
+ return null;
+ }
+
+ return providerId;
+ }
+
+ /**
+ * Gets the original ID (i.e. non-unique route/session ID) from unique ID.
+ * If the corresponding ID could not be generated, it will return null.
+ *
+ * @hide
+ */
+ @Nullable
+ public static String getOriginalId(@NonNull String uniqueId) {
+ if (TextUtils.isEmpty(uniqueId)) {
+ Log.w(TAG, "getOriginalId: uniqueId shouldn't be empty");
+ return null;
+ }
+
+ int firstIndexOfSeparator = uniqueId.indexOf(SEPARATOR);
+ if (firstIndexOfSeparator == -1 || firstIndexOfSeparator + 1 >= uniqueId.length()) {
+ return null;
+ }
+
+ String providerId = uniqueId.substring(firstIndexOfSeparator + 1);
+ if (TextUtils.isEmpty(providerId)) {
+ return null;
+ }
+
+ return providerId;
+ }
+}
diff --git a/media/java/android/media/RouteSessionInfo.java b/media/java/android/media/RouteSessionInfo.java
index cb1688600fac..5330630ef3a9 100644
--- a/media/java/android/media/RouteSessionInfo.java
+++ b/media/java/android/media/RouteSessionInfo.java
@@ -22,6 +22,7 @@ import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import android.util.Log;
import java.util.ArrayList;
import java.util.Collections;
@@ -33,6 +34,7 @@ import java.util.Objects;
* @hide
*/
public class RouteSessionInfo implements Parcelable {
+
@NonNull
public static final Creator<RouteSessionInfo> CREATOR =
new Creator<RouteSessionInfo>() {
@@ -46,8 +48,10 @@ public class RouteSessionInfo implements Parcelable {
}
};
- final int mSessionId;
- final String mPackageName;
+ public static final String TAG = "RouteSessionInfo";
+
+ final String mId;
+ final String mClientPackageName;
final String mRouteType;
@Nullable
final String mProviderId;
@@ -61,15 +65,19 @@ public class RouteSessionInfo implements Parcelable {
RouteSessionInfo(@NonNull Builder builder) {
Objects.requireNonNull(builder, "builder must not be null.");
- mSessionId = builder.mSessionId;
- mPackageName = builder.mPackageName;
+ mId = builder.mId;
+ mClientPackageName = builder.mClientPackageName;
mRouteType = builder.mRouteType;
mProviderId = builder.mProviderId;
- mSelectedRoutes = Collections.unmodifiableList(builder.mSelectedRoutes);
- mSelectableRoutes = Collections.unmodifiableList(builder.mSelectableRoutes);
- mDeselectableRoutes = Collections.unmodifiableList(builder.mDeselectableRoutes);
- mTransferrableRoutes = Collections.unmodifiableList(builder.mTransferrableRoutes);
+ mSelectedRoutes = Collections.unmodifiableList(
+ convertToUniqueRouteIds(builder.mSelectedRoutes));
+ mSelectableRoutes = Collections.unmodifiableList(
+ convertToUniqueRouteIds(builder.mSelectableRoutes));
+ mDeselectableRoutes = Collections.unmodifiableList(
+ convertToUniqueRouteIds(builder.mDeselectableRoutes));
+ mTransferrableRoutes = Collections.unmodifiableList(
+ convertToUniqueRouteIds(builder.mTransferrableRoutes));
mControlHints = builder.mControlHints;
}
@@ -77,8 +85,8 @@ public class RouteSessionInfo implements Parcelable {
RouteSessionInfo(@NonNull Parcel src) {
Objects.requireNonNull(src, "src must not be null.");
- mSessionId = src.readInt();
- mPackageName = ensureString(src.readString());
+ mId = ensureString(src.readString());
+ mClientPackageName = ensureString(src.readString());
mRouteType = ensureString(src.readString());
mProviderId = src.readString();
@@ -105,73 +113,50 @@ public class RouteSessionInfo implements Parcelable {
}
/**
- * Gets non-unique session id (int) from unique session id (string).
- * If the corresponding session id could not be generated, it will return null.
- * @hide
+ * Returns whether the session info is valid or not
+ *
+ * TODO in this CL: Remove this method.
*/
- @Nullable
- public static Integer getSessionId(@NonNull String uniqueSessionId) {
- int lastIndexOfSeparator = uniqueSessionId.lastIndexOf("/");
- if (lastIndexOfSeparator == -1 || lastIndexOfSeparator + 1 >= uniqueSessionId.length()) {
- return null;
- }
-
- String integerString = uniqueSessionId.substring(lastIndexOfSeparator + 1);
- if (TextUtils.isEmpty(integerString)) {
- return null;
- }
-
- try {
- return Integer.parseInt(integerString);
- } catch (NumberFormatException ex) {
- return null;
- }
+ public boolean isValid() {
+ return !TextUtils.isEmpty(mId)
+ && !TextUtils.isEmpty(mClientPackageName)
+ && !TextUtils.isEmpty(mRouteType)
+ && mSelectedRoutes.size() > 0;
}
/**
- * Gets provider ID (string) from unique session id (string).
- * If the corresponding provider ID could not be generated, it will return null.
- * @hide
+ * Gets the id of the session. The sessions which are given by {@link MediaRouter2} will have
+ * unique IDs.
+ * <p>
+ * In order to ensure uniqueness in {@link MediaRouter2} side, the value of this method
+ * can be different from what was set in {@link MediaRoute2ProviderService}.
*
- * TODO: This logic seems error-prone. Consider to use long uniqueId.
+ * @see Builder#Builder(String, String, String)
*/
- @Nullable
- public static String getProviderId(@NonNull String uniqueSessionId) {
- int lastIndexOfSeparator = uniqueSessionId.lastIndexOf("/");
- if (lastIndexOfSeparator == -1) {
- return null;
- }
-
- String result = uniqueSessionId.substring(0, lastIndexOfSeparator);
- if (TextUtils.isEmpty(result)) {
- return null;
+ @NonNull
+ public String getId() {
+ if (mProviderId != null) {
+ return MediaRouter2Utils.toUniqueId(mProviderId, mId);
+ } else {
+ return mId;
}
- return result;
- }
-
- /**
- * Returns whether the session info is valid or not
- */
- public boolean isValid() {
- return !TextUtils.isEmpty(mPackageName)
- && !TextUtils.isEmpty(mRouteType)
- && mSelectedRoutes.size() > 0;
}
/**
- * Gets the id of the session
+ * Gets the original id set by {@link Builder#Builder(String, String, String)}.
+ * @hide
*/
@NonNull
- public int getSessionId() {
- return mSessionId;
+ public String getOriginalId() {
+ return mId;
}
/**
* Gets the client package name of the session
*/
@NonNull
- public String getPackageName() {
- return mPackageName;
+ public String getClientPackageName() {
+ return mClientPackageName;
}
/**
@@ -193,19 +178,6 @@ public class RouteSessionInfo implements Parcelable {
}
/**
- * Gets the unique id of the session.
- * @hide
- */
- @NonNull
- public String getUniqueSessionId() {
- StringBuilder sessionIdBuilder = new StringBuilder()
- .append(mProviderId)
- .append("/")
- .append(mSessionId);
- return sessionIdBuilder.toString();
- }
-
- /**
* Gets the list of ids of selected routes for the session. It shouldn't be empty.
*/
@NonNull
@@ -252,8 +224,8 @@ public class RouteSessionInfo implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeInt(mSessionId);
- dest.writeString(mPackageName);
+ dest.writeString(mId);
+ dest.writeString(mClientPackageName);
dest.writeString(mRouteType);
dest.writeString(mProviderId);
dest.writeStringList(mSelectedRoutes);
@@ -267,7 +239,7 @@ public class RouteSessionInfo implements Parcelable {
public String toString() {
StringBuilder result = new StringBuilder()
.append("RouteSessionInfo{ ")
- .append("sessionId=").append(mSessionId)
+ .append("sessionId=").append(mId)
.append(", routeType=").append(mRouteType)
.append(", selectedRoutes={")
.append(String.join(",", mSelectedRoutes))
@@ -285,12 +257,30 @@ public class RouteSessionInfo implements Parcelable {
return result.toString();
}
+ private List<String> convertToUniqueRouteIds(@NonNull List<String> routeIds) {
+ if (routeIds == null) {
+ Log.w(TAG, "routeIds is null. Returning an empty list");
+ return Collections.emptyList();
+ }
+
+ // mProviderId can be null if not set. Return the original list for this case.
+ if (mProviderId == null) {
+ return routeIds;
+ }
+
+ List<String> result = new ArrayList<>();
+ for (String routeId : routeIds) {
+ result.add(MediaRouter2Utils.toUniqueId(mProviderId, routeId));
+ }
+ return result;
+ }
+
/**
* Builder class for {@link RouteSessionInfo}.
*/
public static final class Builder {
- final String mPackageName;
- final int mSessionId;
+ final String mId;
+ final String mClientPackageName;
final String mRouteType;
String mProviderId;
final List<String> mSelectedRoutes;
@@ -299,22 +289,42 @@ public class RouteSessionInfo implements Parcelable {
final List<String> mTransferrableRoutes;
Bundle mControlHints;
- public Builder(int sessionId, @NonNull String packageName,
+ /**
+ * Constructor for builder to create {@link RouteSessionInfo}.
+ * <p>
+ * In order to ensure ID uniqueness in {@link MediaRouter2} side, the value of
+ * {@link RouteSessionInfo#getId()} can be different from what was set in
+ * {@link MediaRoute2ProviderService}.
+ * </p>
+ *
+ * @see MediaRoute2Info#getId()
+ */
+ public Builder(@NonNull String id, @NonNull String clientPackageName,
@NonNull String routeType) {
- mSessionId = sessionId;
- mPackageName = Objects.requireNonNull(packageName, "packageName must not be null");
- mRouteType = Objects.requireNonNull(routeType,
- "routeType must not be null");
-
+ if (TextUtils.isEmpty(id)) {
+ throw new IllegalArgumentException("id must not be empty");
+ }
+ mId = id;
+ mClientPackageName = Objects.requireNonNull(
+ clientPackageName, "clientPackageName must not be null");
+ mRouteType = Objects.requireNonNull(routeType, "routeType must not be null");
mSelectedRoutes = new ArrayList<>();
mSelectableRoutes = new ArrayList<>();
mDeselectableRoutes = new ArrayList<>();
mTransferrableRoutes = new ArrayList<>();
}
- public Builder(RouteSessionInfo sessionInfo) {
- mSessionId = sessionInfo.mSessionId;
- mPackageName = sessionInfo.mPackageName;
+ /**
+ * Constructor for builder to create {@link RouteSessionInfo} with
+ * existing {@link RouteSessionInfo} instance.
+ *
+ * @param sessionInfo the existing instance to copy data from.
+ */
+ public Builder(@NonNull RouteSessionInfo sessionInfo) {
+ Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
+
+ mId = sessionInfo.mId;
+ mClientPackageName = sessionInfo.mClientPackageName;
mRouteType = sessionInfo.mRouteType;
mProviderId = sessionInfo.mProviderId;
@@ -334,23 +344,14 @@ public class RouteSessionInfo implements Parcelable {
* @hide
*/
@NonNull
- public Builder setProviderId(String providerId) {
+ public Builder setProviderId(@NonNull String providerId) {
+ if (TextUtils.isEmpty(providerId)) {
+ throw new IllegalArgumentException("providerId must not be empty");
+ }
mProviderId = providerId;
- convertToUniqueRouteIds(providerId, mSelectedRoutes);
- convertToUniqueRouteIds(providerId, mSelectableRoutes);
- convertToUniqueRouteIds(providerId, mDeselectableRoutes);
- convertToUniqueRouteIds(providerId, mTransferrableRoutes);
return this;
}
- private void convertToUniqueRouteIds(@NonNull String providerId,
- @NonNull List<String> routeIds) {
- for (int i = 0; i < routeIds.size(); i++) {
- String routeId = routeIds.get(i);
- routeIds.set(i, MediaRoute2Info.toUniqueId(providerId, routeId));
- }
- }
-
/**
* Clears the selected routes.
*/
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index aff725734ee1..aece39d78694 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -940,16 +940,15 @@ public final class MediaSessionManager {
/**
* Called when a media key event is dispatched through the media session service. The
* session token can be {@link null} if the framework has sent the media key event to the
- * media button receiver to revive the media app's playback.
- *
- * the session is dead when , but the framework sent
+ * media button receiver to revive the media app's playback after the corresponding session
+ * is released.
*
* @param event Dispatched media key event.
* @param packageName Package
* @param sessionToken The media session's token. Can be {@code null}.
*/
default void onMediaKeyEventDispatched(@NonNull KeyEvent event, @NonNull String packageName,
- @NonNull MediaSession.Token sessionToken) { }
+ @Nullable MediaSession.Token sessionToken) { }
}
/**
diff --git a/media/java/android/media/soundtrigger/SoundTriggerManager.java b/media/java/android/media/soundtrigger/SoundTriggerManager.java
index 1c38301c7935..938ffcd5f731 100644
--- a/media/java/android/media/soundtrigger/SoundTriggerManager.java
+++ b/media/java/android/media/soundtrigger/SoundTriggerManager.java
@@ -428,8 +428,7 @@ public final class SoundTriggerManager {
*/
@RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
public int setParameter(@Nullable UUID soundModelId,
- @ModelParams int modelParam, int value)
- throws UnsupportedOperationException, IllegalArgumentException {
+ @ModelParams int modelParam, int value) {
try {
return mSoundTriggerService.setParameter(new ParcelUuid(soundModelId), modelParam,
value);
@@ -449,15 +448,10 @@ public final class SoundTriggerManager {
* @param soundModelId UUID of model to get parameter
* @param modelParam {@link ModelParams}
* @return value of parameter
- * @throws UnsupportedOperationException if hal or model do not support this API.
- * {@link SoundTriggerManager#queryParameter} should be checked first.
- * @throws IllegalArgumentException if invalid model handle or parameter is passed.
- * {@link SoundTriggerManager#queryParameter} should be checked first.
*/
@RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
public int getParameter(@NonNull UUID soundModelId,
- @ModelParams int modelParam)
- throws UnsupportedOperationException, IllegalArgumentException {
+ @ModelParams int modelParam) {
try {
return mSoundTriggerService.getParameter(new ParcelUuid(soundModelId), modelParam);
} catch (RemoteException e) {
@@ -479,8 +473,7 @@ public final class SoundTriggerManager {
public ModelParamRange queryParameter(@Nullable UUID soundModelId,
@ModelParams int modelParam) {
try {
- return mSoundTriggerService.queryParameter(new ParcelUuid(soundModelId),
- modelParam);
+ return mSoundTriggerService.queryParameter(new ParcelUuid(soundModelId), modelParam);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/media/java/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl b/media/java/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl
index 1a3b40261a62..909f1a00006f 100644
--- a/media/java/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl
+++ b/media/java/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl
@@ -30,6 +30,14 @@ parcelable SoundTriggerModuleProperties {
* Unique implementation ID. The UUID must change with each version of
the engine implementation */
String uuid;
+ /**
+ * String naming the architecture used for running the supported models.
+ * (eg. a platform running models on a DSP could implement this string to convey the DSP
+ * architecture used)
+ * This property is supported for soundtrigger HAL v2.3 and above.
+ * If running a previous version, the string will be empty.
+ */
+ String supportedModelArch;
/** Maximum number of concurrent sound models loaded */
int maxSoundModels;
/** Maximum number of key phrases */
diff --git a/media/java/android/media/soundtrigger_middleware/Status.aidl b/media/java/android/media/soundtrigger_middleware/Status.aidl
index d8f9d8f7e891..5d082e272295 100644
--- a/media/java/android/media/soundtrigger_middleware/Status.aidl
+++ b/media/java/android/media/soundtrigger_middleware/Status.aidl
@@ -26,4 +26,6 @@ enum Status {
RESOURCE_CONTENTION = 1,
/** Operation is not supported in this implementation. This is a permanent condition. */
OPERATION_NOT_SUPPORTED = 2,
+ /** Temporary lack of permission. */
+ TEMPORARY_PERMISSION_DENIED = 3,
}
diff --git a/media/java/android/media/tv/tuner/DemuxCapabilities.java b/media/java/android/media/tv/tuner/DemuxCapabilities.java
index bda166ec9d14..83abf8616468 100644
--- a/media/java/android/media/tv/tuner/DemuxCapabilities.java
+++ b/media/java/android/media/tv/tuner/DemuxCapabilities.java
@@ -16,11 +16,32 @@
package android.media.tv.tuner;
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.annotation.Size;
+import android.media.tv.tuner.filter.FilterConfiguration;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Capabilities info for Demux.
+ *
* @hide
*/
public class DemuxCapabilities {
+
+ /** @hide */
+ @IntDef(flag = true, value = {
+ FilterConfiguration.FILTER_TYPE_TS,
+ FilterConfiguration.FILTER_TYPE_MMTP,
+ FilterConfiguration.FILTER_TYPE_IP,
+ FilterConfiguration.FILTER_TYPE_TLV,
+ FilterConfiguration.FILTER_TYPE_ALP
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FilterCapabilities {}
+
private final int mNumDemux;
private final int mNumRecord;
private final int mNumPlayback;
@@ -34,7 +55,8 @@ public class DemuxCapabilities {
private final int mFilterCaps;
private final int[] mLinkCaps;
- DemuxCapabilities(int numDemux, int numRecord, int numPlayback, int numTsFilter,
+ // Used by JNI
+ private DemuxCapabilities(int numDemux, int numRecord, int numPlayback, int numTsFilter,
int numSectionFilter, int numAudioFilter, int numVideoFilter, int numPesFilter,
int numPcrFilter, int numBytesInSectionFilter, int filterCaps, int[] linkCaps) {
mNumDemux = numDemux;
@@ -51,52 +73,73 @@ public class DemuxCapabilities {
mLinkCaps = linkCaps;
}
- /** Gets total number of demuxes. */
+ /**
+ * Gets total number of demuxes.
+ */
public int getNumDemux() {
return mNumDemux;
}
- /** Gets max number of recordings at a time. */
+ /**
+ * Gets max number of recordings at a time.
+ */
public int getNumRecord() {
return mNumRecord;
}
- /** Gets max number of playbacks at a time. */
+ /**
+ * Gets max number of playbacks at a time.
+ */
public int getNumPlayback() {
return mNumPlayback;
}
- /** Gets number of TS filters. */
+ /**
+ * Gets number of TS filters.
+ */
public int getNumTsFilter() {
return mNumTsFilter;
}
- /** Gets number of section filters. */
+ /**
+ * Gets number of section filters.
+ */
public int getNumSectionFilter() {
return mNumSectionFilter;
}
- /** Gets number of audio filters. */
+ /**
+ * Gets number of audio filters.
+ */
public int getNumAudioFilter() {
return mNumAudioFilter;
}
- /** Gets number of video filters. */
+ /**
+ * Gets number of video filters.
+ */
public int getNumVideoFilter() {
return mNumVideoFilter;
}
- /** Gets number of PES filters. */
+ /**
+ * Gets number of PES filters.
+ */
public int getNumPesFilter() {
return mNumPesFilter;
}
- /** Gets number of PCR filters. */
+ /**
+ * Gets number of PCR filters.
+ */
public int getNumPcrFilter() {
return mNumPcrFilter;
}
- /** Gets number of bytes in the mask of a section filter. */
+ /**
+ * Gets number of bytes in the mask of a section filter.
+ */
public int getNumBytesInSectionFilter() {
return mNumBytesInSectionFilter;
}
/**
* Gets filter capabilities in bit field.
*
- * The bits of the returned value is corresponding to the types in
- * {@link TunerConstants.FilterType}.
+ * <p>The bits of the returned value is corresponding to the types in
+ * {@link FilterConfiguration}.
*/
+ @FilterCapabilities
public int getFilterCapabilities() {
return mFilterCaps;
}
@@ -104,10 +147,12 @@ public class DemuxCapabilities {
/**
* Gets link capabilities.
*
- * The returned array contains the same elements as the number of types in
- * {@link TunerConstants.FilterType}.
- * The ith element represents the filter's capability as the source for the ith type
+ * <p>The returned array contains the same elements as the number of types in
+ * {@link FilterConfiguration}.
+ * <p>The ith element represents the filter's capability as the source for the ith type.
*/
+ @Nullable
+ @Size(5)
public int[] getLinkCapabilities() {
return mLinkCaps;
}
diff --git a/media/java/android/media/tv/tuner/Descrambler.java b/media/java/android/media/tv/tuner/Descrambler.java
new file mode 100644
index 000000000000..f9f7a22c3de8
--- /dev/null
+++ b/media/java/android/media/tv/tuner/Descrambler.java
@@ -0,0 +1,103 @@
+/*
+ * 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.Nullable;
+import android.media.tv.tuner.Tuner.Filter;
+import android.media.tv.tuner.TunerConstants.DemuxPidType;
+
+/**
+ * This class is used to interact with descramblers.
+ *
+ * <p> Descrambler is a hardware component used to descramble data.
+ *
+ * <p> This class controls the TIS interaction with Tuner HAL.
+ * @hide
+ */
+public class Descrambler implements AutoCloseable {
+ private long mNativeContext;
+
+ private native int nativeAddPid(int pidType, int pid, Filter filter);
+ private native int nativeRemovePid(int pidType, int pid, Filter filter);
+ private native int nativeSetKeyToken(byte[] keyToken);
+ private native int nativeClose();
+
+ private Descrambler() {}
+
+ /**
+ * Add packets' PID to the descrambler for descrambling.
+ *
+ * The descrambler will start descrambling packets with this PID. Multiple PIDs can be added
+ * into one descrambler instance because descambling can happen simultaneously on packets
+ * from different PIDs.
+ *
+ * If the Descrambler previously contained a filter for the PID, the old filter is replaced
+ * by the specified filter.
+ *
+ * @param pidType the type of the PID.
+ * @param pid the PID of packets to start to be descrambled.
+ * @param filter an optional filter instance to identify upper stream.
+ * @return result status of the operation.
+ *
+ * @hide
+ */
+ public int addPid(@DemuxPidType int pidType, int pid, @Nullable Filter filter) {
+ return nativeAddPid(pidType, pid, filter);
+ }
+
+ /**
+ * Remove packets' PID from the descrambler
+ *
+ * The descrambler will stop descrambling packets with this PID.
+ *
+ * @param pidType the type of the PID.
+ * @param pid the PID of packets to stop to be descrambled.
+ * @param filter an optional filter instance to identify upper stream.
+ * @return result status of the operation.
+ *
+ * @hide
+ */
+ public int removePid(@DemuxPidType int pidType, int pid, @Nullable Filter filter) {
+ return nativeRemovePid(pidType, pid, filter);
+ }
+
+ /**
+ * Set a key token to link descrambler to a key slot
+ *
+ * A descrambler instance can have only one key slot to link, but a key slot can hold a few
+ * keys for different purposes.
+ *
+ * @param keyToken the token to be used to link the key slot.
+ * @return result status of the operation.
+ *
+ * @hide
+ */
+ public int setKeyToken(byte[] keyToken) {
+ return nativeSetKeyToken(keyToken);
+ }
+
+ /**
+ * Release the descrambler instance.
+ *
+ * @hide
+ */
+ @Override
+ public void close() {
+ nativeClose();
+ }
+
+}
diff --git a/media/java/android/media/tv/tuner/Dvr.java b/media/java/android/media/tv/tuner/Dvr.java
new file mode 100644
index 000000000000..bc2f184611a9
--- /dev/null
+++ b/media/java/android/media/tv/tuner/Dvr.java
@@ -0,0 +1,153 @@
+/*
+ * 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.NonNull;
+import android.media.tv.tuner.Tuner.DvrCallback;
+import android.media.tv.tuner.Tuner.Filter;
+import android.media.tv.tuner.dvr.DvrSettings;
+import android.os.ParcelFileDescriptor;
+
+/** @hide */
+public class Dvr {
+ private long mNativeContext;
+ private DvrCallback mCallback;
+
+ private native int nativeAttachFilter(Filter filter);
+ private native int nativeDetachFilter(Filter filter);
+ private native int nativeConfigureDvr(DvrSettings settings);
+ private native int nativeStartDvr();
+ private native int nativeStopDvr();
+ private native int nativeFlushDvr();
+ private native int nativeClose();
+ private native void nativeSetFileDescriptor(int fd);
+ private native int nativeRead(int size);
+ private native int nativeRead(byte[] bytes, int offset, int size);
+ private native int nativeWrite(int size);
+ private native int nativeWrite(byte[] bytes, int offset, int size);
+
+ private Dvr() {}
+
+ /**
+ * Attaches a filter to DVR interface for recording.
+ *
+ * @param filter the filter to be attached.
+ * @return result status of the operation.
+ */
+ public int attachFilter(Filter filter) {
+ return nativeAttachFilter(filter);
+ }
+
+ /**
+ * Detaches a filter from DVR interface.
+ *
+ * @param filter the filter to be detached.
+ * @return result status of the operation.
+ */
+ public int detachFilter(Filter filter) {
+ return nativeDetachFilter(filter);
+ }
+
+ /**
+ * Configures the DVR.
+ *
+ * @param settings the settings of the DVR interface.
+ * @return result status of the operation.
+ */
+ public int configure(DvrSettings settings) {
+ return nativeConfigureDvr(settings);
+ }
+
+ /**
+ * Starts DVR.
+ *
+ * Starts consuming playback data or producing data for recording.
+ *
+ * @return result status of the operation.
+ */
+ public int start() {
+ return nativeStartDvr();
+ }
+
+ /**
+ * Stops DVR.
+ *
+ * Stops consuming playback data or producing data for recording.
+ *
+ * @return result status of the operation.
+ */
+ public int stop() {
+ return nativeStopDvr();
+ }
+
+ /**
+ * Flushed DVR data.
+ *
+ * @return result status of the operation.
+ */
+ public int flush() {
+ return nativeFlushDvr();
+ }
+
+ /**
+ * closes the DVR instance to release resources.
+ *
+ * @return result status of the operation.
+ */
+ public int close() {
+ return nativeClose();
+ }
+
+ /**
+ * Sets file descriptor to read/write data.
+ */
+ public void setFileDescriptor(ParcelFileDescriptor fd) {
+ nativeSetFileDescriptor(fd.getFd());
+ }
+
+ /**
+ * Reads data from the file for DVR playback.
+ */
+ public int read(int size) {
+ return nativeRead(size);
+ }
+
+ /**
+ * Reads data from the buffer for DVR playback.
+ */
+ public int read(@NonNull byte[] bytes, int offset, int size) {
+ if (size + offset > bytes.length) {
+ throw new ArrayIndexOutOfBoundsException(
+ "Array length=" + bytes.length + ", offset=" + offset + ", size=" + size);
+ }
+ return nativeRead(bytes, offset, size);
+ }
+
+ /**
+ * Writes recording data to file.
+ */
+ public int write(int size) {
+ return nativeWrite(size);
+ }
+
+ /**
+ * Writes recording data to buffer.
+ */
+ public int write(@NonNull byte[] bytes, int offset, int size) {
+ return nativeWrite(bytes, offset, size);
+ }
+}
diff --git a/media/java/android/media/tv/tuner/DvrSettings.java b/media/java/android/media/tv/tuner/DvrSettings.java
deleted file mode 100644
index 76160dc9fcf8..000000000000
--- a/media/java/android/media/tv/tuner/DvrSettings.java
+++ /dev/null
@@ -1,124 +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.media.tv.tuner.TunerConstants.DataFormat;
-import android.media.tv.tuner.TunerConstants.DvrSettingsType;
-
-/**
- * DVR settings.
- *
- * @hide
- */
-public class DvrSettings {
- private int mStatusMask;
- private int mLowThreshold;
- private int mHighThreshold;
- private int mPacketSize;
-
- @DataFormat
- private int mDataFormat;
- @DvrSettingsType
- private int mType;
-
- private DvrSettings(int statusMask, int lowThreshold, int highThreshold, int packetSize,
- @DataFormat int dataFormat, @DvrSettingsType int type) {
- mStatusMask = statusMask;
- mLowThreshold = lowThreshold;
- mHighThreshold = highThreshold;
- mPacketSize = packetSize;
- mDataFormat = dataFormat;
- mType = type;
- }
-
- /**
- * Creates a new builder.
- */
- public static Builder newBuilder() {
- return new Builder();
- }
-
- /**
- * Builder for DvrSettings.
- */
- public static final class Builder {
- private int mStatusMask;
- private int mLowThreshold;
- private int mHighThreshold;
- private int mPacketSize;
- @DataFormat
- private int mDataFormat;
- @DvrSettingsType
- private int mType;
-
- /**
- * Sets status mask.
- */
- public Builder setStatusMask(int statusMask) {
- this.mStatusMask = statusMask;
- return this;
- }
-
- /**
- * Sets low threshold.
- */
- public Builder setLowThreshold(int lowThreshold) {
- this.mLowThreshold = lowThreshold;
- return this;
- }
-
- /**
- * Sets high threshold.
- */
- public Builder setHighThreshold(int highThreshold) {
- this.mHighThreshold = highThreshold;
- return this;
- }
-
- /**
- * Sets packet size.
- */
- public Builder setPacketSize(int packetSize) {
- this.mPacketSize = packetSize;
- return this;
- }
-
- /**
- * Sets data format.
- */
- public Builder setDataFormat(@DataFormat int dataFormat) {
- this.mDataFormat = dataFormat;
- return this;
- }
-
- /**
- * Sets settings type.
- */
- public Builder setType(@DvrSettingsType int type) {
- this.mType = type;
- return this;
- }
-
- /**
- * Builds a DvrSettings instance.
- */
- public DvrSettings build() {
- return new DvrSettings(
- mStatusMask, mLowThreshold, mHighThreshold, mPacketSize, mDataFormat, mType);
- }
- }
-}
diff --git a/media/java/android/media/tv/tuner/FrontendCapabilities.java b/media/java/android/media/tv/tuner/FrontendCapabilities.java
deleted file mode 100644
index fcfd7c8c8639..000000000000
--- a/media/java/android/media/tv/tuner/FrontendCapabilities.java
+++ /dev/null
@@ -1,291 +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;
-
-/**
- * Frontend Capabilities.
- * @hide
- */
-public class FrontendCapabilities {
- /** Analog Capabilities. */
- public class Analog extends FrontendCapabilities {
- private final int mTypeCap;
- private final int mSifStandardCap;
-
- Analog(int typeCap, int sifStandardCap) {
- mTypeCap = typeCap;
- mSifStandardCap = sifStandardCap;
- }
- /**
- * Gets type capability.
- */
- public int getTypeCapability() {
- return mTypeCap;
- }
- /** Gets SIF standard capability. */
- public int getSifStandardCapability() {
- return mSifStandardCap;
- }
- }
-
- /** ATSC Capabilities. */
- public class Atsc extends FrontendCapabilities {
- private final int mModulationCap;
-
- Atsc(int modulationCap) {
- mModulationCap = modulationCap;
- }
- /** Gets modulation capability. */
- public int getModulationCapability() {
- return mModulationCap;
- }
- }
-
- /** ATSC-3 Capabilities. */
- public class Atsc3 extends FrontendCapabilities {
- private final int mBandwidthCap;
- private final int mModulationCap;
- private final int mTimeInterleaveModeCap;
- private final int mCodeRateCap;
- private final int mFecCap;
- private final int mDemodOutputFormatCap;
-
- Atsc3(int bandwidthCap, int modulationCap, int timeInterleaveModeCap, int codeRateCap,
- int fecCap, int demodOutputFormatCap) {
- mBandwidthCap = bandwidthCap;
- mModulationCap = modulationCap;
- mTimeInterleaveModeCap = timeInterleaveModeCap;
- mCodeRateCap = codeRateCap;
- mFecCap = fecCap;
- mDemodOutputFormatCap = demodOutputFormatCap;
- }
-
- /** Gets bandwidth capability. */
- public int getBandwidthCapability() {
- return mBandwidthCap;
- }
- /** Gets modulation capability. */
- public int getModulationCapability() {
- return mModulationCap;
- }
- /** Gets time interleave mod capability. */
- public int getTimeInterleaveModeCapability() {
- return mTimeInterleaveModeCap;
- }
- /** Gets code rate capability. */
- public int getCodeRateCapability() {
- return mCodeRateCap;
- }
- /** Gets FEC capability. */
- public int getFecCapability() {
- return mFecCap;
- }
- /** Gets demodulator output format capability. */
- public int getDemodOutputFormatCapability() {
- return mDemodOutputFormatCap;
- }
- }
-
- /** DVBS Capabilities. */
- public class Dvbs extends FrontendCapabilities {
- private final int mModulationCap;
- private final long mInnerFecCap;
- private final int mStandard;
-
- Dvbs(int modulationCap, long innerFecCap, int standard) {
- mModulationCap = modulationCap;
- mInnerFecCap = innerFecCap;
- mStandard = standard;
- }
-
- /** Gets modulation capability. */
- public int getModulationCapability() {
- return mModulationCap;
- }
- /** Gets inner FEC capability. */
- public long getInnerFecCapability() {
- return mInnerFecCap;
- }
- /** Gets DVBS standard capability. */
- public int getStandardCapability() {
- return mStandard;
- }
- }
-
- /** DVBC Capabilities. */
- public class Dvbc extends FrontendCapabilities {
- private final int mModulationCap;
- private final int mFecCap;
- private final int mAnnexCap;
-
- Dvbc(int modulationCap, int fecCap, int annexCap) {
- mModulationCap = modulationCap;
- mFecCap = fecCap;
- mAnnexCap = annexCap;
- }
-
- /** Gets modulation capability. */
- public int getModulationCapability() {
- return mModulationCap;
- }
- /** Gets FEC capability. */
- public int getFecCapability() {
- return mFecCap;
- }
- /** Gets annex capability. */
- public int getAnnexCapability() {
- return mAnnexCap;
- }
- }
-
- /** DVBT Capabilities. */
- public class Dvbt extends FrontendCapabilities {
- private final int mTransmissionModeCap;
- private final int mBandwidthCap;
- private final int mConstellationCap;
- private final int mCoderateCap;
- private final int mHierarchyCap;
- private final int mGuardIntervalCap;
- private final boolean mIsT2Supported;
- private final boolean mIsMisoSupported;
-
- Dvbt(int transmissionModeCap, int bandwidthCap, int constellationCap, int coderateCap,
- int hierarchyCap, int guardIntervalCap, boolean isT2Supported,
- boolean isMisoSupported) {
- mTransmissionModeCap = transmissionModeCap;
- mBandwidthCap = bandwidthCap;
- mConstellationCap = constellationCap;
- mCoderateCap = coderateCap;
- mHierarchyCap = hierarchyCap;
- mGuardIntervalCap = guardIntervalCap;
- mIsT2Supported = isT2Supported;
- mIsMisoSupported = isMisoSupported;
- }
-
- /** Gets transmission mode capability. */
- public int getTransmissionModeCapability() {
- return mTransmissionModeCap;
- }
- /** Gets bandwidth capability. */
- public int getBandwidthCapability() {
- return mBandwidthCap;
- }
- /** Gets constellation capability. */
- public int getConstellationCapability() {
- return mConstellationCap;
- }
- /** Gets code rate capability. */
- public int getCodeRateCapability() {
- return mCoderateCap;
- }
- /** Gets hierarchy capability. */
- public int getHierarchyCapability() {
- return mHierarchyCap;
- }
- /** Gets guard interval capability. */
- public int getGuardIntervalCapability() {
- return mGuardIntervalCap;
- }
- /** Returns whether T2 is supported. */
- public boolean getIsT2Supported() {
- return mIsT2Supported;
- }
- /** Returns whether MISO is supported. */
- public boolean getIsMisoSupported() {
- return mIsMisoSupported;
- }
- }
-
- /** ISDBS Capabilities. */
- public class Isdbs extends FrontendCapabilities {
- private final int mModulationCap;
- private final int mCoderateCap;
-
- Isdbs(int modulationCap, int coderateCap) {
- mModulationCap = modulationCap;
- mCoderateCap = coderateCap;
- }
-
- /** Gets modulation capability. */
- public int getModulationCapability() {
- return mModulationCap;
- }
- /** Gets code rate capability. */
- public int getCodeRateCapability() {
- return mCoderateCap;
- }
- }
-
- /** ISDBS-3 Capabilities. */
- public class Isdbs3 extends FrontendCapabilities {
- private final int mModulationCap;
- private final int mCoderateCap;
-
- Isdbs3(int modulationCap, int coderateCap) {
- mModulationCap = modulationCap;
- mCoderateCap = coderateCap;
- }
-
- /** Gets modulation capability. */
- public int getModulationCapability() {
- return mModulationCap;
- }
- /** Gets code rate capability. */
- public int getCodeRateCapability() {
- return mCoderateCap;
- }
- }
-
- /** ISDBC Capabilities. */
- public class Isdbc extends FrontendCapabilities {
- private final int mModeCap;
- private final int mBandwidthCap;
- private final int mModulationCap;
- private final int mCoderateCap;
- private final int mGuardIntervalCap;
-
- Isdbc(int modeCap, int bandwidthCap, int modulationCap, int coderateCap,
- int guardIntervalCap) {
- mModeCap = modeCap;
- mBandwidthCap = bandwidthCap;
- mModulationCap = modulationCap;
- mCoderateCap = coderateCap;
- mGuardIntervalCap = guardIntervalCap;
- }
-
- /** Gets mode capability. */
- public int getModeCapability() {
- return mModeCap;
- }
- /** Gets bandwidth capability. */
- public int getBandwidthCapability() {
- return mBandwidthCap;
- }
- /** Gets modulation capability. */
- public int getModulationCapability() {
- return mModulationCap;
- }
- /** Gets code rate capability. */
- public int getCodeRateCapability() {
- return mCoderateCap;
- }
- /** Gets guard interval capability. */
- public int getGuardIntervalCapability() {
- return mGuardIntervalCap;
- }
- }
-}
diff --git a/media/java/android/media/tv/tuner/FrontendSettings.java b/media/java/android/media/tv/tuner/FrontendSettings.java
index e2e9910f53ee..ad8422caaa02 100644
--- a/media/java/android/media/tv/tuner/FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/FrontendSettings.java
@@ -17,7 +17,6 @@
package android.media.tv.tuner;
import android.annotation.SystemApi;
-import android.media.tv.tuner.TunerConstants.FrontendSettingsType;
/**
* Frontend settings for tune and scan operations.
@@ -35,7 +34,6 @@ public abstract class FrontendSettings {
/**
* Returns the frontend type.
*/
- @FrontendSettingsType
public abstract int getType();
/**
diff --git a/media/java/android/media/tv/tuner/Lnb.java b/media/java/android/media/tv/tuner/Lnb.java
new file mode 100644
index 000000000000..f181b49239d7
--- /dev/null
+++ b/media/java/android/media/tv/tuner/Lnb.java
@@ -0,0 +1,202 @@
+/*
+ * 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.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.content.Context;
+import android.hardware.tv.tuner.V1_0.Constants;
+import android.media.tv.tuner.Tuner.LnbCallback;
+import android.media.tv.tuner.TunerConstants.Result;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * LNB (low-noise block downconverter) for satellite tuner.
+ *
+ * A Tuner LNB (low-noise block downconverter) is used by satellite frontend to receive the
+ * microwave signal from the satellite, amplify it, and downconvert the frequency to a lower
+ * frequency.
+ *
+ * @hide
+ */
+public class Lnb implements AutoCloseable {
+ /** @hide */
+ @IntDef({VOLTAGE_NONE, VOLTAGE_5V, VOLTAGE_11V, VOLTAGE_12V, VOLTAGE_13V, VOLTAGE_14V,
+ VOLTAGE_15V, VOLTAGE_18V, VOLTAGE_19V})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Voltage {}
+
+ /**
+ * LNB power voltage not set.
+ */
+ public static final int VOLTAGE_NONE = Constants.LnbVoltage.NONE;
+ /**
+ * LNB power voltage 5V.
+ */
+ public static final int VOLTAGE_5V = Constants.LnbVoltage.VOLTAGE_5V;
+ /**
+ * LNB power voltage 11V.
+ */
+ public static final int VOLTAGE_11V = Constants.LnbVoltage.VOLTAGE_11V;
+ /**
+ * LNB power voltage 12V.
+ */
+ public static final int VOLTAGE_12V = Constants.LnbVoltage.VOLTAGE_12V;
+ /**
+ * LNB power voltage 13V.
+ */
+ public static final int VOLTAGE_13V = Constants.LnbVoltage.VOLTAGE_13V;
+ /**
+ * LNB power voltage 14V.
+ */
+ public static final int VOLTAGE_14V = Constants.LnbVoltage.VOLTAGE_14V;
+ /**
+ * LNB power voltage 15V.
+ */
+ public static final int VOLTAGE_15V = Constants.LnbVoltage.VOLTAGE_15V;
+ /**
+ * LNB power voltage 18V.
+ */
+ public static final int VOLTAGE_18V = Constants.LnbVoltage.VOLTAGE_18V;
+ /**
+ * LNB power voltage 19V.
+ */
+ public static final int VOLTAGE_19V = Constants.LnbVoltage.VOLTAGE_19V;
+
+ /** @hide */
+ @IntDef({TONE_NONE, TONE_CONTINUOUS})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Tone {}
+
+ /**
+ * LNB tone mode not set.
+ */
+ public static final int TONE_NONE = Constants.LnbTone.NONE;
+ /**
+ * LNB continuous tone mode.
+ */
+ public static final int TONE_CONTINUOUS = Constants.LnbTone.CONTINUOUS;
+
+ /** @hide */
+ @IntDef({POSITION_UNDEFINED, POSITION_A, POSITION_B})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Position {}
+
+ /**
+ * LNB position is not defined.
+ */
+ public static final int POSITION_UNDEFINED = Constants.LnbPosition.UNDEFINED;
+ /**
+ * Position A of two-band LNBs
+ */
+ public static final int POSITION_A = Constants.LnbPosition.POSITION_A;
+ /**
+ * Position B of two-band LNBs
+ */
+ public static final int POSITION_B = Constants.LnbPosition.POSITION_B;
+
+ int mId;
+ LnbCallback mCallback;
+ Context mContext;
+
+ private native int nativeSetVoltage(int voltage);
+ private native int nativeSetTone(int tone);
+ private native int nativeSetSatellitePosition(int position);
+ private native int nativeSendDiseqcMessage(byte[] message);
+ private native int nativeClose();
+
+ Lnb(int id) {
+ mId = id;
+ }
+
+ /** @hide */
+ public void setCallback(@Nullable LnbCallback callback) {
+ mCallback = callback;
+ if (mCallback == null) {
+ return;
+ }
+ }
+
+ /**
+ * Sets the LNB's power voltage.
+ *
+ * @param voltage the power voltage constant the Lnb to use.
+ * @return result status of the operation.
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+ @Result
+ public int setVoltage(@Voltage int voltage) {
+ TunerUtils.checkTunerPermission(mContext);
+ return nativeSetVoltage(voltage);
+ }
+
+ /**
+ * Sets the LNB's tone mode.
+ *
+ * @param tone the tone mode the Lnb to use.
+ * @return result status of the operation.
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+ @Result
+ public int setTone(@Tone int tone) {
+ TunerUtils.checkTunerPermission(mContext);
+ return nativeSetTone(tone);
+ }
+
+ /**
+ * Selects the LNB's position.
+ *
+ * @param position the position the Lnb to use.
+ * @return result status of the operation.
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+ @Result
+ public int setSatellitePosition(@Position int position) {
+ TunerUtils.checkTunerPermission(mContext);
+ return nativeSetSatellitePosition(position);
+ }
+
+ /**
+ * Sends DiSEqC (Digital Satellite Equipment Control) message.
+ *
+ * The response message from the device comes back through callback onDiseqcMessage.
+ *
+ * @param message a byte array of data for DiSEqC message which is specified by EUTELSAT Bus
+ * Functional Specification Version 4.2.
+ *
+ * @return result status of the operation.
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+ @Result
+ public int sendDiseqcMessage(@NonNull byte[] message) {
+ TunerUtils.checkTunerPermission(mContext);
+ return nativeSendDiseqcMessage(message);
+ }
+
+ /**
+ * Releases the LNB instance.
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+ public void close() {
+ TunerUtils.checkTunerPermission(mContext);
+ nativeClose();
+ }
+}
diff --git a/media/java/android/media/tv/tuner/TimeFilter.java b/media/java/android/media/tv/tuner/TimeFilter.java
new file mode 100644
index 000000000000..8bd0d26ce951
--- /dev/null
+++ b/media/java/android/media/tv/tuner/TimeFilter.java
@@ -0,0 +1,127 @@
+/*
+ * 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.Nullable;
+import android.media.tv.tuner.TunerConstants.Result;
+
+/**
+ * A timer filter is used to filter data based on timestamps.
+ *
+ * <p> If the timestamp is set, data is discarded if its timestamp is smaller than the
+ * timestamp in this time filter.
+ *
+ * <p> The format of the timestamps is the same as PTS defined in ISO/IEC 13818-1:2019. The
+ * timestamps may or may not be related to PTS or DTS.
+ *
+ * @hide
+ */
+public class TimeFilter implements AutoCloseable {
+ private native int nativeSetTimestamp(long timestamp);
+ private native int nativeClearTimestamp();
+ private native Long nativeGetTimestamp();
+ private native Long nativeGetSourceTime();
+ private native int nativeClose();
+
+ private boolean mEnable = false;
+
+ /**
+ * Set timestamp for time based filter.
+ *
+ * It is used to set initial timestamp and enable time filtering. Once set, the time will be
+ * increased automatically like a clock. Contents are discarded if their timestamps are
+ * older than the time in the time filter.
+ *
+ * This method can be called more than once to reset the initial timestamp.
+ *
+ * @param timestamp initial timestamp for the time filter before it's increased. 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.
+ * @return result status of the operation.
+ */
+ @Result
+ public int setCurrentTimestamp(long timestamp) {
+ int res = nativeSetTimestamp(timestamp);
+ // TODO: use a constant for SUCCESS
+ if (res == 0) {
+ mEnable = true;
+ }
+ return res;
+ }
+
+ /**
+ * Clear the timestamp in the time filter.
+ *
+ * It is used to clear the time value of the time filter. Time filtering is disabled then.
+ *
+ * @return result status of the operation.
+ */
+ @Result
+ public int clearTimestamp() {
+ int res = nativeClearTimestamp();
+ if (res == 0) {
+ mEnable = false;
+ }
+ return res;
+ }
+
+ /**
+ * Get the current time in the time filter.
+ *
+ * It is used to inquiry current time in the time filter.
+ *
+ * @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. {@code null} if the timestamp is
+ * never set.
+ */
+ @Nullable
+ public Long getTimeStamp() {
+ if (!mEnable) {
+ return null;
+ }
+ return nativeGetTimestamp();
+ }
+
+ /**
+ * Get the timestamp from the beginning of incoming data stream.
+ *
+ * It is used to inquiry the timestamp from the beginning of incoming data stream.
+ *
+ * @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.
+ */
+ @Nullable
+ public Long getSourceTime() {
+ if (!mEnable) {
+ return null;
+ }
+ return nativeGetSourceTime();
+ }
+
+ /**
+ * Close the Time Filter instance
+ *
+ * It is to release the TimeFilter instance. Resources are reclaimed so the instance must
+ * not be accessed after this method is called.
+ */
+ @Override
+ public void close() {
+ nativeClose();
+ }
+}
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 173551812f3a..1424c338769f 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -21,18 +21,12 @@ import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
-import android.media.tv.tuner.TunerConstants.DemuxPidType;
import android.media.tv.tuner.TunerConstants.FilterStatus;
import android.media.tv.tuner.TunerConstants.FilterSubtype;
-import android.media.tv.tuner.TunerConstants.FilterType;
import android.media.tv.tuner.TunerConstants.FrontendScanType;
-import android.media.tv.tuner.TunerConstants.LnbPosition;
-import android.media.tv.tuner.TunerConstants.LnbTone;
-import android.media.tv.tuner.TunerConstants.LnbVoltage;
import android.media.tv.tuner.TunerConstants.Result;
-import android.media.tv.tuner.filter.FilterConfiguration;
+import android.media.tv.tuner.filter.FilterConfiguration.FilterType;
import android.media.tv.tuner.filter.FilterEvent;
-import android.media.tv.tuner.filter.Settings;
import android.media.tv.tuner.frontend.FrontendCallback;
import android.media.tv.tuner.frontend.FrontendInfo;
import android.media.tv.tuner.frontend.FrontendStatus;
@@ -40,7 +34,6 @@ import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import java.io.FileDescriptor;
import java.util.List;
/**
@@ -223,11 +216,6 @@ public final class Tuner implements AutoCloseable {
}
break;
}
- case MSG_ON_LNB_EVENT: {
- if (mLnb != null && mLnb.mCallback != null) {
- mLnb.mCallback.onEvent(msg.arg1);
- }
- }
default:
// fall through
}
@@ -427,8 +415,14 @@ public final class Tuner implements AutoCloseable {
return mFrontend.mId;
}
- /** @hide */
- private static DemuxCapabilities getDemuxCapabilities() {
+ /**
+ * Gets Demux capabilities.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+ @Nullable
+ public static DemuxCapabilities getDemuxCapabilities(@NonNull Context context) {
+ TunerUtils.checkTunerPermission(context);
return nativeGetDemuxCapabilities();
}
@@ -458,122 +452,11 @@ public final class Tuner implements AutoCloseable {
* Tuner data filter.
*
* <p> This class is used to filter wanted data according to the filter's configuration.
+ * TODO: remove
*/
public class Filter {
- private long mNativeContext;
- private FilterCallback mCallback;
- int mId;
-
- private native int nativeConfigureFilter(
- int type, int subType, FilterConfiguration settings);
- private native int nativeGetId();
- private native int nativeSetDataSource(Filter source);
- private native int nativeStartFilter();
- private native int nativeStopFilter();
- private native int nativeFlushFilter();
- private native int nativeRead(byte[] buffer, int offset, int size);
- private native int nativeClose();
-
- private Filter(int id) {
- mId = id;
- }
-
- private void onFilterStatus(int status) {
- if (mHandler != null) {
- mHandler.sendMessage(
- mHandler.obtainMessage(MSG_ON_FILTER_STATUS, status, 0, this));
- }
- }
-
- /**
- * Configures the filter.
- *
- * @param settings the settings of the filter.
- * @return result status of the operation.
- * @hide
- */
- public int configure(FilterConfiguration settings) {
- int subType = -1;
- Settings s = settings.getSettings();
- if (s != null) {
- subType = s.getType();
- }
- return nativeConfigureFilter(settings.getType(), subType, settings);
- }
-
- /**
- * Gets the filter Id.
- *
- * @return the hardware resource Id for the filter.
- * @hide
- */
- public int getId() {
- return nativeGetId();
- }
-
- /**
- * Sets the filter's data source.
- *
- * A filter uses demux as data source by default. If the data was packetized
- * by multiple protocols, multiple filters may need to work together to
- * extract all protocols' header. Then a filter's data source can be output
- * from another filter.
- *
- * @param source the filter instance which provides data input. Switch to
- * use demux as data source if the filter instance is NULL.
- * @return result status of the operation.
- * @hide
- */
- public int setDataSource(@Nullable Filter source) {
- return nativeSetDataSource(source);
- }
-
- /**
- * Starts the filter.
- *
- * @return result status of the operation.
- * @hide
- */
- public int start() {
- return nativeStartFilter();
- }
-
-
- /**
- * Stops the filter.
- *
- * @return result status of the operation.
- * @hide
- */
- public int stop() {
- return nativeStopFilter();
- }
-
- /**
- * Flushes the filter.
- *
- * @return result status of the operation.
- * @hide
- */
- public int flush() {
- return nativeFlushFilter();
- }
-
- /** @hide */
- public int read(@NonNull byte[] buffer, int offset, int size) {
- size = Math.min(size, buffer.length - offset);
- return nativeRead(buffer, offset, size);
- }
-
- /**
- * Release the Filter instance.
- *
- * @return result status of the operation.
- * @hide
- */
- public int close() {
- return nativeClose();
- }
+ FilterCallback mCallback;
+ private Filter() {}
}
private Filter openFilter(@FilterType int mainType, @FilterSubtype int subType, int bufferSize,
@@ -589,211 +472,6 @@ public final class Tuner implements AutoCloseable {
return filter;
}
- /**
- * A timer filter is used to filter data based on timestamps.
- *
- * <p> If the timestamp is set, data is discarded if its timestamp is smaller than the
- * timestamp in this time filter.
- *
- * <p> The format of the timestamps is the same as PTS defined in ISO/IEC 13818-1:2019. The
- * timestamps may or may not be related to PTS or DTS.
- *
- * @hide
- */
- public class TimeFilter {
- private native int nativeSetTimestamp(long timestamp);
- private native int nativeClearTimestamp();
- private native Long nativeGetTimestamp();
- private native Long nativeGetSourceTime();
- private native int nativeClose();
-
- private boolean mEnable = false;
-
- /**
- * Set timestamp for time based filter.
- *
- * It is used to set initial timestamp and enable time filtering. Once set, the time will be
- * increased automatically like a clock. Contents are discarded if their timestamps are
- * older than the time in the time filter.
- *
- * This method can be called more than once to reset the initial timestamp.
- *
- * @param timestamp initial timestamp for the time filter before it's increased. 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.
- * @return result status of the operation.
- */
- @Result
- public int setCurrentTimestamp(long timestamp) {
- int res = nativeSetTimestamp(timestamp);
- // TODO: use a constant for SUCCESS
- if (res == 0) {
- mEnable = true;
- }
- return res;
- }
-
- /**
- * Clear the timestamp in the time filter.
- *
- * It is used to clear the time value of the time filter. Time filtering is disabled then.
- *
- * @return result status of the operation.
- */
- @Result
- public int clearTimestamp() {
- int res = nativeClearTimestamp();
- if (res == 0) {
- mEnable = false;
- }
- return res;
- }
-
- /**
- * Get the current time in the time filter.
- *
- * It is used to inquiry current time in the time filter.
- *
- * @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. {@code null} if the timestamp is
- * never set.
- */
- @Nullable
- public Long getTimeStamp() {
- if (!mEnable) {
- return null;
- }
- return nativeGetTimestamp();
- }
-
- /**
- * Get the timestamp from the beginning of incoming data stream.
- *
- * It is used to inquiry the timestamp from the beginning of incoming data stream.
- *
- * @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.
- */
- @Nullable
- public Long getSourceTime() {
- if (!mEnable) {
- return null;
- }
- return nativeGetSourceTime();
- }
-
- /**
- * Close the Time Filter instance
- *
- * It is to release the TimeFilter instance. Resources are reclaimed so the instance must
- * not be accessed after this method is called.
- *
- * @return result status of the operation.
- */
- @Result
- public int close() {
- return nativeClose();
- }
- }
-
- /**
- * Open a time filter instance.
- *
- * It is used to open time filter of demux.
- *
- * @return a time filter instance.
- * @hide
- */
- public TimeFilter openTimeFilter() {
- return nativeOpenTimeFilter();
- }
-
- /** @hide */
- public class Lnb {
- private int mId;
- private LnbCallback mCallback;
-
- private native int nativeSetVoltage(int voltage);
- private native int nativeSetTone(int tone);
- private native int nativeSetSatellitePosition(int position);
- private native int nativeSendDiseqcMessage(byte[] message);
- private native int nativeClose();
-
- private Lnb(int id) {
- mId = id;
- }
-
- public void setCallback(@Nullable LnbCallback callback) {
- mCallback = callback;
- if (mCallback == null) {
- return;
- }
- if (mHandler == null) {
- mHandler = createEventHandler();
- }
- }
-
- /**
- * Sets the LNB's power voltage.
- *
- * @param voltage the power voltage the Lnb to use.
- * @return result status of the operation.
- */
- @Result
- public int setVoltage(@LnbVoltage int voltage) {
- return nativeSetVoltage(voltage);
- }
-
- /**
- * Sets the LNB's tone mode.
- *
- * @param tone the tone mode the Lnb to use.
- * @return result status of the operation.
- */
- @Result
- public int setTone(@LnbTone int tone) {
- return nativeSetTone(tone);
- }
-
- /**
- * Selects the LNB's position.
- *
- * @param position the position the Lnb to use.
- * @return result status of the operation.
- */
- @Result
- public int setSatellitePosition(@LnbPosition int position) {
- return nativeSetSatellitePosition(position);
- }
-
- /**
- * Sends DiSEqC (Digital Satellite Equipment Control) message.
- *
- * The response message from the device comes back through callback onDiseqcMessage.
- *
- * @param message a byte array of data for DiSEqC message which is specified by EUTELSAT Bus
- * Functional Specification Version 4.2.
- *
- * @return result status of the operation.
- */
- @Result
- public int sendDiseqcMessage(byte[] message) {
- return nativeSendDiseqcMessage(message);
- }
-
- /**
- * Releases the LNB instance
- *
- * @return result status of the operation.
- */
- @Result
- public int close() {
- return nativeClose();
- }
- }
-
private List<Integer> getLnbIds() {
mLnbIds = nativeGetLnbIds();
return mLnbIds;
@@ -822,81 +500,11 @@ public final class Tuner implements AutoCloseable {
* <p> Descrambler is a hardware component used to descramble data.
*
* <p> This class controls the TIS interaction with Tuner HAL.
- * TODO: make it static and extends Closable.
+ * TODO: Remove
*/
public class Descrambler {
- private long mNativeContext;
-
- private native int nativeAddPid(int pidType, int pid, Filter filter);
- private native int nativeRemovePid(int pidType, int pid, Filter filter);
- private native int nativeSetKeyToken(byte[] keyToken);
- private native int nativeClose();
-
- private Descrambler() {}
-
- /**
- * Add packets' PID to the descrambler for descrambling.
- *
- * The descrambler will start descrambling packets with this PID. Multiple PIDs can be added
- * into one descrambler instance because descambling can happen simultaneously on packets
- * from different PIDs.
- *
- * If the Descrambler previously contained a filter for the PID, the old filter is replaced
- * by the specified filter.
- *
- * @param pidType the type of the PID.
- * @param pid the PID of packets to start to be descrambled.
- * @param filter an optional filter instance to identify upper stream.
- * @return result status of the operation.
- *
- * @hide
- */
- public int addPid(@DemuxPidType int pidType, int pid, @Nullable Filter filter) {
- return nativeAddPid(pidType, pid, filter);
- }
-
- /**
- * Remove packets' PID from the descrambler
- *
- * The descrambler will stop descrambling packets with this PID.
- *
- * @param pidType the type of the PID.
- * @param pid the PID of packets to stop to be descrambled.
- * @param filter an optional filter instance to identify upper stream.
- * @return result status of the operation.
- *
- * @hide
- */
- public int removePid(@DemuxPidType int pidType, int pid, @Nullable Filter filter) {
- return nativeRemovePid(pidType, pid, filter);
- }
-
- /**
- * Set a key token to link descrambler to a key slot
- *
- * A descrambler instance can have only one key slot to link, but a key slot can hold a few
- * keys for different purposes.
- *
- * @param keyToken the token to be used to link the key slot.
- * @return result status of the operation.
- *
- * @hide
- */
- public int setKeyToken(byte[] keyToken) {
- return nativeSetKeyToken(keyToken);
+ private Descrambler() {
}
-
- /**
- * Release the descrambler instance.
- *
- * @return result status of the operation.
- *
- * @hide
- */
- public int close() {
- return nativeClose();
- }
-
}
/**
@@ -911,137 +519,6 @@ public final class Tuner implements AutoCloseable {
return nativeOpenDescrambler();
}
- // TODO: consider splitting Dvr to Playback and Recording
- /** @hide */
- public class Dvr {
- private long mNativeContext;
- private DvrCallback mCallback;
-
- private native int nativeAttachFilter(Filter filter);
- private native int nativeDetachFilter(Filter filter);
- private native int nativeConfigureDvr(DvrSettings settings);
- private native int nativeStartDvr();
- private native int nativeStopDvr();
- private native int nativeFlushDvr();
- private native int nativeClose();
- private native void nativeSetFileDescriptor(FileDescriptor fd);
- private native int nativeRead(int size);
- private native int nativeRead(byte[] bytes, int offset, int size);
- private native int nativeWrite(int size);
- private native int nativeWrite(byte[] bytes, int offset, int size);
-
- private Dvr() {}
-
- /**
- * Attaches a filter to DVR interface for recording.
- *
- * @param filter the filter to be attached.
- * @return result status of the operation.
- */
- public int attachFilter(Filter filter) {
- return nativeAttachFilter(filter);
- }
-
- /**
- * Detaches a filter from DVR interface.
- *
- * @param filter the filter to be detached.
- * @return result status of the operation.
- */
- public int detachFilter(Filter filter) {
- return nativeDetachFilter(filter);
- }
-
- /**
- * Configures the DVR.
- *
- * @param settings the settings of the DVR interface.
- * @return result status of the operation.
- */
- public int configure(DvrSettings settings) {
- return nativeConfigureDvr(settings);
- }
-
- /**
- * Starts DVR.
- *
- * Starts consuming playback data or producing data for recording.
- *
- * @return result status of the operation.
- */
- public int start() {
- return nativeStartDvr();
- }
-
- /**
- * Stops DVR.
- *
- * Stops consuming playback data or producing data for recording.
- *
- * @return result status of the operation.
- */
- public int stop() {
- return nativeStopDvr();
- }
-
- /**
- * Flushed DVR data.
- *
- * @return result status of the operation.
- */
- public int flush() {
- return nativeFlushDvr();
- }
-
- /**
- * closes the DVR instance to release resources.
- *
- * @return result status of the operation.
- */
- public int close() {
- return nativeClose();
- }
-
- /**
- * Sets file descriptor to read/write data.
- */
- public void setFileDescriptor(FileDescriptor fd) {
- nativeSetFileDescriptor(fd);
- }
-
- /**
- * Reads data from the file for DVR playback.
- */
- public int read(int size) {
- return nativeRead(size);
- }
-
- /**
- * Reads data from the buffer for DVR playback.
- */
- public int read(@NonNull byte[] bytes, int offset, int size) {
- if (size + offset > bytes.length) {
- throw new ArrayIndexOutOfBoundsException(
- "Array length=" + bytes.length + ", offset=" + offset + ", size=" + size);
- }
- return nativeRead(bytes, offset, size);
- }
-
- /**
- * Writes recording data to file.
- */
- public int write(int size) {
- return nativeWrite(size);
- }
-
- /**
- * Writes recording data to buffer.
- */
- public int write(@NonNull byte[] bytes, int offset, int size) {
- return nativeWrite(bytes, offset, size);
- }
- }
-
private Dvr openDvr(int type, int bufferSize) {
Dvr dvr = nativeOpenDvr(type, bufferSize);
return dvr;
diff --git a/media/java/android/media/tv/tuner/TunerConstants.java b/media/java/android/media/tv/tuner/TunerConstants.java
index e02443202085..f2617f401806 100644
--- a/media/java/android/media/tv/tuner/TunerConstants.java
+++ b/media/java/android/media/tv/tuner/TunerConstants.java
@@ -38,35 +38,6 @@ public final class TunerConstants {
/** @hide */
- @IntDef({FRONTEND_TYPE_UNDEFINED, FRONTEND_TYPE_ANALOG, FRONTEND_TYPE_ATSC, FRONTEND_TYPE_ATSC3,
- FRONTEND_TYPE_DVBC, FRONTEND_TYPE_DVBS, FRONTEND_TYPE_DVBT, FRONTEND_TYPE_ISDBS,
- FRONTEND_TYPE_ISDBS3, FRONTEND_TYPE_ISDBT})
- @Retention(RetentionPolicy.SOURCE)
- public @interface FrontendType {}
-
- /** @hide */
- public static final int FRONTEND_TYPE_UNDEFINED = Constants.FrontendType.UNDEFINED;
- /** @hide */
- public static final int FRONTEND_TYPE_ANALOG = Constants.FrontendType.ANALOG;
- /** @hide */
- public static final int FRONTEND_TYPE_ATSC = Constants.FrontendType.ATSC;
- /** @hide */
- public static final int FRONTEND_TYPE_ATSC3 = Constants.FrontendType.ATSC3;
- /** @hide */
- public static final int FRONTEND_TYPE_DVBC = Constants.FrontendType.DVBC;
- /** @hide */
- public static final int FRONTEND_TYPE_DVBS = Constants.FrontendType.DVBS;
- /** @hide */
- public static final int FRONTEND_TYPE_DVBT = Constants.FrontendType.DVBT;
- /** @hide */
- public static final int FRONTEND_TYPE_ISDBS = Constants.FrontendType.ISDBS;
- /** @hide */
- public static final int FRONTEND_TYPE_ISDBS3 = Constants.FrontendType.ISDBS3;
- /** @hide */
- public static final int FRONTEND_TYPE_ISDBT = Constants.FrontendType.ISDBT;
-
-
- /** @hide */
@IntDef({FRONTEND_EVENT_TYPE_LOCKED, FRONTEND_EVENT_TYPE_NO_SIGNAL,
FRONTEND_EVENT_TYPE_LOST_LOCK})
@Retention(RetentionPolicy.SOURCE)
@@ -80,20 +51,6 @@ public final class TunerConstants {
/** @hide */
- @IntDef({DATA_FORMAT_TS, DATA_FORMAT_PES, DATA_FORMAT_ES, DATA_FORMAT_SHV_TLV})
- @Retention(RetentionPolicy.SOURCE)
- public @interface DataFormat {}
- /** @hide */
- public static final int DATA_FORMAT_TS = Constants.DataFormat.TS;
- /** @hide */
- public static final int DATA_FORMAT_PES = Constants.DataFormat.PES;
- /** @hide */
- public static final int DATA_FORMAT_ES = Constants.DataFormat.ES;
- /** @hide */
- public static final int DATA_FORMAT_SHV_TLV = Constants.DataFormat.SHV_TLV;
-
-
- /** @hide */
@IntDef({DEMUX_T_PID, DEMUX_MMPT_PID})
@Retention(RetentionPolicy.SOURCE)
public @interface DemuxPidType {}
@@ -102,45 +59,6 @@ public final class TunerConstants {
/** @hide */
public static final int DEMUX_MMPT_PID = 2;
- /** @hide */
- @IntDef({FRONTEND_SETTINGS_ANALOG, FRONTEND_SETTINGS_ATSC, FRONTEND_SETTINGS_ATSC3,
- FRONTEND_SETTINGS_DVBS, FRONTEND_SETTINGS_DVBC, FRONTEND_SETTINGS_DVBT,
- FRONTEND_SETTINGS_ISDBS, FRONTEND_SETTINGS_ISDBS3, FRONTEND_SETTINGS_ISDBT})
- @Retention(RetentionPolicy.SOURCE)
- public @interface FrontendSettingsType {}
- /** @hide */
- public static final int FRONTEND_SETTINGS_ANALOG = 1;
- /** @hide */
- public static final int FRONTEND_SETTINGS_ATSC = 2;
- /** @hide */
- public static final int FRONTEND_SETTINGS_ATSC3 = 3;
- /** @hide */
- public static final int FRONTEND_SETTINGS_DVBS = 4;
- /** @hide */
- public static final int FRONTEND_SETTINGS_DVBC = 5;
- /** @hide */
- public static final int FRONTEND_SETTINGS_DVBT = 6;
- /** @hide */
- public static final int FRONTEND_SETTINGS_ISDBS = 7;
- /** @hide */
- public static final int FRONTEND_SETTINGS_ISDBS3 = 8;
- /** @hide */
- public static final int FRONTEND_SETTINGS_ISDBT = 9;
-
- /** @hide */
- @IntDef({FILTER_TYPE_TS, FILTER_TYPE_MMTP, FILTER_TYPE_IP, FILTER_TYPE_TLV, FILTER_TYPE_ALP})
- @Retention(RetentionPolicy.SOURCE)
- public @interface FilterType {}
- /** @hide */
- public static final int FILTER_TYPE_TS = Constants.DemuxFilterMainType.TS;
- /** @hide */
- public static final int FILTER_TYPE_MMTP = Constants.DemuxFilterMainType.MMTP;
- /** @hide */
- public static final int FILTER_TYPE_IP = Constants.DemuxFilterMainType.IP;
- /** @hide */
- public static final int FILTER_TYPE_TLV = Constants.DemuxFilterMainType.TLV;
- /** @hide */
- public static final int FILTER_TYPE_ALP = Constants.DemuxFilterMainType.ALP;
/** @hide */
@IntDef({FILTER_SUBTYPE_UNDEFINED, FILTER_SUBTYPE_SECTION, FILTER_SUBTYPE_PES,
@@ -186,8 +104,8 @@ public final class TunerConstants {
public static final int FILTER_SUBTYPE_PTP = 16;
/** @hide */
- @IntDef({FILTER_STATUS_DATA_READY, FILTER_STATUS_LOW_WATER, FILTER_STATUS_HIGH_WATER,
- FILTER_STATUS_OVERFLOW})
+ @IntDef(flag = true, prefix = "FILTER_STATUS_", value = {FILTER_STATUS_DATA_READY,
+ FILTER_STATUS_LOW_WATER, FILTER_STATUS_HIGH_WATER, FILTER_STATUS_OVERFLOW})
@Retention(RetentionPolicy.SOURCE)
public @interface FilterStatus {}
@@ -742,73 +660,6 @@ public final class TunerConstants {
Constants.FrontendDvbtHierarchy.HIERARCHY_4_INDEPTH;
/** @hide */
- @IntDef({FRONTEND_ANALOG_TYPE_UNDEFINED, FRONTEND_ANALOG_TYPE_PAL, FRONTEND_ANALOG_TYPE_SECAM,
- FRONTEND_ANALOG_TYPE_NTSC})
- @Retention(RetentionPolicy.SOURCE)
- public @interface FrontendAnalogType {}
- /** @hide */
- public static final int FRONTEND_ANALOG_TYPE_UNDEFINED = Constants.FrontendAnalogType.UNDEFINED;
- /** @hide */
- public static final int FRONTEND_ANALOG_TYPE_PAL = Constants.FrontendAnalogType.PAL;
- /** @hide */
- public static final int FRONTEND_ANALOG_TYPE_SECAM = Constants.FrontendAnalogType.SECAM;
- /** @hide */
- public static final int FRONTEND_ANALOG_TYPE_NTSC = Constants.FrontendAnalogType.NTSC;
-
- /** @hide */
- @IntDef({FRONTEND_ANALOG_SIF_UNDEFINED, FRONTEND_ANALOG_SIF_BG, FRONTEND_ANALOG_SIF_BG_A2,
- FRONTEND_ANALOG_SIF_BG_NICAM, FRONTEND_ANALOG_SIF_I, FRONTEND_ANALOG_SIF_DK,
- FRONTEND_ANALOG_SIF_DK1, FRONTEND_ANALOG_SIF_DK2, FRONTEND_ANALOG_SIF_DK3,
- FRONTEND_ANALOG_SIF_DK_NICAM, FRONTEND_ANALOG_SIF_L, FRONTEND_ANALOG_SIF_M,
- FRONTEND_ANALOG_SIF_M_BTSC, FRONTEND_ANALOG_SIF_M_A2, FRONTEND_ANALOG_SIF_M_EIA_J,
- FRONTEND_ANALOG_SIF_I_NICAM, FRONTEND_ANALOG_SIF_L_NICAM, FRONTEND_ANALOG_SIF_L_PRIME})
- @Retention(RetentionPolicy.SOURCE)
- public @interface FrontendAnalogSifStandard {}
- /** @hide */
- public static final int FRONTEND_ANALOG_SIF_UNDEFINED =
- Constants.FrontendAnalogSifStandard.UNDEFINED;
- /** @hide */
- public static final int FRONTEND_ANALOG_SIF_BG = Constants.FrontendAnalogSifStandard.BG;
- /** @hide */
- public static final int FRONTEND_ANALOG_SIF_BG_A2 = Constants.FrontendAnalogSifStandard.BG_A2;
- /** @hide */
- public static final int FRONTEND_ANALOG_SIF_BG_NICAM =
- Constants.FrontendAnalogSifStandard.BG_NICAM;
- /** @hide */
- public static final int FRONTEND_ANALOG_SIF_I = Constants.FrontendAnalogSifStandard.I;
- /** @hide */
- public static final int FRONTEND_ANALOG_SIF_DK = Constants.FrontendAnalogSifStandard.DK;
- /** @hide */
- public static final int FRONTEND_ANALOG_SIF_DK1 = Constants.FrontendAnalogSifStandard.DK1;
- /** @hide */
- public static final int FRONTEND_ANALOG_SIF_DK2 = Constants.FrontendAnalogSifStandard.DK2;
- /** @hide */
- public static final int FRONTEND_ANALOG_SIF_DK3 = Constants.FrontendAnalogSifStandard.DK3;
- /** @hide */
- public static final int FRONTEND_ANALOG_SIF_DK_NICAM =
- Constants.FrontendAnalogSifStandard.DK_NICAM;
- /** @hide */
- public static final int FRONTEND_ANALOG_SIF_L = Constants.FrontendAnalogSifStandard.L;
- /** @hide */
- public static final int FRONTEND_ANALOG_SIF_M = Constants.FrontendAnalogSifStandard.M;
- /** @hide */
- public static final int FRONTEND_ANALOG_SIF_M_BTSC = Constants.FrontendAnalogSifStandard.M_BTSC;
- /** @hide */
- public static final int FRONTEND_ANALOG_SIF_M_A2 = Constants.FrontendAnalogSifStandard.M_A2;
- /** @hide */
- public static final int FRONTEND_ANALOG_SIF_M_EIA_J =
- Constants.FrontendAnalogSifStandard.M_EIA_J;
- /** @hide */
- public static final int FRONTEND_ANALOG_SIF_I_NICAM =
- Constants.FrontendAnalogSifStandard.I_NICAM;
- /** @hide */
- public static final int FRONTEND_ANALOG_SIF_L_NICAM =
- Constants.FrontendAnalogSifStandard.L_NICAM;
- /** @hide */
- public static final int FRONTEND_ANALOG_SIF_L_PRIME =
- Constants.FrontendAnalogSifStandard.L_PRIME;
-
- /** @hide */
@IntDef({FRONTEND_ATSC_MODULATION_UNDEFINED, FRONTEND_ATSC_MODULATION_AUTO,
FRONTEND_ATSC_MODULATION_MOD_8VSB, FRONTEND_ATSC_MODULATION_MOD_16VSB})
@Retention(RetentionPolicy.SOURCE)
@@ -1267,79 +1118,47 @@ public final class TunerConstants {
/** @hide */
public static final int FILTER_SETTINGS_ALP = Constants.DemuxFilterMainType.ALP;
- /** @hide */
- @IntDef({DVR_SETTINGS_RECORD, DVR_SETTINGS_PLAYBACK})
- @Retention(RetentionPolicy.SOURCE)
- public @interface DvrSettingsType {}
- /** @hide */
- public static final int DVR_SETTINGS_RECORD = Constants.DvrType.RECORD;
- /** @hide */
- public static final int DVR_SETTINGS_PLAYBACK = Constants.DvrType.PLAYBACK;
-
-
- /** @hide */
- @IntDef({LNB_VOLTAGE_NONE, LNB_VOLTAGE_5V, LNB_VOLTAGE_11V, LNB_VOLTAGE_12V, LNB_VOLTAGE_13V,
- LNB_VOLTAGE_14V, LNB_VOLTAGE_15V, LNB_VOLTAGE_18V, LNB_VOLTAGE_19V})
- @Retention(RetentionPolicy.SOURCE)
- public @interface LnbVoltage {}
- /** @hide */
- public static final int LNB_VOLTAGE_NONE = Constants.LnbVoltage.NONE;
- /** @hide */
- public static final int LNB_VOLTAGE_5V = Constants.LnbVoltage.VOLTAGE_5V;
- /** @hide */
- public static final int LNB_VOLTAGE_11V = Constants.LnbVoltage.VOLTAGE_11V;
- /** @hide */
- public static final int LNB_VOLTAGE_12V = Constants.LnbVoltage.VOLTAGE_12V;
- /** @hide */
- public static final int LNB_VOLTAGE_13V = Constants.LnbVoltage.VOLTAGE_13V;
- /** @hide */
- public static final int LNB_VOLTAGE_14V = Constants.LnbVoltage.VOLTAGE_14V;
- /** @hide */
- public static final int LNB_VOLTAGE_15V = Constants.LnbVoltage.VOLTAGE_15V;
- /** @hide */
- public static final int LNB_VOLTAGE_18V = Constants.LnbVoltage.VOLTAGE_18V;
- /** @hide */
- public static final int LNB_VOLTAGE_19V = Constants.LnbVoltage.VOLTAGE_19V;
-
- /** @hide */
- @IntDef({LNB_TONE_NONE, LNB_TONE_CONTINUOUS})
- @Retention(RetentionPolicy.SOURCE)
- public @interface LnbTone {}
- /** @hide */
- public static final int LNB_TONE_NONE = Constants.LnbTone.NONE;
- /** @hide */
- public static final int LNB_TONE_CONTINUOUS = Constants.LnbTone.CONTINUOUS;
-
- /** @hide */
- @IntDef({LNB_POSITION_UNDEFINED, LNB_POSITION_A, LNB_POSITION_B})
- @Retention(RetentionPolicy.SOURCE)
- public @interface LnbPosition {}
- /** @hide */
- public static final int LNB_POSITION_UNDEFINED = Constants.LnbPosition.UNDEFINED;
- /** @hide */
- public static final int LNB_POSITION_A = Constants.LnbPosition.POSITION_A;
- /** @hide */
- public static final int LNB_POSITION_B = Constants.LnbPosition.POSITION_B;
-
/** @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 {}
- /** @hide */
+
+ /**
+ * Operation succeeded.
+ * @hide
+ */
public static final int RESULT_SUCCESS = Constants.Result.SUCCESS;
- /** @hide */
+ /**
+ * Operation failed because the corresponding resources are not available.
+ * @hide
+ */
public static final int RESULT_UNAVAILABLE = Constants.Result.UNAVAILABLE;
- /** @hide */
+ /**
+ * Operation failed because the corresponding resources are not initialized.
+ * @hide
+ */
public static final int RESULT_NOT_INITIALIZED = Constants.Result.NOT_INITIALIZED;
- /** @hide */
+ /**
+ * Operation failed because it's not in a valid state.
+ * @hide
+ */
public static final int RESULT_INVALID_STATE = Constants.Result.INVALID_STATE;
- /** @hide */
+ /**
+ * Operation failed because there are invalid arguments.
+ * @hide
+ */
public static final int RESULT_INVALID_ARGUMENT = Constants.Result.INVALID_ARGUMENT;
- /** @hide */
+ /**
+ * Memory allocation failed.
+ * @hide
+ */
public static final int RESULT_OUT_OF_MEMORY = Constants.Result.OUT_OF_MEMORY;
- /** @hide */
+ /**
+ * Operation failed due to unknown errors.
+ * @hide
+ */
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 a7ccb0288e03..8780b726e3b2 100644
--- a/media/java/android/media/tv/tuner/TunerUtils.java
+++ b/media/java/android/media/tv/tuner/TunerUtils.java
@@ -20,7 +20,8 @@ import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.tv.tuner.V1_0.Constants;
import android.media.tv.tuner.TunerConstants.FilterSubtype;
-import android.media.tv.tuner.TunerConstants.FilterType;
+import android.media.tv.tuner.filter.FilterConfiguration;
+import android.media.tv.tuner.filter.FilterConfiguration.FilterType;
/**
* Utility class for tuner framework.
@@ -50,7 +51,7 @@ public final class TunerUtils {
* @param subtype filter subtype.
*/
public static int getFilterSubtype(@FilterType int mainType, @FilterSubtype int subtype) {
- if (mainType == TunerConstants.FILTER_TYPE_TS) {
+ if (mainType == FilterConfiguration.FILTER_TYPE_TS) {
switch (subtype) {
case TunerConstants.FILTER_SUBTYPE_UNDEFINED:
return Constants.DemuxTsFilterType.UNDEFINED;
@@ -73,7 +74,7 @@ public final class TunerUtils {
default:
break;
}
- } else if (mainType == TunerConstants.FILTER_TYPE_MMTP) {
+ } else if (mainType == FilterConfiguration.FILTER_TYPE_MMTP) {
switch (subtype) {
case TunerConstants.FILTER_SUBTYPE_UNDEFINED:
return Constants.DemuxMmtpFilterType.UNDEFINED;
@@ -95,7 +96,7 @@ public final class TunerUtils {
break;
}
- } else if (mainType == TunerConstants.FILTER_TYPE_IP) {
+ } else if (mainType == FilterConfiguration.FILTER_TYPE_IP) {
switch (subtype) {
case TunerConstants.FILTER_SUBTYPE_UNDEFINED:
return Constants.DemuxIpFilterType.UNDEFINED;
@@ -112,7 +113,7 @@ public final class TunerUtils {
default:
break;
}
- } else if (mainType == TunerConstants.FILTER_TYPE_TLV) {
+ } else if (mainType == FilterConfiguration.FILTER_TYPE_TLV) {
switch (subtype) {
case TunerConstants.FILTER_SUBTYPE_UNDEFINED:
return Constants.DemuxTlvFilterType.UNDEFINED;
@@ -125,7 +126,7 @@ public final class TunerUtils {
default:
break;
}
- } else if (mainType == TunerConstants.FILTER_TYPE_ALP) {
+ } else if (mainType == FilterConfiguration.FILTER_TYPE_ALP) {
switch (subtype) {
case TunerConstants.FILTER_SUBTYPE_UNDEFINED:
return Constants.DemuxAlpFilterType.UNDEFINED;
diff --git a/media/java/android/media/tv/tuner/dvr/DvrSettings.java b/media/java/android/media/tv/tuner/dvr/DvrSettings.java
new file mode 100644
index 000000000000..2c2607471e7e
--- /dev/null
+++ b/media/java/android/media/tv/tuner/dvr/DvrSettings.java
@@ -0,0 +1,180 @@
+/*
+ * 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.dvr;
+
+import android.annotation.BytesLong;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.hardware.tv.tuner.V1_0.Constants;
+import android.media.tv.tuner.Dvr;
+import android.media.tv.tuner.TunerConstants.FilterStatus;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * DVR settings used to configure {@link Dvr}.
+ *
+ * @hide
+ */
+public class DvrSettings {
+
+ /** @hide */
+ @IntDef(prefix = "DATA_FORMAT_",
+ value = {DATA_FORMAT_TS, DATA_FORMAT_PES, DATA_FORMAT_ES, DATA_FORMAT_SHV_TLV})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DataFormat {}
+
+ /**
+ * Transport Stream.
+ */
+ public static final int DATA_FORMAT_TS = Constants.DataFormat.TS;
+ /**
+ * Packetized Elementary Stream.
+ */
+ public static final int DATA_FORMAT_PES = Constants.DataFormat.PES;
+ /**
+ * Elementary Stream.
+ */
+ public static final int DATA_FORMAT_ES = Constants.DataFormat.ES;
+ /**
+ * TLV (type-length-value) Stream for SHV
+ */
+ public static final int DATA_FORMAT_SHV_TLV = Constants.DataFormat.SHV_TLV;
+
+
+ /** @hide */
+ @IntDef(prefix = "TYPE_", value = {TYPE_RECORD, TYPE_PLAYBACK})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Type {}
+
+ /**
+ * DVR for recording.
+ */
+ public static final int TYPE_RECORD = Constants.DvrType.RECORD;
+ /**
+ * DVR for playback of recorded programs.
+ */
+ public static final int TYPE_PLAYBACK = Constants.DvrType.PLAYBACK;
+
+
+
+ private final int mStatusMask;
+ private final long mLowThreshold;
+ private final long mHighThreshold;
+ private final long mPacketSize;
+
+ @DataFormat
+ private final int mDataFormat;
+ @Type
+ private final int mType;
+
+ private DvrSettings(int statusMask, long lowThreshold, long highThreshold, long packetSize,
+ @DataFormat int dataFormat, @Type int type) {
+ mStatusMask = statusMask;
+ mLowThreshold = lowThreshold;
+ mHighThreshold = highThreshold;
+ mPacketSize = packetSize;
+ mDataFormat = dataFormat;
+ mType = type;
+ }
+
+ /**
+ * Creates a builder for {@link DvrSettings}.
+ */
+ @NonNull
+ public static Builder newBuilder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder for {@link DvrSettings}.
+ */
+ public static final class Builder {
+ private int mStatusMask;
+ private long mLowThreshold;
+ private long mHighThreshold;
+ private long mPacketSize;
+ @DataFormat
+ private int mDataFormat;
+ @Type
+ private int mType;
+
+ /**
+ * Sets status mask.
+ */
+ @NonNull
+ public Builder setStatusMask(@FilterStatus int statusMask) {
+ this.mStatusMask = statusMask;
+ return this;
+ }
+
+ /**
+ * Sets low threshold in bytes.
+ */
+ @NonNull
+ public Builder setLowThreshold(@BytesLong long lowThreshold) {
+ this.mLowThreshold = lowThreshold;
+ return this;
+ }
+
+ /**
+ * Sets high threshold in bytes.
+ */
+ @NonNull
+ public Builder setHighThreshold(@BytesLong long highThreshold) {
+ this.mHighThreshold = highThreshold;
+ return this;
+ }
+
+ /**
+ * Sets packet size in bytes.
+ */
+ @NonNull
+ public Builder setPacketSize(@BytesLong long packetSize) {
+ this.mPacketSize = packetSize;
+ return this;
+ }
+
+ /**
+ * Sets data format.
+ */
+ @NonNull
+ public Builder setDataFormat(@DataFormat int dataFormat) {
+ this.mDataFormat = dataFormat;
+ return this;
+ }
+
+ /**
+ * Sets settings type.
+ */
+ @NonNull
+ public Builder setType(@Type int type) {
+ this.mType = type;
+ return this;
+ }
+
+ /**
+ * Builds a {@link DvrSettings} object.
+ */
+ @NonNull
+ public DvrSettings build() {
+ return new DvrSettings(
+ mStatusMask, mLowThreshold, mHighThreshold, mPacketSize, mDataFormat, mType);
+ }
+ }
+}
diff --git a/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java
index 4d02b849899d..f0fe533093ba 100644
--- a/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java
@@ -16,8 +16,6 @@
package android.media.tv.tuner.filter;
-import android.media.tv.tuner.TunerConstants;
-
/**
* Filter configuration for a ALP filter.
* @hide
@@ -32,6 +30,6 @@ public class AlpFilterConfiguration extends FilterConfiguration {
@Override
public int getType() {
- return TunerConstants.FILTER_TYPE_ALP;
+ return FilterConfiguration.FILTER_TYPE_ALP;
}
}
diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
new file mode 100644
index 000000000000..804c0c53982f
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -0,0 +1,139 @@
+/*
+ * 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.filter;
+
+import android.annotation.BytesLong;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.media.tv.tuner.Tuner.FilterCallback;
+
+/**
+ * Tuner data filter.
+ *
+ * <p>This class is used to filter wanted data according to the filter's configuration.
+ *
+ * @hide
+ */
+public class Filter implements AutoCloseable {
+ private long mNativeContext;
+ private FilterCallback mCallback;
+ private final int mId;
+
+ private native int nativeConfigureFilter(
+ int type, int subType, FilterConfiguration settings);
+ private native int nativeGetId();
+ private native int nativeSetDataSource(Filter source);
+ private native int nativeStartFilter();
+ private native int nativeStopFilter();
+ private native int nativeFlushFilter();
+ private native int nativeRead(byte[] buffer, long offset, long size);
+ private native int nativeClose();
+
+ private Filter(int id) {
+ mId = id;
+ }
+
+ private void onFilterStatus(int status) {
+ }
+
+ /**
+ * Configures the filter.
+ *
+ * @param config the configuration of the filter.
+ * @return result status of the operation.
+ */
+ public int configure(@NonNull FilterConfiguration config) {
+ int subType = -1;
+ Settings s = config.getSettings();
+ if (s != null) {
+ subType = s.getType();
+ }
+ return nativeConfigureFilter(config.getType(), subType, config);
+ }
+
+ /**
+ * Gets the filter Id.
+ */
+ public int getId() {
+ return nativeGetId();
+ }
+
+ /**
+ * Sets the filter's data source.
+ *
+ * A filter uses demux as data source by default. If the data was packetized
+ * by multiple protocols, multiple filters may need to work together to
+ * extract all protocols' header. Then a filter's data source can be output
+ * from another filter.
+ *
+ * @param source the filter instance which provides data input. Switch to
+ * use demux as data source if the filter instance is NULL.
+ * @return result status of the operation.
+ */
+ public int setDataSource(@Nullable Filter source) {
+ return nativeSetDataSource(source);
+ }
+
+ /**
+ * Starts filtering data.
+ *
+ * @return result status of the operation.
+ */
+ public int start() {
+ return nativeStartFilter();
+ }
+
+
+ /**
+ * Stops filtering data.
+ *
+ * @return result status of the operation.
+ */
+ public int stop() {
+ return nativeStopFilter();
+ }
+
+ /**
+ * Flushes the filter. Data in filter buffer is cleared.
+ *
+ * @return result status of the operation.
+ */
+ public int flush() {
+ return nativeFlushFilter();
+ }
+
+ /**
+ * Copies filtered data from filter buffer to the given byte array.
+ *
+ * @param buffer the buffer to store the filtered data.
+ * @param offset the index of the first byte in {@code buffer} to write.
+ * @param size the maximum number of bytes to read.
+ * @return the number of bytes read.
+ */
+ public int read(@NonNull byte[] buffer, @BytesLong long offset, @BytesLong long size) {
+ size = Math.min(size, buffer.length - offset);
+ return nativeRead(buffer, offset, size);
+ }
+
+ /**
+ * Releases the Filter instance.
+ */
+ @Override
+ public void close() {
+ nativeClose();
+ }
+}
diff --git a/media/java/android/media/tv/tuner/filter/FilterConfiguration.java b/media/java/android/media/tv/tuner/filter/FilterConfiguration.java
index 930ca744a168..99b10cde34f9 100644
--- a/media/java/android/media/tv/tuner/filter/FilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/FilterConfiguration.java
@@ -16,28 +16,62 @@
package android.media.tv.tuner.filter;
+import android.annotation.IntDef;
import android.annotation.Nullable;
-import android.media.tv.tuner.TunerConstants.FilterType;
+import android.hardware.tv.tuner.V1_0.Constants;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
- * Demux Filter configuration.
+ * Filter configuration used to configure filters.
*
* @hide
*/
public abstract class FilterConfiguration {
+
+ /** @hide */
+ @IntDef({FILTER_TYPE_TS, FILTER_TYPE_MMTP, FILTER_TYPE_IP, FILTER_TYPE_TLV, FILTER_TYPE_ALP})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FilterType {}
+
+ /**
+ * TS filter type.
+ */
+ public static final int FILTER_TYPE_TS = Constants.DemuxFilterMainType.TS;
+ /**
+ * MMTP filter type.
+ */
+ public static final int FILTER_TYPE_MMTP = Constants.DemuxFilterMainType.MMTP;
+ /**
+ * IP filter type.
+ */
+ public static final int FILTER_TYPE_IP = Constants.DemuxFilterMainType.IP;
+ /**
+ * TLV filter type.
+ */
+ public static final int FILTER_TYPE_TLV = Constants.DemuxFilterMainType.TLV;
+ /**
+ * ALP filter type.
+ */
+ public static final int FILTER_TYPE_ALP = Constants.DemuxFilterMainType.ALP;
+
@Nullable
- protected final Settings mSettings;
+ private final Settings mSettings;
- protected FilterConfiguration(Settings settings) {
+ /* package */ FilterConfiguration(Settings settings) {
mSettings = settings;
}
/**
- * Gets filter configuration type
+ * Gets filter configuration type.
+ * @hide
*/
@FilterType
public abstract int getType();
+ /** @hide */
+ @Nullable
public Settings getSettings() {
return mSettings;
}
diff --git a/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java
index 2c706c0ce9ce..c89636887628 100644
--- a/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java
@@ -16,8 +16,6 @@
package android.media.tv.tuner.filter;
-import android.media.tv.tuner.TunerConstants;
-
/**
* Filter configuration for a IP filter.
* @hide
@@ -35,6 +33,6 @@ public class IpFilterConfiguration extends FilterConfiguration {
@Override
public int getType() {
- return TunerConstants.FILTER_TYPE_IP;
+ return FilterConfiguration.FILTER_TYPE_IP;
}
}
diff --git a/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java
index f70e70a5bdb2..9045ce67a61f 100644
--- a/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java
@@ -16,8 +16,6 @@
package android.media.tv.tuner.filter;
-import android.media.tv.tuner.TunerConstants;
-
/**
* Filter configuration for a MMTP filter.
* @hide
@@ -31,6 +29,6 @@ public class MmtpFilterConfiguration extends FilterConfiguration {
@Override
public int getType() {
- return TunerConstants.FILTER_TYPE_MMTP;
+ return FilterConfiguration.FILTER_TYPE_MMTP;
}
}
diff --git a/media/java/android/media/tv/tuner/filter/PesSettings.java b/media/java/android/media/tv/tuner/filter/PesSettings.java
index 3d2c265cc00b..f38abf12e120 100644
--- a/media/java/android/media/tv/tuner/filter/PesSettings.java
+++ b/media/java/android/media/tv/tuner/filter/PesSettings.java
@@ -16,45 +16,54 @@
package android.media.tv.tuner.filter;
+import android.annotation.NonNull;
import android.media.tv.tuner.TunerConstants;
import android.media.tv.tuner.TunerUtils;
+import android.media.tv.tuner.filter.FilterConfiguration.FilterType;
/**
* Filter Settings for a PES Data.
+ *
* @hide
*/
public class PesSettings extends Settings {
- private int mStreamId;
- private boolean mIsRaw;
+ private final int mStreamId;
+ private final boolean mIsRaw;
- private PesSettings(int mainType, int streamId, boolean isRaw) {
+ private PesSettings(@FilterType int mainType, int streamId, boolean isRaw) {
super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_PES));
mStreamId = streamId;
mIsRaw = isRaw;
}
/**
- * Creates a builder for PesSettings.
+ * Creates a builder for {@link PesSettings}.
+ *
+ * @param mainType the filter main type of the settings.
*/
- public static Builder newBuilder(int mainType) {
+ @NonNull
+ public static Builder newBuilder(@FilterType int mainType) {
return new Builder(mainType);
}
/**
- * Builder for PesSettings.
+ * Builder for {@link PesSettings}.
*/
public static class Builder {
private final int mMainType;
private int mStreamId;
private boolean mIsRaw;
- public Builder(int mainType) {
+ private Builder(int mainType) {
mMainType = mainType;
}
/**
* Sets stream ID.
+ *
+ * @param streamId the stream ID.
*/
+ @NonNull
public Builder setStreamId(int streamId) {
mStreamId = streamId;
return this;
@@ -62,16 +71,20 @@ public class PesSettings extends Settings {
/**
* Sets whether it's raw.
- * true if the filter send onFilterStatus instead of onFilterEvent.
+ *
+ * @param isRaw {@code true} if the data is raw. Filter sends onFilterStatus callback
+ * instead of onFilterEvent for raw data. {@code false} otherwise.
*/
+ @NonNull
public Builder setIsRaw(boolean isRaw) {
mIsRaw = isRaw;
return this;
}
/**
- * Builds a PesSettings instance.
+ * Builds a {@link PesSettings} object.
*/
+ @NonNull
public PesSettings build() {
return new PesSettings(mMainType, mStreamId, mIsRaw);
}
diff --git a/media/java/android/media/tv/tuner/filter/Settings.java b/media/java/android/media/tv/tuner/filter/Settings.java
index 789ed0895750..146aca74ce0e 100644
--- a/media/java/android/media/tv/tuner/filter/Settings.java
+++ b/media/java/android/media/tv/tuner/filter/Settings.java
@@ -18,18 +18,20 @@ package android.media.tv.tuner.filter;
/**
* Settings for filters of different subtypes.
+ *
* @hide
*/
public abstract class Settings {
- protected final int mType;
+ private final int mType;
- protected Settings(int type) {
+ /* package */ Settings(int type) {
mType = type;
}
/**
* Gets filter settings type.
- * @return
+ *
+ * @hide
*/
public int getType() {
return mType;
diff --git a/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java
index 1f8bcb3b4a71..de8ee754a28c 100644
--- a/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/TlvFilterConfiguration.java
@@ -16,8 +16,6 @@
package android.media.tv.tuner.filter;
-import android.media.tv.tuner.TunerConstants;
-
/**
* Filter configuration for a TLV filter.
* @hide
@@ -33,6 +31,6 @@ public class TlvFilterConfiguration extends FilterConfiguration {
@Override
public int getType() {
- return TunerConstants.FILTER_TYPE_TLV;
+ return FilterConfiguration.FILTER_TYPE_TLV;
}
}
diff --git a/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java
index 103698cd0d75..d0241b6aba09 100644
--- a/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/TsFilterConfiguration.java
@@ -16,14 +16,15 @@
package android.media.tv.tuner.filter;
-import android.media.tv.tuner.TunerConstants;
+import android.annotation.NonNull;
/**
* Filter configuration for a TS filter.
+ *
* @hide
*/
public class TsFilterConfiguration extends FilterConfiguration {
- private int mTpid;
+ private final int mTpid;
private TsFilterConfiguration(Settings settings, int tpid) {
super(settings);
@@ -32,42 +33,50 @@ public class TsFilterConfiguration extends FilterConfiguration {
@Override
public int getType() {
- return TunerConstants.FILTER_TYPE_TS;
+ return FilterConfiguration.FILTER_TYPE_TS;
}
/**
- * Creates a new builder.
+ * Creates a builder for {@link TsFilterConfiguration}.
*/
- public static TsFilterConfiguration.Builder newBuilder() {
- return new TsFilterConfiguration.Builder();
+ @NonNull
+ public static Builder newBuilder() {
+ return new Builder();
}
/**
- * Builder for TsFilterConfiguration.
+ * Builder for {@link TsFilterConfiguration}.
*/
public static class Builder {
private Settings mSettings;
private int mTpid;
/**
- * Sets settings.
+ * Sets filter settings.
+ *
+ * @param settings the filter settings.
*/
- public TsFilterConfiguration.Builder setSettings(Settings settings) {
+ @NonNull
+ public Builder setSettings(@NonNull Settings settings) {
mSettings = settings;
return this;
}
/**
- * Sets TPID.
+ * Sets Tag Protocol ID.
+ *
+ * @param tpid the Tag Protocol ID.
*/
- public TsFilterConfiguration.Builder setTpid(int tpid) {
+ @NonNull
+ public Builder setTpid(int tpid) {
mTpid = tpid;
return this;
}
/**
- * Builds a TsFilterConfiguration instance.
+ * Builds a {@link TsFilterConfiguration} object.
*/
+ @NonNull
public TsFilterConfiguration build() {
return new TsFilterConfiguration(mSettings, mTpid);
}
diff --git a/media/java/android/media/tv/tuner/frontend/AnalogFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/AnalogFrontendCapabilities.java
new file mode 100644
index 000000000000..2962e98790e5
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/AnalogFrontendCapabilities.java
@@ -0,0 +1,41 @@
+/*
+ * 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.frontend;
+
+/**
+ * Analog Capabilities.
+ * @hide
+ */
+public class AnalogFrontendCapabilities extends FrontendCapabilities {
+ private final int mTypeCap;
+ private final int mSifStandardCap;
+
+ AnalogFrontendCapabilities(int typeCap, int sifStandardCap) {
+ mTypeCap = typeCap;
+ mSifStandardCap = sifStandardCap;
+ }
+ /**
+ * Gets type capability.
+ */
+ public int getTypeCapability() {
+ return mTypeCap;
+ }
+ /** Gets SIF standard capability. */
+ public int getSifStandardCapability() {
+ return mSifStandardCap;
+ }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java
index 16308ced2300..aec8ce895ab8 100644
--- a/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java
@@ -16,33 +16,153 @@
package android.media.tv.tuner.frontend;
-import android.media.tv.tuner.FrontendSettings;
-import android.media.tv.tuner.TunerConstants;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.hardware.tv.tuner.V1_0.Constants;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
- * Frontend settings for analog.
+ * Frontend settings for analog tuner.
+ *
* @hide
*/
public class AnalogFrontendSettings extends FrontendSettings {
- private int mAnalogType;
- private int mSifStandard;
+ /** @hide */
+ @IntDef(flag = true, value = {SIGNAL_TYPE_UNDEFINED, SIGNAL_TYPE_PAL, SIGNAL_TYPE_SECAM,
+ SIGNAL_TYPE_NTSC})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SignalType {}
+
+ /**
+ * Undefined analog signal type.
+ */
+ public static final int SIGNAL_TYPE_UNDEFINED = Constants.FrontendAnalogType.UNDEFINED;
+ /**
+ * PAL analog signal type.
+ */
+ public static final int SIGNAL_TYPE_PAL = Constants.FrontendAnalogType.PAL;
+ /**
+ * SECM analog signal type.
+ */
+ public static final int SIGNAL_TYPE_SECAM = Constants.FrontendAnalogType.SECAM;
+ /**
+ * NTSC analog signal type.
+ */
+ public static final int SIGNAL_TYPE_NTSC = Constants.FrontendAnalogType.NTSC;
+
+
+ /** @hide */
+ @IntDef(flag = true, value = {SIF_UNDEFINED, SIF_BG, SIF_BG_A2, SIF_BG_NICAM, SIF_I, SIF_DK,
+ SIF_DK1, SIF_DK2, SIF_DK3, SIF_DK_NICAM, SIF_L, SIF_M, SIF_M_BTSC, SIF_M_A2,
+ SIF_M_EIA_J, SIF_I_NICAM, SIF_L_NICAM, SIF_L_PRIME})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SifStandard {}
+
+ /**
+ * Undefined Analog Standard Interchange Format (SIF).
+ */
+ public static final int SIF_UNDEFINED = Constants.FrontendAnalogSifStandard.UNDEFINED;
+ /**
+ * BG Analog Standard Interchange Format (SIF).
+ */
+ public static final int SIF_BG = Constants.FrontendAnalogSifStandard.BG;
+ /**
+ * BG-A2 Analog Standard Interchange Format (SIF).
+ */
+ public static final int SIF_BG_A2 = Constants.FrontendAnalogSifStandard.BG_A2;
+ /**
+ * BG-NICAM Analog Standard Interchange Format (SIF).
+ */
+ public static final int SIF_BG_NICAM = Constants.FrontendAnalogSifStandard.BG_NICAM;
+ /**
+ * I Analog Standard Interchange Format (SIF).
+ */
+ public static final int SIF_I = Constants.FrontendAnalogSifStandard.I;
+ /**
+ * DK Analog Standard Interchange Format (SIF).
+ */
+ public static final int SIF_DK = Constants.FrontendAnalogSifStandard.DK;
+ /**
+ * DK1 Analog Standard Interchange Format (SIF).
+ */
+ public static final int SIF_DK1 = Constants.FrontendAnalogSifStandard.DK1;
+ /**
+ * DK2 Analog Standard Interchange Format (SIF).
+ */
+ public static final int SIF_DK2 = Constants.FrontendAnalogSifStandard.DK2;
+ /**
+ * DK3 Analog Standard Interchange Format (SIF).
+ */
+ public static final int SIF_DK3 = Constants.FrontendAnalogSifStandard.DK3;
+ /**
+ * DK-NICAM Analog Standard Interchange Format (SIF).
+ */
+ public static final int SIF_DK_NICAM = Constants.FrontendAnalogSifStandard.DK_NICAM;
+ /**
+ * L Analog Standard Interchange Format (SIF).
+ */
+ public static final int SIF_L = Constants.FrontendAnalogSifStandard.L;
+ /**
+ * M Analog Standard Interchange Format (SIF).
+ */
+ public static final int SIF_M = Constants.FrontendAnalogSifStandard.M;
+ /**
+ * M-BTSC Analog Standard Interchange Format (SIF).
+ */
+ public static final int SIF_M_BTSC = Constants.FrontendAnalogSifStandard.M_BTSC;
+ /**
+ * M-A2 Analog Standard Interchange Format (SIF).
+ */
+ public static final int SIF_M_A2 = Constants.FrontendAnalogSifStandard.M_A2;
+ /**
+ * M-EIA-J Analog Standard Interchange Format (SIF).
+ */
+ public static final int SIF_M_EIA_J = Constants.FrontendAnalogSifStandard.M_EIA_J;
+ /**
+ * I-NICAM Analog Standard Interchange Format (SIF).
+ */
+ public static final int SIF_I_NICAM = Constants.FrontendAnalogSifStandard.I_NICAM;
+ /**
+ * L-NICAM Analog Standard Interchange Format (SIF).
+ */
+ public static final int SIF_L_NICAM = Constants.FrontendAnalogSifStandard.L_NICAM;
+ /**
+ * L-PRIME Analog Standard Interchange Format (SIF).
+ */
+ public static final int SIF_L_PRIME = Constants.FrontendAnalogSifStandard.L_PRIME;
+
+
+ private final int mAnalogType;
+ private final int mSifStandard;
@Override
public int getType() {
- return TunerConstants.FRONTEND_TYPE_ANALOG;
+ return FrontendSettings.TYPE_ANALOG;
}
+
+ /**
+ * Gets analog signal type.
+ */
+ @SignalType
public int getAnalogType() {
return mAnalogType;
}
+ /**
+ * Gets Standard Interchange Format (SIF).
+ */
+ @SifStandard
public int getSifStandard() {
return mSifStandard;
}
/**
- * Creates a new builder object.
+ * Creates a builder for {@link AnalogFrontendSettings}.
*/
+ @NonNull
public static Builder newBuilder() {
return new Builder();
}
@@ -54,7 +174,7 @@ public class AnalogFrontendSettings extends FrontendSettings {
}
/**
- * Builder for FrontendAnalogSettings.
+ * Builder for {@link AnalogFrontendSettings}.
*/
public static class Builder {
private int mFrequency;
@@ -64,8 +184,9 @@ public class AnalogFrontendSettings extends FrontendSettings {
private Builder() {}
/**
- * Sets frequency.
+ * Sets frequency in Hz.
*/
+ @NonNull
public Builder setFrequency(int frequency) {
mFrequency = frequency;
return this;
@@ -74,22 +195,25 @@ public class AnalogFrontendSettings extends FrontendSettings {
/**
* Sets analog type.
*/
- public Builder setAnalogType(int analogType) {
+ @NonNull
+ public Builder setAnalogType(@SignalType int analogType) {
mAnalogType = analogType;
return this;
}
/**
- * Sets sif standard.
+ * Sets Standard Interchange Format (SIF).
*/
- public Builder setSifStandard(int sifStandard) {
+ @NonNull
+ public Builder setSifStandard(@SifStandard int sifStandard) {
mSifStandard = sifStandard;
return this;
}
/**
- * Builds a FrontendAnalogSettings instance.
+ * Builds a {@link AnalogFrontendSettings} object.
*/
+ @NonNull
public AnalogFrontendSettings build() {
return new AnalogFrontendSettings(mFrequency, mAnalogType, mSifStandard);
}
diff --git a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendCapabilities.java
new file mode 100644
index 000000000000..677f9387c6d2
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendCapabilities.java
@@ -0,0 +1,65 @@
+/*
+ * 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.frontend;
+
+/**
+ * ATSC-3 Capabilities.
+ * @hide
+ */
+public class Atsc3FrontendCapabilities extends FrontendCapabilities {
+ private final int mBandwidthCap;
+ private final int mModulationCap;
+ private final int mTimeInterleaveModeCap;
+ private final int mCodeRateCap;
+ private final int mFecCap;
+ private final int mDemodOutputFormatCap;
+
+ Atsc3FrontendCapabilities(int bandwidthCap, int modulationCap, int timeInterleaveModeCap,
+ int codeRateCap, int fecCap, int demodOutputFormatCap) {
+ mBandwidthCap = bandwidthCap;
+ mModulationCap = modulationCap;
+ mTimeInterleaveModeCap = timeInterleaveModeCap;
+ mCodeRateCap = codeRateCap;
+ mFecCap = fecCap;
+ mDemodOutputFormatCap = demodOutputFormatCap;
+ }
+
+ /** Gets bandwidth capability. */
+ public int getBandwidthCapability() {
+ return mBandwidthCap;
+ }
+ /** Gets modulation capability. */
+ public int getModulationCapability() {
+ return mModulationCap;
+ }
+ /** Gets time interleave mod capability. */
+ public int getTimeInterleaveModeCapability() {
+ return mTimeInterleaveModeCap;
+ }
+ /** Gets code rate capability. */
+ public int getCodeRateCapability() {
+ return mCodeRateCap;
+ }
+ /** Gets FEC capability. */
+ public int getFecCapability() {
+ return mFecCap;
+ }
+ /** Gets demodulator output format capability. */
+ public int getDemodOutputFormatCapability() {
+ return mDemodOutputFormatCap;
+ }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
index bce8a640c7f2..5b09e36ea113 100644
--- a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
@@ -16,10 +16,6 @@
package android.media.tv.tuner.frontend;
-
-import android.media.tv.tuner.FrontendSettings;
-import android.media.tv.tuner.TunerConstants;
-
import java.util.List;
/**
@@ -37,6 +33,6 @@ public class Atsc3FrontendSettings extends FrontendSettings {
@Override
public int getType() {
- return TunerConstants.FRONTEND_TYPE_ATSC3;
+ return FrontendSettings.TYPE_ATSC3;
}
}
diff --git a/media/java/android/media/tv/tuner/frontend/AtscFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/AtscFrontendCapabilities.java
new file mode 100644
index 000000000000..6ae3c632f5db
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/AtscFrontendCapabilities.java
@@ -0,0 +1,33 @@
+/*
+ * 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.frontend;
+
+/**
+ * ATSC Capabilities.
+ * @hide
+ */
+public class AtscFrontendCapabilities extends FrontendCapabilities {
+ private final int mModulationCap;
+
+ AtscFrontendCapabilities(int modulationCap) {
+ mModulationCap = modulationCap;
+ }
+ /** Gets modulation capability. */
+ public int getModulationCapability() {
+ return mModulationCap;
+ }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java
index 14c5cdd112e1..19e18d017e67 100644
--- a/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java
@@ -16,9 +16,6 @@
package android.media.tv.tuner.frontend;
-import android.media.tv.tuner.FrontendSettings;
-import android.media.tv.tuner.TunerConstants;
-
/**
* Frontend settings for ATSC.
* @hide
@@ -32,6 +29,6 @@ public class AtscFrontendSettings extends FrontendSettings {
@Override
public int getType() {
- return TunerConstants.FRONTEND_TYPE_ATSC;
+ return FrontendSettings.TYPE_ATSC;
}
}
diff --git a/media/java/android/media/tv/tuner/frontend/DvbcFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/DvbcFrontendCapabilities.java
new file mode 100644
index 000000000000..edea7af06774
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/DvbcFrontendCapabilities.java
@@ -0,0 +1,46 @@
+/*
+ * 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.frontend;
+
+/**
+ * DVBC Capabilities.
+ * @hide
+ */
+public class DvbcFrontendCapabilities extends FrontendCapabilities {
+ private final int mModulationCap;
+ private final int mFecCap;
+ private final int mAnnexCap;
+
+ DvbcFrontendCapabilities(int modulationCap, int fecCap, int annexCap) {
+ mModulationCap = modulationCap;
+ mFecCap = fecCap;
+ mAnnexCap = annexCap;
+ }
+
+ /** Gets modulation capability. */
+ public int getModulationCapability() {
+ return mModulationCap;
+ }
+ /** Gets FEC capability. */
+ public int getFecCapability() {
+ return mFecCap;
+ }
+ /** Gets annex capability. */
+ public int getAnnexCapability() {
+ return mAnnexCap;
+ }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
index 07e49ff24de1..60618f6f6896 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
@@ -16,9 +16,6 @@
package android.media.tv.tuner.frontend;
-import android.media.tv.tuner.FrontendSettings;
-import android.media.tv.tuner.TunerConstants;
-
/**
* Frontend settings for DVBC.
* @hide
@@ -37,6 +34,6 @@ public class DvbcFrontendSettings extends FrontendSettings {
@Override
public int getType() {
- return TunerConstants.FRONTEND_TYPE_DVBC;
+ return FrontendSettings.TYPE_DVBC;
}
}
diff --git a/media/java/android/media/tv/tuner/frontend/DvbsFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/DvbsFrontendCapabilities.java
new file mode 100644
index 000000000000..f5a41574cd04
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/DvbsFrontendCapabilities.java
@@ -0,0 +1,46 @@
+/*
+ * 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.frontend;
+
+/**
+ * DVBS Capabilities.
+ * @hide
+ */
+public class DvbsFrontendCapabilities extends FrontendCapabilities {
+ private final int mModulationCap;
+ private final long mInnerFecCap;
+ private final int mStandard;
+
+ DvbsFrontendCapabilities(int modulationCap, long innerFecCap, int standard) {
+ mModulationCap = modulationCap;
+ mInnerFecCap = innerFecCap;
+ mStandard = standard;
+ }
+
+ /** Gets modulation capability. */
+ public int getModulationCapability() {
+ return mModulationCap;
+ }
+ /** Gets inner FEC capability. */
+ public long getInnerFecCapability() {
+ return mInnerFecCap;
+ }
+ /** Gets DVBS standard capability. */
+ public int getStandardCapability() {
+ return mStandard;
+ }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
index 23c0a7b15e52..586787f9eb73 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
@@ -16,9 +16,6 @@
package android.media.tv.tuner.frontend;
-import android.media.tv.tuner.FrontendSettings;
-import android.media.tv.tuner.TunerConstants;
-
/**
* Frontend settings for DVBS.
* @hide
@@ -38,6 +35,6 @@ public class DvbsFrontendSettings extends FrontendSettings {
@Override
public int getType() {
- return TunerConstants.FRONTEND_TYPE_DVBS;
+ return FrontendSettings.TYPE_DVBS;
}
}
diff --git a/media/java/android/media/tv/tuner/frontend/DvbtFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/DvbtFrontendCapabilities.java
new file mode 100644
index 000000000000..e9c16ddd4dc8
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/DvbtFrontendCapabilities.java
@@ -0,0 +1,78 @@
+/*
+ * 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.frontend;
+
+/**
+ * DVBT Capabilities.
+ * @hide
+ */
+public class DvbtFrontendCapabilities extends FrontendCapabilities {
+ private final int mTransmissionModeCap;
+ private final int mBandwidthCap;
+ private final int mConstellationCap;
+ private final int mCoderateCap;
+ private final int mHierarchyCap;
+ private final int mGuardIntervalCap;
+ private final boolean mIsT2Supported;
+ private final boolean mIsMisoSupported;
+
+ DvbtFrontendCapabilities(int transmissionModeCap, int bandwidthCap, int constellationCap,
+ int coderateCap, int hierarchyCap, int guardIntervalCap, boolean isT2Supported,
+ boolean isMisoSupported) {
+ mTransmissionModeCap = transmissionModeCap;
+ mBandwidthCap = bandwidthCap;
+ mConstellationCap = constellationCap;
+ mCoderateCap = coderateCap;
+ mHierarchyCap = hierarchyCap;
+ mGuardIntervalCap = guardIntervalCap;
+ mIsT2Supported = isT2Supported;
+ mIsMisoSupported = isMisoSupported;
+ }
+
+ /** Gets transmission mode capability. */
+ public int getTransmissionModeCapability() {
+ return mTransmissionModeCap;
+ }
+ /** Gets bandwidth capability. */
+ public int getBandwidthCapability() {
+ return mBandwidthCap;
+ }
+ /** Gets constellation capability. */
+ public int getConstellationCapability() {
+ return mConstellationCap;
+ }
+ /** Gets code rate capability. */
+ public int getCodeRateCapability() {
+ return mCoderateCap;
+ }
+ /** Gets hierarchy capability. */
+ public int getHierarchyCapability() {
+ return mHierarchyCap;
+ }
+ /** Gets guard interval capability. */
+ public int getGuardIntervalCapability() {
+ return mGuardIntervalCap;
+ }
+ /** Returns whether T2 is supported. */
+ public boolean getIsT2Supported() {
+ return mIsT2Supported;
+ }
+ /** Returns whether MISO is supported. */
+ public boolean getIsMisoSupported() {
+ return mIsMisoSupported;
+ }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
index eec00f3fab80..6b350a7865a2 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
@@ -16,10 +16,6 @@
package android.media.tv.tuner.frontend;
-
-import android.media.tv.tuner.FrontendSettings;
-import android.media.tv.tuner.TunerConstants;
-
/**
* Frontend settings for DVBT.
* @hide
@@ -45,6 +41,6 @@ public class DvbtFrontendSettings extends FrontendSettings {
@Override
public int getType() {
- return TunerConstants.FRONTEND_TYPE_DVBT;
+ return FrontendSettings.TYPE_DVBT;
}
}
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/FrontendCapabilities.java
new file mode 100644
index 000000000000..7350bc0c3914
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/FrontendCapabilities.java
@@ -0,0 +1,24 @@
+/*
+ * 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.frontend;
+
+/**
+ * Frontend Capabilities.
+ * @hide
+ */
+public abstract class FrontendCapabilities {
+}
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendInfo.java b/media/java/android/media/tv/tuner/frontend/FrontendInfo.java
index ef6c029fe626..99e8dd2fe31f 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendInfo.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendInfo.java
@@ -16,8 +16,7 @@
package android.media.tv.tuner.frontend;
-import android.media.tv.tuner.FrontendCapabilities;
-import android.media.tv.tuner.TunerConstants.FrontendType;
+import android.media.tv.tuner.frontend.FrontendSettings.Type;
/**
* Frontend info.
@@ -55,7 +54,7 @@ public class FrontendInfo {
return mId;
}
/** Gets frontend type. */
- @FrontendType
+ @Type
public int getType() {
return mType;
}
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
new file mode 100644
index 000000000000..210aef4163e5
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
@@ -0,0 +1,100 @@
+/*
+ * 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.frontend;
+
+import android.annotation.IntDef;
+import android.hardware.tv.tuner.V1_0.Constants;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Frontend settings for tune and scan operations.
+ *
+ * @hide
+ */
+public abstract class FrontendSettings {
+ /** @hide */
+ @IntDef({TYPE_UNDEFINED, TYPE_ANALOG, TYPE_ATSC, TYPE_ATSC3, TYPE_DVBC, TYPE_DVBS, TYPE_DVBT,
+ TYPE_ISDBS, TYPE_ISDBS3, TYPE_ISDBT})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Type {}
+
+ /**
+ * Undefined frontend type.
+ */
+ public static final int TYPE_UNDEFINED = Constants.FrontendType.UNDEFINED;
+ /**
+ * Analog frontend type.
+ */
+ public static final int TYPE_ANALOG = Constants.FrontendType.ANALOG;
+ /**
+ * Advanced Television Systems Committee (ATSC) frontend type.
+ */
+ public static final int TYPE_ATSC = Constants.FrontendType.ATSC;
+ /**
+ * Advanced Television Systems Committee 3.0 (ATSC-3) frontend type.
+ */
+ public static final int TYPE_ATSC3 = Constants.FrontendType.ATSC3;
+ /**
+ * Digital Video Broadcasting-Cable (DVB-C) frontend type.
+ */
+ public static final int TYPE_DVBC = Constants.FrontendType.DVBC;
+ /**
+ * Digital Video Broadcasting-Satellite (DVB-S) frontend type.
+ */
+ public static final int TYPE_DVBS = Constants.FrontendType.DVBS;
+ /**
+ * Digital Video Broadcasting-Terrestrial (DVB-T) frontend type.
+ */
+ public static final int TYPE_DVBT = Constants.FrontendType.DVBT;
+ /**
+ * Integrated Services Digital Broadcasting-Satellite (ISDB-S) frontend type.
+ */
+ public static final int TYPE_ISDBS = Constants.FrontendType.ISDBS;
+ /**
+ * Integrated Services Digital Broadcasting-Satellite 3 (ISDB-S3) frontend type.
+ */
+ public static final int TYPE_ISDBS3 = Constants.FrontendType.ISDBS3;
+ /**
+ * Integrated Services Digital Broadcasting-Terrestrial (ISDB-T) frontend type.
+ */
+ public static final int TYPE_ISDBT = Constants.FrontendType.ISDBT;
+
+ private final int mFrequency;
+
+ /** @hide */
+ public FrontendSettings(int frequency) {
+ mFrequency = frequency;
+ }
+
+ /**
+ * Returns the frontend type.
+ */
+ @Type
+ public abstract int getType();
+
+ /**
+ * Gets the frequency.
+ *
+ * @return the frequency in Hz.
+ */
+ public int getFrequency() {
+ return mFrequency;
+ }
+
+}
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
index 89ec536a1d6c..fb5d62afd2f2 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
@@ -16,13 +16,13 @@
package android.media.tv.tuner.frontend;
+import android.media.tv.tuner.Lnb;
import android.media.tv.tuner.TunerConstants;
import android.media.tv.tuner.TunerConstants.FrontendDvbcSpectralInversion;
import android.media.tv.tuner.TunerConstants.FrontendDvbtHierarchy;
import android.media.tv.tuner.TunerConstants.FrontendInnerFec;
import android.media.tv.tuner.TunerConstants.FrontendModulation;
import android.media.tv.tuner.TunerConstants.FrontendStatusType;
-import android.media.tv.tuner.TunerConstants.LnbVoltage;
/**
* Frontend status
@@ -128,7 +128,7 @@ public class FrontendStatus {
return (int) mValue;
}
/** Power Voltage Type for LNB. */
- @LnbVoltage
+ @Lnb.Voltage
public int getLnbVoltage() {
if (mType != TunerConstants.FRONTEND_STATUS_TYPE_LNB_VOLTAGE) {
throw new IllegalStateException();
diff --git a/media/java/android/media/tv/tuner/frontend/IsdbcFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/IsdbcFrontendCapabilities.java
new file mode 100644
index 000000000000..6544b17609c2
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/IsdbcFrontendCapabilities.java
@@ -0,0 +1,59 @@
+/*
+ * 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.frontend;
+
+/**
+ * ISDBC Capabilities.
+ * @hide
+ */
+public class IsdbcFrontendCapabilities extends FrontendCapabilities {
+ private final int mModeCap;
+ private final int mBandwidthCap;
+ private final int mModulationCap;
+ private final int mCoderateCap;
+ private final int mGuardIntervalCap;
+
+ IsdbcFrontendCapabilities(int modeCap, int bandwidthCap, int modulationCap, int coderateCap,
+ int guardIntervalCap) {
+ mModeCap = modeCap;
+ mBandwidthCap = bandwidthCap;
+ mModulationCap = modulationCap;
+ mCoderateCap = coderateCap;
+ mGuardIntervalCap = guardIntervalCap;
+ }
+
+ /** Gets mode capability. */
+ public int getModeCapability() {
+ return mModeCap;
+ }
+ /** Gets bandwidth capability. */
+ public int getBandwidthCapability() {
+ return mBandwidthCap;
+ }
+ /** Gets modulation capability. */
+ public int getModulationCapability() {
+ return mModulationCap;
+ }
+ /** Gets code rate capability. */
+ public int getCodeRateCapability() {
+ return mCoderateCap;
+ }
+ /** Gets guard interval capability. */
+ public int getGuardIntervalCapability() {
+ return mGuardIntervalCap;
+ }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendCapabilities.java
new file mode 100644
index 000000000000..92832b7fcbdd
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendCapabilities.java
@@ -0,0 +1,40 @@
+/*
+ * 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.frontend;
+
+/**
+ * ISDBS-3 Capabilities.
+ * @hide
+ */
+public class Isdbs3FrontendCapabilities extends FrontendCapabilities {
+ private final int mModulationCap;
+ private final int mCoderateCap;
+
+ Isdbs3FrontendCapabilities(int modulationCap, int coderateCap) {
+ mModulationCap = modulationCap;
+ mCoderateCap = coderateCap;
+ }
+
+ /** Gets modulation capability. */
+ public int getModulationCapability() {
+ return mModulationCap;
+ }
+ /** Gets code rate capability. */
+ public int getCodeRateCapability() {
+ return mCoderateCap;
+ }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
index 736d0b199d29..45932a74a946 100644
--- a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
@@ -16,9 +16,6 @@
package android.media.tv.tuner.frontend;
-import android.media.tv.tuner.FrontendSettings;
-import android.media.tv.tuner.TunerConstants;
-
/**
* Frontend settings for ISDBS-3.
* @hide
@@ -37,6 +34,6 @@ public class Isdbs3FrontendSettings extends FrontendSettings {
@Override
public int getType() {
- return TunerConstants.FRONTEND_TYPE_ISDBS3;
+ return FrontendSettings.TYPE_ISDBS3;
}
}
diff --git a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendCapabilities.java
new file mode 100644
index 000000000000..b930b2578092
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendCapabilities.java
@@ -0,0 +1,40 @@
+/*
+ * 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.frontend;
+
+/**
+ * ISDBS Capabilities.
+ * @hide
+ */
+public class IsdbsFrontendCapabilities extends FrontendCapabilities {
+ private final int mModulationCap;
+ private final int mCoderateCap;
+
+ IsdbsFrontendCapabilities(int modulationCap, int coderateCap) {
+ mModulationCap = modulationCap;
+ mCoderateCap = coderateCap;
+ }
+
+ /** Gets modulation capability. */
+ public int getModulationCapability() {
+ return mModulationCap;
+ }
+ /** Gets code rate capability. */
+ public int getCodeRateCapability() {
+ return mCoderateCap;
+ }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
index 7fd5da78c600..e726a9a701f0 100644
--- a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
@@ -16,9 +16,6 @@
package android.media.tv.tuner.frontend;
-import android.media.tv.tuner.FrontendSettings;
-import android.media.tv.tuner.TunerConstants;
-
/**
* Frontend settings for ISDBS.
* @hide
@@ -37,6 +34,6 @@ public class IsdbsFrontendSettings extends FrontendSettings {
@Override
public int getType() {
- return TunerConstants.FRONTEND_TYPE_ISDBS;
+ return FrontendSettings.TYPE_ISDBS;
}
}
diff --git a/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java
index 3f83267fe5b4..f2b7d2413911 100644
--- a/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java
@@ -16,10 +16,6 @@
package android.media.tv.tuner.frontend;
-
-import android.media.tv.tuner.FrontendSettings;
-import android.media.tv.tuner.TunerConstants;
-
/**
* Frontend settings for ISDBT.
* @hide
@@ -37,6 +33,6 @@ public class IsdbtFrontendSettings extends FrontendSettings {
@Override
public int getType() {
- return TunerConstants.FRONTEND_TYPE_ISDBT;
+ return FrontendSettings.TYPE_ISDBT;
}
}
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index ee6761344613..536a061190d7 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -143,6 +143,10 @@ cc_library_shared {
"libutils",
],
+ header_libs: [
+ "libstagefright_foundation_headers",
+ ],
+
export_include_dirs: ["."],
cflags: [
diff --git a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
index ec177321bba3..8c0273b06e8c 100644
--- a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
+++ b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
@@ -64,7 +64,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService
"com.android.mediarouteprovider.TYPE_SPECIAL";
Map<String, MediaRoute2Info> mRoutes = new HashMap<>();
- Map<String, Integer> mRouteSessionMap = new HashMap<>();
+ Map<String, String> mRouteSessionMap = new HashMap<>();
private int mNextSessionId = 1000;
private void initializeRoutes() {
@@ -177,7 +177,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService
}
maybeDeselectRoute(routeId);
- final int sessionId = mNextSessionId;
+ final String sessionId = String.valueOf(mNextSessionId);
mNextSessionId++;
mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
@@ -196,7 +196,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService
}
@Override
- public void onDestroySession(int sessionId, RouteSessionInfo lastSessionInfo) {
+ public void onDestroySession(String sessionId, RouteSessionInfo lastSessionInfo) {
for (String routeId : lastSessionInfo.getSelectedRoutes()) {
mRouteSessionMap.remove(routeId);
MediaRoute2Info route = mRoutes.get(routeId);
@@ -209,7 +209,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService
}
@Override
- public void onSelectRoute(int sessionId, String routeId) {
+ public void onSelectRoute(String sessionId, String routeId) {
RouteSessionInfo sessionInfo = getSessionInfo(sessionId);
MediaRoute2Info route = mRoutes.get(routeId);
if (route == null || sessionInfo == null) {
@@ -218,7 +218,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService
maybeDeselectRoute(routeId);
mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
- .setClientPackageName(sessionInfo.getPackageName())
+ .setClientPackageName(sessionInfo.getClientPackageName())
.build());
mRouteSessionMap.put(routeId, sessionId);
@@ -232,7 +232,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService
}
@Override
- public void onDeselectRoute(int sessionId, String routeId) {
+ public void onDeselectRoute(String sessionId, String routeId) {
RouteSessionInfo sessionInfo = getSessionInfo(sessionId);
MediaRoute2Info route = mRoutes.get(routeId);
@@ -254,7 +254,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService
}
@Override
- public void onTransferToRoute(int sessionId, String routeId) {
+ public void onTransferToRoute(String sessionId, String routeId) {
RouteSessionInfo sessionInfo = getSessionInfo(sessionId);
RouteSessionInfo newSessionInfo = new RouteSessionInfo.Builder(sessionInfo)
.clearSelectedRoutes()
@@ -271,7 +271,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService
return;
}
- int sessionId = mRouteSessionMap.get(routeId);
+ String sessionId = mRouteSessionMap.get(routeId);
onDeselectRoute(sessionId, routeId);
}
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
index af69c7e8699f..ce4bb8ef2688 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
@@ -400,6 +400,7 @@ public class MediaRouter2Test {
assertTrue(createRouteMap(controller2.getSelectedRoutes()).containsKey(ROUTE_ID2));
assertTrue(TextUtils.equals(TYPE_SAMPLE, controller1.getRouteType()));
assertTrue(TextUtils.equals(TYPE_SAMPLE, controller2.getRouteType()));
+
} finally {
releaseControllers(createdControllers);
mRouter2.unregisterRouteCallback(routeCallback);
@@ -486,20 +487,21 @@ public class MediaRouter2Test {
public void onSessionInfoChanged(RouteSessionController controller,
RouteSessionInfo oldInfo, RouteSessionInfo newInfo) {
if (onSessionCreatedLatch.getCount() != 0
- || controllers.get(0).getSessionId() != controller.getSessionId()) {
+ || !TextUtils.equals(
+ controllers.get(0).getSessionId(), controller.getSessionId())) {
return;
}
if (onSessionInfoChangedLatchForSelect.getCount() != 0) {
// Check oldInfo
- assertEquals(controller.getSessionId(), oldInfo.getSessionId());
+ assertEquals(controller.getSessionId(), oldInfo.getId());
assertEquals(1, oldInfo.getSelectedRoutes().size());
assertTrue(oldInfo.getSelectedRoutes().contains(ROUTE_ID1));
assertTrue(oldInfo.getSelectableRoutes().contains(
ROUTE_ID4_TO_SELECT_AND_DESELECT));
// Check newInfo
- assertEquals(controller.getSessionId(), newInfo.getSessionId());
+ assertEquals(controller.getSessionId(), newInfo.getId());
assertEquals(2, newInfo.getSelectedRoutes().size());
assertTrue(newInfo.getSelectedRoutes().contains(ROUTE_ID1));
assertTrue(newInfo.getSelectedRoutes().contains(
@@ -510,7 +512,7 @@ public class MediaRouter2Test {
onSessionInfoChangedLatchForSelect.countDown();
} else {
// Check newInfo
- assertEquals(controller.getSessionId(), newInfo.getSessionId());
+ assertEquals(controller.getSessionId(), newInfo.getId());
assertEquals(1, newInfo.getSelectedRoutes().size());
assertTrue(newInfo.getSelectedRoutes().contains(ROUTE_ID1));
assertFalse(newInfo.getSelectedRoutes().contains(
@@ -587,18 +589,19 @@ public class MediaRouter2Test {
public void onSessionInfoChanged(RouteSessionController controller,
RouteSessionInfo oldInfo, RouteSessionInfo newInfo) {
if (onSessionCreatedLatch.getCount() != 0
- || controllers.get(0).getSessionId() != controller.getSessionId()) {
+ || !TextUtils.equals(
+ controllers.get(0).getSessionId(), controller.getSessionId())) {
return;
}
// Check oldInfo
- assertEquals(controller.getSessionId(), oldInfo.getSessionId());
+ assertEquals(controller.getSessionId(), oldInfo.getId());
assertEquals(1, oldInfo.getSelectedRoutes().size());
assertTrue(oldInfo.getSelectedRoutes().contains(ROUTE_ID1));
assertTrue(oldInfo.getTransferrableRoutes().contains(ROUTE_ID5_TO_TRANSFER_TO));
// Check newInfo
- assertEquals(controller.getSessionId(), newInfo.getSessionId());
+ assertEquals(controller.getSessionId(), newInfo.getId());
assertEquals(1, newInfo.getSelectedRoutes().size());
assertFalse(newInfo.getSelectedRoutes().contains(ROUTE_ID1));
assertTrue(newInfo.getSelectedRoutes().contains(ROUTE_ID5_TO_TRANSFER_TO));
@@ -665,7 +668,8 @@ public class MediaRouter2Test {
public void onSessionInfoChanged(RouteSessionController controller,
RouteSessionInfo oldInfo, RouteSessionInfo newInfo) {
if (onSessionCreatedLatch.getCount() != 0
- || controllers.get(0).getSessionId() != controller.getSessionId()) {
+ || !TextUtils.equals(
+ controllers.get(0).getSessionId(), controller.getSessionId())) {
return;
}
onSessionInfoChangedLatch.countDown();
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
index c23a5b0d76e3..9ff9177c1b40 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
@@ -99,7 +99,7 @@ public class MediaRouterManagerTest {
public static final String TYPE_SPECIAL =
"com.android.mediarouteprovider.TYPE_SPECIAL";
- private static final String TYPE_LIVE_AUDIO = "android.media.intent.category.LIVE_AUDIO";
+ private static final String TYPE_LIVE_AUDIO = "android.media.intent.route.TYPE_LIVE_AUDIO";
private static final int TIMEOUT_MS = 5000;
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/RouteSessionTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/RouteSessionTest.java
index 2e81a646b0db..9971fc3bbe9f 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/RouteSessionTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/RouteSessionTest.java
@@ -29,6 +29,7 @@ import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class RouteSessionTest {
+ private static final String TEST_SESSION_ID = "test_session_id";
private static final String TEST_PACKAGE_NAME = "com.android.mediaroutertest";
private static final String TEST_CONTROL_CATEGORY = "com.android.mediaroutertest.category";
@@ -36,17 +37,17 @@ public class RouteSessionTest {
@Test
public void testValidity() {
- RouteSessionInfo emptyPackageSession = new RouteSessionInfo.Builder(1,
+ RouteSessionInfo emptyPackageSession = new RouteSessionInfo.Builder(TEST_SESSION_ID,
"",
TEST_CONTROL_CATEGORY)
.addSelectedRoute(TEST_ROUTE_ID1)
.build();
- RouteSessionInfo emptyCategorySession = new RouteSessionInfo.Builder(1,
+ RouteSessionInfo emptyCategorySession = new RouteSessionInfo.Builder(TEST_SESSION_ID,
TEST_PACKAGE_NAME, "")
.addSelectedRoute(TEST_ROUTE_ID1)
.build();
- RouteSessionInfo emptySelectedRouteSession = new RouteSessionInfo.Builder(1,
+ RouteSessionInfo emptySelectedRouteSession = new RouteSessionInfo.Builder(TEST_SESSION_ID,
TEST_PACKAGE_NAME, TEST_CONTROL_CATEGORY)
.build();
@@ -54,9 +55,9 @@ public class RouteSessionTest {
.addSelectedRoute(TEST_ROUTE_ID1)
.build();
- assertFalse(emptySelectedRouteSession.isValid());
assertFalse(emptyPackageSession.isValid());
assertFalse(emptyCategorySession.isValid());
+ assertFalse(emptySelectedRouteSession.isValid());
assertTrue(validSession.isValid());
}
}
diff --git a/mms/OWNERS b/mms/OWNERS
index ba00d5d75010..befc320b949c 100644
--- a/mms/OWNERS
+++ b/mms/OWNERS
@@ -12,3 +12,5 @@ satk@google.com
shuoq@google.com
refuhoo@google.com
nazaninb@google.com
+sarahchin@google.com
+dbright@google.com \ No newline at end of file
diff --git a/mms/java/android/telephony/MmsManager.java b/mms/java/android/telephony/MmsManager.java
index 24ea3cfc5a75..65542673a607 100644
--- a/mms/java/android/telephony/MmsManager.java
+++ b/mms/java/android/telephony/MmsManager.java
@@ -16,11 +16,8 @@
package android.telephony;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.app.ActivityThread;
import android.app.PendingIntent;
-import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import android.os.RemoteException;
@@ -30,16 +27,22 @@ import com.android.internal.telephony.IMms;
/**
* Manages MMS operations such as sending multimedia messages.
+ * Get this object by calling the static method {@link #getInstance()}.
+ * @hide
*/
-public final class MmsManager {
+public class MmsManager {
private static final String TAG = "MmsManager";
- private final Context mContext;
+
+ /** Singleton object constructed during class initialization. */
+ private static final MmsManager sInstance = new MmsManager();
/**
- * @hide
+ * Get the MmsManager singleton instance.
+ *
+ * @return the {@link MmsManager} singleton instance.
*/
- public MmsManager(@NonNull Context context) {
- mContext = context;
+ public static MmsManager getInstance() {
+ return sInstance;
}
/**
@@ -53,9 +56,8 @@ public final class MmsManager {
* @param sentIntent if not NULL this <code>PendingIntent</code> is broadcast when the message
* is successfully sent, or failed
*/
- public void sendMultimediaMessage(int subId, @NonNull Uri contentUri,
- @Nullable String locationUrl, @Nullable Bundle configOverrides,
- @Nullable PendingIntent sentIntent) {
+ public void sendMultimediaMessage(int subId, Uri contentUri, String locationUrl,
+ Bundle configOverrides, PendingIntent sentIntent) {
try {
final IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
if (iMms == null) {
@@ -82,9 +84,8 @@ public final class MmsManager {
* broadcast when the message is downloaded, or the download is failed
* @throws IllegalArgumentException if locationUrl or contentUri is empty
*/
- public void downloadMultimediaMessage(int subId, @NonNull String locationUrl,
- @NonNull Uri contentUri, @Nullable Bundle configOverrides,
- @Nullable PendingIntent downloadedIntent) {
+ public void downloadMultimediaMessage(int subId, String locationUrl, Uri contentUri,
+ Bundle configOverrides, PendingIntent downloadedIntent) {
try {
final IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
if (iMms == null) {
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
index f7802d205a3a..6ec385438e23 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
@@ -19,14 +19,19 @@ import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
+import com.android.systemui.statusbar.notification.collection.NotificationRowBinder;
import com.android.systemui.statusbar.notification.logging.NotifLog;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.util.leak.LeakDetector;
import javax.inject.Inject;
import javax.inject.Singleton;
+import dagger.Lazy;
+
/**
* Car specific notification entry manager that does nothing when adding a notification.
*
@@ -42,8 +47,12 @@ public class CarNotificationEntryManager extends NotificationEntryManager {
NotificationGroupManager groupManager,
NotificationRankingManager rankingManager,
KeyguardEnvironment keyguardEnvironment,
- FeatureFlags featureFlags) {
- super(notifLog, groupManager, rankingManager, keyguardEnvironment, featureFlags);
+ FeatureFlags featureFlags,
+ Lazy<NotificationRowBinder> notificationRowBinderLazy,
+ Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
+ LeakDetector leakDetector) {
+ super(notifLog, groupManager, rankingManager, keyguardEnvironment, featureFlags,
+ notificationRowBinderLazy, notificationRemoteInputManagerLazy, leakDetector);
}
@Override
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 1ee85188eac3..4521f5fb470e 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.car;
-import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
import android.animation.Animator;
@@ -107,10 +106,10 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.collection.init.NewNotifPipeline;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
@@ -269,7 +268,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
HeadsUpManagerPhone headsUpManagerPhone,
DynamicPrivacyController dynamicPrivacyController,
BypassHeadsUpNotifier bypassHeadsUpNotifier,
- @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowNotificationLongPress,
Lazy<NewNotifPipeline> newNotifPipeline,
FalsingManager falsingManager,
BroadcastDispatcher broadcastDispatcher,
@@ -277,7 +275,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
NotificationGutsManager notificationGutsManager,
NotificationLogger notificationLogger,
NotificationEntryManager notificationEntryManager,
- NotificationRowContentBinder notificationRowContentBinder,
NotificationInterruptionStateProvider notificationInterruptionStateProvider,
NotificationViewHierarchyManager notificationViewHierarchyManager,
KeyguardViewMediator keyguardViewMediator,
@@ -336,6 +333,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
KeyguardDismissUtil keyguardDismissUtil,
ExtensionController extensionController,
UserInfoControllerImpl userInfoControllerImpl,
+ NotificationRowBinderImpl notificationRowBinder,
DismissCallbackRegistry dismissCallbackRegistry,
/* Car Settings injected components. */
CarServiceProvider carServiceProvider,
@@ -357,7 +355,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
headsUpManagerPhone,
dynamicPrivacyController,
bypassHeadsUpNotifier,
- allowNotificationLongPress,
newNotifPipeline,
falsingManager,
broadcastDispatcher,
@@ -365,7 +362,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
notificationGutsManager,
notificationLogger,
notificationEntryManager,
- notificationRowContentBinder,
notificationInterruptionStateProvider,
notificationViewHierarchyManager,
keyguardViewMediator,
@@ -424,6 +420,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
keyguardDismissUtil,
extensionController,
userInfoControllerImpl,
+ notificationRowBinder,
dismissCallbackRegistry);
mScrimController = scrimController;
mLockscreenLockIconController = lockscreenLockIconController;
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
index 7108e65c8bce..e5a091f94077 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.car;
-import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
import android.content.Context;
@@ -67,10 +66,10 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.collection.init.NewNotifPipeline;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.DozeParameters;
@@ -139,7 +138,6 @@ public class CarStatusBarModule {
HeadsUpManagerPhone headsUpManagerPhone,
DynamicPrivacyController dynamicPrivacyController,
BypassHeadsUpNotifier bypassHeadsUpNotifier,
- @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowNotificationLongPress,
Lazy<NewNotifPipeline> newNotifPipeline,
FalsingManager falsingManager,
BroadcastDispatcher broadcastDispatcher,
@@ -147,7 +145,6 @@ public class CarStatusBarModule {
NotificationGutsManager notificationGutsManager,
NotificationLogger notificationLogger,
NotificationEntryManager notificationEntryManager,
- NotificationRowContentBinder notificationRowContentBinder,
NotificationInterruptionStateProvider notificationInterruptionStateProvider,
NotificationViewHierarchyManager notificationViewHierarchyManager,
KeyguardViewMediator keyguardViewMediator,
@@ -206,6 +203,7 @@ public class CarStatusBarModule {
KeyguardDismissUtil keyguardDismissUtil,
ExtensionController extensionController,
UserInfoControllerImpl userInfoControllerImpl,
+ NotificationRowBinderImpl notificationRowBinder,
DismissCallbackRegistry dismissCallbackRegistry,
CarServiceProvider carServiceProvider,
Lazy<PowerManagerHelper> powerManagerHelperLazy,
@@ -226,7 +224,6 @@ public class CarStatusBarModule {
headsUpManagerPhone,
dynamicPrivacyController,
bypassHeadsUpNotifier,
- allowNotificationLongPress,
newNotifPipeline,
falsingManager,
broadcastDispatcher,
@@ -234,7 +231,6 @@ public class CarStatusBarModule {
notificationGutsManager,
notificationLogger,
notificationEntryManager,
- notificationRowContentBinder,
notificationInterruptionStateProvider,
notificationViewHierarchyManager,
keyguardViewMediator,
@@ -292,6 +288,7 @@ public class CarStatusBarModule {
keyguardDismissUtil,
extensionController,
userInfoControllerImpl,
+ notificationRowBinder,
dismissCallbackRegistry,
carServiceProvider,
powerManagerHelperLazy,
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 1e19786f5d41..ec445d4dcbee 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -160,7 +160,7 @@ public class ExternalStorageProvider extends FileSystemProvider {
final int userId = UserHandle.myUserId();
final List<VolumeInfo> volumes = mStorageManager.getVolumes();
for (VolumeInfo volume : volumes) {
- if (!volume.isMountedReadable()) continue;
+ if (!volume.isMountedReadable() || volume.getMountUserId() != userId) continue;
final String rootId;
final String title;
@@ -192,9 +192,8 @@ public class ExternalStorageProvider extends FileSystemProvider {
title = mStorageManager.getBestVolumeDescription(privateVol);
storageUuid = StorageManager.convert(privateVol.fsUuid);
}
- } else if ((volume.getType() == VolumeInfo.TYPE_PUBLIC
- || volume.getType() == VolumeInfo.TYPE_STUB)
- && volume.getMountUserId() == userId) {
+ } else if (volume.getType() == VolumeInfo.TYPE_PUBLIC
+ || volume.getType() == VolumeInfo.TYPE_STUB) {
rootId = volume.getFsUuid();
title = mStorageManager.getBestVolumeDescription(volume);
storageUuid = null;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index a2bd210b67a6..a784e04ee6a0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -16,6 +16,9 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
@@ -148,17 +151,18 @@ public class A2dpProfile implements LocalBluetoothProfile {
}
public boolean connect(BluetoothDevice device) {
- if (mService == null) return false;
- return mService.connect(device);
+ if (mService == null) {
+ return false;
+ }
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
public boolean disconnect(BluetoothDevice device) {
- if (mService == null) return false;
- // Downgrade priority as user is disconnecting the headset.
- if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+ if (mService == null) {
+ return false;
}
- return mService.disconnect(device);
+
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
public int getConnectionStatus(BluetoothDevice device) {
@@ -182,12 +186,12 @@ public class A2dpProfile implements LocalBluetoothProfile {
if (mService == null) {
return false;
}
- return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
}
public int getPreferred(BluetoothDevice device) {
if (mService == null) {
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return CONNECTION_POLICY_FORBIDDEN;
}
return mService.getConnectionPolicy(device);
}
@@ -197,11 +201,11 @@ public class A2dpProfile implements LocalBluetoothProfile {
return;
}
if (preferred) {
- if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+ if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
}
boolean isA2dpPlaying() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
index bc03c343a909..8ca5a74652dc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
@@ -16,6 +16,9 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
import android.bluetooth.BluetoothA2dpSink;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
@@ -116,18 +119,15 @@ final class A2dpSinkProfile implements LocalBluetoothProfile {
if (mService == null) {
return false;
}
- return mService.connect(device);
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
public boolean disconnect(BluetoothDevice device) {
if (mService == null) {
return false;
}
- // Downgrade priority as user is disconnecting the headset.
- if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
- }
- return mService.disconnect(device);
+
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
public int getConnectionStatus(BluetoothDevice device) {
@@ -141,12 +141,12 @@ final class A2dpSinkProfile implements LocalBluetoothProfile {
if (mService == null) {
return false;
}
- return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
}
public int getPreferred(BluetoothDevice device) {
if (mService == null) {
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return CONNECTION_POLICY_FORBIDDEN;
}
return mService.getConnectionPolicy(device);
}
@@ -156,11 +156,11 @@ final class A2dpSinkProfile implements LocalBluetoothProfile {
return;
}
if (preferred) {
- if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+ if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
index 560cb3b9b5b4..d65b5da22056 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
@@ -16,6 +16,9 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
@@ -112,18 +115,15 @@ public class HeadsetProfile implements LocalBluetoothProfile {
if (mService == null) {
return false;
}
- return mService.connect(device);
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
public boolean disconnect(BluetoothDevice device) {
if (mService == null) {
return false;
}
- // Downgrade priority as user is disconnecting the headset.
- if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
- }
- return mService.disconnect(device);
+
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
public int getConnectionStatus(BluetoothDevice device) {
@@ -165,12 +165,12 @@ public class HeadsetProfile implements LocalBluetoothProfile {
if (mService == null) {
return false;
}
- return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
}
public int getPreferred(BluetoothDevice device) {
if (mService == null) {
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return CONNECTION_POLICY_FORBIDDEN;
}
return mService.getConnectionPolicy(device);
}
@@ -180,11 +180,11 @@ public class HeadsetProfile implements LocalBluetoothProfile {
return;
}
if (preferred) {
- if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+ if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
index b4b55f363020..9f1af669c708 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -16,6 +16,9 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
@@ -146,17 +149,18 @@ public class HearingAidProfile implements LocalBluetoothProfile {
}
public boolean connect(BluetoothDevice device) {
- if (mService == null) return false;
- return mService.connect(device);
+ if (mService == null) {
+ return false;
+ }
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
public boolean disconnect(BluetoothDevice device) {
- if (mService == null) return false;
- // Downgrade priority as user is disconnecting the hearing aid.
- if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+ if (mService == null) {
+ return false;
}
- return mService.disconnect(device);
+
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
public int getConnectionStatus(BluetoothDevice device) {
@@ -180,12 +184,12 @@ public class HearingAidProfile implements LocalBluetoothProfile {
if (mService == null) {
return false;
}
- return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
}
public int getPreferred(BluetoothDevice device) {
if (mService == null) {
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return CONNECTION_POLICY_FORBIDDEN;
}
return mService.getConnectionPolicy(device);
}
@@ -195,11 +199,11 @@ public class HearingAidProfile implements LocalBluetoothProfile {
return;
}
if (preferred) {
- if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+ if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
index a372e23654e0..678f2e37c6bf 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
@@ -16,6 +16,9 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
@@ -126,7 +129,7 @@ final class HfpClientProfile implements LocalBluetoothProfile {
if (mService == null) {
return false;
}
- return mService.connect(device);
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
@Override
@@ -134,11 +137,8 @@ final class HfpClientProfile implements LocalBluetoothProfile {
if (mService == null) {
return false;
}
- // Downgrade priority as user is disconnecting the headset.
- if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
- }
- return mService.disconnect(device);
+
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
@Override
@@ -154,13 +154,13 @@ final class HfpClientProfile implements LocalBluetoothProfile {
if (mService == null) {
return false;
}
- return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
}
@Override
public int getPreferred(BluetoothDevice device) {
if (mService == null) {
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return CONNECTION_POLICY_FORBIDDEN;
}
return mService.getConnectionPolicy(device);
}
@@ -171,11 +171,11 @@ final class HfpClientProfile implements LocalBluetoothProfile {
return;
}
if (preferred) {
- if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+ if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
index 975a1e67af5b..588083e73481 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
@@ -16,6 +16,9 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
@@ -99,13 +102,17 @@ public class HidProfile implements LocalBluetoothProfile {
}
public boolean connect(BluetoothDevice device) {
- if (mService == null) return false;
- return mService.connect(device);
+ if (mService == null) {
+ return false;
+ }
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
public boolean disconnect(BluetoothDevice device) {
- if (mService == null) return false;
- return mService.disconnect(device);
+ if (mService == null) {
+ return false;
+ }
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
public int getConnectionStatus(BluetoothDevice device) {
@@ -119,12 +126,12 @@ public class HidProfile implements LocalBluetoothProfile {
if (mService == null) {
return false;
}
- return mService.getConnectionPolicy(device) != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return mService.getConnectionPolicy(device) != CONNECTION_POLICY_FORBIDDEN;
}
public int getPreferred(BluetoothDevice device) {
if (mService == null) {
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return CONNECTION_POLICY_FORBIDDEN;
}
return mService.getConnectionPolicy(device);
}
@@ -132,11 +139,11 @@ public class HidProfile implements LocalBluetoothProfile {
public void setPreferred(BluetoothDevice device, boolean preferred) {
if (mService == null) return;
if (preferred) {
- if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+ if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
index 95139a1bfab9..7d121aaa1ad1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
@@ -16,6 +16,9 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
@@ -115,18 +118,15 @@ public final class MapClientProfile implements LocalBluetoothProfile {
if (mService == null) {
return false;
}
- return mService.connect(device);
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
public boolean disconnect(BluetoothDevice device) {
if (mService == null) {
return false;
}
- // Downgrade priority as user is disconnecting.
- if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
- }
- return mService.disconnect(device);
+
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
public int getConnectionStatus(BluetoothDevice device) {
@@ -140,12 +140,12 @@ public final class MapClientProfile implements LocalBluetoothProfile {
if (mService == null) {
return false;
}
- return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
}
public int getPreferred(BluetoothDevice device) {
if (mService == null) {
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return CONNECTION_POLICY_FORBIDDEN;
}
return mService.getConnectionPolicy(device);
}
@@ -155,11 +155,11 @@ public final class MapClientProfile implements LocalBluetoothProfile {
return;
}
if (preferred) {
- if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+ if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
index 31a0eea56b42..a96a4e73feea 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
@@ -16,6 +16,8 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
@@ -119,10 +121,8 @@ public class MapProfile implements LocalBluetoothProfile {
if (mService == null) {
return false;
}
- if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
- }
- return mService.disconnect(device);
+
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
public int getConnectionStatus(BluetoothDevice device) {
@@ -136,12 +136,12 @@ public class MapProfile implements LocalBluetoothProfile {
if (mService == null) {
return false;
}
- return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
}
public int getPreferred(BluetoothDevice device) {
if (mService == null) {
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return CONNECTION_POLICY_FORBIDDEN;
}
return mService.getConnectionPolicy(device);
}
@@ -155,7 +155,7 @@ public class MapProfile implements LocalBluetoothProfile {
mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
index 4ea0df621bea..56267fc596cf 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
@@ -16,6 +16,9 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
@@ -129,7 +132,7 @@ public final class PbapClientProfile implements LocalBluetoothProfile {
return false;
}
Log.d(TAG,"PBAPClientProfile attempting to connect to " + device.getAddress());
- return mService.connect(device);
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
public boolean disconnect(BluetoothDevice device) {
@@ -137,7 +140,7 @@ public final class PbapClientProfile implements LocalBluetoothProfile {
if (mService == null) {
return false;
}
- return mService.disconnect(device);
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
public int getConnectionStatus(BluetoothDevice device) {
@@ -151,12 +154,12 @@ public final class PbapClientProfile implements LocalBluetoothProfile {
if (mService == null) {
return false;
}
- return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
}
public int getPreferred(BluetoothDevice device) {
if (mService == null) {
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return CONNECTION_POLICY_FORBIDDEN;
}
return mService.getConnectionPolicy(device);
}
@@ -166,11 +169,11 @@ public final class PbapClientProfile implements LocalBluetoothProfile {
return;
}
if (preferred) {
- if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+ if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
index 3f920a8cf1dd..f7c0bf5c8c9d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
@@ -16,6 +16,8 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
@@ -96,8 +98,10 @@ public class PbapServerProfile implements LocalBluetoothProfile {
}
public boolean disconnect(BluetoothDevice device) {
- if (mService == null) return false;
- return mService.disconnect(device);
+ if (mService == null) {
+ return false;
+ }
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
public int getConnectionStatus(BluetoothDevice device) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
index 0ca4d6195a32..3022c5b566eb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
@@ -16,6 +16,9 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
@@ -112,17 +115,15 @@ final class SapProfile implements LocalBluetoothProfile {
if (mService == null) {
return false;
}
- return mService.connect(device);
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
public boolean disconnect(BluetoothDevice device) {
if (mService == null) {
return false;
}
- if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
- }
- return mService.disconnect(device);
+
+ return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
public int getConnectionStatus(BluetoothDevice device) {
@@ -136,12 +137,12 @@ final class SapProfile implements LocalBluetoothProfile {
if (mService == null) {
return false;
}
- return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
}
public int getPreferred(BluetoothDevice device) {
if (mService == null) {
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return CONNECTION_POLICY_FORBIDDEN;
}
return mService.getConnectionPolicy(device);
}
@@ -151,11 +152,11 @@ final class SapProfile implements LocalBluetoothProfile {
return;
}
if (preferred) {
- if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+ if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
}
} else {
- mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
+ mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java
index 976445eb8c04..ccb6646cf683 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java
@@ -16,6 +16,9 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.verify;
@@ -67,13 +70,13 @@ public class A2dpSinkProfileTest {
@Test
public void connect_shouldConnectBluetoothA2dpSink() {
mProfile.connect(mBluetoothDevice);
- verify(mService).connect(mBluetoothDevice);
+ verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_ALLOWED);
}
@Test
public void disconnect_shouldDisconnectBluetoothA2dpSink() {
mProfile.disconnect(mBluetoothDevice);
- verify(mService).disconnect(mBluetoothDevice);
+ verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_FORBIDDEN);
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java
index 69c020dd5c08..91807609df1a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java
@@ -16,6 +16,9 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.verify;
@@ -67,13 +70,13 @@ public class HfpClientProfileTest {
@Test
public void connect_shouldConnectBluetoothHeadsetClient() {
mProfile.connect(mBluetoothDevice);
- verify(mService).connect(mBluetoothDevice);
+ verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_ALLOWED);
}
@Test
public void disconnect_shouldDisconnectBluetoothHeadsetClient() {
mProfile.disconnect(mBluetoothDevice);
- verify(mService).disconnect(mBluetoothDevice);
+ verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_FORBIDDEN);
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java
index 6f667094a5aa..1425c381256b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java
@@ -16,6 +16,9 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.verify;
@@ -67,13 +70,13 @@ public class MapClientProfileTest {
@Test
public void connect_shouldConnectBluetoothMapClient() {
mProfile.connect(mBluetoothDevice);
- verify(mService).connect(mBluetoothDevice);
+ verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_ALLOWED);
}
@Test
public void disconnect_shouldDisconnectBluetoothMapClient() {
mProfile.disconnect(mBluetoothDevice);
- verify(mService).disconnect(mBluetoothDevice);
+ verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_FORBIDDEN);
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java
index b21ec9c3e52a..15f560bef73e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java
@@ -16,6 +16,9 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.verify;
@@ -67,13 +70,13 @@ public class PbapClientProfileTest {
@Test
public void connect_shouldConnectBluetoothPbapClient() {
mProfile.connect(mBluetoothDevice);
- verify(mService).connect(mBluetoothDevice);
+ verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_ALLOWED);
}
@Test
public void disconnect_shouldDisconnectBluetoothPbapClient() {
mProfile.disconnect(mBluetoothDevice);
- verify(mService).disconnect(mBluetoothDevice);
+ verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_FORBIDDEN);
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java
index ec880345f6f0..4f978a822890 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java
@@ -16,6 +16,9 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.verify;
@@ -66,13 +69,13 @@ public class SapProfileTest {
@Test
public void connect_shouldConnectBluetoothSap() {
mProfile.connect(mBluetoothDevice);
- verify(mService).connect(mBluetoothDevice);
+ verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_ALLOWED);
}
@Test
public void disconnect_shouldDisconnectBluetoothSap() {
mProfile.disconnect(mBluetoothDevice);
- verify(mService).disconnect(mBluetoothDevice);
+ verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_FORBIDDEN);
}
@Test
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 347d6c2264eb..1c63efc3b5bc 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -194,6 +194,9 @@
<uses-permission android:name="android.permission.MANAGE_APPOPS" />
+ <!-- Permission required for storage tests - FuseDaemonHostTest -->
+ <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
+
<!-- Permission needed to run network tests in CTS -->
<uses-permission android:name="android.permission.MANAGE_TEST_NETWORKS" />
<!-- Permission needed to test tcp keepalive offload. -->
diff --git a/packages/SystemUI/docs/broadcasts.md b/packages/SystemUI/docs/broadcasts.md
index 56a637fe588c..28657f28e53b 100644
--- a/packages/SystemUI/docs/broadcasts.md
+++ b/packages/SystemUI/docs/broadcasts.md
@@ -42,24 +42,29 @@ Acquire the dispatcher by using `@Inject` to obtain a `BroadcastDispatcher`. The
```kotlin
/**
- * Register a receiver for broadcast with the dispatcher
- *
- * @param receiver A receiver to dispatch the [Intent]
- * @param filter A filter to determine what broadcasts should be dispatched to this receiver.
- * It will only take into account actions and categories for filtering. It must
- * have at least one action.
- * @param handler A handler to dispatch [BroadcastReceiver.onReceive]. By default, it is the
- * main handler. Pass `null` to use the default.
- * @param user A user handle to determine which broadcast should be dispatched to this receiver.
- * By default, it is the current user.
- * @throws IllegalArgumentException if the filter has other constraints that are not actions or
- * categories or the filter has no actions.
- */
+ * Register a receiver for broadcast with the dispatcher
+ *
+ * @param receiver A receiver to dispatch the [Intent]
+ * @param filter A filter to determine what broadcasts should be dispatched to this receiver.
+ * It will only take into account actions and categories for filtering. It must
+ * have at least one action.
+ * @param executor An executor to dispatch [BroadcastReceiver.onReceive]. Pass null to use an
+ * executor in the main thread (default).
+ * @param user A user handle to determine which broadcast should be dispatched to this receiver.
+ * By default, it is the current user.
+ * @throws IllegalArgumentException if the filter has other constraints that are not actions or
+ * categories or the filter has no actions.
+ */
@JvmOverloads
-fun registerReceiver(BroadcastReceiver, IntentFilter, Handler? = mainHandler, UserHandle = context.user)
+fun registerReceiver(
+ BroadcastReceiver,
+ IntentFilter,
+ Executor? = context.mainExecutor,
+ UserHandle = context.user
+) {
```
-All subscriptions are done with the same overloaded method. As specified in the doc, in order to pass a `UserHandle` with the default `Handler`, pass `null` for the `Handler`.
+All subscriptions are done with the same overloaded method. As specified in the doc, in order to pass a `UserHandle` with the default `Executor`, pass `null` for the `Executor`.
In the same way as with `Context`, subscribing the same `BroadcastReceiver` for the same user using different filters will result on two subscriptions, not in replacing the filter.
diff --git a/packages/SystemUI/res/layout/media_carousel.xml b/packages/SystemUI/res/layout/media_carousel.xml
new file mode 100644
index 000000000000..e91f840fe238
--- /dev/null
+++ b/packages/SystemUI/res/layout/media_carousel.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<!-- Carousel for media controls -->
+<HorizontalScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/qs_media_height"
+ android:padding="@dimen/qs_media_padding"
+ android:scrollbars="none"
+ android:visibility="gone"
+ >
+ <LinearLayout
+ android:id="@+id/media_carousel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <!-- QSMediaPlayers will be added here dynamically -->
+ </LinearLayout>
+</HorizontalScrollView>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 431862f1cc11..a58e3d78bb08 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1638,7 +1638,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
- broadcastDispatcher.registerReceiver(mBroadcastReceiver, filter, mHandler);
+ broadcastDispatcher.registerReceiverWithHandler(mBroadcastReceiver, filter, mHandler);
final IntentFilter allUserFilter = new IntentFilter();
allUserFilter.addAction(Intent.ACTION_USER_INFO_CHANGED);
@@ -1649,8 +1649,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
allUserFilter.addAction(ACTION_USER_UNLOCKED);
allUserFilter.addAction(ACTION_USER_STOPPED);
allUserFilter.addAction(ACTION_USER_REMOVED);
- broadcastDispatcher.registerReceiver(mBroadcastAllReceiver, allUserFilter, mHandler,
- UserHandle.ALL);
+ broadcastDispatcher.registerReceiverWithHandler(mBroadcastAllReceiver, allUserFilter,
+ mHandler, UserHandle.ALL);
mSubscriptionManager.addOnSubscriptionsChangedListener(mSubscriptionListener);
try {
diff --git a/packages/SystemUI/src/com/android/systemui/DumpController.kt b/packages/SystemUI/src/com/android/systemui/DumpController.kt
index 8c7075bee6cc..f14c4cd8e6c6 100644
--- a/packages/SystemUI/src/com/android/systemui/DumpController.kt
+++ b/packages/SystemUI/src/com/android/systemui/DumpController.kt
@@ -30,6 +30,14 @@ import javax.inject.Singleton
/**
* Controller that allows any [Dumpable] to subscribe and be dumped along with other SystemUI
* dependencies.
+ *
+ * To dump a specific dumpable on-demand:
+ *
+ * ```
+ * $ adb shell dumpsys activity service com.android.systemui/.SystemUIService dependency DumpController <tag1>,<tag2>,<tag3>
+ * ```
+ *
+ * Where tag1, tag2, etc. are the tags of the dumpables you want to dump.
*/
@Singleton
class DumpController @Inject constructor() : Dumpable {
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 0e736dcd11a8..c533755c76da 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -266,7 +266,7 @@ public class ScreenDecorations extends SystemUI implements Tunable {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_SWITCHED);
- mBroadcastDispatcher.registerReceiver(mIntentReceiver, filter, mHandler);
+ mBroadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter, mHandler);
mOverlay.addOnLayoutChangeListener(new OnLayoutChangeListener() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
index 8cb0cc5db1d3..cedf7c354ccc 100644
--- a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
@@ -20,6 +20,7 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.IntentFilter
import android.os.Handler
+import android.os.HandlerExecutor
import android.os.Looper
import android.os.Message
import android.os.UserHandle
@@ -32,13 +33,14 @@ import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import java.io.FileDescriptor
import java.io.PrintWriter
+import java.util.concurrent.Executor
import javax.inject.Inject
import javax.inject.Singleton
data class ReceiverData(
val receiver: BroadcastReceiver,
val filter: IntentFilter,
- val handler: Handler,
+ val executor: Executor,
val user: UserHandle
)
@@ -76,24 +78,49 @@ open class BroadcastDispatcher @Inject constructor (
* @param filter A filter to determine what broadcasts should be dispatched to this receiver.
* It will only take into account actions and categories for filtering. It must
* have at least one action.
- * @param handler A handler to dispatch [BroadcastReceiver.onReceive]. By default, it is the
- * main handler. Pass `null` to use the default.
+ * @param handler A handler to dispatch [BroadcastReceiver.onReceive].
* @param user A user handle to determine which broadcast should be dispatched to this receiver.
* By default, it is the current user.
* @throws IllegalArgumentException if the filter has other constraints that are not actions or
* categories or the filter has no actions.
*/
+ @Deprecated(message = "Replacing Handler for Executor in SystemUI",
+ replaceWith = ReplaceWith("registerReceiver(receiver, filter, executor, user)"))
@JvmOverloads
- fun registerReceiver(
+ fun registerReceiverWithHandler(
receiver: BroadcastReceiver,
filter: IntentFilter,
- handler: Handler? = mainHandler,
+ handler: Handler,
user: UserHandle = context.user
) {
+ registerReceiver(receiver, filter, HandlerExecutor(handler), user)
+ }
+
+ /**
+ * Register a receiver for broadcast with the dispatcher
+ *
+ * @param receiver A receiver to dispatch the [Intent]
+ * @param filter A filter to determine what broadcasts should be dispatched to this receiver.
+ * It will only take into account actions and categories for filtering. It must
+ * have at least one action.
+ * @param executor An executor to dispatch [BroadcastReceiver.onReceive]. Pass null to use an
+ * executor in the main thread (default).
+ * @param user A user handle to determine which broadcast should be dispatched to this receiver.
+ * By default, it is the current user.
+ * @throws IllegalArgumentException if the filter has other constraints that are not actions or
+ * categories or the filter has no actions.
+ */
+ @JvmOverloads
+ fun registerReceiver(
+ receiver: BroadcastReceiver,
+ filter: IntentFilter,
+ executor: Executor? = context.mainExecutor,
+ user: UserHandle = context.user
+ ) {
checkFilter(filter)
this.handler
.obtainMessage(MSG_ADD_RECEIVER,
- ReceiverData(receiver, filter, handler ?: mainHandler, user))
+ ReceiverData(receiver, filter, executor ?: context.mainExecutor, user))
.sendToTarget()
}
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt
index b2942bb14c6b..0c631aacab82 100644
--- a/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt
@@ -193,7 +193,7 @@ class UserBroadcastDispatcher(
it.filter.hasAction(intent.action) &&
it.filter.matchCategories(intent.categories) == null }
?.forEach {
- it.handler.post {
+ it.executor.execute {
if (DEBUG) Log.w(TAG,
"[$index] Dispatching ${intent.action} to ${it.receiver}")
it.receiver.pendingResult = pendingResult
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index 97224f1234dd..ccbbb2465742 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -111,7 +111,10 @@ public class BubbleData {
}
private final Context mContext;
+ /** Bubbles that are actively in the stack. */
private final List<Bubble> mBubbles;
+ /** Bubbles that are being loaded but haven't been added to the stack just yet. */
+ private final List<Bubble> mPendingBubbles;
private Bubble mSelectedBubble;
private boolean mExpanded;
private final int mMaxBubbles;
@@ -143,6 +146,7 @@ public class BubbleData {
public BubbleData(Context context) {
mContext = context;
mBubbles = new ArrayList<>();
+ mPendingBubbles = new ArrayList<>();
mStateChange = new Update(mBubbles);
mMaxBubbles = mContext.getResources().getInteger(R.integer.bubbles_max_rendered);
}
@@ -188,7 +192,15 @@ public class BubbleData {
Bubble getOrCreateBubble(NotificationEntry entry) {
Bubble bubble = getBubbleWithKey(entry.getKey());
if (bubble == null) {
+ // Check for it in pending
+ for (int i = 0; i < mPendingBubbles.size(); i++) {
+ Bubble b = mPendingBubbles.get(i);
+ if (b.getKey().equals(entry.getKey())) {
+ return b;
+ }
+ }
bubble = new Bubble(entry);
+ mPendingBubbles.add(bubble);
} else {
bubble.setEntry(entry);
}
@@ -204,7 +216,7 @@ public class BubbleData {
if (DEBUG_BUBBLE_DATA) {
Log.d(TAG, "notificationEntryUpdated: " + bubble);
}
-
+ mPendingBubbles.remove(bubble); // No longer pending once we're here
Bubble prevBubble = getBubbleWithKey(bubble.getKey());
suppressFlyout |= !shouldShowFlyout(bubble.getEntry());
@@ -377,6 +389,12 @@ public class BubbleData {
}
private void doRemove(String key, @DismissReason int reason) {
+ // If it was pending remove it
+ for (int i = 0; i < mPendingBubbles.size(); i++) {
+ if (mPendingBubbles.get(i).getKey().equals(key)) {
+ mPendingBubbles.remove(mPendingBubbles.get(i));
+ }
+ }
int indexToRemove = indexForKey(key);
if (indexToRemove == -1) {
return;
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 59d68bca93c3..6528f3762473 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
@@ -97,13 +97,11 @@ public class ExpandedAnimationController
private boolean mSpringingBubbleToTouch = false;
private int mExpandedViewPadding;
- private float mLauncherGridDiff;
public ExpandedAnimationController(Point displaySize, int expandedViewPadding,
int orientation) {
updateOrientation(orientation, displaySize);
mExpandedViewPadding = expandedViewPadding;
- mLauncherGridDiff = 30f;
}
/**
@@ -569,15 +567,7 @@ public class ExpandedAnimationController
* @return Space between bubbles in row above expanded view.
*/
private float getSpaceBetweenBubbles() {
- /**
- * Ordered left to right:
- * Screen edge
- * [mExpandedViewPadding]
- * Expanded view edge
- * [launcherGridDiff] --- arbitrary value until launcher exports widths
- * Launcher's app icon grid edge that we must match
- */
- final float rowMargins = (mExpandedViewPadding + mLauncherGridDiff) * 2;
+ final float rowMargins = mExpandedViewPadding * 2;
final float maxRowWidth = getWidthForDisplayingBubbles() - rowMargins;
final float totalBubbleWidth = mBubblesMaxRendered * mBubbleSizePx;
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java
index 6744d74004f0..20917bd9dcb0 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java
@@ -21,9 +21,12 @@ import com.android.systemui.appops.AppOpsController;
import com.android.systemui.appops.AppOpsControllerImpl;
import com.android.systemui.classifier.FalsingManagerProxy;
import com.android.systemui.doze.DozeHost;
+import com.android.systemui.globalactions.GlobalActionsComponent;
+import com.android.systemui.globalactions.GlobalActionsImpl;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.GlobalActions;
import com.android.systemui.plugins.VolumeDialogController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.PowerNotificationWarnings;
@@ -99,6 +102,17 @@ public abstract class DependencyBinder {
/**
*/
@Binds
+ public abstract GlobalActions provideGlobalActions(GlobalActionsImpl controllerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract GlobalActions.GlobalActionsManager provideGlobalActionsManager(
+ GlobalActionsComponent controllerImpl);
+
+ /**
+ */
+ @Binds
public abstract LocationController provideLocationController(
LocationControllerImpl controllerImpl);
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
index 26337b1f24b1..3aa14a31a5d9 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
@@ -26,17 +26,24 @@ import android.app.KeyguardManager;
import android.app.NotificationManager;
import android.app.WallpaperManager;
import android.app.admin.DevicePolicyManager;
+import android.app.trust.TrustManager;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.IPackageManager;
import android.content.res.Resources;
import android.hardware.SensorPrivacyManager;
+import android.media.AudioManager;
+import android.net.ConnectivityManager;
import android.os.Handler;
import android.os.PowerManager;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.Vibrator;
import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
+import android.telecom.TelecomManager;
+import android.telephony.TelephonyManager;
import android.view.IWindowManager;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
@@ -66,6 +73,12 @@ public class SystemServicesModule {
return context.getSystemService(AccessibilityManager.class);
}
+ @Provides
+ @Singleton
+ static ActivityManager provideActivityManager(Context context) {
+ return context.getSystemService(ActivityManager.class);
+ }
+
@Singleton
@Provides
static AlarmManager provideAlarmManager(Context context) {
@@ -74,10 +87,21 @@ public class SystemServicesModule {
@Provides
@Singleton
- static ActivityManager provideActivityManager(Context context) {
- return context.getSystemService(ActivityManager.class);
+ static AudioManager provideAudioManager(Context context) {
+ return context.getSystemService(AudioManager.class);
}
+ @Provides
+ @Singleton
+ static ConnectivityManager provideConnectivityManagager(Context context) {
+ return context.getSystemService(ConnectivityManager.class);
+ }
+
+ @Provides
+ @Singleton
+ static ContentResolver provideContentResolver(Context context) {
+ return context.getContentResolver();
+ }
@Provides
@DisplayId
@@ -185,6 +209,31 @@ public class SystemServicesModule {
@Provides
@Singleton
+ static TelecomManager provideTelecomManager(Context context) {
+ return context.getSystemService(TelecomManager.class);
+ }
+
+ @Provides
+ @Singleton
+ static TelephonyManager provideTelephonyManager(Context context) {
+ return context.getSystemService(TelephonyManager.class);
+ }
+
+ @Provides
+ @Singleton
+ static TrustManager provideTrustManager(Context context) {
+ return context.getSystemService(TrustManager.class);
+ }
+
+ @Provides
+ @Singleton
+ @Nullable
+ static Vibrator provideVibrator(Context context) {
+ return context.getSystemService(Vibrator.class);
+ }
+
+ @Provides
+ @Singleton
static UserManager provideUserManager(Context context) {
return context.getSystemService(UserManager.class);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 58ddda9ce720..b19523866814 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -31,6 +31,8 @@ import com.android.systemui.recents.Recents;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.notification.collection.NotifListBuilderImpl;
+import com.android.systemui.statusbar.notification.collection.NotificationRowBinder;
+import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
import com.android.systemui.statusbar.notification.people.PeopleHubModule;
import com.android.systemui.statusbar.phone.KeyguardLiftController;
@@ -82,6 +84,11 @@ public abstract class SystemUIModule {
keyguardUpdateMonitor, dumpController);
}
+ /** */
+ @Binds
+ public abstract NotificationRowBinder bindNotificationRowBinder(
+ NotificationRowBinderImpl notificationRowBinder);
+
@Singleton
@Provides
static SysUiState provideSysUiState() {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index c9faf69cfd6f..4e887262659e 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -91,7 +91,7 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi
if (mDebuggable) {
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_AOD_BRIGHTNESS);
- mBroadcastDispatcher.registerReceiver(this, filter, handler, UserHandle.ALL);
+ mBroadcastDispatcher.registerReceiverWithHandler(this, filter, handler, UserHandle.ALL);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
index 19b6f8232a9a..e949007a158b 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
@@ -19,7 +19,6 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.Dependency;
import com.android.systemui.SystemUI;
import com.android.systemui.plugins.GlobalActions;
import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
@@ -29,6 +28,7 @@ import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.ExtensionController.Extension;
import javax.inject.Inject;
+import javax.inject.Provider;
import javax.inject.Singleton;
/**
@@ -38,23 +38,29 @@ import javax.inject.Singleton;
public class GlobalActionsComponent extends SystemUI implements Callbacks, GlobalActionsManager {
private final CommandQueue mCommandQueue;
+ private final ExtensionController mExtensionController;
+ private final Provider<GlobalActions> mGlobalActionsProvider;
private GlobalActions mPlugin;
private Extension<GlobalActions> mExtension;
private IStatusBarService mBarService;
@Inject
- public GlobalActionsComponent(Context context, CommandQueue commandQueue) {
+ public GlobalActionsComponent(Context context, CommandQueue commandQueue,
+ ExtensionController extensionController,
+ Provider<GlobalActions> globalActionsProvider) {
super(context);
mCommandQueue = commandQueue;
+ mExtensionController = extensionController;
+ mGlobalActionsProvider = globalActionsProvider;
}
@Override
public void start() {
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
- mExtension = Dependency.get(ExtensionController.class).newExtension(GlobalActions.class)
+ mExtension = mExtensionController.newExtension(GlobalActions.class)
.withPlugin(GlobalActions.class)
- .withDefault(() -> new GlobalActionsImpl(mContext, mCommandQueue))
+ .withDefault(mGlobalActionsProvider::get)
.withCallback(this::onExtensionCallback)
.build();
mPlugin = mExtension.get();
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 3ccad64d76dc..fc19fa0c55fc 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -21,8 +21,10 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOM
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.Dialog;
+import android.app.IActivityManager;
import android.app.KeyguardManager;
import android.app.PendingIntent;
import android.app.StatusBarManager;
@@ -30,11 +32,13 @@ import android.app.WallpaperManager;
import android.app.admin.DevicePolicyManager;
import android.app.trust.TrustManager;
import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.UserInfo;
+import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
@@ -44,13 +48,11 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.Vibrator;
import android.provider.Settings;
-import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
import android.sysprop.TelephonyProperties;
import android.telecom.TelecomManager;
@@ -92,6 +94,7 @@ import com.android.systemui.MultiListLayout;
import com.android.systemui.MultiListLayout.MultiListAdapter;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
+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;
@@ -106,6 +109,8 @@ import com.android.systemui.volume.SystemUIInterpolators.LogAccelerateInterpolat
import java.util.ArrayList;
import java.util.List;
+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
@@ -146,6 +151,13 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private final LockPatternUtils mLockPatternUtils;
private final KeyguardManager mKeyguardManager;
private final BroadcastDispatcher mBroadcastDispatcher;
+ private final ContentResolver mContentResolver;
+ private final Resources mResources;
+ private final UserManager mUserManager;
+ private final TrustManager mTrustManager;
+ private final IActivityManager mIActivityManager;
+ private final TelecomManager mTelecomManager;
+ private final MetricsLogger mMetricsLogger;
private ArrayList<Action> mItems;
private ActionsDialog mDialog;
@@ -161,8 +173,6 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private boolean mIsWaitingForEcmExit = false;
private boolean mHasTelephony;
private boolean mHasVibrator;
- private boolean mHasLogoutButton;
- private boolean mHasLockdownButton;
private final boolean mShowSilentToggle;
private final EmergencyAffordanceManager mEmergencyAffordanceManager;
private final ScreenshotHelper mScreenshotHelper;
@@ -173,17 +183,32 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
/**
* @param context everything needs a context :(
*/
- public GlobalActionsDialog(Context context, GlobalActionsManager windowManagerFuncs) {
+ @Inject
+ public GlobalActionsDialog(Context context, GlobalActionsManager windowManagerFuncs,
+ AudioManager audioManager, IDreamManager iDreamManager,
+ DevicePolicyManager devicePolicyManager, LockPatternUtils lockPatternUtils,
+ KeyguardManager keyguardManager, BroadcastDispatcher broadcastDispatcher,
+ ConnectivityManager connectivityManager, TelephonyManager telephonyManager,
+ ContentResolver contentResolver, @Nullable Vibrator vibrator, @Main Resources resources,
+ ConfigurationController configurationController, ActivityStarter activityStarter,
+ KeyguardStateController keyguardStateController, UserManager userManager,
+ TrustManager trustManager, IActivityManager iActivityManager,
+ TelecomManager telecomManager, MetricsLogger metricsLogger) {
mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
mWindowManagerFuncs = windowManagerFuncs;
- mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
- mDreamManager = IDreamManager.Stub.asInterface(
- ServiceManager.getService(DreamService.DREAM_SERVICE));
- mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService(
- Context.DEVICE_POLICY_SERVICE);
- mLockPatternUtils = new LockPatternUtils(mContext);
- mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
- mBroadcastDispatcher = Dependency.get(BroadcastDispatcher.class);
+ mAudioManager = audioManager;
+ mDreamManager = iDreamManager;
+ mDevicePolicyManager = devicePolicyManager;
+ mLockPatternUtils = lockPatternUtils;
+ mKeyguardManager = keyguardManager;
+ mBroadcastDispatcher = broadcastDispatcher;
+ mContentResolver = contentResolver;
+ mResources = resources;
+ mUserManager = userManager;
+ mTrustManager = trustManager;
+ mIActivityManager = iActivityManager;
+ mTelecomManager = telecomManager;
+ mMetricsLogger = metricsLogger;
// receive broadcasts
IntentFilter filter = new IntentFilter();
@@ -192,32 +217,25 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
filter.addAction(TelephonyManager.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter);
- ConnectivityManager cm = (ConnectivityManager)
- context.getSystemService(Context.CONNECTIVITY_SERVICE);
- mHasTelephony = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+ mHasTelephony = connectivityManager.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
// get notified of phone state changes
- TelephonyManager telephonyManager =
- (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
- mContext.getContentResolver().registerContentObserver(
+ contentResolver.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true,
mAirplaneModeObserver);
- Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
mHasVibrator = vibrator != null && vibrator.hasVibrator();
- mShowSilentToggle = SHOW_SILENT_TOGGLE && !mContext.getResources().getBoolean(
+ mShowSilentToggle = SHOW_SILENT_TOGGLE && !resources.getBoolean(
R.bool.config_useFixedVolume);
mEmergencyAffordanceManager = new EmergencyAffordanceManager(context);
mScreenshotHelper = new ScreenshotHelper(context);
mScreenRecordHelper = new ScreenRecordHelper(context);
- Dependency.get(ConfigurationController.class).addCallback(this);
+ configurationController.addCallback(this);
- mActivityStarter = Dependency.get(ActivityStarter.class);
- KeyguardStateController keyguardStateController =
- Dependency.get(KeyguardStateController.class);
+ mActivityStarter = activityStarter;
keyguardStateController.addCallback(new KeyguardStateController.Callback() {
@Override
public void onUnlockedChanged() {
@@ -343,12 +361,9 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
onAirplaneModeChanged();
mItems = new ArrayList<Action>();
- String[] defaultActions = mContext.getResources().getStringArray(
- R.array.config_globalActionsList);
+ String[] defaultActions = mResources.getStringArray(R.array.config_globalActionsList);
ArraySet<String> addedKeys = new ArraySet<String>();
- mHasLogoutButton = false;
- mHasLockdownButton = false;
for (int i = 0; i < defaultActions.length; i++) {
String actionKey = defaultActions[i];
if (addedKeys.contains(actionKey)) {
@@ -360,7 +375,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
} else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) {
mItems.add(mAirplaneModeOn);
} else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey)) {
- if (Settings.Global.getInt(mContext.getContentResolver(),
+ if (Settings.Global.getInt(mContentResolver,
Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0 && isCurrentUserOwner()) {
mItems.add(new BugReportAction());
}
@@ -375,11 +390,10 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
} else if (GLOBAL_ACTION_KEY_SETTINGS.equals(actionKey)) {
mItems.add(getSettingsAction());
} else if (GLOBAL_ACTION_KEY_LOCKDOWN.equals(actionKey)) {
- if (Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ if (Settings.Secure.getIntForUser(mContentResolver,
Settings.Secure.LOCKDOWN_IN_POWER_MENU, 0, getCurrentUser().id) != 0
&& shouldDisplayLockdown()) {
mItems.add(getLockdownAction());
- mHasLockdownButton = true;
}
} else if (GLOBAL_ACTION_KEY_VOICEASSIST.equals(actionKey)) {
mItems.add(getVoiceAssistAction());
@@ -393,7 +407,6 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
if (mDevicePolicyManager.isLogoutEnabled()
&& getCurrentUser().id != UserHandle.USER_SYSTEM) {
mItems.add(new LogoutAction());
- mHasLogoutButton = true;
}
} else if (GLOBAL_ACTION_KEY_EMERGENCY.equals(actionKey)) {
if (!mEmergencyAffordanceManager.needsEmergencyAffordance()) {
@@ -474,8 +487,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
@Override
public boolean onLongPress() {
- UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- if (!um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
+ if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
mWindowManagerFuncs.reboot(true);
return true;
}
@@ -560,9 +572,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
@Override
public void onPress() {
- MetricsLogger.action(mContext, MetricsEvent.ACTION_EMERGENCY_DIALER_FROM_POWER_MENU);
- Intent intent = mContext.getSystemService(TelecomManager.class)
- .createLaunchEmergencyDialerIntent(null /* number */);
+ mMetricsLogger.action(MetricsEvent.ACTION_EMERGENCY_DIALER_FROM_POWER_MENU);
+ Intent intent = mTelecomManager.createLaunchEmergencyDialerIntent(null /* number */);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
@@ -579,8 +590,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
@Override
public boolean onLongPress() {
- UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- if (!um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
+ if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
mWindowManagerFuncs.reboot(true);
return true;
}
@@ -618,8 +628,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
@Override
public void run() {
mScreenshotHelper.takeScreenshot(1, true, true, mHandler, null);
- MetricsLogger.action(mContext,
- MetricsEvent.ACTION_SCREENSHOT_POWER_MENU);
+ mMetricsLogger.action(MetricsEvent.ACTION_SCREENSHOT_POWER_MENU);
}
}, 500);
}
@@ -666,11 +675,11 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
public void run() {
try {
// Take an "interactive" bugreport.
- MetricsLogger.action(mContext,
+ mMetricsLogger.action(
MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_INTERACTIVE);
- if (!ActivityManager.getService().launchBugReportHandlerApp()) {
+ if (!mIActivityManager.launchBugReportHandlerApp()) {
Log.w(TAG, "Bugreport handler could not be launched");
- ActivityManager.getService().requestInteractiveBugReport();
+ mIActivityManager.requestInteractiveBugReport();
}
} catch (RemoteException e) {
}
@@ -687,8 +696,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
try {
// Take a "full" bugreport.
- MetricsLogger.action(mContext, MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_FULL);
- ActivityManager.getService().requestFullBugReport();
+ mMetricsLogger.action(MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_FULL);
+ mIActivityManager.requestFullBugReport();
} catch (RemoteException e) {
}
return false;
@@ -726,8 +735,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mHandler.postDelayed(() -> {
try {
int currentUserId = getCurrentUser().id;
- ActivityManager.getService().switchUser(UserHandle.USER_SYSTEM);
- ActivityManager.getService().stopUser(currentUserId, true /*force*/, null);
+ mIActivityManager.switchUser(UserHandle.USER_SYSTEM);
+ mIActivityManager.stopUser(currentUserId, true /*force*/, null);
} catch (RemoteException re) {
Log.e(TAG, "Couldn't logout user " + re);
}
@@ -834,20 +843,18 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
private void lockProfiles() {
- final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- final TrustManager tm = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
final int currentUserId = getCurrentUser().id;
- final int[] profileIds = um.getEnabledProfileIds(currentUserId);
+ final int[] profileIds = mUserManager.getEnabledProfileIds(currentUserId);
for (final int id : profileIds) {
if (id != currentUserId) {
- tm.setDeviceLockedForUser(id, true);
+ mTrustManager.setDeviceLockedForUser(id, true);
}
}
}
private UserInfo getCurrentUser() {
try {
- return ActivityManager.getService().getCurrentUser();
+ return mIActivityManager.getCurrentUser();
} catch (RemoteException re) {
return null;
}
@@ -859,9 +866,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
private void addUsersToMenu(ArrayList<Action> items) {
- UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- if (um.isUserSwitcherEnabled()) {
- List<UserInfo> users = um.getUsers();
+ if (mUserManager.isUserSwitcherEnabled()) {
+ List<UserInfo> users = mUserManager.getUsers();
UserInfo currentUser = getCurrentUser();
for (final UserInfo user : users) {
if (user.supportsSwitchToByUser()) {
@@ -875,7 +881,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
+ (isCurrentUser ? " \u2714" : "")) {
public void onPress() {
try {
- ActivityManager.getService().switchUser(user.id);
+ mIActivityManager.switchUser(user.id);
} catch (RemoteException re) {
Log.e(TAG, "Couldn't switch user " + re);
}
@@ -932,7 +938,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
/** {@inheritDoc} */
public void onShow(DialogInterface dialog) {
- MetricsLogger.visible(mContext, MetricsEvent.POWER_MENU);
+ mMetricsLogger.visible(MetricsEvent.POWER_MENU);
}
/**
@@ -1492,7 +1498,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
if (mHasTelephony) return;
boolean airplaneModeOn = Settings.Global.getInt(
- mContext.getContentResolver(),
+ mContentResolver,
Settings.Global.AIRPLANE_MODE_ON,
0) == 1;
mAirplaneState = airplaneModeOn ? ToggleAction.State.On : ToggleAction.State.Off;
@@ -1504,7 +1510,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
*/
private void changeAirplaneModeSystemSetting(boolean on) {
Settings.Global.putInt(
- mContext.getContentResolver(),
+ mContentResolver,
Settings.Global.AIRPLANE_MODE_ON,
on ? 1 : 0);
Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index d385123117d3..c911bf28effd 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -45,24 +45,32 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import javax.inject.Inject;
+
+import dagger.Lazy;
+
public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks,
PluginListener<GlobalActionsPanelPlugin> {
private static final float SHUTDOWN_SCRIM_ALPHA = 0.95f;
private final Context mContext;
+ private final Lazy<GlobalActionsDialog> mGlobalActionsDialogLazy;
private final KeyguardStateController mKeyguardStateController;
private final DeviceProvisionedController mDeviceProvisionedController;
private final ExtensionController.Extension<GlobalActionsPanelPlugin> mPanelExtension;
private GlobalActionsPanelPlugin mPlugin;
private final CommandQueue mCommandQueue;
- private GlobalActionsDialog mGlobalActions;
+ private GlobalActionsDialog mGlobalActionsDialog;
private boolean mDisabled;
private final PluginManager mPluginManager;
private final String mPluginPackageName;
- public GlobalActionsImpl(Context context, CommandQueue commandQueue) {
+ @Inject
+ public GlobalActionsImpl(Context context, CommandQueue commandQueue,
+ Lazy<GlobalActionsDialog> globalActionsDialogLazy) {
mContext = context;
+ mGlobalActionsDialogLazy = globalActionsDialogLazy;
mKeyguardStateController = Dependency.get(KeyguardStateController.class);
mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
mPluginManager = Dependency.get(PluginManager.class);
@@ -83,19 +91,17 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks,
mCommandQueue.removeCallback(this);
mPluginManager.removePluginListener(this);
if (mPlugin != null) mPlugin.onDestroy();
- if (mGlobalActions != null) {
- mGlobalActions.destroy();
- mGlobalActions = null;
+ if (mGlobalActionsDialog != null) {
+ mGlobalActionsDialog.destroy();
+ mGlobalActionsDialog = null;
}
}
@Override
public void showGlobalActions(GlobalActionsManager manager) {
if (mDisabled) return;
- if (mGlobalActions == null) {
- mGlobalActions = new GlobalActionsDialog(mContext, manager);
- }
- mGlobalActions.showDialog(mKeyguardStateController.isShowing(),
+ mGlobalActionsDialog = mGlobalActionsDialogLazy.get();
+ mGlobalActionsDialog.showDialog(mKeyguardStateController.isShowing(),
mDeviceProvisionedController.isDeviceProvisioned(),
mPlugin != null ? mPlugin : mPanelExtension.get());
Dependency.get(KeyguardUpdateMonitor.class).requestFaceAuth();
@@ -189,8 +195,8 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks,
final boolean disabled = (state2 & DISABLE2_GLOBAL_ACTIONS) != 0;
if (displayId != mContext.getDisplayId() || disabled == mDisabled) return;
mDisabled = disabled;
- if (disabled && mGlobalActions != null) {
- mGlobalActions.dismissDialog();
+ if (disabled && mGlobalActionsDialog != null) {
+ mGlobalActionsDialog.dismissDialog();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 59ac329e1983..48750fa5e769 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -233,7 +233,7 @@ public class PowerUI extends SystemUI implements CommandQueue.Callbacks {
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_USER_SWITCHED);
- mBroadcastDispatcher.registerReceiver(this, filter, mHandler);
+ mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mHandler);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt
index f710f7fc47e2..f66a1ece1868 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt
@@ -68,7 +68,7 @@ class DoubleLineTileLayout(context: Context) : ViewGroup(context), QSPanel.QSTil
override fun updateResources(): Boolean {
with(mContext.resources) {
smallTileSize = getDimensionPixelSize(R.dimen.qs_quick_tile_size)
- cellMarginHorizontal = getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal)
+ cellMarginHorizontal = getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal) / 2
cellMarginVertical = getDimensionPixelSize(R.dimen.new_qs_vertical_margin)
}
requestLayout()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
index f7e4c794836e..1077834e7146 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
@@ -124,7 +124,7 @@ public class QSMediaPlayer {
}
}
});
- btn.setImageDrawable(mContext.getResources().getDrawable(R.drawable.lb_ic_replay));
+ btn.setImageDrawable(mContext.getResources().getDrawable(R.drawable.lb_ic_play));
btn.setImageTintList(ColorStateList.valueOf(mForegroundColor));
btn.setVisibility(View.VISIBLE);
@@ -199,8 +199,7 @@ public class QSMediaPlayer {
List<ResolveInfo> info = pm.queryBroadcastReceiversAsUser(it, 0, mContext.getUser());
if (info != null) {
for (ResolveInfo inf : info) {
- if (inf.activityInfo.packageName.equals(notif.contentIntent.getCreatorPackage())) {
- Log.d(TAG, "Found receiver for package: " + inf);
+ if (inf.activityInfo.packageName.equals(mController.getPackageName())) {
mRecvComponent = inf.getComponentInfo().getComponentName();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 51e352b30019..35b8312ba25c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -184,21 +184,10 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
// Add media carousel
if (useQsMediaPlayer(context)) {
- HorizontalScrollView mediaScrollView = new HorizontalScrollView(mContext);
- mediaScrollView.setHorizontalScrollBarEnabled(false);
- int playerHeight = (int) getResources().getDimension(R.dimen.qs_media_height);
- int padding = (int) getResources().getDimension(R.dimen.qs_media_padding);
- LayoutParams lpView = new LayoutParams(LayoutParams.MATCH_PARENT, playerHeight);
- lpView.setMarginStart(padding);
- lpView.setMarginEnd(padding);
- addView(mediaScrollView, lpView);
-
- LayoutParams lpCarousel = new LayoutParams(LayoutParams.MATCH_PARENT,
- LayoutParams.WRAP_CONTENT);
- mMediaCarousel = new LinearLayout(mContext);
- mMediaCarousel.setOrientation(LinearLayout.HORIZONTAL);
- mediaScrollView.addView(mMediaCarousel, lpCarousel);
- mediaScrollView.setVisibility(View.GONE);
+ HorizontalScrollView mediaScrollView = (HorizontalScrollView) LayoutInflater.from(
+ mContext).inflate(R.layout.media_carousel, this, false);
+ mMediaCarousel = mediaScrollView.findViewById(R.id.media_carousel);
+ addView(mediaScrollView);
} else {
mMediaCarousel = null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
index d40e25064352..cec1cb2fb53b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
@@ -106,7 +106,7 @@ public class QuickQSMediaPlayer {
}
}
});
- btn.setImageDrawable(mContext.getResources().getDrawable(R.drawable.lb_ic_replay));
+ btn.setImageDrawable(mContext.getResources().getDrawable(R.drawable.lb_ic_play));
btn.setImageTintList(ColorStateList.valueOf(mForegroundColor));
btn.setVisibility(View.VISIBLE);
}
@@ -136,14 +136,25 @@ public class QuickQSMediaPlayer {
* @param actionsContainer a LinearLayout containing the media action buttons
* @param actionsToShow indices of which actions to display in the mini player
* (max 3: Notification.MediaStyle.MAX_MEDIA_BUTTONS_IN_COMPACT)
+ * @param contentIntent Intent to send when user taps on the view
*/
public void setMediaSession(MediaSession.Token token, Icon icon, int iconColor, int bgColor,
- View actionsContainer, int[] actionsToShow) {
- Log.d(TAG, "Setting media session: " + token);
+ View actionsContainer, int[] actionsToShow, PendingIntent contentIntent) {
mToken = token;
mForegroundColor = iconColor;
mBackgroundColor = bgColor;
- mController = new MediaController(mContext, token);
+
+ String oldPackage = "";
+ if (mController != null) {
+ oldPackage = mController.getPackageName();
+ }
+ MediaController controller = new MediaController(mContext, token);
+ boolean samePlayer = mToken.equals(token) && oldPackage.equals(controller.getPackageName());
+ if (mController != null && !samePlayer && !isPlaying(controller)) {
+ // Only update if this is a different session and currently playing
+ return;
+ }
+ mController = controller;
MediaMetadata mMediaMetadata = mController.getMetadata();
// Try to find a receiver for the media button that matches this app
@@ -153,7 +164,6 @@ public class QuickQSMediaPlayer {
if (info != null) {
for (ResolveInfo inf : info) {
if (inf.activityInfo.packageName.equals(mController.getPackageName())) {
- Log.d(TAG, "Found receiver for package: " + inf);
mRecvComponent = inf.getComponentInfo().getComponentName();
}
}
@@ -165,6 +175,16 @@ public class QuickQSMediaPlayer {
return;
}
+ // Action
+ mMediaNotifView.setOnClickListener(v -> {
+ try {
+ contentIntent.send();
+ mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+ } catch (PendingIntent.CanceledException e) {
+ Log.e(TAG, "Pending intent was canceled: " + e.getMessage());
+ }
+ });
+
// Album art
addAlbumArtBackground(mMediaMetadata, mBackgroundColor);
@@ -237,12 +257,12 @@ public class QuickQSMediaPlayer {
* Check whether the media controlled by this player is currently playing
* @return whether it is playing, or false if no controller information
*/
- public boolean isPlaying() {
- if (mController == null) {
+ public boolean isPlaying(MediaController controller) {
+ if (controller == null) {
return false;
}
- PlaybackState state = mController.getPlaybackState();
+ PlaybackState state = controller.getPlaybackState();
if (state == null) {
return false;
}
@@ -261,12 +281,11 @@ public class QuickQSMediaPlayer {
private void addAlbumArtBackground(MediaMetadata metadata, int bgColor) {
Bitmap albumArt = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
float radius = mContext.getResources().getDimension(R.dimen.qs_media_corner_radius);
- if (albumArt != null) {
- Rect bounds = new Rect();
- mMediaNotifView.getBoundsOnScreen(bounds);
- int width = bounds.width();
- int height = bounds.height();
-
+ Rect bounds = new Rect();
+ mMediaNotifView.getBoundsOnScreen(bounds);
+ int width = bounds.width();
+ int height = bounds.height();
+ if (albumArt != null && width > 0 && height > 0) {
Bitmap original = albumArt.copy(Bitmap.Config.ARGB_8888, true);
Bitmap scaled = scaleBitmap(original, width, height);
Canvas canvas = new Canvas(scaled);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index db52e7d37a92..b05d4fdf7db7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -85,20 +85,19 @@ public class QuickQSPanel extends QSPanel {
mHorizontalLinearLayout.setClipChildren(false);
mHorizontalLinearLayout.setClipToPadding(false);
- LayoutParams lp = new LayoutParams(0, LayoutParams.MATCH_PARENT, 1);
-
mTileLayout = new DoubleLineTileLayout(context);
mMediaTileLayout = mTileLayout;
mRegularTileLayout = new HeaderTileLayout(context);
+ LayoutParams lp = new LayoutParams(0, LayoutParams.MATCH_PARENT, 1);
lp.setMarginEnd(10);
lp.setMarginStart(0);
mHorizontalLinearLayout.addView((View) mTileLayout, lp);
mMediaPlayer = new QuickQSMediaPlayer(mContext, mHorizontalLinearLayout);
-
- lp.setMarginEnd(0);
- lp.setMarginStart(10);
- mHorizontalLinearLayout.addView(mMediaPlayer.getView(), lp);
+ LayoutParams lp2 = new LayoutParams(0, LayoutParams.MATCH_PARENT, 1);
+ lp2.setMarginEnd(0);
+ lp2.setMarginStart(25);
+ mHorizontalLinearLayout.addView(mMediaPlayer.getView(), lp2);
sDefaultMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
index bbff117c6f81..ad79cadcc12d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -322,7 +322,7 @@ public class TileLifecycleManager extends BroadcastReceiver implements
filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
try {
mUserReceiverRegistered.set(true);
- mBroadcastDispatcher.registerReceiver(this, filter, mHandler, mUser);
+ mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mHandler, mUser);
} catch (Exception ex) {
mUserReceiverRegistered.set(false);
Log.e(TAG, "Could not register unlock receiver", ex);
diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java
index 077d2602e43f..9599d77bf65a 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java
@@ -98,7 +98,8 @@ public abstract class CurrentUserTracker {
if (!mReceiverRegistered) {
mCurrentUserId = ActivityManager.getCurrentUser();
IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
- mBroadcastDispatcher.registerReceiver(this, filter, null, UserHandle.ALL);
+ mBroadcastDispatcher.registerReceiver(this, filter, null,
+ UserHandle.ALL);
mReceiverRegistered = true;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 667e721ae37d..43d0399c6d62 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -584,15 +584,7 @@ public class NotificationRemoteInputManager implements Dumpable {
public void bindRow(ExpandableNotificationRow row) {
row.setRemoteInputController(mRemoteInputController);
- }
-
- /**
- * Return on-click handler for notification remote views
- *
- * @return on-click handler
- */
- public RemoteViews.OnClickHandler getRemoteViewsOnClickHandler() {
- return mOnClickHandler;
+ row.setRemoteViewClickHandler(mOnClickHandler);
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarDependenciesModule.java
index ec8dbead7de2..d1f6ebf3826d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarDependenciesModule.java
@@ -18,8 +18,6 @@ package com.android.systemui.statusbar;
import android.content.Context;
-import com.android.systemui.statusbar.notification.row.NotificationRowModule;
-
import javax.inject.Singleton;
import dagger.Module;
@@ -28,7 +26,7 @@ import dagger.Provides;
/**
* Dagger Module providing common dependencies of StatusBar.
*/
-@Module(includes = {NotificationRowModule.class})
+@Module
public class StatusBarDependenciesModule {
/**
* Provides our instance of CommandQueue which is considered optional.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index dc84b5785ad9..9c626f7b877d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -307,6 +307,7 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
case Icon.TYPE_RESOURCE:
return a.getResPackage().equals(b.getResPackage()) && a.getResId() == b.getResId();
case Icon.TYPE_URI:
+ case Icon.TYPE_URI_ADAPTIVE_BITMAP:
return a.getUriString().equals(b.getUriString());
default:
return false;
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 a2578ab3cfa6..36dcaac15a5f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -32,7 +32,6 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.NotificationVisibility;
-import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLifetimeExtender;
@@ -68,6 +67,8 @@ import java.util.Set;
import javax.inject.Inject;
import javax.inject.Singleton;
+import dagger.Lazy;
+
/**
* NotificationEntryManager is responsible for the adding, removing, and updating of
* {@link NotificationEntry}s. It also handles tasks such as their inflation and their interaction
@@ -126,8 +127,9 @@ public class NotificationEntryManager implements
new ArrayMap<>();
// Lazily retrieved dependencies
- private NotificationRemoteInputManager mRemoteInputManager;
- private NotificationRowBinder mNotificationRowBinder;
+ private final Lazy<NotificationRowBinder> mNotificationRowBinderLazy;
+ private final Lazy<NotificationRemoteInputManager> mRemoteInputManagerLazy;
+ private final LeakDetector mLeakDetector;
private final KeyguardEnvironment mKeyguardEnvironment;
private final NotificationGroupManager mGroupManager;
@@ -173,12 +175,18 @@ public class NotificationEntryManager implements
NotificationGroupManager groupManager,
NotificationRankingManager rankingManager,
KeyguardEnvironment keyguardEnvironment,
- FeatureFlags featureFlags) {
+ FeatureFlags featureFlags,
+ Lazy<NotificationRowBinder> notificationRowBinderLazy,
+ Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
+ LeakDetector leakDetector) {
mNotifLog = notifLog;
mGroupManager = groupManager;
mRankingManager = rankingManager;
mKeyguardEnvironment = keyguardEnvironment;
mFeatureFlags = featureFlags;
+ mNotificationRowBinderLazy = notificationRowBinderLazy;
+ mRemoteInputManagerLazy = notificationRemoteInputManagerLazy;
+ mLeakDetector = leakDetector;
}
/** Once called, the NEM will start processing notification events from system server. */
@@ -204,20 +212,6 @@ public class NotificationEntryManager implements
mRemoveInterceptor = interceptor;
}
- /**
- * Our dependencies can have cyclic references, so some need to be lazy
- */
- private NotificationRemoteInputManager getRemoteInputManager() {
- if (mRemoteInputManager == null) {
- mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);
- }
- return mRemoteInputManager;
- }
-
- public void setRowBinder(NotificationRowBinder notificationRowBinder) {
- mNotificationRowBinder = notificationRowBinder;
- }
-
public void setUpWithPresenter(NotificationPresenter presenter,
NotificationListContainer listContainer,
HeadsUpManager headsUpManager) {
@@ -468,7 +462,7 @@ public class NotificationEntryManager implements
handleGroupSummaryRemoved(key);
removeVisibleNotification(key);
updateNotifications("removeNotificationInternal");
- Dependency.get(LeakDetector.class).trackGarbage(entry);
+ mLeakDetector.trackGarbage(entry);
removedByUser |= entryDismissed;
mNotifLog.log(NotifEvent.NOTIF_REMOVED, entry.getSbn(),
@@ -507,8 +501,8 @@ public class NotificationEntryManager implements
boolean isForeground = (entry.getSbn().getNotification().flags
& Notification.FLAG_FOREGROUND_SERVICE) != 0;
boolean keepForReply =
- getRemoteInputManager().shouldKeepForRemoteInputHistory(childEntry)
- || getRemoteInputManager().shouldKeepForSmartReplyHistory(childEntry);
+ mRemoteInputManagerLazy.get().shouldKeepForRemoteInputHistory(childEntry)
+ || mRemoteInputManagerLazy.get().shouldKeepForSmartReplyHistory(childEntry);
if (isForeground || keepForReply) {
// the child is a foreground service notification which we can't remove or it's
// a child we're keeping around for reply!
@@ -536,12 +530,13 @@ public class NotificationEntryManager implements
NotificationEntry entry = new NotificationEntry(notification, ranking);
- Dependency.get(LeakDetector.class).trackInstance(entry);
+ mLeakDetector.trackInstance(entry);
// Construct the expanded view.
if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
- requireBinder().inflateViews(entry, () -> performRemoveNotification(notification,
- REASON_CANCEL));
+ mNotificationRowBinderLazy.get()
+ .inflateViews(entry, () -> performRemoveNotification(notification,
+ REASON_CANCEL));
}
abortExistingInflation(key, "addNotification");
@@ -586,15 +581,16 @@ public class NotificationEntryManager implements
}
if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
- requireBinder().inflateViews(entry, () -> performRemoveNotification(notification,
- REASON_CANCEL));
+ mNotificationRowBinderLazy.get()
+ .inflateViews(entry, () -> performRemoveNotification(notification,
+ REASON_CANCEL));
}
updateNotifications("updateNotificationInternal");
if (DEBUG) {
// Is this for you?
- boolean isForCurrentUser = Dependency.get(KeyguardEnvironment.class)
+ boolean isForCurrentUser = mKeyguardEnvironment
.isNotificationForCurrentProfiles(notification);
Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
}
@@ -644,11 +640,12 @@ public class NotificationEntryManager implements
// By comparing the old and new UI adjustments, reinflate the view accordingly.
for (NotificationEntry entry : entries) {
- requireBinder().onNotificationRankingUpdated(
- entry,
- oldImportances.get(entry.getKey()),
- oldAdjustments.get(entry.getKey()),
- NotificationUiAdjustment.extractFromNotificationEntry(entry));
+ mNotificationRowBinderLazy.get()
+ .onNotificationRankingUpdated(
+ entry,
+ oldImportances.get(entry.getKey()),
+ oldAdjustments.get(entry.getKey()),
+ NotificationUiAdjustment.extractFromNotificationEntry(entry));
}
updateNotifications("updateNotificationRanking");
@@ -728,14 +725,6 @@ public class NotificationEntryManager implements
}
}
- private NotificationRowBinder requireBinder() {
- if (mNotificationRowBinder == null) {
- throw new RuntimeException("You must initialize NotificationEntryManager by calling"
- + "setRowBinder() before using.");
- }
- return mNotificationRowBinder;
- }
-
/*
* -----
* Annexed from NotificationData below:
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 ec1efa58868e..b960b42b3e2d 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
@@ -24,7 +24,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
-import java.util.Objects;
/**
* Represents a set of grouped notifications. The final notification list is usually a mix of
@@ -58,22 +57,15 @@ public class GroupEntry extends ListEntry {
@VisibleForTesting
public void setSummary(@Nullable NotificationEntry summary) {
- if (!Objects.equals(mSummary, summary)) {
- mSummary = summary;
- onGroupingUpdated();
- }
+ mSummary = summary;
}
void clearChildren() {
- if (mChildren.size() != 0) {
- mChildren.clear();
- onGroupingUpdated();
- }
+ mChildren.clear();
}
void addChild(NotificationEntry child) {
mChildren.add(child);
- onGroupingUpdated();
}
void sortChildren(Comparator<? super NotificationEntry> c) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
index 601b3e053e8e..dc68c4bdba78 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
@@ -18,38 +18,20 @@ package com.android.systemui.statusbar.notification.collection;
import android.annotation.Nullable;
-import com.android.systemui.Dependency;
-import com.android.systemui.statusbar.notification.collection.provider.DerivedMember;
-import com.android.systemui.statusbar.notification.collection.provider.IsHighPriorityProvider;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.Objects;
-
/**
* Abstract superclass for top-level entries, i.e. things that can appear in the final notification
* list shown to users. In practice, this means either GroupEntries or NotificationEntries.
*/
public abstract class ListEntry {
private final String mKey;
- private final IsHighPriorityProvider mIsHighPriorityProvider = new IsHighPriorityProvider();
- private final List<DerivedMember> mDerivedMemberList = Arrays.asList(mIsHighPriorityProvider);
@Nullable private GroupEntry mParent;
@Nullable private GroupEntry mPreviousParent;
private int mSection;
int mFirstAddedIteration = -1;
- // TODO: (b/145659174) remove groupManager when moving to NewNotifPipeline. Logic
- // replaced in GroupEntry and NotifListBuilderImpl
- private final NotificationGroupManager mGroupManager;
-
ListEntry(String key) {
mKey = key;
-
- // TODO: (b/145659174) remove
- mGroupManager = Dependency.get(NotificationGroupManager.class);
}
public String getKey() {
@@ -68,11 +50,7 @@ public abstract class ListEntry {
}
void setParent(@Nullable GroupEntry parent) {
- if (!Objects.equals(mParent, parent)) {
- invalidateParent();
- mParent = parent;
- onGroupingUpdated();
- }
+ mParent = parent;
}
@Nullable public GroupEntry getPreviousParent() {
@@ -91,58 +69,4 @@ public abstract class ListEntry {
void setSection(int section) {
mSection = section;
}
-
- /**
- * Resets the cached values of DerivedMembers.
- */
- void invalidateDerivedMembers() {
- for (int i = 0; i < mDerivedMemberList.size(); i++) {
- mDerivedMemberList.get(i).invalidate();
- }
- }
-
- /**
- * Whether this notification is shown to the user as a high priority notification: visible on
- * the lock screen/status bar and in the top section in the shade.
- */
- public boolean isHighPriority() {
- return mIsHighPriorityProvider.get(this);
- }
-
- private void invalidateParent() {
- // invalidate our parent (GroupEntry) since DerivedMembers may be dependent on children
- if (getParent() != null) {
- getParent().invalidateDerivedMembers();
- }
-
- // TODO: (b/145659174) remove
- final NotificationEntry notifEntry = getRepresentativeEntry();
- if (notifEntry != null && mGroupManager.isGroupChild(notifEntry.getSbn())) {
- NotificationEntry summary = mGroupManager.getLogicalGroupSummary(notifEntry.getSbn());
- if (summary != null) {
- summary.invalidateDerivedMembers();
- }
- }
- }
-
- void onGroupingUpdated() {
- for (int i = 0; i < mDerivedMemberList.size(); i++) {
- mDerivedMemberList.get(i).onGroupingUpdated();
- }
- invalidateParent();
- }
-
- void onSbnUpdated() {
- for (int i = 0; i < mDerivedMemberList.size(); i++) {
- mDerivedMemberList.get(i).onSbnUpdated();
- }
- invalidateParent();
- }
-
- void onRankingUpdated() {
- for (int i = 0; i < mDerivedMemberList.size(); i++) {
- mDerivedMemberList.get(i).onRankingUpdated();
- }
- invalidateParent();
- }
}
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 28e486df29b9..7301fe1df398 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
@@ -204,11 +204,8 @@ public final class NotificationEntry extends ListEntry {
+ " doesn't match existing key " + mKey);
}
- if (!Objects.equals(mSbn, sbn)) {
- mSbn = sbn;
- mBubbleMetadata = mSbn.getNotification().getBubbleMetadata();
- onSbnUpdated();
- }
+ mSbn = sbn;
+ mBubbleMetadata = mSbn.getNotification().getBubbleMetadata();
}
/**
@@ -233,10 +230,7 @@ public final class NotificationEntry extends ListEntry {
+ " doesn't match existing key " + mKey);
}
- if (!Objects.equals(mRanking, ranking)) {
- mRanking = ranking;
- onRankingUpdated();
- }
+ mRanking = ranking;
}
/*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
index 7010943559ba..3bbd722517f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
@@ -24,6 +24,7 @@ import android.service.notification.StatusBarNotification
import com.android.systemui.statusbar.NotificationMediaManager
import com.android.systemui.statusbar.notification.NotificationFilter
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
import com.android.systemui.statusbar.notification.logging.NotifEvent
import com.android.systemui.statusbar.notification.logging.NotifLog
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
@@ -54,7 +55,8 @@ open class NotificationRankingManager @Inject constructor(
private val notifFilter: NotificationFilter,
private val notifLog: NotifLog,
sectionsFeatureManager: NotificationSectionsFeatureManager,
- private val peopleNotificationIdentifier: PeopleNotificationIdentifier
+ private val peopleNotificationIdentifier: PeopleNotificationIdentifier,
+ private val highPriorityProvider: HighPriorityProvider
) {
var rankingMap: RankingMap? = null
@@ -81,6 +83,9 @@ open class NotificationRankingManager @Inject constructor(
val aHeadsUp = a.isRowHeadsUp
val bHeadsUp = b.isRowHeadsUp
+ val aIsHighPriority = a.isHighPriority()
+ val bIsHighPriority = b.isHighPriority()
+
when {
usePeopleFiltering && aIsPeople != bIsPeople -> if (aIsPeople) -1 else 1
aHeadsUp != bHeadsUp -> if (aHeadsUp) -1 else 1
@@ -90,8 +95,8 @@ open class NotificationRankingManager @Inject constructor(
aMedia != bMedia -> if (aMedia) -1 else 1
// Upsort PRIORITY_MAX system notifications
aSystemMax != bSystemMax -> if (aSystemMax) -1 else 1
- a.isHighPriority != b.isHighPriority ->
- -1 * a.isHighPriority.compareTo(b.isHighPriority)
+ aIsHighPriority != bIsHighPriority ->
+ -1 * aIsHighPriority.compareTo(bIsHighPriority)
aRank != bRank -> aRank - bRank
else -> nb.notification.`when`.compareTo(na.notification.`when`)
}
@@ -154,7 +159,7 @@ open class NotificationRankingManager @Inject constructor(
) {
if (usePeopleFiltering && entry.isPeopleNotification()) {
entry.bucket = BUCKET_PEOPLE
- } else if (isHeadsUp || isMedia || isSystemMax || entry.isHighPriority) {
+ } else if (isHeadsUp || isMedia || isSystemMax || entry.isHighPriority()) {
entry.bucket = BUCKET_ALERTING
} else {
entry.bucket = BUCKET_SILENT
@@ -178,10 +183,6 @@ open class NotificationRankingManager @Inject constructor(
// TODO: notify group manager here?
groupManager.onEntryUpdated(entry, oldSbn)
}
-
- // TODO: (b/145659174) remove after moving to new NotifPipeline
- // (should be able to remove all groupManager code post-migration)
- entry.invalidateDerivedMembers()
}
}
}
@@ -191,6 +192,9 @@ open class NotificationRankingManager @Inject constructor(
sbn.isPeopleNotification()
private fun StatusBarNotification.isPeopleNotification() =
peopleNotificationIdentifier.isPeopleNotification(this)
+
+ private fun NotificationEntry.isHighPriority() =
+ highPriorityProvider.isHighPriority(this)
}
// Convenience functions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
index 20f206b91f10..6a2774b1842d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.collection;
+import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
@@ -29,7 +30,6 @@ import android.util.Log;
import android.view.ViewGroup;
import com.android.internal.util.NotificationMessagingUtil;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -41,8 +41,8 @@ import com.android.systemui.statusbar.notification.NotificationClicker;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder;
import com.android.systemui.statusbar.notification.row.RowInflaterTask;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -52,60 +52,64 @@ import com.android.systemui.statusbar.policy.HeadsUpManager;
import java.util.Objects;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
/** Handles inflating and updating views for notifications. */
+@Singleton
public class NotificationRowBinderImpl implements NotificationRowBinder {
private static final String TAG = "NotificationViewManager";
- private final NotificationGroupManager mGroupManager =
- Dependency.get(NotificationGroupManager.class);
- private final NotificationGutsManager mGutsManager =
- Dependency.get(NotificationGutsManager.class);
- private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider =
- Dependency.get(NotificationInterruptionStateProvider.class);
+ private final NotificationGroupManager mGroupManager;
+ private final NotificationGutsManager mGutsManager;
+ private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
private final Context mContext;
- private final NotificationRowContentBinder mRowContentBinder;
private final NotificationMessagingUtil mMessagingUtil;
private final ExpandableNotificationRow.ExpansionLogger mExpansionLogger =
this::logNotificationExpansion;
+ private final NotificationRemoteInputManager mNotificationRemoteInputManager;
+ private final NotificationLockscreenUserManager mNotificationLockscreenUserManager;
private final boolean mAllowLongPress;
private final KeyguardBypassController mKeyguardBypassController;
private final StatusBarStateController mStatusBarStateController;
- private NotificationRemoteInputManager mRemoteInputManager;
private NotificationPresenter mPresenter;
private NotificationListContainer mListContainer;
private HeadsUpManager mHeadsUpManager;
- private NotificationRowContentBinder.InflationCallback mInflationCallback;
+ private NotificationContentInflater.InflationCallback mInflationCallback;
private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener;
private BindRowCallback mBindRowCallback;
private NotificationClicker mNotificationClicker;
private final NotificationLogger mNotificationLogger;
+ @Inject
public NotificationRowBinderImpl(
Context context,
- NotificationRowContentBinder rowContentBinder,
- boolean allowLongPress,
+ NotificationRemoteInputManager notificationRemoteInputManager,
+ NotificationLockscreenUserManager notificationLockscreenUserManager,
+ @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress,
KeyguardBypassController keyguardBypassController,
StatusBarStateController statusBarStateController,
+ NotificationGroupManager notificationGroupManager,
+ NotificationGutsManager notificationGutsManager,
+ NotificationInterruptionStateProvider notificationInterruptionStateProvider,
NotificationLogger logger) {
mContext = context;
- mRowContentBinder = rowContentBinder;
mMessagingUtil = new NotificationMessagingUtil(context);
+ mNotificationRemoteInputManager = notificationRemoteInputManager;
+ mNotificationLockscreenUserManager = notificationLockscreenUserManager;
mAllowLongPress = allowLongPress;
mKeyguardBypassController = keyguardBypassController;
mStatusBarStateController = statusBarStateController;
+ mGroupManager = notificationGroupManager;
+ mGutsManager = notificationGutsManager;
+ mNotificationInterruptionStateProvider = notificationInterruptionStateProvider;
mNotificationLogger = logger;
}
- private NotificationRemoteInputManager getRemoteInputManager() {
- if (mRemoteInputManager == null) {
- mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);
- }
- return mRemoteInputManager;
- }
-
/**
* Sets up late-bound dependencies for this component.
*/
@@ -120,7 +124,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
mOnAppOpsClickListener = mGutsManager::openGuts;
}
- public void setInflationCallback(NotificationRowContentBinder.InflationCallback callback) {
+ public void setInflationCallback(NotificationContentInflater.InflationCallback callback) {
mInflationCallback = callback;
}
@@ -159,6 +163,19 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
private void bindRow(NotificationEntry entry, PackageManager pmUser,
StatusBarNotification sbn, ExpandableNotificationRow row,
Runnable onDismissRunnable) {
+ row.setExpansionLogger(mExpansionLogger, entry.getSbn().getKey());
+ row.setBypassController(mKeyguardBypassController);
+ row.setStatusBarStateController(mStatusBarStateController);
+ row.setGroupManager(mGroupManager);
+ row.setHeadsUpManager(mHeadsUpManager);
+ row.setOnExpandClickListener(mPresenter);
+ row.setInflationCallback(mInflationCallback);
+ if (mAllowLongPress) {
+ row.setLongPressListener(mGutsManager::openGuts);
+ }
+ mListContainer.bindRow(row);
+ mNotificationRemoteInputManager.bindRow(row);
+
// Get the app name.
// Note that Notification.Builder#bindHeaderAppName has similar logic
// but since this field is used in the guts, it must be accurate.
@@ -176,33 +193,15 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
} catch (PackageManager.NameNotFoundException e) {
// Do nothing
}
-
- row.initialize(
- appname,
- sbn.getKey(),
- mExpansionLogger,
- mKeyguardBypassController,
- mGroupManager,
- mHeadsUpManager,
- mRowContentBinder,
- mPresenter);
-
- // TODO: Either move these into ExpandableNotificationRow#initialize or out of row entirely
- row.setStatusBarStateController(mStatusBarStateController);
- row.setInflationCallback(mInflationCallback);
- row.setAppOpsOnClickListener(mOnAppOpsClickListener);
- if (mAllowLongPress) {
- row.setLongPressListener(mGutsManager::openGuts);
- }
- mListContainer.bindRow(row);
- getRemoteInputManager().bindRow(row);
-
+ row.setAppName(appname);
row.setOnDismissRunnable(onDismissRunnable);
row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
if (ENABLE_REMOTE_INPUT) {
row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
}
+ row.setAppOpsOnClickListener(mOnAppOpsClickListener);
+
mBindRowCallback.onBindRow(entry, pmUser, sbn, row);
}
@@ -271,8 +270,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
if (mNotificationInterruptionStateProvider.shouldHeadsUp(entry)) {
row.setInflationFlags(FLAG_CONTENT_VIEW_HEADS_UP);
}
- row.setNeedsRedaction(
- Dependency.get(NotificationLockscreenUserManager.class).needsRedaction(entry));
+ row.setNeedsRedaction(mNotificationLockscreenUserManager.needsRedaction(entry));
row.inflateViews();
// bind the click event to the content area
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
index 9312c2260d46..db107f531e9e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
@@ -43,6 +43,7 @@ import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import javax.inject.Inject;
@@ -62,6 +63,7 @@ public class KeyguardCoordinator implements Coordinator {
private final BroadcastDispatcher mBroadcastDispatcher;
private final StatusBarStateController mStatusBarStateController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final HighPriorityProvider mHighPriorityProvider;
@Inject
public KeyguardCoordinator(
@@ -71,15 +73,16 @@ public class KeyguardCoordinator implements Coordinator {
NotificationLockscreenUserManager lockscreenUserManager,
BroadcastDispatcher broadcastDispatcher,
StatusBarStateController statusBarStateController,
- KeyguardUpdateMonitor keyguardUpdateMonitor) {
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ HighPriorityProvider highPriorityProvider) {
mContext = context;
mMainHandler = mainThreadHandler;
mKeyguardStateController = keyguardStateController;
mLockscreenUserManager = lockscreenUserManager;
-
mBroadcastDispatcher = broadcastDispatcher;
mStatusBarStateController = statusBarStateController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mHighPriorityProvider = highPriorityProvider;
}
@Override
@@ -151,7 +154,7 @@ public class KeyguardCoordinator implements Coordinator {
}
if (NotificationUtils.useNewInterruptionModel(mContext)
&& hideSilentNotificationsOnLockscreen()) {
- return entry.isHighPriority();
+ return mHighPriorityProvider.isHighPriority(entry);
} else {
return entry.getRepresentativeEntry() != null
&& !entry.getRepresentativeEntry().getRanking().isAmbient();
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 eeb54abf92b4..2436bb9f82f0 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
@@ -76,6 +76,7 @@ public class NotifCoordinators implements Dumpable {
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println();
pw.println(TAG + ":");
for (Coordinator c : mCoordinators) {
pw.println("\t" + c.getClass());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/GroupCoalescer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/GroupCoalescer.java
index 069c15f9d7a2..c3e3c5373b7b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/GroupCoalescer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/GroupCoalescer.java
@@ -259,7 +259,7 @@ public class GroupCoalescer implements Dumpable {
pw.println("Coalesced notifications:");
for (EventBatch batch : mBatches.values()) {
pw.println(" Batch " + batch.mGroupKey + ":");
- pw.println(" Created" + (now - batch.mCreatedTimestamp) + "ms ago");
+ pw.println(" Created " + (now - batch.mCreatedTimestamp) + "ms ago");
for (CoalescedEvent event : batch.mMembers) {
pw.println(" " + event.getKey());
eventCount++;
@@ -299,5 +299,5 @@ public class GroupCoalescer implements Dumpable {
void onNotificationBatchPosted(List<CoalescedEvent> events);
}
- private static final int GROUP_LINGER_DURATION = 40;
+ private static final int GROUP_LINGER_DURATION = 500;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/DerivedMember.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/DerivedMember.java
deleted file mode 100644
index 815e6f7eaa46..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/DerivedMember.java
+++ /dev/null
@@ -1,62 +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.provider;
-/**
- * Caches a computed value until invalidate() is called
- * @param <Parent> Object used to computeValue
- * @param <Value> type of value to cache until invalidate is called
- */
-public abstract class DerivedMember<Parent, Value> {
- private Value mValue;
- protected abstract Value computeValue(Parent parent);
-
- /**
- * Gets the last cached value, else recomputes the value.
- */
- public Value get(Parent parent) {
- if (mValue == null) {
- mValue = computeValue(parent);
- }
- return mValue;
- }
-
- /**
- * Resets the cached value.
- * Next time "get" is called, the value is recomputed.
- */
- public void invalidate() {
- mValue = null;
- }
-
- /**
- * Called when a NotificationEntry's status bar notification has updated.
- * Derived members can invalidate here.
- */
- public void onSbnUpdated() {}
-
- /**
- * Called when a NotificationEntry's Ranking has updated.
- * Derived members can invalidate here.
- */
- public void onRankingUpdated() {}
-
- /**
- * Called when a ListEntry's grouping information (parent or children) has changed.
- * Derived members can invalidate here.
- */
- public void onGroupingUpdated() {}
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
new file mode 100644
index 000000000000..3cc5e62c0bda
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
@@ -0,0 +1,109 @@
+/*
+ * 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.provider;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+
+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.people.PeopleNotificationIdentifier;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Determines whether a notification is considered 'high priority'.
+ *
+ * Notifications that are high priority are visible on the lock screen/status bar and in the top
+ * section in the shade.
+ */
+@Singleton
+public class HighPriorityProvider {
+ private final PeopleNotificationIdentifier mPeopleNotificationIdentifier;
+
+ @Inject
+ public HighPriorityProvider(PeopleNotificationIdentifier peopleNotificationIdentifier) {
+ mPeopleNotificationIdentifier = peopleNotificationIdentifier;
+ }
+
+ /**
+ * @return true if the ListEntry is high priority, else false
+ *
+ * A NotificationEntry is considered high priority if it:
+ * - has importance greater than or equal to IMPORTANCE_DEFAULT
+ * OR
+ * - their importance has NOT been set to a low priority option by the user AND the
+ * notification fulfills one of the following:
+ * - has a person associated with it
+ * - has a media session associated with it
+ * - has messaging style
+ *
+ * A GroupEntry is considered high priority if its representativeEntry (summary) or children are
+ * high priority
+ */
+ public boolean isHighPriority(ListEntry entry) {
+ if (entry == null) {
+ return false;
+ }
+
+ final NotificationEntry notifEntry = entry.getRepresentativeEntry();
+ return notifEntry.getRanking().getImportance() >= NotificationManager.IMPORTANCE_DEFAULT
+ || hasHighPriorityCharacteristics(notifEntry)
+ || hasHighPriorityChild(entry);
+ }
+
+
+ private boolean hasHighPriorityChild(ListEntry entry) {
+ if (entry instanceof GroupEntry) {
+ for (NotificationEntry child : ((GroupEntry) entry).getChildren()) {
+ if (isHighPriority(child)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean hasHighPriorityCharacteristics(NotificationEntry entry) {
+ return !hasUserSetImportance(entry)
+ && (isImportantOngoing(entry)
+ || entry.getSbn().getNotification().hasMediaSession()
+ || isPeopleNotification(entry)
+ || isMessagingStyle(entry));
+ }
+
+ private boolean isImportantOngoing(NotificationEntry entry) {
+ return entry.getSbn().getNotification().isForegroundService()
+ && entry.getRanking().getImportance() >= NotificationManager.IMPORTANCE_LOW;
+ }
+
+ private boolean isMessagingStyle(NotificationEntry entry) {
+ return Notification.MessagingStyle.class.equals(
+ entry.getSbn().getNotification().getNotificationStyle());
+ }
+
+ private boolean isPeopleNotification(NotificationEntry entry) {
+ return mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn());
+ }
+
+ private boolean hasUserSetImportance(NotificationEntry entry) {
+ return entry.getRanking().getChannel() != null
+ && entry.getRanking().getChannel().hasUserSetImportance();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/IsHighPriorityProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/IsHighPriorityProvider.java
deleted file mode 100644
index 76e256b9be2d..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/IsHighPriorityProvider.java
+++ /dev/null
@@ -1,148 +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.provider;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.Person;
-
-import com.android.systemui.Dependency;
-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.phone.NotificationGroupManager;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Whether the ListEntry is shown to the user as a high priority notification: visible on
- * the lock screen/status bar and in the top section in the shade.
- *
- * A NotificationEntry is considered high priority if it:
- * - has importance greater than or equal to IMPORTANCE_DEFAULT
- * OR
- * - their importance has NOT been set to a low priority option by the user AND the notification
- * fulfills one of the following:
- * - has a person associated with it
- * - has a media session associated with it
- * - has messaging style
- *
- * A GroupEntry is considered high priority if its representativeEntry (summary) or children are
- * high priority
- */
-public class IsHighPriorityProvider extends DerivedMember<ListEntry, Boolean> {
- // TODO: (b/145659174) remove groupManager when moving to NewNotifPipeline. Logic
- // replaced in GroupEntry and NotifListBuilderImpl
- private final NotificationGroupManager mGroupManager;
-
-
- public IsHighPriorityProvider() {
- // TODO: (b/145659174) remove
- mGroupManager = Dependency.get(NotificationGroupManager.class);
- }
-
- @Override
- protected Boolean computeValue(ListEntry entry) {
- if (entry == null) {
- return false;
- }
-
- return isHighPriority(entry);
- }
-
- private boolean isHighPriority(ListEntry listEntry) {
- // requires groups have been set (AFTER PipelineState.STATE_TRANSFORMING)
- final NotificationEntry notifEntry = listEntry.getRepresentativeEntry();
- return notifEntry.getRanking().getImportance() >= NotificationManager.IMPORTANCE_DEFAULT
- || hasHighPriorityCharacteristics(notifEntry)
- || hasHighPriorityChild(listEntry);
-
- }
-
- private boolean hasHighPriorityChild(ListEntry entry) {
- // TODO: (b/145659174) remove
- if (entry instanceof NotificationEntry) {
- NotificationEntry notifEntry = (NotificationEntry) entry;
- if (mGroupManager.isSummaryOfGroup(notifEntry.getSbn())) {
- List<NotificationEntry> logicalChildren =
- mGroupManager.getLogicalChildren(notifEntry.getSbn());
- for (NotificationEntry child : logicalChildren) {
- if (child.isHighPriority()) {
- return true;
- }
- }
- }
- }
-
- if (entry instanceof GroupEntry) {
- for (NotificationEntry child : ((GroupEntry) entry).getChildren()) {
- if (child.isHighPriority()) {
- return true;
- }
- }
- }
- return false;
- }
-
- private boolean hasHighPriorityCharacteristics(NotificationEntry entry) {
- return !hasUserSetImportance(entry)
- && (isImportantOngoing(entry)
- || entry.getSbn().getNotification().hasMediaSession()
- || hasPerson(entry)
- || isMessagingStyle(entry));
- }
-
- private boolean isImportantOngoing(NotificationEntry entry) {
- return entry.getSbn().getNotification().isForegroundService()
- && entry.getRanking().getImportance() >= NotificationManager.IMPORTANCE_LOW;
- }
-
- private boolean isMessagingStyle(NotificationEntry entry) {
- return Notification.MessagingStyle.class.equals(
- entry.getSbn().getNotification().getNotificationStyle());
- }
-
- private boolean hasPerson(NotificationEntry entry) {
- // TODO: cache favorite and recent contacts to check contact affinity
- Notification notification = entry.getSbn().getNotification();
- ArrayList<Person> people = notification.extras != null
- ? notification.extras.getParcelableArrayList(Notification.EXTRA_PEOPLE_LIST)
- : new ArrayList<>();
- return people != null && !people.isEmpty();
- }
-
- private boolean hasUserSetImportance(NotificationEntry entry) {
- return entry.getRanking().getChannel() != null
- && entry.getRanking().getChannel().hasUserSetImportance();
- }
-
- @Override
- public void onSbnUpdated() {
- invalidate();
- }
-
- @Override
- public void onRankingUpdated() {
- invalidate();
- }
-
- @Override
- public void onGroupingUpdated() {
- invalidate();
- }
-}
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 a8a35d07b3f0..3c247df692f4 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
@@ -65,6 +65,7 @@ import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.widget.Chronometer;
import android.widget.FrameLayout;
import android.widget.ImageView;
+import android.widget.RemoteViews;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
@@ -149,7 +150,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private StatusBarStateController mStatusbarStateController;
private KeyguardBypassController mBypassController;
private LayoutListener mLayoutListener;
- private NotificationRowContentBinder mNotificationContentBinder;
+ private final NotificationContentInflater mNotificationInflater;
private int mIconTransformContentShift;
private int mIconTransformContentShiftNoIcon;
private int mMaxHeadsUpHeightBeforeN;
@@ -463,7 +464,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
* Inflate views based off the inflation flags set. Inflation happens asynchronously.
*/
public void inflateViews() {
- mNotificationContentBinder.bindContent(mEntry, this, mInflationFlags, mBindParams,
+ mNotificationInflater.bindContent(mEntry, this, mInflationFlags, mBindParams,
false /* forceInflate */, mInflationCallback);
}
@@ -477,7 +478,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
// View should not be reinflated in the future
clearInflationFlags(inflationFlag);
Runnable freeViewRunnable =
- () -> mNotificationContentBinder.unbindContent(mEntry, this, inflationFlag);
+ () -> mNotificationInflater.unbindContent(mEntry, this, inflationFlag);
switch (inflationFlag) {
case FLAG_CONTENT_VIEW_HEADS_UP:
getPrivateLayout().performWhenContentInactive(VISIBLE_TYPE_HEADSUP,
@@ -741,10 +742,23 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
return mIsHeadsUp || mHeadsupDisappearRunning;
}
+
+ public void setGroupManager(NotificationGroupManager groupManager) {
+ mGroupManager = groupManager;
+ mPrivateLayout.setGroupManager(groupManager);
+ }
+
public void setRemoteInputController(RemoteInputController r) {
mPrivateLayout.setRemoteInputController(r);
}
+ public void setAppName(String appName) {
+ mAppName = appName;
+ if (mMenuRow != null && mMenuRow.getMenuView() != null) {
+ mMenuRow.setAppName(mAppName);
+ }
+ }
+
public void addChildNotification(ExpandableNotificationRow row) {
addChildNotification(row, -1);
}
@@ -838,7 +852,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mIsChildInGroup = isChildInGroup;
if (mIsLowPriority) {
int flags = FLAG_CONTENT_VIEW_CONTRACTED | FLAG_CONTENT_VIEW_EXPANDED;
- mNotificationContentBinder.bindContent(mEntry, this, flags, mBindParams,
+ mNotificationInflater.bindContent(mEntry, this, flags, mBindParams,
false /* forceInflate */, mInflationCallback);
}
}
@@ -1091,6 +1105,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
return mPrivateLayout.getContractedNotificationHeader();
}
+ public void setOnExpandClickListener(OnExpandClickListener onExpandClickListener) {
+ mOnExpandClickListener = onExpandClickListener;
+ }
+
public void setLongPressListener(LongPressListener longPressListener) {
mLongPressListener = longPressListener;
}
@@ -1113,6 +1131,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
}
+ public void setHeadsUpManager(HeadsUpManager headsUpManager) {
+ mHeadsUpManager = headsUpManager;
+ }
+
public HeadsUpManager getHeadsUpManager() {
return mHeadsUpManager;
}
@@ -1237,7 +1259,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
l.reInflateViews();
}
mEntry.getSbn().clearPackageContext();
- mNotificationContentBinder.bindContent(mEntry, this, mInflationFlags, mBindParams,
+ mNotificationInflater.bindContent(mEntry, this, mInflationFlags, mBindParams,
true /* forceInflate */, mInflationCallback);
}
@@ -1612,6 +1634,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mBindParams.usesIncreasedHeadsUpHeight = use;
}
+ public void setRemoteViewClickHandler(RemoteViews.OnClickHandler remoteViewClickHandler) {
+ mNotificationInflater.setRemoteViewClickHandler(remoteViewClickHandler);
+ }
+
/**
* Set callback for notification content inflation
*
@@ -1626,7 +1652,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mNeedsRedaction = needsRedaction;
if (needsRedaction) {
setInflationFlags(FLAG_CONTENT_VIEW_PUBLIC);
- mNotificationContentBinder.bindContent(mEntry, this, FLAG_CONTENT_VIEW_PUBLIC,
+ mNotificationInflater.bindContent(mEntry, this, FLAG_CONTENT_VIEW_PUBLIC,
mBindParams, false /* forceInflate */, mInflationCallback);
} else {
clearInflationFlags(FLAG_CONTENT_VIEW_PUBLIC);
@@ -1635,12 +1661,18 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
}
+ @VisibleForTesting
+ public NotificationContentInflater getNotificationInflater() {
+ return mNotificationInflater;
+ }
+
public interface ExpansionLogger {
void logNotificationExpansion(String key, boolean userAction, boolean expanded);
}
public ExpandableNotificationRow(Context context, AttributeSet attrs) {
super(context, attrs);
+ mNotificationInflater = new NotificationContentInflater();
mMenuRow = new NotificationMenuRow(mContext);
mImageResolver = new NotificationInlineImageResolver(context,
new NotificationInlineImageCache());
@@ -1648,30 +1680,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
initDimens();
}
- /**
- * Initialize row.
- */
- public void initialize(
- String appName,
- String notificationKey,
- ExpansionLogger logger,
- KeyguardBypassController bypassController,
- NotificationGroupManager groupManager,
- HeadsUpManager headsUpManager,
- NotificationRowContentBinder rowContentBinder,
- OnExpandClickListener onExpandClickListener) {
- mAppName = appName;
- if (mMenuRow != null && mMenuRow.getMenuView() != null) {
- mMenuRow.setAppName(mAppName);
- }
- mLogger = logger;
- mLoggingKey = notificationKey;
+ public void setBypassController(KeyguardBypassController bypassController) {
mBypassController = bypassController;
- mGroupManager = groupManager;
- mPrivateLayout.setGroupManager(groupManager);
- mHeadsUpManager = headsUpManager;
- mNotificationContentBinder = rowContentBinder;
- mOnExpandClickListener = onExpandClickListener;
}
public void setStatusBarStateController(StatusBarStateController statusBarStateController) {
@@ -2910,6 +2920,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
return 0;
}
+ public void setExpansionLogger(ExpansionLogger logger, String key) {
+ mLogger = logger;
+ mLoggingKey = key;
+ }
+
public void onExpandedByGesture(boolean userExpanded) {
int event = MetricsEvent.ACTION_NOTIFICATION_GESTURE_EXPANDER;
if (mGroupManager.isSummaryOfGroup(mEntry.getSbn())) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCache.java
deleted file mode 100644
index c11c60fcdd04..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCache.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.row;
-
-import android.widget.RemoteViews;
-
-import androidx.annotation.Nullable;
-
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
-
-/**
- * Caches {@link RemoteViews} for a notification's content views.
- */
-public interface NotifRemoteViewCache {
-
- /**
- * Whether the notification has the remote view cached
- *
- * @param entry notification
- * @param flag inflation flag for content view
- * @return true if the remote view is cached
- */
- boolean hasCachedView(NotificationEntry entry, @InflationFlag int flag);
-
- /**
- * Get the remote view for the content flag specified.
- *
- * @param entry notification
- * @param flag inflation flag for the content view
- * @return the remote view if it is cached, null otherwise
- */
- @Nullable RemoteViews getCachedView(NotificationEntry entry, @InflationFlag int flag);
-
- /**
- * Cache a remote view for a given content flag on a notification.
- *
- * @param entry notification
- * @param flag inflation flag for the content view
- * @param remoteView remote view to store
- */
- void putCachedView(
- NotificationEntry entry,
- @InflationFlag int flag,
- RemoteViews remoteView);
-
- /**
- * Remove a cached remote view for a given content flag on a notification.
- *
- * @param entry notification
- * @param flag inflation flag for the content view
- */
- void removeCachedView(NotificationEntry entry, @InflationFlag int flag);
-
- /**
- * Clear a notification's remote view cache.
- *
- * @param entry notification
- */
- void clearCache(NotificationEntry entry);
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImpl.java
deleted file mode 100644
index a19099a6ea52..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImpl.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.row;
-
-import android.util.ArrayMap;
-import android.util.SparseArray;
-import android.widget.RemoteViews;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.internal.statusbar.NotificationVisibility;
-import com.android.systemui.statusbar.notification.NotificationEntryListener;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
-
-import java.util.Map;
-
-import javax.inject.Inject;
-
-/**
- * Implementation of remote view cache that keeps remote views cached for all active notifications.
- */
-public class NotifRemoteViewCacheImpl implements NotifRemoteViewCache {
- private final Map<NotificationEntry, SparseArray<RemoteViews>> mNotifCachedContentViews =
- new ArrayMap<>();
-
- @Inject
- NotifRemoteViewCacheImpl(NotificationEntryManager entryManager) {
- entryManager.addNotificationEntryListener(mEntryListener);
- }
-
- @Override
- public boolean hasCachedView(NotificationEntry entry, @InflationFlag int flag) {
- return getCachedView(entry, flag) != null;
- }
-
- @Override
- public @Nullable RemoteViews getCachedView(NotificationEntry entry, @InflationFlag int flag) {
- return getContentViews(entry).get(flag);
- }
-
- @Override
- public void putCachedView(
- NotificationEntry entry,
- @InflationFlag int flag,
- RemoteViews remoteView) {
- getContentViews(entry).put(flag, remoteView);
- }
-
- @Override
- public void removeCachedView(NotificationEntry entry, @InflationFlag int flag) {
- getContentViews(entry).remove(flag);
- }
-
- @Override
- public void clearCache(NotificationEntry entry) {
- getContentViews(entry).clear();
- }
-
- private @NonNull SparseArray<RemoteViews> getContentViews(NotificationEntry entry) {
- SparseArray<RemoteViews> contentViews = mNotifCachedContentViews.get(entry);
- if (contentViews == null) {
- throw new IllegalStateException(
- String.format("Remote view cache was never created for notification %s",
- entry.getKey()));
- }
- return contentViews;
- }
-
- private final NotificationEntryListener mEntryListener = new NotificationEntryListener() {
- @Override
- public void onPendingEntryAdded(NotificationEntry entry) {
- mNotifCachedContentViews.put(entry, new SparseArray<>());
- }
-
- @Override
- public void onEntryRemoved(
- NotificationEntry entry,
- @Nullable NotificationVisibility visibility,
- boolean removedByUser) {
- mNotifCachedContentViews.remove(entry);
- }
- };
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index e1a6747b5398..30f22ac5e161 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.notification.row;
-import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
@@ -27,6 +26,7 @@ import android.content.Context;
import android.os.AsyncTask;
import android.os.CancellationSignal;
import android.service.notification.StatusBarNotification;
+import android.util.ArrayMap;
import android.util.Log;
import android.view.View;
import android.widget.RemoteViews;
@@ -35,7 +35,6 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.ImageMessageConsumer;
import com.android.systemui.Dependency;
import com.android.systemui.statusbar.InflationTask;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.MediaNotificationProcessor;
@@ -50,30 +49,17 @@ import com.android.systemui.util.Assert;
import java.util.HashMap;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
/**
* {@link NotificationContentInflater} binds content to a {@link ExpandableNotificationRow} by
* asynchronously building the content's {@link RemoteViews} and applying it to the row.
*/
-@Singleton
-@VisibleForTesting(visibility = PACKAGE)
public class NotificationContentInflater implements NotificationRowContentBinder {
public static final String TAG = "NotifContentInflater";
+ private RemoteViews.OnClickHandler mRemoteViewClickHandler;
private boolean mInflateSynchronously = false;
- private final NotificationRemoteInputManager mRemoteInputManager;
- private final NotifRemoteViewCache mRemoteViewCache;
-
- @Inject
- public NotificationContentInflater(
- NotifRemoteViewCache remoteViewCache,
- NotificationRemoteInputManager remoteInputManager) {
- mRemoteViewCache = remoteViewCache;
- mRemoteInputManager = remoteInputManager;
- }
+ private final ArrayMap<Integer, RemoteViews> mCachedContentViews = new ArrayMap<>();
@Override
public void bindContent(
@@ -90,27 +76,27 @@ public class NotificationContentInflater implements NotificationRowContentBinder
return;
}
- StatusBarNotification sbn = entry.getSbn();
+ StatusBarNotification sbn = row.getEntry().getSbn();
// To check if the notification has inline image and preload inline image if necessary.
row.getImageResolver().preloadImages(sbn.getNotification());
if (forceInflate) {
- mRemoteViewCache.clearCache(entry);
+ mCachedContentViews.clear();
}
AsyncInflationTask task = new AsyncInflationTask(
+ sbn,
mInflateSynchronously,
contentToBind,
- mRemoteViewCache,
- entry,
+ mCachedContentViews,
row,
bindParams.isLowPriority,
bindParams.isChildInGroup,
bindParams.usesIncreasedHeight,
bindParams.usesIncreasedHeadsUpHeight,
callback,
- mRemoteInputManager.getRemoteViewsOnClickHandler());
+ mRemoteViewClickHandler);
if (mInflateSynchronously) {
task.onPostExecute(task.doInBackground());
} else {
@@ -137,15 +123,13 @@ public class NotificationContentInflater implements NotificationRowContentBinder
result = inflateSmartReplyViews(result, reInflateFlags, entry,
row.getContext(), packageContext, row.getHeadsUpManager(),
row.getExistingSmartRepliesAndActions());
-
apply(
inflateSynchronously,
result,
reInflateFlags,
- mRemoteViewCache,
- entry,
+ mCachedContentViews,
row,
- mRemoteInputManager.getRemoteViewsOnClickHandler(),
+ mRemoteViewClickHandler,
null);
return result;
}
@@ -165,7 +149,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
int curFlag = 1;
while (contentToUnbind != 0) {
if ((contentToUnbind & curFlag) != 0) {
- freeNotificationView(entry, row, curFlag);
+ freeNotificationView(row, curFlag);
}
contentToUnbind &= ~curFlag;
curFlag = curFlag << 1;
@@ -173,25 +157,34 @@ public class NotificationContentInflater implements NotificationRowContentBinder
}
/**
+ * Set click handler for notification remote views
+ *
+ * @param remoteViewClickHandler click handler for remote views
+ */
+ public void setRemoteViewClickHandler(RemoteViews.OnClickHandler remoteViewClickHandler) {
+ mRemoteViewClickHandler = remoteViewClickHandler;
+ }
+
+ /**
* Frees the content view associated with the inflation flag. Will only succeed if the
* view is safe to remove.
*
* @param inflateFlag the flag corresponding to the content view which should be freed
*/
- private void freeNotificationView(NotificationEntry entry, ExpandableNotificationRow row,
+ private void freeNotificationView(ExpandableNotificationRow row,
@InflationFlag int inflateFlag) {
switch (inflateFlag) {
case FLAG_CONTENT_VIEW_HEADS_UP:
if (row.getPrivateLayout().isContentViewInactive(VISIBLE_TYPE_HEADSUP)) {
row.getPrivateLayout().setHeadsUpChild(null);
- mRemoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP);
+ mCachedContentViews.remove(FLAG_CONTENT_VIEW_HEADS_UP);
row.getPrivateLayout().setHeadsUpInflatedSmartReplies(null);
}
break;
case FLAG_CONTENT_VIEW_PUBLIC:
if (row.getPublicLayout().isContentViewInactive(VISIBLE_TYPE_CONTRACTED)) {
row.getPublicLayout().setContractedChild(null);
- mRemoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC);
+ mCachedContentViews.remove(FLAG_CONTENT_VIEW_PUBLIC);
}
break;
case FLAG_CONTENT_VIEW_CONTRACTED:
@@ -252,12 +245,11 @@ public class NotificationContentInflater implements NotificationRowContentBinder
return result;
}
- private static CancellationSignal apply(
+ public static CancellationSignal apply(
boolean inflateSynchronously,
InflationProgress result,
@InflationFlag int reInflateFlags,
- NotifRemoteViewCache remoteViewCache,
- NotificationEntry entry,
+ ArrayMap<Integer, RemoteViews> cachedContentViews,
ExpandableNotificationRow row,
RemoteViews.OnClickHandler remoteViewClickHandler,
@Nullable InflationCallback callback) {
@@ -269,7 +261,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
if ((reInflateFlags & flag) != 0) {
boolean isNewView =
!canReapplyRemoteView(result.newContentView,
- remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED));
+ cachedContentViews.get(FLAG_CONTENT_VIEW_CONTRACTED));
ApplyCallback applyCallback = new ApplyCallback() {
@Override
public void setResultView(View v) {
@@ -281,8 +273,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder
return result.newContentView;
}
};
- applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, remoteViewCache,
- entry, row, isNewView, remoteViewClickHandler, callback, privateLayout,
+ applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, cachedContentViews,
+ row, isNewView, remoteViewClickHandler, callback, privateLayout,
privateLayout.getContractedChild(), privateLayout.getVisibleWrapper(
NotificationContentView.VISIBLE_TYPE_CONTRACTED),
runningInflations, applyCallback);
@@ -293,7 +285,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
if (result.newExpandedView != null) {
boolean isNewView =
!canReapplyRemoteView(result.newExpandedView,
- remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED));
+ cachedContentViews.get(FLAG_CONTENT_VIEW_EXPANDED));
ApplyCallback applyCallback = new ApplyCallback() {
@Override
public void setResultView(View v) {
@@ -305,8 +297,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder
return result.newExpandedView;
}
};
- applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, remoteViewCache,
- entry, row, isNewView, remoteViewClickHandler,
+ applyRemoteView(inflateSynchronously, result, reInflateFlags, flag,
+ cachedContentViews, row, isNewView, remoteViewClickHandler,
callback, privateLayout, privateLayout.getExpandedChild(),
privateLayout.getVisibleWrapper(
NotificationContentView.VISIBLE_TYPE_EXPANDED), runningInflations,
@@ -319,7 +311,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
if (result.newHeadsUpView != null) {
boolean isNewView =
!canReapplyRemoteView(result.newHeadsUpView,
- remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP));
+ cachedContentViews.get(FLAG_CONTENT_VIEW_HEADS_UP));
ApplyCallback applyCallback = new ApplyCallback() {
@Override
public void setResultView(View v) {
@@ -331,8 +323,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder
return result.newHeadsUpView;
}
};
- applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, remoteViewCache,
- entry, row, isNewView, remoteViewClickHandler,
+ applyRemoteView(inflateSynchronously, result, reInflateFlags, flag,
+ cachedContentViews, row, isNewView, remoteViewClickHandler,
callback, privateLayout, privateLayout.getHeadsUpChild(),
privateLayout.getVisibleWrapper(
VISIBLE_TYPE_HEADSUP), runningInflations,
@@ -344,7 +336,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
if ((reInflateFlags & flag) != 0) {
boolean isNewView =
!canReapplyRemoteView(result.newPublicView,
- remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC));
+ cachedContentViews.get(FLAG_CONTENT_VIEW_PUBLIC));
ApplyCallback applyCallback = new ApplyCallback() {
@Override
public void setResultView(View v) {
@@ -356,16 +348,15 @@ public class NotificationContentInflater implements NotificationRowContentBinder
return result.newPublicView;
}
};
- applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, remoteViewCache,
- entry, row, isNewView, remoteViewClickHandler, callback,
+ applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, cachedContentViews,
+ row, isNewView, remoteViewClickHandler, callback,
publicLayout, publicLayout.getContractedChild(),
publicLayout.getVisibleWrapper(NotificationContentView.VISIBLE_TYPE_CONTRACTED),
runningInflations, applyCallback);
}
// Let's try to finish, maybe nobody is even inflating anything
- finishIfDone(result, reInflateFlags, remoteViewCache, runningInflations, callback, entry,
- row);
+ finishIfDone(result, reInflateFlags, cachedContentViews, runningInflations, callback, row);
CancellationSignal cancellationSignal = new CancellationSignal();
cancellationSignal.setOnCancelListener(
() -> runningInflations.values().forEach(CancellationSignal::cancel));
@@ -378,8 +369,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
final InflationProgress result,
final @InflationFlag int reInflateFlags,
@InflationFlag int inflationId,
- final NotifRemoteViewCache remoteViewCache,
- final NotificationEntry entry,
+ final ArrayMap<Integer, RemoteViews> cachedContentViews,
final ExpandableNotificationRow row,
boolean isNewView,
RemoteViews.OnClickHandler remoteViewClickHandler,
@@ -432,8 +422,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder
existingWrapper.onReinflated();
}
runningInflations.remove(inflationId);
- finishIfDone(result, reInflateFlags, remoteViewCache, runningInflations,
- callback, entry, row);
+ finishIfDone(result, reInflateFlags, cachedContentViews, runningInflations,
+ callback, row);
}
@Override
@@ -498,11 +488,11 @@ public class NotificationContentInflater implements NotificationRowContentBinder
* @return true if the inflation was finished
*/
private static boolean finishIfDone(InflationProgress result,
- @InflationFlag int reInflateFlags, NotifRemoteViewCache remoteViewCache,
+ @InflationFlag int reInflateFlags, ArrayMap<Integer, RemoteViews> cachedContentViews,
HashMap<Integer, CancellationSignal> runningInflations,
- @Nullable InflationCallback endListener, NotificationEntry entry,
- ExpandableNotificationRow row) {
+ @Nullable InflationCallback endListener, ExpandableNotificationRow row) {
Assert.isMainThread();
+ NotificationEntry entry = row.getEntry();
NotificationContentView privateLayout = row.getPrivateLayout();
NotificationContentView publicLayout = row.getPublicLayout();
if (runningInflations.isEmpty()) {
@@ -510,27 +500,23 @@ public class NotificationContentInflater implements NotificationRowContentBinder
if (result.inflatedContentView != null) {
// New view case
privateLayout.setContractedChild(result.inflatedContentView);
- remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED,
- result.newContentView);
- } else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED)) {
+ cachedContentViews.put(FLAG_CONTENT_VIEW_CONTRACTED, result.newContentView);
+ } else if (cachedContentViews.get(FLAG_CONTENT_VIEW_CONTRACTED) != null) {
// Reinflation case. Only update if it's still cached (i.e. view has not been
// freed while inflating).
- remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED,
- result.newContentView);
+ cachedContentViews.put(FLAG_CONTENT_VIEW_CONTRACTED, result.newContentView);
}
}
if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0) {
if (result.inflatedExpandedView != null) {
privateLayout.setExpandedChild(result.inflatedExpandedView);
- remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED,
- result.newExpandedView);
+ cachedContentViews.put(FLAG_CONTENT_VIEW_EXPANDED, result.newExpandedView);
} else if (result.newExpandedView == null) {
privateLayout.setExpandedChild(null);
- remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED);
- } else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED)) {
- remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED,
- result.newExpandedView);
+ cachedContentViews.put(FLAG_CONTENT_VIEW_EXPANDED, null);
+ } else if (cachedContentViews.get(FLAG_CONTENT_VIEW_EXPANDED) != null) {
+ cachedContentViews.put(FLAG_CONTENT_VIEW_EXPANDED, result.newExpandedView);
}
if (result.newExpandedView != null) {
privateLayout.setExpandedInflatedSmartReplies(
@@ -544,14 +530,12 @@ public class NotificationContentInflater implements NotificationRowContentBinder
if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
if (result.inflatedHeadsUpView != null) {
privateLayout.setHeadsUpChild(result.inflatedHeadsUpView);
- remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP,
- result.newHeadsUpView);
+ cachedContentViews.put(FLAG_CONTENT_VIEW_HEADS_UP, result.newHeadsUpView);
} else if (result.newHeadsUpView == null) {
privateLayout.setHeadsUpChild(null);
- remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP);
- } else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP)) {
- remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP,
- result.newHeadsUpView);
+ cachedContentViews.put(FLAG_CONTENT_VIEW_HEADS_UP, null);
+ } else if (cachedContentViews.get(FLAG_CONTENT_VIEW_HEADS_UP) != null) {
+ cachedContentViews.put(FLAG_CONTENT_VIEW_HEADS_UP, result.newHeadsUpView);
}
if (result.newHeadsUpView != null) {
privateLayout.setHeadsUpInflatedSmartReplies(
@@ -564,18 +548,16 @@ public class NotificationContentInflater implements NotificationRowContentBinder
if ((reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC) != 0) {
if (result.inflatedPublicView != null) {
publicLayout.setContractedChild(result.inflatedPublicView);
- remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC,
- result.newPublicView);
- } else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC)) {
- remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC,
- result.newPublicView);
+ cachedContentViews.put(FLAG_CONTENT_VIEW_PUBLIC, result.newPublicView);
+ } else if (cachedContentViews.get(FLAG_CONTENT_VIEW_PUBLIC) != null) {
+ cachedContentViews.put(FLAG_CONTENT_VIEW_PUBLIC, result.newPublicView);
}
}
entry.headsUpStatusBarText = result.headsUpStatusBarText;
entry.headsUpStatusBarTextPublic = result.headsUpStatusBarTextPublic;
if (endListener != null) {
- endListener.onAsyncInflationFinished(entry, reInflateFlags);
+ endListener.onAsyncInflationFinished(row.getEntry(), reInflateFlags);
}
return true;
}
@@ -633,7 +615,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
public static class AsyncInflationTask extends AsyncTask<Void, Void, InflationProgress>
implements InflationCallback, InflationTask {
- private final NotificationEntry mEntry;
+ private final StatusBarNotification mSbn;
private final Context mContext;
private final boolean mInflateSynchronously;
private final boolean mIsLowPriority;
@@ -642,17 +624,17 @@ public class NotificationContentInflater implements NotificationRowContentBinder
private final InflationCallback mCallback;
private final boolean mUsesIncreasedHeadsUpHeight;
private @InflationFlag int mReInflateFlags;
- private final NotifRemoteViewCache mRemoteViewCache;
+ private final ArrayMap<Integer, RemoteViews> mCachedContentViews;
private ExpandableNotificationRow mRow;
private Exception mError;
private RemoteViews.OnClickHandler mRemoteViewClickHandler;
private CancellationSignal mCancellationSignal;
private AsyncInflationTask(
+ StatusBarNotification notification,
boolean inflateSynchronously,
@InflationFlag int reInflateFlags,
- NotifRemoteViewCache cache,
- NotificationEntry entry,
+ ArrayMap<Integer, RemoteViews> cachedContentViews,
ExpandableNotificationRow row,
boolean isLowPriority,
boolean isChildInGroup,
@@ -660,11 +642,11 @@ public class NotificationContentInflater implements NotificationRowContentBinder
boolean usesIncreasedHeadsUpHeight,
InflationCallback callback,
RemoteViews.OnClickHandler remoteViewClickHandler) {
- mEntry = entry;
mRow = row;
+ mSbn = notification;
mInflateSynchronously = inflateSynchronously;
mReInflateFlags = reInflateFlags;
- mRemoteViewCache = cache;
+ mCachedContentViews = cachedContentViews;
mContext = mRow.getContext();
mIsLowPriority = isLowPriority;
mIsChildInGroup = isChildInGroup;
@@ -672,6 +654,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
mUsesIncreasedHeadsUpHeight = usesIncreasedHeadsUpHeight;
mRemoteViewClickHandler = remoteViewClickHandler;
mCallback = callback;
+ NotificationEntry entry = row.getEntry();
entry.setInflationTask(this);
}
@@ -684,13 +667,12 @@ public class NotificationContentInflater implements NotificationRowContentBinder
@Override
protected InflationProgress doInBackground(Void... params) {
try {
- final StatusBarNotification sbn = mEntry.getSbn();
final Notification.Builder recoveredBuilder
= Notification.Builder.recoverBuilder(mContext,
- sbn.getNotification());
+ mSbn.getNotification());
- Context packageContext = sbn.getPackageContext(mContext);
- Notification notification = sbn.getNotification();
+ Context packageContext = mSbn.getPackageContext(mContext);
+ Notification notification = mSbn.getNotification();
if (notification.isMediaNotification()) {
MediaNotificationProcessor processor = new MediaNotificationProcessor(mContext,
packageContext);
@@ -699,7 +681,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
InflationProgress inflationProgress = createRemoteViews(mReInflateFlags,
recoveredBuilder, mIsLowPriority, mIsChildInGroup, mUsesIncreasedHeight,
mUsesIncreasedHeadsUpHeight, packageContext);
- return inflateSmartReplyViews(inflationProgress, mReInflateFlags, mEntry,
+ return inflateSmartReplyViews(inflationProgress, mReInflateFlags, mRow.getEntry(),
mRow.getContext(), packageContext, mRow.getHeadsUpManager(),
mRow.getExistingSmartRepliesAndActions());
} catch (Exception e) {
@@ -712,15 +694,15 @@ public class NotificationContentInflater implements NotificationRowContentBinder
protected void onPostExecute(InflationProgress result) {
if (mError == null) {
mCancellationSignal = apply(mInflateSynchronously, result, mReInflateFlags,
- mRemoteViewCache, mEntry, mRow, mRemoteViewClickHandler, this);
+ mCachedContentViews, mRow, mRemoteViewClickHandler, this);
} else {
handleError(mError);
}
}
private void handleError(Exception e) {
- mEntry.onInflationTaskFinished();
- StatusBarNotification sbn = mEntry.getSbn();
+ mRow.getEntry().onInflationTaskFinished();
+ StatusBarNotification sbn = mRow.getEntry().getSbn();
final String ident = sbn.getPackageName() + "/0x"
+ Integer.toHexString(sbn.getId());
Log.e(StatusBar.TAG, "couldn't inflate view for notification " + ident, e);
@@ -754,10 +736,10 @@ public class NotificationContentInflater implements NotificationRowContentBinder
@Override
public void onAsyncInflationFinished(NotificationEntry entry,
@InflationFlag int inflatedFlags) {
- mEntry.onInflationTaskFinished();
+ mRow.getEntry().onInflationTaskFinished();
mRow.onNotificationUpdated();
if (mCallback != null) {
- mCallback.onAsyncInflationFinished(mEntry, inflatedFlags);
+ mCallback.onAsyncInflationFinished(mRow.getEntry(), inflatedFlags);
}
// Notify the resolver that the inflation task has finished,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 6f2abba128d6..779a224ecb62 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -53,6 +53,7 @@ import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -81,6 +82,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
private final Context mContext;
private final VisualStabilityManager mVisualStabilityManager;
private final AccessibilityManager mAccessibilityManager;
+ private final HighPriorityProvider mHighPriorityProvider;
// Dependencies:
private final NotificationLockscreenUserManager mLockscreenUserManager =
@@ -109,12 +111,14 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
@Inject
public NotificationGutsManager(Context context, VisualStabilityManager visualStabilityManager,
Lazy<StatusBar> statusBarLazy, @Main Handler mainHandler,
- AccessibilityManager accessibilityManager) {
+ AccessibilityManager accessibilityManager,
+ HighPriorityProvider highPriorityProvider) {
mContext = context;
mVisualStabilityManager = visualStabilityManager;
mStatusBarLazy = statusBarLazy;
mMainHandler = mainHandler;
mAccessibilityManager = accessibilityManager;
+ mHighPriorityProvider = highPriorityProvider;
}
public void setUpWithPresenter(NotificationPresenter presenter,
@@ -331,8 +335,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
row.getIsNonblockable(),
isForBlockingHelper,
row.getEntry().getImportance(),
- row.getEntry().isHighPriority());
-
+ mHighPriorityProvider.isHighPriority(row.getEntry()));
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index b4ccb567504a..edfd1b4d3c85 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -46,6 +46,7 @@ import com.android.systemui.R;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.AlphaOptimizedImageView;
import com.android.systemui.statusbar.notification.row.NotificationGuts.GutsContent;
+import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import java.util.ArrayList;
@@ -269,7 +270,8 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
}
mAppOpsItem = createAppOpsItem(mContext);
if (mIsUsingBidirectionalSwipe) {
- mInfoItem = createInfoItem(mContext, !mParent.getEntry().isHighPriority());
+ mInfoItem = createInfoItem(mContext,
+ mParent.getEntry().getBucket() == NotificationSectionsManager.BUCKET_SILENT);
} else {
mInfoItem = createInfoItem(mContext);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
deleted file mode 100644
index df8653cf2406..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.row;
-
-import javax.inject.Singleton;
-
-import dagger.Binds;
-import dagger.Module;
-
-/**
- * Dagger Module containing notification row and view inflation implementations.
- */
-@Module
-public abstract class NotificationRowModule {
- /**
- * Provides notification row content binder instance.
- */
- @Binds
- @Singleton
- public abstract NotificationRowContentBinder provideNotificationRowContentBinder(
- NotificationContentInflater contentBinderImpl);
-
- /**
- * Provides notification remote view cache instance.
- */
- @Binds
- @Singleton
- public abstract NotifRemoteViewCache provideNotifRemoteViewCache(
- NotifRemoteViewCacheImpl cacheImpl);
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
index 352ba0f75a32..76fdfc6fbabc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
@@ -28,6 +28,7 @@ import android.media.session.MediaSession;
import android.media.session.PlaybackState;
import android.metrics.LogMaker;
import android.os.Handler;
+import android.service.notification.StatusBarNotification;
import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
@@ -176,27 +177,30 @@ public class NotificationMediaTemplateViewWrapper extends NotificationTemplateVi
final MediaSession.Token token = mRow.getEntry().getSbn().getNotification().extras
.getParcelable(Notification.EXTRA_MEDIA_SESSION);
- if (Utils.useQsMediaPlayer(mContext)) {
+ if (Utils.useQsMediaPlayer(mContext) && token != null) {
final int[] compactActions = mRow.getEntry().getSbn().getNotification().extras
.getIntArray(Notification.EXTRA_COMPACT_ACTIONS);
int tintColor = getNotificationHeader().getOriginalIconColor();
StatusBarWindowController ctrl = Dependency.get(StatusBarWindowController.class);
QuickQSPanel panel = ctrl.getStatusBarView().findViewById(
com.android.systemui.R.id.quick_qs_panel);
+ StatusBarNotification sbn = mRow.getEntry().getSbn();
+ Notification notif = sbn.getNotification();
panel.getMediaPlayer().setMediaSession(token,
- mRow.getEntry().getSbn().getNotification().getSmallIcon(),
+ notif.getSmallIcon(),
tintColor,
mBackgroundColor,
mActions,
- compactActions);
+ compactActions,
+ notif.contentIntent);
QSPanel bigPanel = ctrl.getStatusBarView().findViewById(
com.android.systemui.R.id.quick_settings_panel);
bigPanel.addMediaSession(token,
- mRow.getEntry().getSbn().getNotification().getSmallIcon(),
+ notif.getSmallIcon(),
tintColor,
mBackgroundColor,
mActions,
- mRow.getEntry().getSbn());
+ sbn);
}
boolean showCompactSeekbar = mMediaManager.getShowCompactMediaSeekbar();
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 a3b1b5f8360b..a6842badc153 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -366,8 +366,8 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_USER_SWITCHED);
- mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter, Handler.getMain(),
- UserHandle.ALL);
+ mBroadcastDispatcher.registerReceiverWithHandler(mBroadcastReceiver, filter,
+ Handler.getMain(), UserHandle.ALL);
notifyNavigationBarScreenOn();
mOverviewProxyService.addCallback(mOverviewProxyListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index 896b6e570da2..fe0739f9088c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -32,6 +32,7 @@ import com.android.systemui.statusbar.InflationTask;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.AsyncInflationTask;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.statusbar.phone.NotificationGroupManager.NotificationGroup;
import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener;
@@ -427,7 +428,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis
* The notification is still pending inflation but we've decided that we no longer need
* the content view (e.g. suppression might have changed and we decided we need to transfer
* back). However, there is no way to abort just this inflation if other inflation requests
- * have started (see {@link InflationTask#supersedeTask(InflationTask)}). So instead
+ * have started (see {@link AsyncInflationTask#supersedeTask(InflationTask)}). So instead
* we just flag it as aborted and free when it's inflated.
*/
boolean mAbortOnInflation;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 5b34aa7781c1..00e38f814dff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -179,7 +179,7 @@ public class PhoneStatusBarPolicy
filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
- broadcastDispatcher.registerReceiver(mIntentReceiver, filter, mHandler);
+ broadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter, mHandler);
// listen for user / profile change.
try {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 30825ed65eb3..189d3b6f530b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -29,7 +29,6 @@ import static android.view.InsetsState.containsType;
import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS;
-import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
@@ -205,7 +204,6 @@ import com.android.systemui.statusbar.notification.collection.init.NewNotifPipel
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -366,7 +364,6 @@ public class StatusBar extends SystemUI implements DemoMode,
private final HeadsUpManagerPhone mHeadsUpManager;
private final DynamicPrivacyController mDynamicPrivacyController;
private final BypassHeadsUpNotifier mBypassHeadsUpNotifier;
- private final boolean mAllowNotificationLongPress;
private final Lazy<NewNotifPipeline> mNewNotifPipeline;
private final FalsingManager mFalsingManager;
private final BroadcastDispatcher mBroadcastDispatcher;
@@ -389,6 +386,7 @@ public class StatusBar extends SystemUI implements DemoMode,
private final KeyguardDismissUtil mKeyguardDismissUtil;
private final ExtensionController mExtensionController;
private final UserInfoControllerImpl mUserInfoControllerImpl;
+ private final NotificationRowBinderImpl mNotificationRowBinder;
private final DismissCallbackRegistry mDismissCallbackRegistry;
// expanded notifications
@@ -414,7 +412,6 @@ public class StatusBar extends SystemUI implements DemoMode,
private final NotificationGutsManager mGutsManager;
private final NotificationLogger mNotificationLogger;
private final NotificationEntryManager mEntryManager;
- private final NotificationRowContentBinder mRowContentBinder;
private NotificationListController mNotificationListController;
private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
private final NotificationViewHierarchyManager mViewHierarchyManager;
@@ -628,7 +625,6 @@ public class StatusBar extends SystemUI implements DemoMode,
HeadsUpManagerPhone headsUpManagerPhone,
DynamicPrivacyController dynamicPrivacyController,
BypassHeadsUpNotifier bypassHeadsUpNotifier,
- @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowNotificationLongPress,
Lazy<NewNotifPipeline> newNotifPipeline,
FalsingManager falsingManager,
BroadcastDispatcher broadcastDispatcher,
@@ -636,7 +632,6 @@ public class StatusBar extends SystemUI implements DemoMode,
NotificationGutsManager notificationGutsManager,
NotificationLogger notificationLogger,
NotificationEntryManager notificationEntryManager,
- NotificationRowContentBinder notificationRowContentBinder,
NotificationInterruptionStateProvider notificationInterruptionStateProvider,
NotificationViewHierarchyManager notificationViewHierarchyManager,
KeyguardViewMediator keyguardViewMediator,
@@ -696,6 +691,7 @@ public class StatusBar extends SystemUI implements DemoMode,
KeyguardDismissUtil keyguardDismissUtil,
ExtensionController extensionController,
UserInfoControllerImpl userInfoControllerImpl,
+ NotificationRowBinderImpl notificationRowBinder,
DismissCallbackRegistry dismissCallbackRegistry) {
super(context);
mFeatureFlags = featureFlags;
@@ -710,7 +706,6 @@ public class StatusBar extends SystemUI implements DemoMode,
mHeadsUpManager = headsUpManagerPhone;
mDynamicPrivacyController = dynamicPrivacyController;
mBypassHeadsUpNotifier = bypassHeadsUpNotifier;
- mAllowNotificationLongPress = allowNotificationLongPress;
mNewNotifPipeline = newNotifPipeline;
mFalsingManager = falsingManager;
mBroadcastDispatcher = broadcastDispatcher;
@@ -718,7 +713,6 @@ public class StatusBar extends SystemUI implements DemoMode,
mGutsManager = notificationGutsManager;
mNotificationLogger = notificationLogger;
mEntryManager = notificationEntryManager;
- mRowContentBinder = notificationRowContentBinder;
mNotificationInterruptionStateProvider = notificationInterruptionStateProvider;
mViewHierarchyManager = notificationViewHierarchyManager;
mKeyguardViewMediator = keyguardViewMediator;
@@ -776,6 +770,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mKeyguardDismissUtil = keyguardDismissUtil;
mExtensionController = extensionController;
mUserInfoControllerImpl = userInfoControllerImpl;
+ mNotificationRowBinder = notificationRowBinder;
mDismissCallbackRegistry = dismissCallbackRegistry;
mBubbleExpandListener =
@@ -1241,20 +1236,11 @@ public class StatusBar extends SystemUI implements DemoMode,
mStatusBarWindowViewController, this, mNotificationPanelViewController,
(NotificationListContainer) mStackScroller);
- final NotificationRowBinderImpl rowBinder =
- new NotificationRowBinderImpl(
- mContext,
- mRowContentBinder,
- mAllowNotificationLongPress,
- mKeyguardBypassController,
- mStatusBarStateController,
- mNotificationLogger);
-
// TODO: inject this.
mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanelViewController,
mHeadsUpManager, mStatusBarWindow, mStackScroller, mDozeScrimController,
mScrimController, mActivityLaunchAnimator, mDynamicPrivacyController,
- mNotificationAlertingManager, rowBinder, mKeyguardStateController,
+ mNotificationAlertingManager, mNotificationRowBinder, mKeyguardStateController,
mKeyguardIndicationController,
this /* statusBar */, mShadeController, mCommandQueue, mInitController);
@@ -1278,20 +1264,19 @@ public class StatusBar extends SystemUI implements DemoMode,
mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
- mEntryManager.setRowBinder(rowBinder);
- rowBinder.setInflationCallback(mEntryManager);
+ mNotificationRowBinder.setInflationCallback(mEntryManager);
}
mRemoteInputUriController.attach(mEntryManager);
- rowBinder.setNotificationClicker(new NotificationClicker(
+ mNotificationRowBinder.setNotificationClicker(new NotificationClicker(
Optional.of(this), mBubbleController, mNotificationActivityStarter));
mGroupAlertTransferHelper.bind(mEntryManager, mGroupManager);
mNotificationListController.bind();
if (mFeatureFlags.isNewNotifPipelineEnabled()) {
- mNewNotifPipeline.get().initialize(mNotificationListener, rowBinder);
+ mNewNotifPipeline.get().initialize(mNotificationListener, mNotificationRowBinder);
}
mEntryManager.attach(mNotificationListener);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
index df741079de29..be7f0a0c36f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.phone;
-import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
import android.content.Context;
@@ -66,10 +65,10 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.collection.init.NewNotifPipeline;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder;
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -118,7 +117,6 @@ public class StatusBarModule {
HeadsUpManagerPhone headsUpManagerPhone,
DynamicPrivacyController dynamicPrivacyController,
BypassHeadsUpNotifier bypassHeadsUpNotifier,
- @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowNotificationLongPress,
Lazy<NewNotifPipeline> newNotifPipeline,
FalsingManager falsingManager,
BroadcastDispatcher broadcastDispatcher,
@@ -126,7 +124,6 @@ public class StatusBarModule {
NotificationGutsManager notificationGutsManager,
NotificationLogger notificationLogger,
NotificationEntryManager notificationEntryManager,
- NotificationRowContentBinder notificationRowContentBinder,
NotificationInterruptionStateProvider notificationInterruptionStateProvider,
NotificationViewHierarchyManager notificationViewHierarchyManager,
KeyguardViewMediator keyguardViewMediator,
@@ -186,6 +183,7 @@ public class StatusBarModule {
KeyguardDismissUtil keyguardDismissUtil,
ExtensionController extensionController,
UserInfoControllerImpl userInfoControllerImpl,
+ NotificationRowBinderImpl notificationRowBinder,
DismissCallbackRegistry dismissCallbackRegistry) {
return new StatusBar(
context,
@@ -201,7 +199,6 @@ public class StatusBarModule {
headsUpManagerPhone,
dynamicPrivacyController,
bypassHeadsUpNotifier,
- allowNotificationLongPress,
newNotifPipeline,
falsingManager,
broadcastDispatcher,
@@ -209,7 +206,6 @@ public class StatusBarModule {
notificationGutsManager,
notificationLogger,
notificationEntryManager,
- notificationRowContentBinder,
notificationInterruptionStateProvider,
notificationViewHierarchyManager,
keyguardViewMediator,
@@ -268,6 +264,7 @@ public class StatusBarModule {
keyguardDismissUtil,
extensionController,
userInfoControllerImpl,
+ notificationRowBinder,
dismissCallbackRegistry);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index ddacc3aedce0..4f0af9e166c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -188,7 +188,7 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C
// NOTE: This receiver could run before this method returns, as it's not dispatching
// on the main thread and BroadcastDispatcher may not need to register with Context.
// The receiver will return immediately if the view does not have a Handler yet.
- mBroadcastDispatcher.registerReceiver(mIntentReceiver, filter,
+ mBroadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter,
Dependency.get(Dependency.TIME_TICK_HANDLER), UserHandle.ALL);
Dependency.get(TunerService.class).addTunable(this, CLOCK_SECONDS,
StatusBarIconController.ICON_BLACKLIST);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
index 2e26711a3578..b4c154aa28cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
@@ -96,7 +96,7 @@ public class DateView extends TextView {
filter.addAction(Intent.ACTION_TIME_CHANGED);
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
filter.addAction(Intent.ACTION_LOCALE_CHANGED);
- mBroadcastDispatcher.registerReceiver(mIntentReceiver, filter,
+ mBroadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter,
Dependency.get(Dependency.TIME_TICK_HANDLER));
updateClock();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
index 570f153a62c0..cb40d7752f53 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
@@ -79,7 +79,8 @@ public class LocationControllerImpl extends BroadcastReceiver implements Locatio
IntentFilter filter = new IntentFilter();
filter.addAction(LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION);
filter.addAction(LocationManager.MODE_CHANGED_ACTION);
- mBroadcastDispatcher.registerReceiver(this, filter, new Handler(bgLooper), UserHandle.ALL);
+ mBroadcastDispatcher.registerReceiverWithHandler(this, filter,
+ new Handler(bgLooper), UserHandle.ALL);
mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
mStatusBarManager
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 6b842d5fa0b3..5916180d75ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -654,7 +654,7 @@ public class MobileSignalController extends SignalController<
}
boolean isDataDisabled() {
- return !mPhone.isDataCapable();
+ return !mPhone.isDataConnectionEnabled();
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index f640d039ad31..679fa7e2b016 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -326,7 +326,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);
filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
- mBroadcastDispatcher.registerReceiver(this, filter, mReceiverHandler);
+ mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mReceiverHandler);
mListening = true;
updateMobileControllers();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index 019ef3bca709..312c4ac75bfa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -126,7 +126,8 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi
IntentFilter filter = new IntentFilter();
filter.addAction(KeyChain.ACTION_TRUST_STORE_CHANGED);
filter.addAction(Intent.ACTION_USER_UNLOCKED);
- broadcastDispatcher.registerReceiver(mBroadcastReceiver, filter, bgHandler, UserHandle.ALL);
+ broadcastDispatcher.registerReceiverWithHandler(mBroadcastReceiver, filter, bgHandler,
+ UserHandle.ALL);
// TODO: re-register network callback on user change.
mConnectivityManager.registerNetworkCallback(REQUEST, mNetworkCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 7758aba52918..31b9952afe94 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -88,7 +88,7 @@ public class ThemeOverlayController extends SystemUI {
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
- mBroadcastDispatcher.registerReceiver(new BroadcastReceiver() {
+ mBroadcastDispatcher.registerReceiverWithHandler(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (DEBUG) Log.d(TAG, "Updating overlays for user switch / profile added.");
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java
index 7cdba8607d86..cc6d607a60cf 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java
@@ -83,17 +83,19 @@ public abstract class ConcurrencyModule {
* Provide a Background-Thread Executor by default.
*/
@Provides
+ @Singleton
public static Executor provideExecutor(@Background Looper looper) {
- return new ExecutorImpl(new Handler(looper));
+ return new ExecutorImpl(looper);
}
/**
* Provide a Background-Thread Executor.
*/
@Provides
+ @Singleton
@Background
public static Executor provideBackgroundExecutor(@Background Looper looper) {
- return new ExecutorImpl(new Handler(looper));
+ return new ExecutorImpl(looper);
}
/**
@@ -109,26 +111,29 @@ public abstract class ConcurrencyModule {
* Provide a Background-Thread Executor by default.
*/
@Provides
+ @Singleton
public static DelayableExecutor provideDelayableExecutor(@Background Looper looper) {
- return new ExecutorImpl(new Handler(looper));
+ return new ExecutorImpl(looper);
}
/**
* Provide a Background-Thread Executor.
*/
@Provides
+ @Singleton
@Background
public static DelayableExecutor provideBackgroundDelayableExecutor(@Background Looper looper) {
- return new ExecutorImpl(new Handler(looper));
+ return new ExecutorImpl(looper);
}
/**
* Provide a Main-Thread Executor.
*/
@Provides
+ @Singleton
@Main
public static DelayableExecutor provideMainDelayableExecutor(@Main Looper looper) {
- return new ExecutorImpl(new Handler(looper));
+ return new ExecutorImpl(looper);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ExecutorImpl.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/ExecutorImpl.java
index 7e7732135e3a..2bbf9507122a 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/ExecutorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/ExecutorImpl.java
@@ -17,37 +17,69 @@
package com.android.systemui.util.concurrency;
import android.os.Handler;
-import android.os.HandlerExecutor;
+import android.os.Looper;
import android.os.Message;
+import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
/**
* Implementations of {@link DelayableExecutor} for SystemUI.
*/
-public class ExecutorImpl extends HandlerExecutor implements DelayableExecutor {
+public class ExecutorImpl implements DelayableExecutor {
private final Handler mHandler;
- public ExecutorImpl(Handler handler) {
- super(handler);
- mHandler = handler;
+ ExecutorImpl(Looper looper) {
+ mHandler = new Handler(looper, this::onHandleMessage);
+ }
+
+ @Override
+ public void execute(Runnable command) {
+ if (!mHandler.post(command)) {
+ throw new RejectedExecutionException(mHandler + " is shutting down");
+ }
}
@Override
public Runnable executeDelayed(Runnable r, long delay, TimeUnit unit) {
- Object token = new Object();
- Message m = mHandler.obtainMessage(0, token);
+ ExecutionToken token = new ExecutionToken(r);
+ Message m = mHandler.obtainMessage(MSG_EXECUTE_RUNNABLE, token);
mHandler.sendMessageDelayed(m, unit.toMillis(delay));
- return () -> mHandler.removeCallbacksAndMessages(token);
+ return token;
}
@Override
public Runnable executeAtTime(Runnable r, long uptimeMillis, TimeUnit unit) {
- Object token = new Object();
- Message m = mHandler.obtainMessage(0, token);
+ ExecutionToken token = new ExecutionToken(r);
+ Message m = mHandler.obtainMessage(MSG_EXECUTE_RUNNABLE, token);
mHandler.sendMessageAtTime(m, unit.toMillis(uptimeMillis));
- return () -> mHandler.removeCallbacksAndMessages(token);
+ return token;
+ }
+
+ private boolean onHandleMessage(Message msg) {
+ if (msg.what == MSG_EXECUTE_RUNNABLE) {
+ ExecutionToken token = (ExecutionToken) msg.obj;
+ token.runnable.run();
+ } else {
+ throw new IllegalStateException("Unrecognized message: " + msg.what);
+ }
+ return true;
}
+
+ private class ExecutionToken implements Runnable {
+ public final Runnable runnable;
+
+ private ExecutionToken(Runnable runnable) {
+ this.runnable = runnable;
+ }
+
+ @Override
+ public void run() {
+ mHandler.removeCallbacksAndMessages(this);
+ }
+ }
+
+ private static final int MSG_EXECUTE_RUNNABLE = 0;
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index a4ed31d95b2b..112ae6f3758a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -1008,7 +1008,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa
filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
- mBroadcastDispatcher.registerReceiver(this, filter, mWorker);
+ mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mWorker);
}
public void destroy() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 0c22aaeaafb9..2e0fb3bc850d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -150,10 +150,10 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testReceiversRegistered() {
- verify(mBroadcastDispatcher, atLeastOnce()).registerReceiver(
+ verify(mBroadcastDispatcher, atLeastOnce()).registerReceiverWithHandler(
eq(mKeyguardUpdateMonitor.mBroadcastReceiver),
any(IntentFilter.class), any(Handler.class));
- verify(mBroadcastDispatcher, atLeastOnce()).registerReceiver(
+ verify(mBroadcastDispatcher, atLeastOnce()).registerReceiverWithHandler(
eq(mKeyguardUpdateMonitor.mBroadcastAllReceiver),
any(IntentFilter.class), any(Handler.class), eq(UserHandle.ALL));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
index 0c53b03de4d2..2ecc8ea8400c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
@@ -52,6 +52,7 @@ public class TestableDependency extends Dependency {
@Override
protected <T> T createDependency(Object key) {
if (mObjs.containsKey(key)) return (T) mObjs.get(key);
+
mInstantiatedObjects.add(key);
return super.createDependency(key);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
index 42fbf59fef44..22b18373e81d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
@@ -27,6 +27,8 @@ import android.test.suitebuilder.annotation.SmallTest
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
import junit.framework.Assert.assertSame
import org.junit.Before
import org.junit.Test
@@ -39,6 +41,7 @@ import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import java.util.concurrent.Executor
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
@@ -73,6 +76,8 @@ class BroadcastDispatcherTest : SysuiTestCase() {
@Mock
private lateinit var mockHandler: Handler
+ private lateinit var executor: Executor
+
@Captor
private lateinit var argumentCaptor: ArgumentCaptor<ReceiverData>
@@ -83,6 +88,7 @@ class BroadcastDispatcherTest : SysuiTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
+ executor = FakeExecutor(FakeSystemClock())
broadcastDispatcher = TestBroadcastDispatcher(
mockContext,
@@ -98,8 +104,9 @@ class BroadcastDispatcherTest : SysuiTestCase() {
@Test
fun testAddingReceiverToCorrectUBR() {
- broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, mockHandler, user0)
- broadcastDispatcher.registerReceiver(
+ broadcastDispatcher.registerReceiverWithHandler(broadcastReceiver, intentFilter,
+ mockHandler, user0)
+ broadcastDispatcher.registerReceiverWithHandler(
broadcastReceiverOther, intentFilterOther, mockHandler, user1)
testableLooper.processAllMessages()
@@ -115,9 +122,29 @@ class BroadcastDispatcherTest : SysuiTestCase() {
}
@Test
+ fun testAddingReceiverToCorrectUBR_executor() {
+ broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, executor, user0)
+ broadcastDispatcher.registerReceiver(
+ broadcastReceiverOther, intentFilterOther, executor, user1)
+
+ testableLooper.processAllMessages()
+
+ verify(mockUBRUser0).registerReceiver(capture(argumentCaptor))
+
+ assertSame(broadcastReceiver, argumentCaptor.value.receiver)
+ assertSame(intentFilter, argumentCaptor.value.filter)
+
+ verify(mockUBRUser1).registerReceiver(capture(argumentCaptor))
+ assertSame(broadcastReceiverOther, argumentCaptor.value.receiver)
+ assertSame(intentFilterOther, argumentCaptor.value.filter)
+ }
+
+ @Test
fun testRemovingReceiversRemovesFromAllUBR() {
- broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, mockHandler, user0)
- broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, mockHandler, user1)
+ broadcastDispatcher.registerReceiverWithHandler(broadcastReceiver, intentFilter,
+ mockHandler, user0)
+ broadcastDispatcher.registerReceiverWithHandler(broadcastReceiver, intentFilter,
+ mockHandler, user1)
broadcastDispatcher.unregisterReceiver(broadcastReceiver)
@@ -129,8 +156,10 @@ class BroadcastDispatcherTest : SysuiTestCase() {
@Test
fun testRemoveReceiverFromUser() {
- broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, mockHandler, user0)
- broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, mockHandler, user1)
+ broadcastDispatcher.registerReceiverWithHandler(broadcastReceiver, intentFilter,
+ mockHandler, user0)
+ broadcastDispatcher.registerReceiverWithHandler(broadcastReceiver, intentFilter,
+ mockHandler, user1)
broadcastDispatcher.unregisterReceiverForUser(broadcastReceiver, user0)
@@ -143,8 +172,8 @@ class BroadcastDispatcherTest : SysuiTestCase() {
@Test
fun testRegisterCurrentAsActualUser() {
setUserMock(mockContext, user1)
- broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, mockHandler,
- UserHandle.CURRENT)
+ broadcastDispatcher.registerReceiverWithHandler(broadcastReceiver, intentFilter,
+ mockHandler, UserHandle.CURRENT)
testableLooper.processAllMessages()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt
index 21ed15517752..7821ae29592e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt
@@ -26,6 +26,8 @@ import android.test.suitebuilder.annotation.SmallTest
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
@@ -69,8 +71,6 @@ class UserBroadcastDispatcherTest : SysuiTestCase() {
@Mock
private lateinit var mockContext: Context
@Mock
- private lateinit var mockHandler: Handler
- @Mock
private lateinit var mPendingResult: BroadcastReceiver.PendingResult
@Captor
@@ -81,12 +81,14 @@ class UserBroadcastDispatcherTest : SysuiTestCase() {
private lateinit var intentFilter: IntentFilter
private lateinit var intentFilterOther: IntentFilter
private lateinit var handler: Handler
+ private lateinit var fakeExecutor: FakeExecutor
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
handler = Handler(testableLooper.looper)
+ fakeExecutor = FakeExecutor(FakeSystemClock())
userBroadcastDispatcher = UserBroadcastDispatcher(
mockContext, USER_ID, handler, testableLooper.looper)
@@ -108,7 +110,7 @@ class UserBroadcastDispatcherTest : SysuiTestCase() {
intentFilter = IntentFilter(ACTION_1)
userBroadcastDispatcher.registerReceiver(
- ReceiverData(broadcastReceiver, intentFilter, mockHandler, USER_HANDLE))
+ ReceiverData(broadcastReceiver, intentFilter, fakeExecutor, USER_HANDLE))
testableLooper.processAllMessages()
assertTrue(userBroadcastDispatcher.isRegistered())
@@ -128,7 +130,7 @@ class UserBroadcastDispatcherTest : SysuiTestCase() {
intentFilter = IntentFilter(ACTION_1)
userBroadcastDispatcher.registerReceiver(
- ReceiverData(broadcastReceiver, intentFilter, mockHandler, USER_HANDLE))
+ ReceiverData(broadcastReceiver, intentFilter, fakeExecutor, USER_HANDLE))
testableLooper.processAllMessages()
reset(mockContext)
@@ -151,9 +153,9 @@ class UserBroadcastDispatcherTest : SysuiTestCase() {
}
userBroadcastDispatcher.registerReceiver(
- ReceiverData(broadcastReceiver, intentFilter, mockHandler, USER_HANDLE))
+ ReceiverData(broadcastReceiver, intentFilter, fakeExecutor, USER_HANDLE))
userBroadcastDispatcher.registerReceiver(
- ReceiverData(broadcastReceiverOther, intentFilterOther, mockHandler, USER_HANDLE))
+ ReceiverData(broadcastReceiverOther, intentFilterOther, fakeExecutor, USER_HANDLE))
testableLooper.processAllMessages()
assertTrue(userBroadcastDispatcher.isRegistered())
@@ -179,14 +181,15 @@ class UserBroadcastDispatcherTest : SysuiTestCase() {
intentFilterOther = IntentFilter(ACTION_2)
userBroadcastDispatcher.registerReceiver(
- ReceiverData(broadcastReceiver, intentFilter, handler, USER_HANDLE))
+ ReceiverData(broadcastReceiver, intentFilter, fakeExecutor, USER_HANDLE))
userBroadcastDispatcher.registerReceiver(
- ReceiverData(broadcastReceiverOther, intentFilterOther, handler, USER_HANDLE))
+ ReceiverData(broadcastReceiverOther, intentFilterOther, fakeExecutor, USER_HANDLE))
val intent = Intent(ACTION_2)
userBroadcastDispatcher.onReceive(mockContext, intent)
testableLooper.processAllMessages()
+ fakeExecutor.runAllReady()
verify(broadcastReceiver, never()).onReceive(any(), any())
verify(broadcastReceiverOther).onReceive(mockContext, intent)
@@ -198,14 +201,15 @@ class UserBroadcastDispatcherTest : SysuiTestCase() {
intentFilterOther = IntentFilter(ACTION_2)
userBroadcastDispatcher.registerReceiver(
- ReceiverData(broadcastReceiver, intentFilter, handler, USER_HANDLE))
+ ReceiverData(broadcastReceiver, intentFilter, fakeExecutor, USER_HANDLE))
userBroadcastDispatcher.registerReceiver(
- ReceiverData(broadcastReceiver, intentFilterOther, handler, USER_HANDLE))
+ ReceiverData(broadcastReceiver, intentFilterOther, fakeExecutor, USER_HANDLE))
val intent = Intent(ACTION_2)
userBroadcastDispatcher.onReceive(mockContext, intent)
testableLooper.processAllMessages()
+ fakeExecutor.runAllReady()
verify(broadcastReceiver).onReceive(mockContext, intent)
}
@@ -218,14 +222,15 @@ class UserBroadcastDispatcherTest : SysuiTestCase() {
intentFilterOther.addCategory(CATEGORY_2)
userBroadcastDispatcher.registerReceiver(
- ReceiverData(broadcastReceiver, intentFilter, handler, USER_HANDLE))
+ ReceiverData(broadcastReceiver, intentFilter, fakeExecutor, USER_HANDLE))
userBroadcastDispatcher.registerReceiver(
- ReceiverData(broadcastReceiverOther, intentFilterOther, handler, USER_HANDLE))
+ ReceiverData(broadcastReceiverOther, intentFilterOther, fakeExecutor, USER_HANDLE))
val intent = Intent(ACTION_1)
userBroadcastDispatcher.onReceive(mockContext, intent)
testableLooper.processAllMessages()
+ fakeExecutor.runAllReady()
verify(broadcastReceiver).onReceive(mockContext, intent)
verify(broadcastReceiverOther).onReceive(mockContext, intent)
@@ -235,12 +240,13 @@ class UserBroadcastDispatcherTest : SysuiTestCase() {
fun testPendingResult() {
intentFilter = IntentFilter(ACTION_1)
userBroadcastDispatcher.registerReceiver(
- ReceiverData(broadcastReceiver, intentFilter, handler, USER_HANDLE))
+ ReceiverData(broadcastReceiver, intentFilter, fakeExecutor, USER_HANDLE))
val intent = Intent(ACTION_1)
userBroadcastDispatcher.onReceive(mockContext, intent)
testableLooper.processAllMessages()
+ fakeExecutor.runAllReady()
verify(broadcastReceiver).onReceive(mockContext, intent)
verify(broadcastReceiver).pendingResult = mPendingResult
@@ -250,15 +256,16 @@ class UserBroadcastDispatcherTest : SysuiTestCase() {
fun testRemoveReceiverReferences() {
intentFilter = IntentFilter(ACTION_1)
userBroadcastDispatcher.registerReceiver(
- ReceiverData(broadcastReceiver, intentFilter, handler, USER_HANDLE))
+ ReceiverData(broadcastReceiver, intentFilter, fakeExecutor, USER_HANDLE))
intentFilterOther = IntentFilter(ACTION_1)
intentFilterOther.addAction(ACTION_2)
userBroadcastDispatcher.registerReceiver(
- ReceiverData(broadcastReceiverOther, intentFilterOther, handler, USER_HANDLE))
+ ReceiverData(broadcastReceiverOther, intentFilterOther, fakeExecutor, USER_HANDLE))
userBroadcastDispatcher.unregisterReceiver(broadcastReceiver)
testableLooper.processAllMessages()
+ fakeExecutor.runAllReady()
assertFalse(userBroadcastDispatcher.isReceiverReferenceHeld(broadcastReceiver))
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index 167f361b341a..548da8e1f2aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -110,7 +110,7 @@ public class PowerUITest extends SysuiTestCase {
@Test
public void testReceiverIsRegisteredToDispatcherOnStart() {
mPowerUI.start();
- verify(mBroadcastDispatcher).registerReceiver(
+ verify(mBroadcastDispatcher).registerReceiverWithHandler(
any(BroadcastReceiver.class),
any(IntentFilter.class),
any(Handler.class)); //PowerUI does not call with User
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index 3fdbd3edfcaa..77659df738c3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -47,9 +47,6 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-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.NotifRemoteViewCache;
import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
@@ -75,7 +72,6 @@ public class NotificationTestHelper {
public static final UserHandle USER_HANDLE = UserHandle.of(ActivityManager.getCurrentUser());
private static final String GROUP_KEY = "gruKey";
- private static final String APP_NAME = "appName";
private final Context mContext;
private int mId;
@@ -307,6 +303,9 @@ public class NotificationTestHelper {
null /* root */,
false /* attachToRoot */);
ExpandableNotificationRow row = mRow;
+ row.setGroupManager(mGroupManager);
+ row.setHeadsUpManager(mHeadsUpManager);
+ row.setAboveShelfChangedListener(aboveShelf -> {});
final NotificationChannel channel =
new NotificationChannel(
@@ -330,23 +329,6 @@ public class NotificationTestHelper {
entry.setRow(row);
entry.createIcons(mContext, entry.getSbn());
row.setEntry(entry);
-
- NotificationContentInflater contentBinder = new NotificationContentInflater(
- mock(NotifRemoteViewCache.class),
- mock(NotificationRemoteInputManager.class));
- contentBinder.setInflateSynchronously(true);
-
- row.initialize(
- APP_NAME,
- entry.getKey(),
- mock(ExpansionLogger.class),
- mock(KeyguardBypassController.class),
- mGroupManager,
- mHeadsUpManager,
- contentBinder,
- mock(OnExpandClickListener.class));
- row.setAboveShelfChangedListener(aboveShelf -> { });
-
row.setInflationFlags(extraInflationFlags);
inflateAndWait(row);
@@ -359,6 +341,7 @@ public class NotificationTestHelper {
private static void inflateAndWait(ExpandableNotificationRow row) throws Exception {
CountDownLatch countDownLatch = new CountDownLatch(1);
+ row.getNotificationInflater().setInflateSynchronously(true);
NotificationContentInflater.InflationCallback callback =
new NotificationContentInflater.InflationCallback() {
@Override
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 1c294531ea68..a60fd52b7c7f 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
@@ -58,16 +58,13 @@ import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
-import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dependency;
-import com.android.systemui.ForegroundServiceController;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLifetimeExtender;
-import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationPresenter;
@@ -81,24 +78,21 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager.Keyg
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
-import com.android.systemui.statusbar.notification.collection.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.notification.logging.NotifLog;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.NotifRemoteViewCache;
-import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder;
import com.android.systemui.statusbar.notification.row.RowInflaterTask;
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.ShadeController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.Assert;
+import com.android.systemui.util.leak.LeakDetector;
import org.junit.Before;
import org.junit.Test;
@@ -132,22 +126,16 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
@Mock private HeadsUpManager mHeadsUpManager;
@Mock private RankingMap mRankingMap;
@Mock private RemoteInputController mRemoteInputController;
-
- // Dependency mocks:
- @Mock private ForegroundServiceController mForegroundServiceController;
+ @Mock private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
@Mock private NotificationLockscreenUserManager mLockscreenUserManager;
@Mock private NotificationGroupManager mGroupManager;
@Mock private NotificationGutsManager mGutsManager;
@Mock private NotificationRemoteInputManager mRemoteInputManager;
- @Mock private NotificationListener mNotificationListener;
@Mock private DeviceProvisionedController mDeviceProvisionedController;
- @Mock private VisualStabilityManager mVisualStabilityManager;
- @Mock private MetricsLogger mMetricsLogger;
- @Mock private SmartReplyController mSmartReplyController;
@Mock private RowInflaterTask mAsyncInflationTask;
- @Mock private NotificationRowBinder mMockedRowBinder;
@Mock private NotifLog mNotifLog;
@Mock private FeatureFlags mFeatureFlags;
+ @Mock private LeakDetector mLeakDetector;
private int mId;
private NotificationEntry mEntry;
@@ -194,21 +182,9 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mDependency.injectMockDependency(ShadeController.class);
- mDependency.injectTestDependency(ForegroundServiceController.class,
- mForegroundServiceController);
- mDependency.injectTestDependency(NotificationLockscreenUserManager.class,
- mLockscreenUserManager);
- mDependency.injectTestDependency(NotificationGroupManager.class, mGroupManager);
- mDependency.injectTestDependency(NotificationGutsManager.class, mGutsManager);
- mDependency.injectTestDependency(NotificationRemoteInputManager.class, mRemoteInputManager);
- mDependency.injectTestDependency(NotificationListener.class, mNotificationListener);
- mDependency.injectTestDependency(DeviceProvisionedController.class,
- mDeviceProvisionedController);
- mDependency.injectTestDependency(VisualStabilityManager.class, mVisualStabilityManager);
- mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
- mDependency.injectTestDependency(SmartReplyController.class, mSmartReplyController);
- mDependency.injectTestDependency(KeyguardEnvironment.class, mEnvironment);
+ if (!mDependency.hasInstantiatedDependency(SmartReplyController.class)) {
+ mDependency.injectMockDependency(SmartReplyController.class);
+ }
mDependency.injectMockDependency(NotificationMediaManager.class);
mCountDownLatch = new CountDownLatch(1);
@@ -224,6 +200,17 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
mEntry.expandedIcon = mock(StatusBarIconView.class);
+ NotificationRowBinderImpl notificationRowBinder =
+ new NotificationRowBinderImpl(mContext,
+ mRemoteInputManager, mLockscreenUserManager,
+ true, /* allowLongPress */
+ mock(KeyguardBypassController.class),
+ mock(StatusBarStateController.class),
+ mGroupManager,
+ mGutsManager,
+ mNotificationInterruptionStateProvider,
+ mock(NotificationLogger.class));
+
when(mFeatureFlags.isNewNotifPipelineEnabled()).thenReturn(false);
when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
mEntryManager = new TestableNotificationEntryManager(
@@ -236,30 +223,22 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
mock(NotificationFilter.class),
mNotifLog,
mock(NotificationSectionsFeatureManager.class),
- mock(PeopleNotificationIdentifier.class)),
+ mock(PeopleNotificationIdentifier.class),
+ mock(HighPriorityProvider.class)),
mEnvironment,
- mFeatureFlags
+ mFeatureFlags,
+ () -> notificationRowBinder,
+ () -> mRemoteInputManager,
+ mLeakDetector
);
mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mHeadsUpManager);
mEntryManager.addNotificationEntryListener(mEntryListener);
mEntryManager.setNotificationRemoveInterceptor(mRemoveInterceptor);
- NotificationRowContentBinder contentBinder = new NotificationContentInflater(
- mock(NotifRemoteViewCache.class),
- mRemoteInputManager);
-
- NotificationRowBinderImpl notificationRowBinder =
- new NotificationRowBinderImpl(mContext,
- contentBinder,
- true, /* allowLongPress */
- mock(KeyguardBypassController.class),
- mock(StatusBarStateController.class),
- mock(NotificationLogger.class));
notificationRowBinder.setUpWithPresenter(
mPresenter, mListContainer, mHeadsUpManager, mBindCallback);
notificationRowBinder.setInflationCallback(mEntryManager);
notificationRowBinder.setNotificationClicker(mock(NotificationClicker.class));
- mEntryManager.setRowBinder(notificationRowBinder);
setUserSentiment(
mEntry.getKey(), Ranking.USER_SENTIMENT_NEUTRAL);
@@ -379,9 +358,6 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
@Test
public void testRemoveNotification_whilePending() {
-
- mEntryManager.setRowBinder(mMockedRowBinder);
-
mEntryManager.addNotification(mSbn, mRankingMap);
mEntryManager.removeNotification(mSbn.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
@@ -454,7 +430,6 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
@Test
public void testLifetimeExtenders_ifNotificationIsRetainedItIsntRemoved() {
// GIVEN an entry manager with a notification
- mEntryManager.setRowBinder(mMockedRowBinder);
mEntryManager.addActiveNotificationForTest(mEntry);
// GIVEN a lifetime extender that always tries to extend lifetime
@@ -478,7 +453,6 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
Assert.sMainLooper = TestableLooper.get(this).getLooper();
// GIVEN an entry manager with a notification whose life has been extended
- mEntryManager.setRowBinder(mMockedRowBinder);
mEntryManager.addActiveNotificationForTest(mEntry);
final FakeNotificationLifetimeExtender extender = new FakeNotificationLifetimeExtender();
mEntryManager.addNotificationLifetimeExtender(extender);
@@ -497,7 +471,6 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
@Test
public void testLifetimeExtenders_whenNotificationUpdatedRetainersAreCanceled() {
// GIVEN an entry manager with a notification whose life has been extended
- mEntryManager.setRowBinder(mMockedRowBinder);
mEntryManager.addActiveNotificationForTest(mEntry);
NotificationLifetimeExtender extender = mock(NotificationLifetimeExtender.class);
when(extender.shouldExtendLifetime(mEntry)).thenReturn(true);
@@ -514,7 +487,6 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
@Test
public void testLifetimeExtenders_whenNewExtenderTakesPrecedenceOldExtenderIsCanceled() {
// GIVEN an entry manager with a notification
- mEntryManager.setRowBinder(mMockedRowBinder);
mEntryManager.addActiveNotificationForTest(mEntry);
// GIVEN two lifetime extenders, the first which never extends and the second which
@@ -553,7 +525,6 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
@Test
public void testRemoveInterceptor_interceptsDontGetRemoved() throws InterruptedException {
// GIVEN an entry manager with a notification
- mEntryManager.setRowBinder(mMockedRowBinder);
mEntryManager.addActiveNotificationForTest(mEntry);
// GIVEN interceptor that intercepts that entry
@@ -575,7 +546,6 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
Assert.sMainLooper = TestableLooper.get(this).getLooper();
// GIVEN an entry manager with a notification
- mEntryManager.setRowBinder(mMockedRowBinder);
mEntryManager.addActiveNotificationForTest(mEntry);
// GIVEN interceptor that doesn't intercept
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt
index 1afee120e495..bf84f2bc599c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt
@@ -18,12 +18,15 @@ package com.android.systemui.statusbar.notification
import com.android.systemui.statusbar.FeatureFlags
import com.android.systemui.statusbar.NotificationPresenter
+import com.android.systemui.statusbar.NotificationRemoteInputManager
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager
+import com.android.systemui.statusbar.notification.collection.NotificationRowBinder
import com.android.systemui.statusbar.notification.logging.NotifLog
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
import com.android.systemui.statusbar.phone.NotificationGroupManager
+import com.android.systemui.util.leak.LeakDetector
import java.util.concurrent.CountDownLatch
@@ -35,8 +38,12 @@ class TestableNotificationEntryManager(
gm: NotificationGroupManager,
rm: NotificationRankingManager,
ke: KeyguardEnvironment,
- ff: FeatureFlags
-) : NotificationEntryManager(log, gm, rm, ke, ff) {
+ ff: FeatureFlags,
+ rb: dagger.Lazy<NotificationRowBinder>,
+ notificationRemoteInputManagerLazy: dagger.Lazy<NotificationRemoteInputManager>,
+ leakDetector: LeakDetector
+) : NotificationEntryManager(log, gm, rm, ke, ff, rb,
+ notificationRemoteInputManagerLazy, leakDetector) {
public var countDownLatch: CountDownLatch = CountDownLatch(1)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/GroupEntryTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/GroupEntryTest.java
deleted file mode 100644
index a06d6c1e593b..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/GroupEntryTest.java
+++ /dev/null
@@ -1,148 +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;
-
-import static android.app.NotificationManager.IMPORTANCE_HIGH;
-import static android.app.NotificationManager.IMPORTANCE_MIN;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.testing.AndroidTestingRunner;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.RankingBuilder;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class GroupEntryTest extends SysuiTestCase {
- @Test
- public void testIsHighPriority_addChild() {
- // GIVEN a GroupEntry with a lowPrioritySummary and no children
- final GroupEntry parentEntry = new GroupEntry("test_group_key");
- final NotificationEntry lowPrioritySummary = createNotifEntry(false);
- setSummary(parentEntry, lowPrioritySummary);
- assertFalse(parentEntry.isHighPriority());
-
- // WHEN we add a high priority child and invalidate derived members
- addChild(parentEntry, createNotifEntry(true));
- parentEntry.invalidateDerivedMembers();
-
- // THEN the GroupEntry's priority is updated to high even though the summary is still low
- // priority
- assertTrue(parentEntry.isHighPriority());
- assertFalse(lowPrioritySummary.isHighPriority());
- }
-
- @Test
- public void testIsHighPriority_clearChildren() {
- // GIVEN a GroupEntry with a lowPrioritySummary and high priority children
- final GroupEntry parentEntry = new GroupEntry("test_group_key");
- setSummary(parentEntry, createNotifEntry(false));
- addChild(parentEntry, createNotifEntry(true));
- addChild(parentEntry, createNotifEntry(true));
- addChild(parentEntry, createNotifEntry(true));
- assertTrue(parentEntry.isHighPriority());
-
- // WHEN we clear the children and invalidate derived members
- parentEntry.clearChildren();
- parentEntry.invalidateDerivedMembers();
-
- // THEN the parentEntry isn't high priority anymore
- assertFalse(parentEntry.isHighPriority());
- }
-
- @Test
- public void testIsHighPriority_summaryUpdated() {
- // GIVEN a GroupEntry with a lowPrioritySummary and no children
- final GroupEntry parentEntry = new GroupEntry("test_group_key");
- final NotificationEntry lowPrioritySummary = createNotifEntry(false);
- setSummary(parentEntry, lowPrioritySummary);
- assertFalse(parentEntry.isHighPriority());
-
- // WHEN the summary changes to high priority and invalidates its derived members
- lowPrioritySummary.setRanking(
- new RankingBuilder()
- .setKey(lowPrioritySummary.getKey())
- .setImportance(IMPORTANCE_HIGH)
- .build());
- lowPrioritySummary.invalidateDerivedMembers();
- assertTrue(lowPrioritySummary.isHighPriority());
-
- // THEN the GroupEntry's priority is updated to high
- assertTrue(parentEntry.isHighPriority());
- }
-
- @Test
- public void testIsHighPriority_checkChildrenToCalculatePriority() {
- // GIVEN:
- // GroupEntry = parentEntry, summary = lowPrioritySummary
- // NotificationEntry = lowPriorityChild
- // NotificationEntry = highPriorityChild
- final GroupEntry parentEntry = new GroupEntry("test_group_key");
- setSummary(parentEntry, createNotifEntry(false));
- addChild(parentEntry, createNotifEntry(false));
- addChild(parentEntry, createNotifEntry(true));
-
- // THEN the GroupEntry parentEntry is high priority since it has a high priority child
- assertTrue(parentEntry.isHighPriority());
- }
-
- @Test
- public void testIsHighPriority_childEntryRankingUpdated() {
- // GIVEN:
- // GroupEntry = parentEntry, summary = lowPrioritySummary
- // NotificationEntry = lowPriorityChild
- final GroupEntry parentEntry = new GroupEntry("test_group_key");
- final NotificationEntry lowPriorityChild = createNotifEntry(false);
- setSummary(parentEntry, createNotifEntry(false));
- addChild(parentEntry, lowPriorityChild);
-
- // WHEN the child entry ranking changes to high priority and invalidates its derived members
- lowPriorityChild.setRanking(
- new RankingBuilder()
- .setKey(lowPriorityChild.getKey())
- .setImportance(IMPORTANCE_HIGH)
- .build());
- lowPriorityChild.invalidateDerivedMembers();
-
- // THEN the parent entry's high priority value is updated - but not the parent's summary
- assertTrue(parentEntry.isHighPriority());
- assertFalse(parentEntry.getSummary().isHighPriority());
- }
-
- private NotificationEntry createNotifEntry(boolean highPriority) {
- return new NotificationEntryBuilder()
- .setImportance(highPriority ? IMPORTANCE_HIGH : IMPORTANCE_MIN)
- .build();
- }
-
- private void setSummary(GroupEntry parent, NotificationEntry summary) {
- parent.setSummary(summary);
- summary.setParent(parent);
- }
-
- private void addChild(GroupEntry parent, NotificationEntry child) {
- parent.addChild(child);
- child.setParent(parent);
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java
new file mode 100644
index 000000000000..93909dc4d330
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java
@@ -0,0 +1,225 @@
+/*
+ * 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 static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.RankingBuilder;
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class HighPriorityProviderTest extends SysuiTestCase {
+ @Mock private PeopleNotificationIdentifier mPeopleNotificationIdentifier;
+ private HighPriorityProvider mHighPriorityProvider;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mHighPriorityProvider = new HighPriorityProvider(mPeopleNotificationIdentifier);
+ }
+
+ @Test
+ public void highImportance() {
+ // GIVEN notification has high importance
+ final NotificationEntry entry = new NotificationEntryBuilder()
+ .setImportance(IMPORTANCE_HIGH)
+ .build();
+ when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(false);
+
+ // THEN it has high priority
+ assertTrue(mHighPriorityProvider.isHighPriority(entry));
+ }
+
+ @Test
+ public void peopleNotification() {
+ // GIVEN notification is low importance and is a people notification
+ final Notification notification = new Notification.Builder(mContext, "test")
+ .build();
+ final NotificationEntry entry = new NotificationEntryBuilder()
+ .setNotification(notification)
+ .setImportance(IMPORTANCE_LOW)
+ .build();
+ when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(true);
+
+ // THEN it has high priority
+ assertTrue(mHighPriorityProvider.isHighPriority(entry));
+ }
+
+ @Test
+ public void messagingStyle() {
+ // GIVEN notification is low importance but has messaging style
+ final Notification notification = new Notification.Builder(mContext, "test")
+ .setStyle(new Notification.MessagingStyle(""))
+ .build();
+ final NotificationEntry entry = new NotificationEntryBuilder()
+ .setNotification(notification)
+ .build();
+ when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(false);
+
+ // THEN it has high priority
+ assertTrue(mHighPriorityProvider.isHighPriority(entry));
+ }
+
+ @Test
+ public void lowImportanceForeground() {
+ // GIVEN notification is low importance and is associated with a foreground service
+ final Notification notification = mock(Notification.class);
+ when(notification.isForegroundService()).thenReturn(true);
+
+ final NotificationEntry entry = new NotificationEntryBuilder()
+ .setNotification(notification)
+ .setImportance(IMPORTANCE_LOW)
+ .build();
+ when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(false);
+
+ // THEN it has high priority
+ assertTrue(mHighPriorityProvider.isHighPriority(entry));
+ }
+
+ @Test
+ public void minImportanceForeground() {
+ // GIVEN notification is low importance and is associated with a foreground service
+ final Notification notification = mock(Notification.class);
+ when(notification.isForegroundService()).thenReturn(true);
+
+ final NotificationEntry entry = new NotificationEntryBuilder()
+ .setNotification(notification)
+ .setImportance(IMPORTANCE_MIN)
+ .build();
+ when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(false);
+
+ // THEN it does NOT have high priority
+ assertFalse(mHighPriorityProvider.isHighPriority(entry));
+ }
+
+ @Test
+ public void userChangeTrumpsHighPriorityCharacteristics() {
+ // GIVEN notification has high priority characteristics but the user changed the importance
+ // to less than IMPORTANCE_DEFAULT (ie: IMPORTANCE_LOW or IMPORTANCE_MIN)
+ final Notification notification = new Notification.Builder(mContext, "test")
+ .setStyle(new Notification.MessagingStyle(""))
+ .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
+ .build();
+ final NotificationChannel channel = new NotificationChannel("a", "a",
+ IMPORTANCE_LOW);
+ channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
+
+ final NotificationEntry entry = new NotificationEntryBuilder()
+ .setNotification(notification)
+ .setChannel(channel)
+ .build();
+ when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(true);
+
+ // THEN it does NOT have high priority
+ assertFalse(mHighPriorityProvider.isHighPriority(entry));
+ }
+
+ @Test
+ public void testIsHighPriority_summaryUpdated() {
+ // GIVEN a GroupEntry with a lowPrioritySummary and no children
+ final GroupEntry parentEntry = new GroupEntry("test_group_key");
+ final NotificationEntry lowPrioritySummary = createNotifEntry(false);
+ setSummary(parentEntry, lowPrioritySummary);
+ assertFalse(mHighPriorityProvider.isHighPriority(parentEntry));
+
+ // WHEN the summary changes to high priority
+ lowPrioritySummary.setRanking(
+ new RankingBuilder()
+ .setKey(lowPrioritySummary.getKey())
+ .setImportance(IMPORTANCE_HIGH)
+ .build());
+ assertTrue(mHighPriorityProvider.isHighPriority(lowPrioritySummary));
+
+ // THEN the GroupEntry's priority is updated to high
+ assertTrue(mHighPriorityProvider.isHighPriority(parentEntry));
+ }
+
+ @Test
+ public void testIsHighPriority_checkChildrenToCalculatePriority() {
+ // GIVEN:
+ // GroupEntry = parentEntry, summary = lowPrioritySummary
+ // NotificationEntry = lowPriorityChild
+ // NotificationEntry = highPriorityChild
+ final GroupEntry parentEntry = new GroupEntry("test_group_key");
+ setSummary(parentEntry, createNotifEntry(false));
+ addChild(parentEntry, createNotifEntry(false));
+ addChild(parentEntry, createNotifEntry(true));
+
+ // THEN the GroupEntry parentEntry is high priority since it has a high priority child
+ assertTrue(mHighPriorityProvider.isHighPriority(parentEntry));
+ }
+
+ @Test
+ public void testIsHighPriority_childEntryRankingUpdated() {
+ // GIVEN:
+ // GroupEntry = parentEntry, summary = lowPrioritySummary
+ // NotificationEntry = lowPriorityChild
+ final GroupEntry parentEntry = new GroupEntry("test_group_key");
+ final NotificationEntry lowPriorityChild = createNotifEntry(false);
+ setSummary(parentEntry, createNotifEntry(false));
+ addChild(parentEntry, lowPriorityChild);
+
+ // WHEN the child entry ranking changes to high priority
+ lowPriorityChild.setRanking(
+ new RankingBuilder()
+ .setKey(lowPriorityChild.getKey())
+ .setImportance(IMPORTANCE_HIGH)
+ .build());
+
+ // THEN the parent entry's high priority value is updated - but not the parent's summary
+ assertTrue(mHighPriorityProvider.isHighPriority(parentEntry));
+ assertFalse(mHighPriorityProvider.isHighPriority(parentEntry.getSummary()));
+ }
+
+ private NotificationEntry createNotifEntry(boolean highPriority) {
+ return new NotificationEntryBuilder()
+ .setImportance(highPriority ? IMPORTANCE_HIGH : IMPORTANCE_MIN)
+ .build();
+ }
+
+ private void setSummary(GroupEntry parent, NotificationEntry summary) {
+ parent.setSummary(summary);
+ summary.setParent(parent);
+ }
+
+ private void addChild(GroupEntry parent, NotificationEntry child) {
+ parent.addChild(child);
+ child.setParent(parent);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
index 39ae68a40291..5b0b66849027 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
@@ -21,8 +21,6 @@ import static android.app.Notification.CATEGORY_CALL;
import static android.app.Notification.CATEGORY_EVENT;
import static android.app.Notification.CATEGORY_MESSAGE;
import static android.app.Notification.CATEGORY_REMINDER;
-import static android.app.NotificationManager.IMPORTANCE_HIGH;
-import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
@@ -92,44 +90,6 @@ public class NotificationEntryTest extends SysuiTestCase {
}
@Test
- public void testIsHighPriority_notificationUpdates() {
- // GIVEN a notification with high importance
- final NotificationEntry entryHigh = new NotificationEntryBuilder()
- .setImportance(IMPORTANCE_HIGH)
- .build();
-
- // WHEN we get the value for the high priority entry, we're caching the high priority value
- assertTrue(entryHigh.isHighPriority());
-
- // WHEN we change the ranking and derived members (high priority) are invalidated
- entryHigh.setRanking(
- new RankingBuilder()
- .setKey(entryHigh.getKey())
- .setImportance(IMPORTANCE_MIN)
- .build());
- entryHigh.invalidateDerivedMembers();
-
- // THEN the priority is recalculated and is now low
- assertFalse(entryHigh.isHighPriority());
-
- // WHEN the sbn is updated to have messaging style (high priority characteristic)
- // AND the entry invalidates its derived members
- final Notification notification =
- new Notification.Builder(mContext, "test")
- .setStyle(new Notification.MessagingStyle(""))
- .build();
- final StatusBarNotification sbn = entryHigh.getSbn();
- entryHigh.setSbn(new StatusBarNotification(
- sbn.getPackageName(), sbn.getPackageName(),
- sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
- notification, sbn.getUser(), sbn.getOverrideGroupKey(), 0));
- entryHigh.invalidateDerivedMembers();
-
- // THEN the priority is recalculated and is now high
- assertTrue(entryHigh.isHighPriority());
- }
-
- @Test
public void testIsExemptFromDndVisualSuppression_foreground() {
mEntry.getSbn().getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
index 10450fa82cde..e27319103525 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
@@ -28,6 +28,7 @@ import com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking
import com.android.systemui.statusbar.NotificationMediaManager
import com.android.systemui.statusbar.notification.NotificationFilter
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
import com.android.systemui.statusbar.notification.logging.NotifLog
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING
@@ -62,7 +63,8 @@ class NotificationRankingManagerTest : SysuiTestCase() {
mock(NotificationFilter::class.java),
mock(NotifLog::class.java),
mock(NotificationSectionsFeatureManager::class.java),
- personNotificationIdentifier
+ personNotificationIdentifier,
+ HighPriorityProvider(personNotificationIdentifier)
)
}
@@ -182,7 +184,8 @@ class NotificationRankingManagerTest : SysuiTestCase() {
filter: NotificationFilter,
notifLog: NotifLog,
sectionsFeatureManager: NotificationSectionsFeatureManager,
- peopleNotificationIdentifier: PeopleNotificationIdentifier
+ peopleNotificationIdentifier: PeopleNotificationIdentifier,
+ highPriorityProvider: HighPriorityProvider
) : NotificationRankingManager(
mediaManager,
groupManager,
@@ -190,7 +193,8 @@ class NotificationRankingManagerTest : SysuiTestCase() {
filter,
notifLog,
sectionsFeatureManager,
- peopleNotificationIdentifier
+ peopleNotificationIdentifier,
+ highPriorityProvider
) {
fun applyTestRankingMap(r: RankingMap) {
rankingMap = r
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
index 979b8a906ef0..f921cf969e61 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
@@ -45,6 +45,7 @@ import com.android.systemui.statusbar.notification.collection.NotifListBuilderIm
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.NotifFilter;
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
@@ -66,6 +67,7 @@ public class KeyguardCoordinatorTest extends SysuiTestCase {
@Mock private BroadcastDispatcher mBroadcastDispatcher;
@Mock private StatusBarStateController mStatusBarStateController;
@Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock private HighPriorityProvider mHighPriorityProvider;
@Mock private NotifListBuilderImpl mNotifListBuilder;
private NotificationEntry mEntry;
@@ -78,7 +80,7 @@ public class KeyguardCoordinatorTest extends SysuiTestCase {
mKeyguardCoordinator = new KeyguardCoordinator(
mContext, mMainHandler, mKeyguardStateController, mLockscreenUserManager,
mBroadcastDispatcher, mStatusBarStateController,
- mKeyguardUpdateMonitor);
+ mKeyguardUpdateMonitor, mHighPriorityProvider);
mEntry = new NotificationEntryBuilder()
.setUser(new UserHandle(NOTIF_USER_ID))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/provider/IsHighPriorityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/provider/IsHighPriorityProviderTest.java
deleted file mode 100644
index 6fa1a89515c3..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/provider/IsHighPriorityProviderTest.java
+++ /dev/null
@@ -1,184 +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.provider;
-
-import static android.app.NotificationManager.IMPORTANCE_HIGH;
-import static android.app.NotificationManager.IMPORTANCE_LOW;
-import static android.app.NotificationManager.IMPORTANCE_MIN;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.Person;
-import android.testing.AndroidTestingRunner;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class IsHighPriorityProviderTest extends SysuiTestCase {
- private IsHighPriorityProvider mIsHighPriorityProvider;
-
- @Before
- public void setup() {
- mIsHighPriorityProvider = new IsHighPriorityProvider();
- }
-
- @Test
- public void testCache() {
- // GIVEN a notification with high importance
- final NotificationEntry entryHigh = new NotificationEntryBuilder()
- .setImportance(IMPORTANCE_HIGH)
- .build();
-
- // GIVEN notification with min importance
- final NotificationEntry entryMin = new NotificationEntryBuilder()
- .setImportance(IMPORTANCE_MIN)
- .build();
-
- // WHEN we get the value for the high priority entry
- assertTrue(mIsHighPriorityProvider.get(entryHigh));
-
- // THEN the value is cached, so even when passed an entryMin, we still get high priority
- assertTrue(mIsHighPriorityProvider.get(entryMin));
-
- // UNTIL the provider is invalidated
- mIsHighPriorityProvider.invalidate();
-
- // THEN the priority is recalculated
- assertFalse(mIsHighPriorityProvider.get(entryMin));
- }
-
- @Test
- public void highImportance() {
- // GIVEN notification has high importance
- final NotificationEntry entry = new NotificationEntryBuilder()
- .setImportance(IMPORTANCE_HIGH)
- .build();
-
- // THEN it has high priority
- assertTrue(mIsHighPriorityProvider.get(entry));
- }
-
- @Test
- public void peopleNotification() {
- // GIVEN notification is low importance but has a person associated with it
- final Notification notification = new Notification.Builder(mContext, "test")
- .addPerson(
- new Person.Builder()
- .setName("name")
- .setKey("abc")
- .setUri("uri")
- .setBot(true)
- .build())
- .build();
-
- final NotificationEntry entry = new NotificationEntryBuilder()
- .setNotification(notification)
- .setImportance(IMPORTANCE_LOW)
- .build();
-
- // THEN it has high priority
- assertTrue(mIsHighPriorityProvider.get(entry));
- }
-
- @Test
- public void messagingStyle() {
- // GIVEN notification is low importance but has messaging style
- final Notification notification = new Notification.Builder(mContext, "test")
- .setStyle(new Notification.MessagingStyle(""))
- .build();
-
- final NotificationEntry entry = new NotificationEntryBuilder()
- .setNotification(notification)
- .build();
-
- // THEN it has high priority
- assertTrue(mIsHighPriorityProvider.get(entry));
- }
-
- @Test
- public void lowImportanceForeground() {
- // GIVEN notification is low importance and is associated with a foreground service
- final Notification notification = mock(Notification.class);
- when(notification.isForegroundService()).thenReturn(true);
-
- final NotificationEntry entry = new NotificationEntryBuilder()
- .setNotification(notification)
- .setImportance(IMPORTANCE_LOW)
- .build();
-
- // THEN it has high priority
- assertTrue(mIsHighPriorityProvider.get(entry));
- }
-
- @Test
- public void minImportanceForeground() {
- // GIVEN notification is low importance and is associated with a foreground service
- final Notification notification = mock(Notification.class);
- when(notification.isForegroundService()).thenReturn(true);
-
- final NotificationEntry entry = new NotificationEntryBuilder()
- .setNotification(notification)
- .setImportance(IMPORTANCE_MIN)
- .build();
-
- // THEN it does NOT have high priority
- assertFalse(mIsHighPriorityProvider.get(entry));
- }
-
- @Test
- public void userChangeTrumpsHighPriorityCharacteristics() {
- // GIVEN notification has high priority characteristics but the user changed the importance
- // to less than IMPORTANCE_DEFAULT (ie: IMPORTANCE_LOW or IMPORTANCE_MIN)
- final Notification notification = new Notification.Builder(mContext, "test")
- .addPerson(
- new Person.Builder()
- .setName("name")
- .setKey("abc")
- .setUri("uri")
- .setBot(true)
- .build())
- .setStyle(new Notification.MessagingStyle(""))
- .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
- .build();
-
- final NotificationChannel channel = new NotificationChannel("a", "a",
- IMPORTANCE_LOW);
- channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
-
- final NotificationEntry entry = new NotificationEntryBuilder()
- .setNotification(notification)
- .setChannel(channel)
- .build();
-
- // THEN it does NOT have high priority
- assertFalse(mIsHighPriorityProvider.get(entry));
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImplTest.java
deleted file mode 100644
index d7214f3b9228..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImplTest.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.row;
-
-import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
-import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-import android.testing.AndroidTestingRunner;
-import android.widget.RemoteViews;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.NotificationEntryListener;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class NotifRemoteViewCacheImplTest extends SysuiTestCase {
-
- private NotifRemoteViewCacheImpl mNotifRemoteViewCache;
- private NotificationEntry mEntry;
- private NotificationEntryListener mEntryListener;
- @Mock private RemoteViews mRemoteViews;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mEntry = new NotificationEntryBuilder().build();
-
- NotificationEntryManager entryManager = mock(NotificationEntryManager.class);
- mNotifRemoteViewCache = new NotifRemoteViewCacheImpl(entryManager);
- ArgumentCaptor<NotificationEntryListener> entryListenerCaptor =
- ArgumentCaptor.forClass(NotificationEntryListener.class);
- verify(entryManager).addNotificationEntryListener(entryListenerCaptor.capture());
- mEntryListener = entryListenerCaptor.getValue();
- }
-
- @Test
- public void testPutCachedView() {
- // GIVEN an initialized cache for an entry.
- mEntryListener.onPendingEntryAdded(mEntry);
-
- // WHEN a notification's cached remote views is put in.
- mNotifRemoteViewCache.putCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED, mRemoteViews);
-
- // THEN the remote view is cached.
- assertTrue(mNotifRemoteViewCache.hasCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED));
- assertEquals(
- "Cached remote view is not the one we put in.",
- mRemoteViews,
- mNotifRemoteViewCache.getCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED));
- }
-
- @Test
- public void testRemoveCachedView() {
- // GIVEN a cache with a cached view.
- mEntryListener.onPendingEntryAdded(mEntry);
- mNotifRemoteViewCache.putCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED, mRemoteViews);
-
- // WHEN we remove the cached view.
- mNotifRemoteViewCache.removeCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED);
-
- // THEN the remote view is not cached.
- assertFalse(mNotifRemoteViewCache.hasCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED));
- }
-
- @Test
- public void testClearCache() {
- // GIVEN a non-empty cache.
- mEntryListener.onPendingEntryAdded(mEntry);
- mNotifRemoteViewCache.putCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED, mRemoteViews);
- mNotifRemoteViewCache.putCachedView(mEntry, FLAG_CONTENT_VIEW_EXPANDED, mRemoteViews);
-
- // WHEN we clear the cache.
- mNotifRemoteViewCache.clearCache(mEntry);
-
- // THEN the cache is empty.
- assertFalse(mNotifRemoteViewCache.hasCachedView(mEntry, FLAG_CONTENT_VIEW_CONTRACTED));
- assertFalse(mNotifRemoteViewCache.hasCachedView(mEntry, FLAG_CONTENT_VIEW_EXPANDED));
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index cb9da6a40cb9..f916fe5ad7fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -22,16 +22,11 @@ import static com.android.systemui.statusbar.notification.row.NotificationRowCon
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
import static org.junit.Assert.assertEquals;
-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.eq;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
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.Context;
@@ -40,17 +35,16 @@ import android.os.Handler;
import android.os.Looper;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
+import android.util.ArrayMap;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RemoteViews;
-import android.widget.TextView;
import androidx.test.filters.SmallTest;
import androidx.test.filters.Suppress;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.InflationTask;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams;
@@ -63,8 +57,6 @@ import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
import java.util.HashMap;
import java.util.concurrent.CountDownLatch;
@@ -81,11 +73,8 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
private Notification.Builder mBuilder;
private ExpandableNotificationRow mRow;
- @Mock private NotifRemoteViewCache mCache;
-
@Before
public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
mBuilder = new Notification.Builder(mContext).setSmallIcon(
R.drawable.ic_person)
.setContentTitle("Title")
@@ -94,9 +83,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
ExpandableNotificationRow row = new NotificationTestHelper(mContext, mDependency).createRow(
mBuilder.build());
mRow = spy(row);
- mNotificationInflater = new NotificationContentInflater(
- mCache,
- mock(NotificationRemoteInputManager.class));
+ mNotificationInflater = new NotificationContentInflater();
}
@Test
@@ -187,9 +174,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
result,
FLAG_CONTENT_VIEW_EXPANDED,
0,
- mock(NotifRemoteViewCache.class),
- mRow.getEntry(),
- mRow,
+ new ArrayMap() /* cachedContentViews */, mRow,
true /* isNewView */, (v, p, r) -> true,
new InflationCallback() {
@Override
@@ -259,71 +244,6 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
NotificationContentInflater.canReapplyRemoteView(mediaView, decoratedMediaView));
}
- @Test
- public void testUsesSameViewWhenCachedPossibleToReuse() throws Exception {
- // GIVEN a cached view.
- RemoteViews contractedRemoteView = mBuilder.createContentView();
- when(mCache.hasCachedView(mRow.getEntry(), FLAG_CONTENT_VIEW_CONTRACTED))
- .thenReturn(true);
- when(mCache.getCachedView(mRow.getEntry(), FLAG_CONTENT_VIEW_CONTRACTED))
- .thenReturn(contractedRemoteView);
-
- // GIVEN existing bound view with same layout id.
- View view = contractedRemoteView.apply(mContext, null /* parent */);
- mRow.getPrivateLayout().setContractedChild(view);
-
- // WHEN inflater inflates
- inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, mRow);
-
- // THEN the view should be re-used
- assertEquals("Binder inflated a new view even though the old one was cached and usable.",
- view, mRow.getPrivateLayout().getContractedChild());
- }
-
- @Test
- public void testInflatesNewViewWhenCachedNotPossibleToReuse() throws Exception {
- // GIVEN a cached remote view.
- RemoteViews contractedRemoteView = mBuilder.createHeadsUpContentView();
- when(mCache.hasCachedView(mRow.getEntry(), FLAG_CONTENT_VIEW_CONTRACTED))
- .thenReturn(true);
- when(mCache.getCachedView(mRow.getEntry(), FLAG_CONTENT_VIEW_CONTRACTED))
- .thenReturn(contractedRemoteView);
-
- // GIVEN existing bound view with different layout id.
- View view = new TextView(mContext);
- mRow.getPrivateLayout().setContractedChild(view);
-
- // WHEN inflater inflates
- inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, mRow);
-
- // THEN the view should be a new view
- assertNotEquals("Binder (somehow) used the same view when inflating.",
- view, mRow.getPrivateLayout().getContractedChild());
- }
-
- @Test
- public void testInflationCachesCreatedRemoteView() throws Exception {
- // WHEN inflater inflates
- inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, mRow);
-
- // THEN inflater informs cache of the new remote view
- verify(mCache).putCachedView(
- eq(mRow.getEntry()),
- eq(FLAG_CONTENT_VIEW_CONTRACTED),
- any());
- }
-
- @Test
- public void testUnbindRemovesCachedRemoteView() {
- // WHEN inflated unbinds content
- mNotificationInflater.unbindContent(mRow.getEntry(), mRow, FLAG_CONTENT_VIEW_HEADS_UP);
-
- // THEN inflated informs cache to remove remote view
- verify(mCache).removeCachedView(
- eq(mRow.getEntry()),
- eq(FLAG_CONTENT_VIEW_HEADS_UP));
- }
-
private static void inflateAndWait(NotificationContentInflater inflater,
@InflationFlag int contentToInflate,
ExpandableNotificationRow row)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
index 675b3efc5a26..84c651368dc9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
@@ -16,20 +16,17 @@
package com.android.systemui.statusbar.notification.row;
-import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
-import android.graphics.drawable.Icon;
import android.util.ArraySet;
import android.view.NotificationHeaderView;
import android.view.View;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index ccc9496368e9..4e27770982e5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -70,6 +70,7 @@ import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -113,6 +114,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
@Mock private DeviceProvisionedController mDeviceProvisionedController;
@Mock private StatusBar mStatusBar;
@Mock private AccessibilityManager mAccessibilityManager;
+ @Mock private HighPriorityProvider mHighPriorityProvider;
@Before
public void setUp() {
@@ -128,7 +130,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
mGutsManager = new NotificationGutsManager(mContext, mVisualStabilityManager,
- () -> mStatusBar, mHandler, mAccessibilityManager);
+ () -> mStatusBar, mHandler, mAccessibilityManager, mHighPriorityProvider);
mGutsManager.setUpWithPresenter(mPresenter, mStackScroller,
mCheckSaveListener, mOnSettingsClickListener);
mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
@@ -391,6 +393,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
.build();
when(row.getIsNonblockable()).thenReturn(false);
+ when(mHighPriorityProvider.isHighPriority(entry)).thenReturn(true);
StatusBarNotification statusBarNotification = entry.getSbn();
mGutsManager.initializeNotificationInfo(row, notificationInfoView);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index 003d80376c40..518b6703391e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -267,8 +267,6 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
ExpandableNotificationRow notifRow = mock(ExpandableNotificationRow.class,
RETURNS_DEEP_STUBS);
when(notifRow.getVisibility()).thenReturn(View.VISIBLE);
- when(notifRow.getEntry().isHighPriority())
- .thenReturn(children[i] == ChildType.HIPRI);
when(notifRow.getEntry().getBucket()).thenReturn(
children[i] == ChildType.HIPRI ? BUCKET_ALERTING : BUCKET_SILENT);
when(notifRow.getParent()).thenReturn(mNssl);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 39f037cfa70d..9bd3914da3da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -71,6 +71,8 @@ import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
+import com.android.systemui.statusbar.notification.collection.NotificationRowBinder;
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.notification.logging.NotifLog;
import com.android.systemui.statusbar.notification.people.PeopleHubSectionFooterViewAdapter;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
@@ -88,6 +90,7 @@ import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.DeviceConfigProxyFake;
+import com.android.systemui.util.leak.LeakDetector;
import org.junit.After;
import org.junit.Before;
@@ -165,10 +168,14 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
mock(NotificationFilter.class),
mock(NotifLog.class),
mock(NotificationSectionsFeatureManager.class),
- mock(PeopleNotificationIdentifier.class)
+ mock(PeopleNotificationIdentifier.class),
+ mock(HighPriorityProvider.class)
),
mock(NotificationEntryManager.KeyguardEnvironment.class),
- mock(FeatureFlags.class));
+ mock(FeatureFlags.class),
+ () -> mock(NotificationRowBinder.class),
+ () -> mRemoteInputManager,
+ mock(LeakDetector.class));
mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
mEntryManager.setUpForTest(mock(NotificationPresenter.class), null, mHeadsUpManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
index 39afbe0a1a23..8f645b6bd3c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
@@ -190,7 +190,7 @@ public class NavigationBarFragmentTest extends SysuiBaseFragmentTest {
mFragments.dispatchResume();
processAllMessages();
- verify(mBroadcastDispatcher).registerReceiver(
+ verify(mBroadcastDispatcher).registerReceiverWithHandler(
any(BroadcastReceiver.class),
any(IntentFilter.class),
any(Handler.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 3da87ea2fb56..560aadb93a14 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -120,9 +120,9 @@ import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.collection.init.NewNotifPipeline;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
-import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
@@ -248,7 +248,6 @@ public class StatusBarTest extends SysuiTestCase {
@Mock private DismissCallbackRegistry mDismissCallbackRegistry;
@Mock private ScreenPinningRequest mScreenPinningRequest;
@Mock private NotificationEntryManager mEntryManager;
- @Mock private NotificationContentInflater mNotificationContentInflater;
@Mock private LockscreenLockIconController mLockscreenLockIconController;
@Mock private StatusBarNotificationActivityStarter.Builder
mStatusBarNotificationActivityStarterBuilder;
@@ -257,6 +256,7 @@ public class StatusBarTest extends SysuiTestCase {
@Mock private KeyguardDismissUtil mKeyguardDismissUtil;
@Mock private ExtensionController mExtensionController;
@Mock private UserInfoControllerImpl mUserInfoControllerImpl;
+ @Mock private NotificationRowBinderImpl mNotificationRowBinder;
private ShadeController mShadeController;
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
private InitController mInitController = new InitController();
@@ -346,7 +346,6 @@ public class StatusBarTest extends SysuiTestCase {
mHeadsUpManager,
mDynamicPrivacyController,
mBypassHeadsUpNotifier,
- true,
() -> mNewNotifPipeline,
new FalsingManagerFake(),
mBroadcastDispatcher,
@@ -358,7 +357,6 @@ public class StatusBarTest extends SysuiTestCase {
mNotificationGutsManager,
notificationLogger,
mEntryManager,
- mNotificationContentInflater,
mNotificationInterruptionStateProvider,
mNotificationViewHierarchyManager,
mKeyguardViewMediator,
@@ -416,6 +414,7 @@ public class StatusBarTest extends SysuiTestCase {
mKeyguardDismissUtil,
mExtensionController,
mUserInfoControllerImpl,
+ mNotificationRowBinder,
mDismissCallbackRegistry);
when(mStatusBarWindowView.findViewById(R.id.lock_icon_container)).thenReturn(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index c4caeb3ac531..9cb06a5d85c1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -180,7 +180,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
protected void setupNetworkController() {
// For now just pretend to be the data sim, so we can test that too.
mSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
- when(mMockTm.isDataCapable()).thenReturn(true);
+ when(mMockTm.isDataConnectionEnabled()).thenReturn(true);
setDefaultSubId(mSubId);
setSubscriptions(mSubId);
mMobileSignalController = mNetworkController.mMobileSignalControllers.get(mSubId);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index 95b055c7d0b5..5a5ef8bd21af 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -121,7 +121,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest {
@Test
public void testNoInternetIcon_withDefaultSub() {
setupNetworkController();
- when(mMockTm.isDataCapable()).thenReturn(false);
+ when(mMockTm.isDataConnectionEnabled()).thenReturn(false);
setupDefaultSignal();
updateDataConnectionState(TelephonyManager.DATA_CONNECTED, 0);
setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
@@ -135,7 +135,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest {
@Test
public void testDataDisabledIcon_withDefaultSub() {
setupNetworkController();
- when(mMockTm.isDataCapable()).thenReturn(false);
+ when(mMockTm.isDataConnectionEnabled()).thenReturn(false);
setupDefaultSignal();
updateDataConnectionState(TelephonyManager.DATA_DISCONNECTED, 0);
setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
@@ -149,7 +149,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest {
@Test
public void testNonDefaultSIM_showsFullSignal_connected() {
setupNetworkController();
- when(mMockTm.isDataCapable()).thenReturn(false);
+ when(mMockTm.isDataConnectionEnabled()).thenReturn(false);
setupDefaultSignal();
setDefaultSubId(mSubId + 1);
updateDataConnectionState(TelephonyManager.DATA_CONNECTED, 0);
@@ -164,7 +164,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest {
@Test
public void testNonDefaultSIM_showsFullSignal_disconnected() {
setupNetworkController();
- when(mMockTm.isDataCapable()).thenReturn(false);
+ when(mMockTm.isDataConnectionEnabled()).thenReturn(false);
setupDefaultSignal();
setDefaultSubId(mSubId + 1);
updateDataConnectionState(TelephonyManager.DATA_DISCONNECTED, 0);
@@ -429,7 +429,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest {
@Test
public void testDataDisabledIcon_UserNotSetup() {
setupNetworkController();
- when(mMockTm.isDataCapable()).thenReturn(false);
+ when(mMockTm.isDataConnectionEnabled()).thenReturn(false);
setupDefaultSignal();
updateDataConnectionState(TelephonyManager.DATA_DISCONNECTED, 0);
setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
@@ -444,7 +444,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest {
@Test
public void testAlwaysShowDataRatIcon() {
setupDefaultSignal();
- when(mMockTm.isDataCapable()).thenReturn(false);
+ when(mMockTm.isDataConnectionEnabled()).thenReturn(false);
updateDataConnectionState(TelephonyManager.DATA_DISCONNECTED,
TelephonyManager.NETWORK_TYPE_GSM);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
index 2854665aedb1..80aa6f6c49bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
@@ -69,7 +69,7 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase {
@Test
public void testRegisteredWithDispatcher() {
- verify(mBroadcastDispatcher).registerReceiver(any(BroadcastReceiver.class),
+ verify(mBroadcastDispatcher).registerReceiverWithHandler(any(BroadcastReceiver.class),
any(IntentFilter.class),
any(Handler.class)); // VolumeDialogControllerImpl does not call with user
}
diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp
index 797b713d8614..d297f3f84189 100644
--- a/packages/Tethering/Android.bp
+++ b/packages/Tethering/Android.bp
@@ -123,4 +123,5 @@ android_app {
use_embedded_native_libs: true,
// The permission configuration *must* be included to ensure security of the device
required: ["NetworkPermissionConfig"],
+ apex_available: ["com.android.tethering"],
}
diff --git a/packages/Tethering/AndroidManifest.xml b/packages/Tethering/AndroidManifest.xml
index 87a8c3f5c68a..e99c2c529bd2 100644
--- a/packages/Tethering/AndroidManifest.xml
+++ b/packages/Tethering/AndroidManifest.xml
@@ -33,6 +33,7 @@
<uses-permission android:name="android.permission.MANAGE_USB" />
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
+ <uses-permission android:name="android.permission.TETHER_PRIVILEGED" />
<uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp
index 5785707cb9c5..264ce440f59f 100644
--- a/packages/Tethering/common/TetheringLib/Android.bp
+++ b/packages/Tethering/common/TetheringLib/Android.bp
@@ -47,6 +47,16 @@ java_library {
libs: [
"android_system_stubs_current",
],
+
+ hostdex: true, // for hiddenapi check
+ visibility: [
+ "//frameworks/base/packages/Tethering:__subpackages__",
+ //TODO(b/147200698) remove below lines when the platform is built with stubs
+ "//frameworks/base",
+ "//frameworks/base/services",
+ "//frameworks/base/services/core",
+ ],
+ apex_available: ["com.android.tethering"],
}
filegroup {
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
index fe3f51700df9..5692a6fc5c80 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
@@ -16,9 +16,12 @@
package com.android.server.connectivity.tethering;
+import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
+import static android.net.ConnectivityManager.TYPE_ETHERNET;
+import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
-import static android.net.ConnectivityManager.TYPE_NONE;
+import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
@@ -35,8 +38,10 @@ import android.net.util.PrefixUtils;
import android.net.util.SharedLog;
import android.os.Handler;
import android.util.Log;
+import android.util.SparseIntArray;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
import com.android.internal.util.StateMachine;
import java.util.HashMap;
@@ -77,11 +82,25 @@ public class UpstreamNetworkMonitor {
public static final int EVENT_ON_LINKPROPERTIES = 2;
public static final int EVENT_ON_LOST = 3;
public static final int NOTIFY_LOCAL_PREFIXES = 10;
+ // This value is used by deprecated preferredUpstreamIfaceTypes selection which is default
+ // disabled.
+ @VisibleForTesting
+ public static final int TYPE_NONE = -1;
private static final int CALLBACK_LISTEN_ALL = 1;
private static final int CALLBACK_DEFAULT_INTERNET = 2;
private static final int CALLBACK_MOBILE_REQUEST = 3;
+ private static final SparseIntArray sLegacyTypeToTransport = new SparseIntArray();
+ static {
+ sLegacyTypeToTransport.put(TYPE_MOBILE, NetworkCapabilities.TRANSPORT_CELLULAR);
+ sLegacyTypeToTransport.put(TYPE_MOBILE_DUN, NetworkCapabilities.TRANSPORT_CELLULAR);
+ sLegacyTypeToTransport.put(TYPE_MOBILE_HIPRI, NetworkCapabilities.TRANSPORT_CELLULAR);
+ sLegacyTypeToTransport.put(TYPE_WIFI, NetworkCapabilities.TRANSPORT_WIFI);
+ sLegacyTypeToTransport.put(TYPE_BLUETOOTH, NetworkCapabilities.TRANSPORT_BLUETOOTH);
+ sLegacyTypeToTransport.put(TYPE_ETHERNET, NetworkCapabilities.TRANSPORT_ETHERNET);
+ }
+
private final Context mContext;
private final SharedLog mLog;
private final StateMachine mTarget;
@@ -202,7 +221,7 @@ public class UpstreamNetworkMonitor {
final int legacyType = mDunRequired ? TYPE_MOBILE_DUN : TYPE_MOBILE_HIPRI;
final NetworkRequest mobileUpstreamRequest = new NetworkRequest.Builder()
- .setCapabilities(ConnectivityManager.networkCapabilitiesForType(legacyType))
+ .setCapabilities(networkCapabilitiesForType(legacyType))
.build();
// The existing default network and DUN callbacks will be notified.
@@ -354,16 +373,6 @@ public class UpstreamNetworkMonitor {
notifyTarget(EVENT_ON_LINKPROPERTIES, network);
}
- private void handleSuspended(Network network) {
- if (!network.equals(mTetheringUpstreamNetwork)) return;
- mLog.log("SUSPENDED current upstream: " + network);
- }
-
- private void handleResumed(Network network) {
- if (!network.equals(mTetheringUpstreamNetwork)) return;
- mLog.log("RESUMED current upstream: " + network);
- }
-
private void handleLost(Network network) {
// There are few TODOs within ConnectivityService's rematching code
// pertaining to spurious onLost() notifications.
@@ -453,20 +462,6 @@ public class UpstreamNetworkMonitor {
}
@Override
- public void onNetworkSuspended(Network network) {
- if (mCallbackType == CALLBACK_LISTEN_ALL) {
- handleSuspended(network);
- }
- }
-
- @Override
- public void onNetworkResumed(Network network) {
- if (mCallbackType == CALLBACK_LISTEN_ALL) {
- handleResumed(network);
- }
- }
-
- @Override
public void onLost(Network network) {
if (mCallbackType == CALLBACK_DEFAULT_INTERNET) {
mDefaultInternetNetwork = null;
@@ -510,7 +505,7 @@ public class UpstreamNetworkMonitor {
for (int type : preferredTypes) {
NetworkCapabilities nc;
try {
- nc = ConnectivityManager.networkCapabilitiesForType(type);
+ nc = networkCapabilitiesForType(type);
} catch (IllegalArgumentException iae) {
Log.e(TAG, "No NetworkCapabilities mapping for legacy type: " + type);
continue;
@@ -572,4 +567,28 @@ public class UpstreamNetworkMonitor {
return null;
}
+
+ /**
+ * Given a legacy type (TYPE_WIFI, ...) returns the corresponding NetworkCapabilities instance.
+ * This function is used for deprecated legacy type and be disabled by default.
+ */
+ @VisibleForTesting
+ public static NetworkCapabilities networkCapabilitiesForType(int type) {
+ final NetworkCapabilities nc = new NetworkCapabilities();
+
+ // Map from type to transports.
+ final int notFound = -1;
+ final int transport = sLegacyTypeToTransport.get(type, notFound);
+ Preconditions.checkArgument(transport != notFound, "unknown legacy type: " + type);
+ nc.addTransportType(transport);
+
+ if (type == TYPE_MOBILE_DUN) {
+ nc.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
+ // DUN is restricted network, see NetworkCapabilities#FORCE_RESTRICTED_CAPABILITIES.
+ nc.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
+ } else {
+ nc.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+ }
+ return nc;
+ }
}
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
index c90abbbedb5f..5ed75bf26f8b 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
@@ -18,13 +18,14 @@ package com.android.server.connectivity.tethering;
import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
-import static android.net.ConnectivityManager.TYPE_NONE;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static com.android.server.connectivity.tethering.UpstreamNetworkMonitor.TYPE_NONE;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -538,13 +539,15 @@ public class UpstreamNetworkMonitorTest {
mUNM.selectPreferredUpstreamType(preferredTypes));
verify(mEntitleMgr, times(1)).maybeRunProvisioning();
}
+
private void assertSatisfiesLegacyType(int legacyType, UpstreamNetworkState ns) {
if (legacyType == TYPE_NONE) {
assertTrue(ns == null);
return;
}
- final NetworkCapabilities nc = ConnectivityManager.networkCapabilitiesForType(legacyType);
+ final NetworkCapabilities nc =
+ UpstreamNetworkMonitor.networkCapabilitiesForType(legacyType);
assertTrue(nc.satisfiedByNetworkCapabilities(ns.networkCapabilities));
}
diff --git a/packages/services/PacProcessor/jni/Android.bp b/packages/services/PacProcessor/jni/Android.bp
index 2a94237f2aed..61f8143e68b5 100644
--- a/packages/services/PacProcessor/jni/Android.bp
+++ b/packages/services/PacProcessor/jni/Android.bp
@@ -37,4 +37,10 @@ cc_library_shared {
"-Wunused",
"-Wunreachable-code",
],
+ sanitize: {
+ cfi: true,
+ diag: {
+ cfi: true,
+ },
+ },
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 6a6e2b2f3467..58d3489a8cc8 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -35,6 +35,7 @@ import android.annotation.Nullable;
import android.app.ActivityOptions;
import android.app.AlertDialog;
import android.app.PendingIntent;
+import android.app.RemoteAction;
import android.appwidget.AppWidgetManagerInternal;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
@@ -148,6 +149,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
// their capabilities are ready.
private static final int WAIT_MOTION_INJECTOR_TIMEOUT_MILLIS = 1000;
+ static final String FUNCTION_REGISTER_SYSTEM_ACTION = "registerSystemAction";
+ static final String FUNCTION_UNREGISTER_SYSTEM_ACTION = "unregisterSystemAction";
private static final String FUNCTION_REGISTER_UI_TEST_AUTOMATION_SERVICE =
"registerUiTestAutomationService";
@@ -253,6 +256,27 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
+ @VisibleForTesting
+ AccessibilityManagerService(
+ Context context,
+ PackageManager packageManager,
+ AccessibilitySecurityPolicy securityPolicy,
+ SystemActionPerformer systemActionPerformer,
+ AccessibilityWindowManager a11yWindowManager,
+ AccessibilityDisplayListener a11yDisplayListener) {
+ mContext = context;
+ mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
+ mMainHandler = new MainHandler(mContext.getMainLooper());
+ mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class);
+ mPackageManager = packageManager;
+ mSecurityPolicy = securityPolicy;
+ mSystemActionPerformer = systemActionPerformer;
+ mA11yWindowManager = a11yWindowManager;
+ mA11yDisplayListener = a11yDisplayListener;
+ init();
+ }
+
/**
* Creates a new instance.
*
@@ -260,21 +284,24 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
public AccessibilityManagerService(Context context) {
mContext = context;
- mPackageManager = mContext.getPackageManager();
- mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
- mSecurityPolicy = new AccessibilitySecurityPolicy(mContext, this);
mMainHandler = new MainHandler(mContext.getMainLooper());
+ mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class);
+ mPackageManager = mContext.getPackageManager();
+ mSecurityPolicy = new AccessibilitySecurityPolicy(mContext, this);
mSystemActionPerformer = new SystemActionPerformer(mContext, mWindowManagerService);
mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler,
mWindowManagerService, this, mSecurityPolicy, this);
mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler);
- mSecurityPolicy.setAccessibilityWindowManager(mA11yWindowManager);
- mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class);
+ init();
+ }
+ private void init() {
+ mSecurityPolicy.setAccessibilityWindowManager(mA11yWindowManager);
registerBroadcastReceivers();
new AccessibilityContentObserver(mMainHandler).register(
- context.getContentResolver());
+ mContext.getContentResolver());
}
@Override
@@ -623,6 +650,30 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
event.recycle();
}
+ /**
+ * This is the implementation of AccessibilityManager system API.
+ * System UI calls into this method through AccessibilityManager system API to register a
+ * system action.
+ */
+ @Override
+ public void registerSystemAction(RemoteAction action, int actionId) {
+ mSecurityPolicy.enforceCallingPermission(Manifest.permission.MANAGE_ACCESSIBILITY,
+ FUNCTION_REGISTER_SYSTEM_ACTION);
+ mSystemActionPerformer.registerSystemAction(actionId, action);
+ }
+
+ /**
+ * This is the implementation of AccessibilityManager system API.
+ * System UI calls into this method through AccessibilityManager system API to unregister a
+ * system action.
+ */
+ @Override
+ public void unregisterSystemAction(int actionId) {
+ mSecurityPolicy.enforceCallingPermission(Manifest.permission.MANAGE_ACCESSIBILITY,
+ FUNCTION_UNREGISTER_SYSTEM_ACTION);
+ mSystemActionPerformer.unregisterSystemAction(actionId);
+ }
+
@Override
public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(int userId) {
synchronized (mLock) {
diff --git a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
index 17549268503e..11dcfefd7e3b 100644
--- a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
+++ b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
@@ -157,8 +157,13 @@ public class SystemActionPerformer {
/**
* This method is called to register a system action. If a system action is already registered
* with the given id, the existing system action will be overwritten.
+ *
+ * This method is supposed to be package internal since this class is meant to be used by
+ * AccessibilityManagerService only. But Mockito has a bug which requiring this to be public
+ * to be mocked.
*/
- void registerSystemAction(int id, RemoteAction action) {
+ @VisibleForTesting
+ public void registerSystemAction(int id, RemoteAction action) {
synchronized (mSystemActionLock) {
mRegisteredSystemActions.put(id, action);
}
@@ -170,8 +175,13 @@ public class SystemActionPerformer {
/**
* This method is called to unregister a system action previously registered through
* registerSystemAction.
+ *
+ * This method is supposed to be package internal since this class is meant to be used by
+ * AccessibilityManagerService only. But Mockito has a bug which requiring this to be public
+ * to be mocked.
*/
- void unregisterSystemAction(int id) {
+ @VisibleForTesting
+ public void unregisterSystemAction(int id) {
synchronized (mSystemActionLock) {
mRegisteredSystemActions.remove(id);
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 95cd8fc47944..f34b5e71ad7b 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -112,7 +112,6 @@ import com.android.server.autofill.ui.PendingUi;
import com.android.server.inputmethod.InputMethodManagerInternal;
import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -311,20 +310,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@NonNull
private final InputMethodManagerInternal mInputMethodManagerInternal;
- @GuardedBy("mLock")
- @Nullable
- private CompletableFuture<InlineSuggestionsRequest> mSuggestionsRequestFuture;
-
- @GuardedBy("mLock")
- @Nullable
- private CompletableFuture<IInlineSuggestionsResponseCallback>
- mInlineSuggestionsResponseCallbackFuture;
-
@Nullable
private InlineSuggestionsRequestCallbackImpl mInlineSuggestionsRequestCallback;
- private static final int INLINE_REQUEST_TIMEOUT_MS = 1000;
-
/**
* Receiver of assist data from the app's {@link Activity}.
*/
@@ -336,7 +324,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
+ "mForAugmentedAutofillOnly: %s", mForAugmentedAutofillOnly);
return;
}
- if (mCurrentViewId == null) {
+ // Keeps to prevent it is cleared on multiple threads.
+ final AutofillId currentViewId = mCurrentViewId;
+ if (currentViewId == null) {
Slog.w(TAG, "No current view id - session might have finished");
return;
}
@@ -410,7 +400,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (mContexts == null) {
mContexts = new ArrayList<>(1);
}
- mContexts.add(new FillContext(requestId, structure, mCurrentViewId));
+ mContexts.add(new FillContext(requestId, structure, currentViewId));
cancelCurrentRequestLocked();
@@ -422,7 +412,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final ArrayList<FillContext> contexts =
mergePreviousSessionLocked(/* forSave= */ false);
- final InlineSuggestionsRequest suggestionsRequest = getInlineSuggestionsRequest();
+ final InlineSuggestionsRequest suggestionsRequest =
+ mInlineSuggestionsRequestCallback != null
+ ? mInlineSuggestionsRequestCallback.getRequest() : null;
request = new FillRequest(requestId, contexts, mClientState, flags,
suggestionsRequest);
@@ -620,13 +612,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
private void maybeRequestInlineSuggestionsRequestThenFillLocked(@NonNull ViewState viewState,
int newState, int flags) {
if (isInlineSuggestionsEnabled()) {
- mSuggestionsRequestFuture = new CompletableFuture<>();
- mInlineSuggestionsResponseCallbackFuture = new CompletableFuture<>();
-
- if (mInlineSuggestionsRequestCallback == null) {
- mInlineSuggestionsRequestCallback = new InlineSuggestionsRequestCallbackImpl(this);
- }
-
+ mInlineSuggestionsRequestCallback = new InlineSuggestionsRequestCallbackImpl();
mInputMethodManagerInternal.onCreateInlineSuggestionsRequest(
mComponentName, mCurrentViewId, mInlineSuggestionsRequestCallback);
}
@@ -636,10 +622,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
private static final class InlineSuggestionsRequestCallbackImpl
extends IInlineSuggestionsRequestCallback.Stub {
- private final WeakReference<Session> mSession;
+ private static final int INLINE_REQUEST_TIMEOUT_MS = 1000;
+
+ private final CompletableFuture<InlineSuggestionsRequest> mRequest;
+ private final CompletableFuture<IInlineSuggestionsResponseCallback> mResponseCallback;
- private InlineSuggestionsRequestCallbackImpl(Session session) {
- mSession = new WeakReference<>(session);
+ private InlineSuggestionsRequestCallbackImpl() {
+ mRequest = new CompletableFuture<>();
+ mResponseCallback = new CompletableFuture<>();
}
@Override
@@ -648,13 +638,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
Log.d(TAG, "inline suggestions request unsupported, "
+ "falling back to regular autofill");
}
- final Session session = mSession.get();
- if (session != null) {
- synchronized (session.mLock) {
- session.mSuggestionsRequestFuture.cancel(true);
- session.mInlineSuggestionsResponseCallbackFuture.cancel(true);
- }
- }
+ mRequest.cancel(true);
+ mResponseCallback.cancel(true);
}
@Override
@@ -663,13 +648,36 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (sDebug) {
Log.d(TAG, "onInlineSuggestionsRequest() received: " + request);
}
- final Session session = mSession.get();
- if (session != null) {
- synchronized (session.mLock) {
- session.mSuggestionsRequestFuture.complete(request);
- session.mInlineSuggestionsResponseCallbackFuture.complete(callback);
- }
+ mRequest.complete(request);
+ mResponseCallback.complete(callback);
+ }
+
+ @Nullable
+ private InlineSuggestionsRequest getRequest() {
+ try {
+ return mRequest.get(INLINE_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (TimeoutException e) {
+ Log.w(TAG, "Exception getting inline suggestions request in time: " + e);
+ } catch (CancellationException e) {
+ Log.w(TAG, "Inline suggestions request cancelled");
+ } catch (InterruptedException | ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ return null;
+ }
+
+ @Nullable
+ private IInlineSuggestionsResponseCallback getResponseCallback() {
+ try {
+ return mResponseCallback.get(INLINE_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (TimeoutException e) {
+ Log.w(TAG, "Exception getting inline suggestions callback in time: " + e);
+ } catch (CancellationException e) {
+ Log.w(TAG, "Inline suggestions callback cancelled");
+ } catch (InterruptedException | ExecutionException e) {
+ throw new RuntimeException(e);
}
+ return null;
}
}
@@ -2678,22 +2686,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
* Returns whether we made a request to show inline suggestions.
*/
private boolean requestShowInlineSuggestions(FillResponse response) {
- IInlineSuggestionsResponseCallback inlineContentCallback = null;
- synchronized (mLock) {
- if (mInlineSuggestionsResponseCallbackFuture != null) {
- try {
- inlineContentCallback = mInlineSuggestionsResponseCallbackFuture.get(
- INLINE_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
- } catch (TimeoutException e) {
- Log.w(TAG, "Exception getting inline suggestions callback in time: " + e);
- } catch (CancellationException e) {
- Log.w(TAG, "Inline suggestions callback cancelled");
- } catch (InterruptedException | ExecutionException e) {
- throw new RuntimeException(e);
- }
- }
- }
-
+ final IInlineSuggestionsResponseCallback inlineContentCallback =
+ mInlineSuggestionsRequestCallback != null
+ ? mInlineSuggestionsRequestCallback.getResponseCallback() : null;
if (inlineContentCallback == null) {
Log.w(TAG, "Session input method callback is not set yet");
return false;
@@ -3015,10 +3010,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final AutofillId focusedId = AutofillId.withoutSession(mCurrentViewId);
- final InlineSuggestionsRequest inlineSuggestionsRequest = getInlineSuggestionsRequest();
+ final InlineSuggestionsRequest inlineSuggestionsRequest =
+ mInlineSuggestionsRequestCallback != null
+ ? mInlineSuggestionsRequestCallback.getRequest() : null;
final IInlineSuggestionsResponseCallback inlineSuggestionsResponseCallback =
- getInlineSuggestionsResponseCallback();
-
+ mInlineSuggestionsRequestCallback != null
+ ? mInlineSuggestionsRequestCallback.getResponseCallback() : null;
remoteService.onRequestAutofillLocked(id, mClient, taskId, mComponentName, focusedId,
currentValue, inlineSuggestionsRequest, inlineSuggestionsResponseCallback);
@@ -3028,40 +3025,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return mAugmentedAutofillDestroyer;
}
- @Nullable
- private InlineSuggestionsRequest getInlineSuggestionsRequest() {
- if (mSuggestionsRequestFuture != null) {
- try {
- return mSuggestionsRequestFuture.get(
- INLINE_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
- } catch (TimeoutException e) {
- Log.w(TAG, "Exception getting inline suggestions request in time: " + e);
- } catch (CancellationException e) {
- Log.w(TAG, "Inline suggestions request cancelled");
- } catch (InterruptedException | ExecutionException e) {
- throw new RuntimeException(e);
- }
- }
- return null;
- }
-
- @Nullable
- private IInlineSuggestionsResponseCallback getInlineSuggestionsResponseCallback() {
- if (mInlineSuggestionsResponseCallbackFuture != null) {
- try {
- return mInlineSuggestionsResponseCallbackFuture.get(
- INLINE_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
- } catch (TimeoutException e) {
- Log.w(TAG, "Exception getting inline suggestions callback in time: " + e);
- } catch (CancellationException e) {
- Log.w(TAG, "Inline suggestions callback cancelled");
- } catch (InterruptedException | ExecutionException e) {
- throw new RuntimeException(e);
- }
- }
- return null;
- }
-
@GuardedBy("mLock")
private void cancelAugmentedAutofillLocked() {
final RemoteAugmentedAutofillService remoteService = mService
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 3bce322a7655..d45a54e0ff28 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -1452,6 +1452,11 @@ public class BackupManagerService extends IBackupManager.Stub {
if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) {
return;
}
+ dumpWithoutCheckingPermission(fd, pw, args);
+ }
+
+ @VisibleForTesting
+ void dumpWithoutCheckingPermission(FileDescriptor fd, PrintWriter pw, String[] args) {
int userId = binderGetCallingUserId();
if (!isUserReadyForBackup(userId)) {
pw.println("Inactive");
@@ -1460,7 +1465,16 @@ public class BackupManagerService extends IBackupManager.Stub {
if (args != null) {
for (String arg : args) {
- if ("users".equals(arg.toLowerCase())) {
+ if ("-h".equals(arg)) {
+ pw.println("'dumpsys backup' optional arguments:");
+ pw.println(" -h : this help text");
+ pw.println(" a[gents] : dump information about defined backup agents");
+ pw.println(" transportclients : dump information about transport clients");
+ pw.println(" transportstats : dump transport statts");
+ pw.println(" users : dump the list of users for which backup service "
+ + "is running");
+ return;
+ } else if ("users".equals(arg.toLowerCase())) {
pw.print(DUMP_RUNNING_USERS_MESSAGE);
for (int i = 0; i < mUserServices.size(); i++) {
pw.print(" " + mUserServices.keyAt(i));
@@ -1471,11 +1485,12 @@ public class BackupManagerService extends IBackupManager.Stub {
}
}
- UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(UserHandle.USER_SYSTEM, "dump()");
-
- if (userBackupManagerService != null) {
- userBackupManagerService.dump(fd, pw, args);
+ for (int i = 0; i < mUserServices.size(); i++) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(mUserServices.keyAt(i), "dump()");
+ if (userBackupManagerService != null) {
+ userBackupManagerService.dump(fd, pw, args);
+ }
}
}
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 064cd060528d..7b95ab526b41 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -3545,14 +3545,7 @@ public class UserBackupManagerService {
try {
if (args != null) {
for (String arg : args) {
- if ("-h".equals(arg)) {
- pw.println("'dumpsys backup' optional arguments:");
- pw.println(" -h : this help text");
- pw.println(" a[gents] : dump information about defined backup agents");
- pw.println(" users : dump the list of users for which backup service "
- + "is running");
- return;
- } else if ("agents".startsWith(arg)) {
+ if ("agents".startsWith(arg)) {
dumpAgents(pw);
return;
} else if ("transportclients".equals(arg.toLowerCase())) {
@@ -3583,8 +3576,10 @@ public class UserBackupManagerService {
}
private void dumpInternal(PrintWriter pw) {
+ // Add prefix for only non-system users so that system user dumpsys is the same as before
+ String userPrefix = mUserId == UserHandle.USER_SYSTEM ? "" : "User " + mUserId + ":";
synchronized (mQueueLock) {
- pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
+ pw.println(userPrefix + "Backup Manager is " + (mEnabled ? "enabled" : "disabled")
+ " / " + (!mSetupComplete ? "not " : "") + "setup complete / "
+ (this.mPendingInits.size() == 0 ? "not " : "") + "pending init");
pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled"));
@@ -3594,13 +3589,13 @@ public class UserBackupManagerService {
+ " (now = " + System.currentTimeMillis() + ')');
pw.println(" next scheduled: " + KeyValueBackupJob.nextScheduled(mUserId));
- pw.println("Transport whitelist:");
+ pw.println(userPrefix + "Transport whitelist:");
for (ComponentName transport : mTransportManager.getTransportWhitelist()) {
pw.print(" ");
pw.println(transport.flattenToShortString());
}
- pw.println("Available transports:");
+ pw.println(userPrefix + "Available transports:");
final String[] transports = listAllTransports();
if (transports != null) {
for (String t : transports) {
@@ -3626,18 +3621,18 @@ public class UserBackupManagerService {
mTransportManager.dumpTransportClients(pw);
- pw.println("Pending init: " + mPendingInits.size());
+ pw.println(userPrefix + "Pending init: " + mPendingInits.size());
for (String s : mPendingInits) {
pw.println(" " + s);
}
- pw.print("Ancestral: ");
+ pw.print(userPrefix + "Ancestral: ");
pw.println(Long.toHexString(mAncestralToken));
- pw.print("Current: ");
+ pw.print(userPrefix + "Current: ");
pw.println(Long.toHexString(mCurrentToken));
int numPackages = mBackupParticipants.size();
- pw.println("Participants:");
+ pw.println(userPrefix + "Participants:");
for (int i = 0; i < numPackages; i++) {
int uid = mBackupParticipants.keyAt(i);
pw.print(" uid: ");
@@ -3648,7 +3643,7 @@ public class UserBackupManagerService {
}
}
- pw.println("Ancestral packages: "
+ pw.println(userPrefix + "Ancestral packages: "
+ (mAncestralPackages == null ? "none" : mAncestralPackages.size()));
if (mAncestralPackages != null) {
for (String pkg : mAncestralPackages) {
@@ -3657,17 +3652,17 @@ public class UserBackupManagerService {
}
Set<String> processedPackages = mProcessedPackagesJournal.getPackagesCopy();
- pw.println("Ever backed up: " + processedPackages.size());
+ pw.println(userPrefix + "Ever backed up: " + processedPackages.size());
for (String pkg : processedPackages) {
pw.println(" " + pkg);
}
- pw.println("Pending key/value backup: " + mPendingBackups.size());
+ pw.println(userPrefix + "Pending key/value backup: " + mPendingBackups.size());
for (BackupRequest req : mPendingBackups.values()) {
pw.println(" " + req);
}
- pw.println("Full backup queue:" + mFullBackupQueue.size());
+ pw.println(userPrefix + "Full backup queue:" + mFullBackupQueue.size());
for (FullBackupEntry entry : mFullBackupQueue) {
pw.print(" ");
pw.print(entry.lastBackup);
diff --git a/services/core/Android.bp b/services/core/Android.bp
index a1f57cb51188..b2fba730fac1 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -165,9 +165,3 @@ prebuilt_etc {
name: "protolog.conf.json.gz",
src: ":services.core.json.gz",
}
-
-platform_compat_config {
- name: "services-core-platform-compat-config",
- src: ":services.core.unboosted",
-}
-
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 312dd46fbc73..76572d35d516 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -811,6 +811,12 @@ public abstract class PackageManagerInternal {
public abstract boolean isApexPackage(String packageName);
/**
+ * Returns list of {@code packageName} of apks inside the given apex.
+ * @param apexPackageName Package name of the apk container of apex
+ */
+ public abstract List<String> getApksInApex(String apexPackageName);
+
+ /**
* Uninstalls given {@code packageName}.
*
* @param packageName apex package to uninstall.
diff --git a/services/core/java/com/android/server/GnssManagerService.java b/services/core/java/com/android/server/GnssManagerService.java
index bbcfdc63f3f1..32cdc41472c9 100644
--- a/services/core/java/com/android/server/GnssManagerService.java
+++ b/services/core/java/com/android/server/GnssManagerService.java
@@ -47,7 +47,6 @@ import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocationManagerServiceUtils.LinkedListener;
import com.android.server.LocationManagerServiceUtils.LinkedListenerBase;
-import com.android.server.location.AbstractLocationProvider;
import com.android.server.location.CallerIdentity;
import com.android.server.location.GnssBatchingProvider;
import com.android.server.location.GnssCapabilitiesProvider;
@@ -116,11 +115,9 @@ public class GnssManagerService {
private final Handler mHandler;
public GnssManagerService(LocationManagerService locationManagerService,
- Context context,
- AbstractLocationProvider.LocationProviderManager gnssProviderManager,
- LocationUsageLogger locationUsageLogger) {
- this(locationManagerService, context, new GnssLocationProvider(context, gnssProviderManager,
- FgThread.getHandler().getLooper()), locationUsageLogger);
+ Context context, LocationUsageLogger locationUsageLogger) {
+ this(locationManagerService, context,
+ new GnssLocationProvider(context, FgThread.getHandler()), locationUsageLogger);
}
// Can use this constructor to inject GnssLocationProvider for testing
diff --git a/services/core/java/com/android/server/GraphicsStatsService.java b/services/core/java/com/android/server/GraphicsStatsService.java
index 70569db5e2d3..5179fa7a6eb5 100644
--- a/services/core/java/com/android/server/GraphicsStatsService.java
+++ b/services/core/java/com/android/server/GraphicsStatsService.java
@@ -38,11 +38,13 @@ import android.view.IGraphicsStats;
import android.view.IGraphicsStatsCallback;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FastPrintWriter;
import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
@@ -78,6 +80,8 @@ public class GraphicsStatsService extends IGraphicsStats.Stub {
private static final int SAVE_BUFFER = 1;
private static final int DELETE_OLD = 2;
+ private static final int AID_STATSD = 1066; // Statsd uid is set to 1066 forever.
+
// This isn't static because we need this to happen after registerNativeMethods, however
// the class is loaded (and thus static ctor happens) before that occurs.
private final int ASHMEM_SIZE = nGetAshmemSize();
@@ -121,6 +125,7 @@ public class GraphicsStatsService extends IGraphicsStats.Stub {
return true;
}
});
+ nativeInit();
}
/**
@@ -186,6 +191,86 @@ public class GraphicsStatsService extends IGraphicsStats.Stub {
return pfd;
}
+ // If lastFullDay is true, pullGraphicsStats returns stats for the last complete day/24h period
+ // that does not include today. If lastFullDay is false, pullGraphicsStats returns stats for the
+ // current day.
+ // This method is invoked from native code only.
+ @SuppressWarnings({"UnusedDeclaration"})
+ private long pullGraphicsStats(boolean lastFullDay) throws RemoteException {
+ int uid = Binder.getCallingUid();
+
+ // DUMP and PACKAGE_USAGE_STATS permissions are required to invoke this method.
+ // TODO: remove exception for statsd daemon after required permissions are granted. statsd
+ // TODO: should have these permissions granted by data/etc/platform.xml, but it does not.
+ if (uid != AID_STATSD) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new FastPrintWriter(sw);
+ if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) {
+ pw.flush();
+ throw new RemoteException(sw.toString());
+ }
+ }
+
+ long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ return pullGraphicsStatsImpl(lastFullDay);
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ private long pullGraphicsStatsImpl(boolean lastFullDay) {
+ long targetDay;
+ if (lastFullDay) {
+ // Get stats from yesterday. Stats stay constant, because the day is over.
+ targetDay = normalizeDate(System.currentTimeMillis() - 86400000).getTimeInMillis();
+ } else {
+ // Get stats from today. Stats may change as more apps are run today.
+ targetDay = normalizeDate(System.currentTimeMillis()).getTimeInMillis();
+ }
+
+ // Find active buffers for targetDay.
+ ArrayList<HistoricalBuffer> buffers;
+ synchronized (mLock) {
+ buffers = new ArrayList<>(mActive.size());
+ for (int i = 0; i < mActive.size(); i++) {
+ ActiveBuffer buffer = mActive.get(i);
+ if (buffer.mInfo.startTime == targetDay) {
+ try {
+ buffers.add(new HistoricalBuffer(buffer));
+ } catch (IOException ex) {
+ // Ignore
+ }
+ }
+ }
+ }
+
+ // Dump active and historic buffers for targetDay in a serialized
+ // GraphicsStatsServiceDumpProto proto.
+ long dump = nCreateDump(-1, true);
+ try {
+ synchronized (mFileAccessLock) {
+ HashSet<File> skipList = dumpActiveLocked(dump, buffers);
+ buffers.clear();
+ String subPath = String.format("%d", targetDay);
+ File dateDir = new File(mGraphicsStatsDir, subPath);
+ if (dateDir.exists()) {
+ for (File pkg : dateDir.listFiles()) {
+ for (File version : pkg.listFiles()) {
+ File data = new File(version, "total");
+ if (skipList.contains(data)) {
+ continue;
+ }
+ nAddToDump(dump, data.getAbsolutePath());
+ }
+ }
+ }
+ }
+ } finally {
+ return nFinishDumpInMemory(dump);
+ }
+ }
+
private ParcelFileDescriptor getPfd(MemoryFile file) {
try {
if (!file.getFileDescriptor().valid()) {
@@ -379,12 +464,21 @@ public class GraphicsStatsService extends IGraphicsStats.Stub {
}
}
+ @Override
+ protected void finalize() throws Throwable {
+ nativeDestructor();
+ }
+
+ private native void nativeInit();
+ private static native void nativeDestructor();
+
private static native int nGetAshmemSize();
private static native long nCreateDump(int outFd, boolean isProto);
private static native void nAddToDump(long dump, String path, String packageName,
long versionCode, long startTime, long endTime, byte[] data);
private static native void nAddToDump(long dump, String path);
private static native void nFinishDump(long dump);
+ private static native long nFinishDumpInMemory(long dump);
private static native void nSaveBuffer(String path, String packageName, long versionCode,
long startTime, long endTime, byte[] data);
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 0fc5340846f2..dc393d1609de 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -23,8 +23,6 @@ import static android.location.LocationManager.NETWORK_PROVIDER;
import static android.location.LocationManager.PASSIVE_PROVIDER;
import static android.os.PowerManager.locationPowerSaveModeToString;
-import static com.android.internal.util.Preconditions.checkState;
-
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -71,10 +69,8 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
-import android.os.UserManager;
import android.os.WorkSource;
import android.os.WorkSource.WorkChain;
-import android.provider.Settings;
import android.stats.location.LocationStatsEnums;
import android.text.TextUtils;
import android.util.EventLog;
@@ -87,11 +83,11 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.server.location.AbstractLocationProvider;
+import com.android.server.location.AbstractLocationProvider.State;
import com.android.server.location.ActivityRecognitionProxy;
import com.android.server.location.CallerIdentity;
import com.android.server.location.GeocoderProxy;
@@ -105,7 +101,9 @@ import com.android.server.location.LocationRequestStatistics.PackageStatistics;
import com.android.server.location.LocationSettingsStore;
import com.android.server.location.LocationUsageLogger;
import com.android.server.location.MockProvider;
+import com.android.server.location.MockableLocationProvider;
import com.android.server.location.PassiveProvider;
+import com.android.server.location.UserInfoStore;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import java.io.ByteArrayOutputStream;
@@ -121,6 +119,8 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
/**
@@ -193,33 +193,31 @@ public class LocationManagerService extends ILocationManager.Stub {
private final Object mLock = new Object();
private final Context mContext;
private final Handler mHandler;
+ private final UserInfoStore mUserInfoStore;
private final LocationSettingsStore mSettingsStore;
private final LocationUsageLogger mLocationUsageLogger;
+ private final PassiveLocationProviderManager mPassiveManager;
+
private AppOpsManager mAppOps;
private PackageManager mPackageManager;
private PowerManager mPowerManager;
private ActivityManager mActivityManager;
- private UserManager mUserManager;
private GeofenceManager mGeofenceManager;
private LocationFudger mLocationFudger;
private GeocoderProxy mGeocodeProvider;
- @Nullable
- private GnssManagerService mGnssManagerService;
- private PassiveProvider mPassiveProvider; // track passive provider for special cases
+ @Nullable private GnssManagerService mGnssManagerService;
+
@GuardedBy("mLock")
private String mExtraLocationControllerPackage;
- private boolean mExtraLocationControllerPackageEnabled;
-
- // list of currently active providers
@GuardedBy("mLock")
- private final ArrayList<LocationProviderManager> mProviders = new ArrayList<>();
+ private boolean mExtraLocationControllerPackageEnabled;
- // list of non-mock providers, so that when mock providers replace real providers, they can be
- // later re-replaced
- @GuardedBy("mLock")
- private final ArrayList<LocationProviderManager> mRealProviders = new ArrayList<>();
+ // @GuardedBy("mLock")
+ // hold lock for write or to prevent write, no lock for read
+ private final CopyOnWriteArrayList<LocationProviderManager> mProviderManagers =
+ new CopyOnWriteArrayList<>();
@GuardedBy("mLock")
private final HashMap<Object, Receiver> mReceivers = new HashMap<>();
@@ -238,10 +236,6 @@ public class LocationManagerService extends ILocationManager.Stub {
private final HashMap<String, Location> mLastLocationCoarseInterval =
new HashMap<>();
- // current active user on the device - other users are denied location data
- private int mCurrentUserId = UserHandle.USER_SYSTEM;
- private int[] mCurrentUserProfiles = new int[]{UserHandle.USER_SYSTEM};
-
@GuardedBy("mLock")
@PowerManager.LocationPowerSaveMode
private int mBatterySaverMode;
@@ -249,9 +243,18 @@ public class LocationManagerService extends ILocationManager.Stub {
private LocationManagerService(Context context) {
mContext = context;
mHandler = FgThread.getHandler();
+ mUserInfoStore = new UserInfoStore(mContext);
mSettingsStore = new LocationSettingsStore(mContext, mHandler);
mLocationUsageLogger = new LocationUsageLogger();
+ // set up passive provider - we do this early because it has no dependencies on system
+ // services or external code that isn't ready yet, and because this allows the variable to
+ // be final. other more complex providers are initialized later, when system services are
+ // ready
+ mPassiveManager = new PassiveLocationProviderManager();
+ mProviderManagers.add(mPassiveManager);
+ mPassiveManager.setRealProvider(new PassiveProvider(mContext));
+
// Let the package manager query which are the default location
// providers as they get certain permissions granted by default.
PermissionManagerServiceInternal permissionManagerInternal = LocalServices.getService(
@@ -267,6 +270,7 @@ public class LocationManagerService extends ILocationManager.Stub {
}
private void onSystemReady() {
+ mUserInfoStore.onSystemReady();
mSettingsStore.onSystemReady();
synchronized (mLock) {
@@ -274,7 +278,6 @@ public class LocationManagerService extends ILocationManager.Stub {
mAppOps = mContext.getSystemService(AppOpsManager.class);
mPowerManager = mContext.getSystemService(PowerManager.class);
mActivityManager = mContext.getSystemService(ActivityManager.class);
- mUserManager = mContext.getSystemService(UserManager.class);
mLocationFudger = new LocationFudger(mContext, mHandler);
mGeofenceManager = new GeofenceManager(mContext, mSettingsStore);
@@ -362,10 +365,13 @@ public class LocationManagerService extends ILocationManager.Stub {
}
}.register(mContext, mHandler.getLooper(), true);
+ mUserInfoStore.addListener((oldUserId, newUserId) -> {
+ synchronized (mLock) {
+ onUserChangedLocked(oldUserId, newUserId);
+ }
+ });
+
IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
- intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
- intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
@@ -378,14 +384,6 @@ public class LocationManagerService extends ILocationManager.Stub {
}
synchronized (mLock) {
switch (action) {
- case Intent.ACTION_USER_SWITCHED:
- onUserChangedLocked(
- intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
- break;
- case Intent.ACTION_MANAGED_PROFILE_ADDED:
- case Intent.ACTION_MANAGED_PROFILE_REMOVED:
- onUserProfilesChangedLocked();
- break;
case Intent.ACTION_SCREEN_ON:
case Intent.ACTION_SCREEN_OFF:
onScreenStateChangedLocked();
@@ -395,11 +393,10 @@ public class LocationManagerService extends ILocationManager.Stub {
}
}, UserHandle.ALL, intentFilter, null, mHandler);
- // switching the user from null to system here performs the bulk of the initialization
+ // switching the user from null to current here performs the bulk of the initialization
// work. the user being changed will cause a reload of all user specific settings, which
// causes initialization, and propagates changes until a steady state is reached
- mCurrentUserId = UserHandle.USER_NULL;
- onUserChangedLocked(ActivityManager.getCurrentUser());
+ onUserChangedLocked(UserHandle.USER_NULL, mUserInfoStore.getCurrentUserId());
}
}
@@ -415,15 +412,15 @@ public class LocationManagerService extends ILocationManager.Stub {
for (Receiver receiver : mReceivers.values()) {
receiver.updateMonitoring(true);
}
- for (LocationProviderManager p : mProviders) {
- applyRequirementsLocked(p);
+ for (LocationProviderManager manager : mProviderManagers) {
+ applyRequirementsLocked(manager);
}
}
@GuardedBy("mLock")
private void onPermissionsChangedLocked() {
- for (LocationProviderManager p : mProviders) {
- applyRequirementsLocked(p);
+ for (LocationProviderManager manager : mProviderManagers) {
+ applyRequirementsLocked(manager);
}
}
@@ -442,16 +439,16 @@ public class LocationManagerService extends ILocationManager.Stub {
mBatterySaverMode = newLocationMode;
- for (LocationProviderManager p : mProviders) {
- applyRequirementsLocked(p);
+ for (LocationProviderManager manager : mProviderManagers) {
+ applyRequirementsLocked(manager);
}
}
@GuardedBy("mLock")
private void onScreenStateChangedLocked() {
if (mBatterySaverMode == PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF) {
- for (LocationProviderManager p : mProviders) {
- applyRequirementsLocked(p);
+ for (LocationProviderManager manager : mProviderManagers) {
+ applyRequirementsLocked(manager);
}
}
}
@@ -466,8 +463,8 @@ public class LocationManagerService extends ILocationManager.Stub {
intent.putExtra(LocationManager.EXTRA_LOCATION_ENABLED, isLocationEnabledForUser(userId));
mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
- for (LocationProviderManager p : mProviders) {
- p.onUseableChangedLocked(userId);
+ for (LocationProviderManager manager : mProviderManagers) {
+ manager.onUseableChangedLocked(userId);
}
}
@@ -521,36 +518,26 @@ public class LocationManagerService extends ILocationManager.Stub {
@GuardedBy("mLock")
private void onBackgroundThrottleIntervalChangedLocked() {
- for (LocationProviderManager provider : mProviders) {
- applyRequirementsLocked(provider);
+ for (LocationProviderManager manager : mProviderManagers) {
+ applyRequirementsLocked(manager);
}
}
@GuardedBy("mLock")
private void onBackgroundThrottleWhitelistChangedLocked() {
- for (LocationProviderManager p : mProviders) {
- applyRequirementsLocked(p);
+ for (LocationProviderManager manager : mProviderManagers) {
+ applyRequirementsLocked(manager);
}
}
@GuardedBy("lock")
private void onIgnoreSettingsWhitelistChangedLocked() {
- for (LocationProviderManager p : mProviders) {
- applyRequirementsLocked(p);
+ for (LocationProviderManager manager : mProviderManagers) {
+ applyRequirementsLocked(manager);
}
}
@GuardedBy("mLock")
- private void onUserProfilesChangedLocked() {
- mCurrentUserProfiles = mUserManager.getProfileIdsWithDisabled(mCurrentUserId);
- }
-
- @GuardedBy("mLock")
- private boolean isCurrentProfileLocked(int userId) {
- return ArrayUtils.contains(mCurrentUserProfiles, userId);
- }
-
- @GuardedBy("mLock")
private void ensureFallbackFusedProviderPresentLocked(String[] pkgs) {
PackageManager pm = mContext.getPackageManager();
String systemPackageName = mContext.getPackageName();
@@ -558,7 +545,7 @@ public class LocationManagerService extends ILocationManager.Stub {
List<ResolveInfo> rInfos = pm.queryIntentServicesAsUser(
new Intent(FUSED_LOCATION_SERVICE_ACTION),
- PackageManager.GET_META_DATA, mCurrentUserId);
+ PackageManager.GET_META_DATA, mUserInfoStore.getCurrentUserId());
for (ResolveInfo rInfo : rInfos) {
String packageName = rInfo.serviceInfo.packageName;
@@ -623,22 +610,11 @@ public class LocationManagerService extends ILocationManager.Stub {
@GuardedBy("mLock")
private void initializeProvidersLocked() {
- // create a passive location provider, which is always enabled
- LocationProviderManager passiveProviderManager = new LocationProviderManager(
- PASSIVE_PROVIDER);
- addProviderLocked(passiveProviderManager);
- mPassiveProvider = new PassiveProvider(mContext, passiveProviderManager);
- passiveProviderManager.attachLocked(mPassiveProvider);
-
if (GnssManagerService.isGnssSupported()) {
- // Create a gps location provider manager
- LocationProviderManager gnssProviderManager = new LocationProviderManager(GPS_PROVIDER);
- mRealProviders.add(gnssProviderManager);
- addProviderLocked(gnssProviderManager);
-
- mGnssManagerService = new GnssManagerService(this, mContext, gnssProviderManager,
- mLocationUsageLogger);
- gnssProviderManager.attachLocked(mGnssManagerService.getGnssLocationProvider());
+ mGnssManagerService = new GnssManagerService(this, mContext, mLocationUsageLogger);
+ LocationProviderManager gnssManager = new LocationProviderManager(GPS_PROVIDER);
+ mProviderManagers.add(gnssManager);
+ gnssManager.setRealProvider(mGnssManagerService.getGnssLocationProvider());
}
/*
@@ -662,37 +638,31 @@ public class LocationManagerService extends ILocationManager.Stub {
ensureFallbackFusedProviderPresentLocked(pkgs);
- // bind to network provider
- LocationProviderManager networkProviderManager = new LocationProviderManager(
- NETWORK_PROVIDER);
LocationProviderProxy networkProvider = LocationProviderProxy.createAndBind(
mContext,
- networkProviderManager,
NETWORK_LOCATION_SERVICE_ACTION,
com.android.internal.R.bool.config_enableNetworkLocationOverlay,
com.android.internal.R.string.config_networkLocationProviderPackageName,
com.android.internal.R.array.config_locationProviderPackageNames);
if (networkProvider != null) {
- mRealProviders.add(networkProviderManager);
- addProviderLocked(networkProviderManager);
- networkProviderManager.attachLocked(networkProvider);
+ LocationProviderManager networkManager = new LocationProviderManager(NETWORK_PROVIDER);
+ mProviderManagers.add(networkManager);
+ networkManager.setRealProvider(networkProvider);
} else {
Slog.w(TAG, "no network location provider found");
}
// bind to fused provider
- LocationProviderManager fusedProviderManager = new LocationProviderManager(FUSED_PROVIDER);
LocationProviderProxy fusedProvider = LocationProviderProxy.createAndBind(
mContext,
- fusedProviderManager,
FUSED_LOCATION_SERVICE_ACTION,
com.android.internal.R.bool.config_enableFusedLocationOverlay,
com.android.internal.R.string.config_fusedLocationProviderPackageName,
com.android.internal.R.array.config_locationProviderPackageNames);
if (fusedProvider != null) {
- mRealProviders.add(fusedProviderManager);
- addProviderLocked(fusedProviderManager);
- fusedProviderManager.attachLocked(fusedProvider);
+ LocationProviderManager fusedManager = new LocationProviderManager(FUSED_PROVIDER);
+ mProviderManagers.add(fusedManager);
+ fusedManager.setRealProvider(fusedProvider);
} else {
Slog.e(TAG, "no fused location provider found",
new IllegalStateException("Location service needs a fused location provider"));
@@ -754,250 +724,200 @@ public class LocationManagerService extends ILocationManager.Stub {
Boolean.parseBoolean(fragments[7]) /* supportsBearing */,
Integer.parseInt(fragments[8]) /* powerRequirement */,
Integer.parseInt(fragments[9]) /* accuracy */);
- LocationProviderManager testProviderManager = new LocationProviderManager(name);
- addProviderLocked(testProviderManager);
- testProviderManager.attachLocked(
- new MockProvider(mContext, testProviderManager, properties));
+ addTestProvider(name, properties, mContext.getOpPackageName());
}
}
@GuardedBy("mLock")
- private void onUserChangedLocked(int userId) {
- if (mCurrentUserId == userId) {
- return;
- }
-
+ private void onUserChangedLocked(int oldUserId, int newUserId) {
if (D) {
- Log.d(TAG, "foreground user is changing to " + userId);
+ Log.d(TAG, "foreground user is changing to " + newUserId);
}
- int oldUserId = userId;
- mCurrentUserId = userId;
- onUserProfilesChangedLocked();
+ for (LocationProviderManager manager : mProviderManagers) {
+ // update LOCATION_PROVIDERS_ALLOWED for best effort backwards compatibility
+ mSettingsStore.setLocationProviderAllowed(manager.getName(),
+ manager.isUseable(newUserId), newUserId);
- // let providers know the current user has changed
- for (LocationProviderManager p : mProviders) {
- p.onUseableChangedLocked(oldUserId);
- p.onUseableChangedLocked(mCurrentUserId);
+ manager.onUseableChangedLocked(oldUserId);
+ manager.onUseableChangedLocked(newUserId);
}
}
/**
* Location provider manager, manages a LocationProvider.
*/
- class LocationProviderManager implements AbstractLocationProvider.LocationProviderManager {
+ class LocationProviderManager implements MockableLocationProvider.Listener {
private final String mName;
- // remember to clear binder identity before invoking any provider operation
- @GuardedBy("mLock")
- @Nullable
- protected AbstractLocationProvider mProvider;
+ // acquiring mLock makes operations on mProvider atomic, but is otherwise unnecessary
+ protected final MockableLocationProvider mProvider;
+ // useable state for parent user ids, no entry implies false. location state is only kept
+ // for parent user ids, the location state for a profile user id is assumed to be the same
+ // as for the parent. if querying this structure, ensure that the user id being used is a
+ // parent id or the results may be incorrect.
@GuardedBy("mLock")
- private SparseArray<Boolean> mUseable; // combined state for each user id
- @GuardedBy("mLock")
- private boolean mEnabled; // state of provider
-
- @GuardedBy("mLock")
- @Nullable
- private ProviderProperties mProperties;
+ private final SparseArray<Boolean> mUseable;
private LocationProviderManager(String name) {
mName = name;
-
- mProvider = null;
mUseable = new SparseArray<>(1);
- mEnabled = false;
- mProperties = null;
- // update LOCATION_PROVIDERS_ALLOWED for best effort backwards compatibility
- Settings.Secure.putStringForUser(
- mContext.getContentResolver(),
- Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
- "-" + mName,
- mCurrentUserId);
- }
+ // initialize last since this lets our reference escape
+ mProvider = new MockableLocationProvider(mContext, mLock, this);
- @GuardedBy("mLock")
- public void attachLocked(AbstractLocationProvider provider) {
- Objects.requireNonNull(provider);
- checkState(mProvider == null);
-
- if (D) {
- Log.d(TAG, mName + " provider attached");
- }
-
- mProvider = provider;
-
- // it would be more correct to call this for all users, but we know this can only
- // affect the current user since providers are disabled for non-current users
- onUseableChangedLocked(mCurrentUserId);
+ // we can assume all users start with unuseable location state since the initial state
+ // of all providers is disabled. no need to initialize mUseable further.
}
public String getName() {
return mName;
}
- @GuardedBy("mLock")
- public List<String> getPackagesLocked() {
- if (mProvider == null) {
- return Collections.emptyList();
- } else {
- // safe to not clear binder context since this doesn't call into the real provider
- return mProvider.getProviderPackages();
- }
+ public boolean hasProvider() {
+ return mProvider.getProvider() != null;
}
- public boolean isMock() {
- return false;
+ public void setRealProvider(AbstractLocationProvider provider) {
+ mProvider.setRealProvider(provider);
}
- @GuardedBy("mLock")
- public boolean isPassiveLocked() {
- return mProvider == mPassiveProvider;
+ public void setMockProvider(@Nullable MockProvider provider) {
+ mProvider.setMockProvider(provider);
+ }
+
+ public Set<String> getPackages() {
+ return mProvider.getState().providerPackageNames;
}
- @GuardedBy("mLock")
@Nullable
- public ProviderProperties getPropertiesLocked() {
- return mProperties;
+ public ProviderProperties getProperties() {
+ return mProvider.getState().properties;
}
- public void setRequest(ProviderRequest request, WorkSource workSource) {
- // move calls going to providers onto a different thread to avoid deadlock
- mHandler.post(() -> {
- synchronized (mLock) {
- if (mProvider != null) {
- mProvider.onSetRequest(request, workSource);
- }
+ public void setMockProviderEnabled(boolean enabled) {
+ synchronized (mLock) {
+ if (!mProvider.isMock()) {
+ throw new IllegalArgumentException(mName + " provider is not a test provider");
}
- });
+
+ mProvider.setMockProviderEnabled(enabled);
+ }
}
- public void sendExtraCommand(String command, Bundle extras) {
- int uid = Binder.getCallingUid();
- int pid = Binder.getCallingPid();
+ public void setMockProviderLocation(Location location) {
+ synchronized (mLock) {
+ if (!mProvider.isMock()) {
+ throw new IllegalArgumentException(mName + " provider is not a test provider");
+ }
- // move calls going to providers onto a different thread to avoid deadlock
- mHandler.post(() -> {
- synchronized (mLock) {
- if (mProvider != null) {
- mProvider.onSendExtraCommand(uid, pid, command, extras);
- }
+ String locationProvider = location.getProvider();
+ if (!TextUtils.isEmpty(locationProvider) && !mName.equals(locationProvider)) {
+ // The location has an explicit provider that is different from the mock
+ // provider name. The caller may be trying to fool us via b/33091107.
+ EventLog.writeEvent(0x534e4554, "33091107", Binder.getCallingUid(),
+ mName + "!=" + locationProvider);
}
- });
- }
- @GuardedBy("mLock")
- public void dumpLocked(FileDescriptor fd, IndentingPrintWriter pw, String[] args) {
- pw.print(mName + " provider");
- if (isMock()) {
- pw.print(" [mock]");
+ mProvider.setMockProviderLocation(location);
}
- pw.println(":");
+ }
- pw.increaseIndent();
+ public List<LocationRequest> getMockProviderRequests() {
+ synchronized (mLock) {
+ if (!mProvider.isMock()) {
+ throw new IllegalArgumentException(mName + " provider is not a test provider");
+ }
- pw.println("useable=" + isUseableLocked(mCurrentUserId));
- if (!isUseableLocked(mCurrentUserId)) {
- pw.println("attached=" + (mProvider != null));
- pw.println("enabled=" + mEnabled);
+ return mProvider.getCurrentRequest().locationRequests;
}
+ }
- pw.println("properties=" + mProperties);
-
- if (mProvider != null) {
- // in order to be consistent with other provider APIs, this should be run on the
- // location thread... but this likely isn't worth it just for dumping info.
- long identity = Binder.clearCallingIdentity();
- try {
- mProvider.dump(fd, pw, args);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
+ public void setRequest(ProviderRequest request) {
+ mProvider.setRequest(request);
+ }
- pw.decreaseIndent();
+ public void sendExtraCommand(int uid, int pid, String command, Bundle extras) {
+ mProvider.sendExtraCommand(uid, pid, command, extras);
}
+ @GuardedBy("mLock")
@Override
public void onReportLocation(Location location) {
- // likelihood of a 0,0 bug is far greater than this being a valid location
- if (!isMock() && location.getLatitude() == 0 && location.getLongitude() == 0) {
- Slog.w(TAG, "blocking 0,0 location from " + mName + " provider");
- return;
+ // don't validate mock locations
+ if (!location.isFromMockProvider()) {
+ if (location.getLatitude() == 0 && location.getLongitude() == 0) {
+ Slog.w(TAG, "blocking 0,0 location from " + mName + " provider");
+ return;
+ }
}
- synchronized (mLock) {
- handleLocationChangedLocked(location, this);
- }
+ handleLocationChangedLocked(location, this);
}
+ @GuardedBy("mLock")
@Override
public void onReportLocation(List<Location> locations) {
if (mGnssManagerService == null) {
return;
}
- synchronized (mLock) {
- LocationProviderManager gpsProvider = getLocationProviderLocked(GPS_PROVIDER);
- if (gpsProvider == null || !gpsProvider.isUseableLocked()) {
- Slog.w(TAG, "reportLocationBatch() called without user permission");
- return;
- }
- mGnssManagerService.onReportLocation(locations);
+ if (!GPS_PROVIDER.equals(mName) || !isUseable()) {
+ Slog.w(TAG, "reportLocationBatch() called without user permission");
+ return;
}
- }
- @Override
- public void onSetEnabled(boolean enabled) {
- synchronized (mLock) {
- if (enabled == mEnabled) {
- return;
- }
-
- if (D) {
- Log.d(TAG, mName + " provider enabled is now " + mEnabled);
- }
-
- mEnabled = enabled;
-
- // it would be more correct to call this for all users, but we know this can only
- // affect the current user since providers are disabled for non-current users
- onUseableChangedLocked(mCurrentUserId);
- }
+ mGnssManagerService.onReportLocation(locations);
}
+ @GuardedBy("mLock")
@Override
- public void onSetProperties(ProviderProperties properties) {
- synchronized (mLock) {
- mProperties = properties;
+ public void onStateChanged(State oldState, State newState) {
+ if (oldState.enabled != newState.enabled) {
+ // it would be more correct to call this for all users, but we know this can
+ // only affect the current user since providers are disabled for non-current
+ // users
+ onUseableChangedLocked(mUserInfoStore.getCurrentUserId());
}
}
- @GuardedBy("mLock")
- public boolean isUseableLocked() {
- return isUseableLocked(mCurrentUserId);
+ public boolean isUseable() {
+ return isUseable(mUserInfoStore.getCurrentUserId());
}
- @GuardedBy("mLock")
- public boolean isUseableLocked(int userId) {
- return mUseable.get(userId, Boolean.FALSE);
+ public boolean isUseable(int userId) {
+ synchronized (mLock) {
+ // normalize user id to always refer to parent since profile state is always the
+ // same as parent state
+ userId = mUserInfoStore.getParentUserId(userId);
+
+ return mUseable.get(userId, Boolean.FALSE);
+ }
}
@GuardedBy("mLock")
public void onUseableChangedLocked(int userId) {
+ if (userId == UserHandle.USER_NULL) {
+ // only used during initialization - we don't care about the null user
+ return;
+ }
+
+ // normalize user id to always refer to parent since profile state is always the same
+ // as parent state
+ userId = mUserInfoStore.getParentUserId(userId);
+
// if any property that contributes to "useability" here changes state, it MUST result
// in a direct or indrect call to onUseableChangedLocked. this allows the provider to
// guarantee that it will always eventually reach the correct state.
- boolean useable = mProvider != null && mProviders.contains(this)
- && isCurrentProfileLocked(userId) && isLocationEnabledForUser(userId)
- && mEnabled;
+ boolean useable = (userId == mUserInfoStore.getCurrentUserId())
+ && mSettingsStore.isLocationEnabled(userId) && mProvider.getState().enabled;
- if (useable == isUseableLocked(userId)) {
+ if (useable == isUseable(userId)) {
return;
}
+
mUseable.put(userId, useable);
if (D) {
@@ -1007,11 +927,7 @@ public class LocationManagerService extends ILocationManager.Stub {
// fused and passive provider never get public updates for legacy reasons
if (!FUSED_PROVIDER.equals(mName) && !PASSIVE_PROVIDER.equals(mName)) {
// update LOCATION_PROVIDERS_ALLOWED for best effort backwards compatibility
- Settings.Secure.putStringForUser(
- mContext.getContentResolver(),
- Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
- (useable ? "+" : "-") + mName,
- userId);
+ mSettingsStore.setLocationProviderAllowed(mName, useable, userId);
Intent intent = new Intent(LocationManager.PROVIDERS_CHANGED_ACTION);
intent.putExtra(LocationManager.EXTRA_PROVIDER_NAME, mName);
@@ -1029,55 +945,64 @@ public class LocationManagerService extends ILocationManager.Stub {
updateProviderUseableLocked(this);
}
- }
- private class MockLocationProvider extends LocationProviderManager {
+ public void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) {
+ synchronized (mLock) {
+ pw.print(mName + " provider");
+ if (mProvider.isMock()) {
+ pw.print(" [mock]");
+ }
+ pw.println(":");
+
+ pw.increaseIndent();
- private ProviderRequest mCurrentRequest;
+ boolean useable = isUseable();
+ pw.println("useable=" + useable);
+ if (!useable) {
+ pw.println("enabled=" + mProvider.getState().enabled);
+ }
- private MockLocationProvider(String name) {
- super(name);
+ pw.println("properties=" + mProvider.getState().properties);
+ }
+
+ mProvider.dump(fd, pw, args);
+
+ pw.decreaseIndent();
}
+ }
- @Override
- public void attachLocked(AbstractLocationProvider provider) {
- checkState(provider instanceof MockProvider);
- super.attachLocked(provider);
+ class PassiveLocationProviderManager extends LocationProviderManager {
+
+ private PassiveLocationProviderManager() {
+ super(PASSIVE_PROVIDER);
}
- public boolean isMock() {
- return true;
+ @Override
+ public void setRealProvider(AbstractLocationProvider provider) {
+ Preconditions.checkArgument(provider instanceof PassiveProvider);
+ super.setRealProvider(provider);
}
- @GuardedBy("mLock")
- public void setEnabledLocked(boolean enabled) {
- if (mProvider != null) {
- long identity = Binder.clearCallingIdentity();
- try {
- ((MockProvider) mProvider).setEnabled(enabled);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ @Override
+ public void setMockProvider(@Nullable MockProvider provider) {
+ if (provider != null) {
+ throw new IllegalArgumentException("Cannot mock the passive provider");
}
}
- @GuardedBy("mLock")
- public void setLocationLocked(Location location) {
- if (mProvider != null) {
+ public void updateLocation(Location location) {
+ synchronized (mLock) {
+ PassiveProvider passiveProvider = (PassiveProvider) mProvider.getProvider();
+ Preconditions.checkState(passiveProvider != null);
+
long identity = Binder.clearCallingIdentity();
try {
- ((MockProvider) mProvider).setLocation(location);
+ passiveProvider.updateLocation(location);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
-
- @Override
- public void setRequest(ProviderRequest request, WorkSource workSource) {
- super.setRequest(request, workSource);
- mCurrentRequest = request;
- }
}
/**
@@ -1181,17 +1106,17 @@ public class LocationManagerService extends ILocationManager.Stub {
// See if receiver has any enabled update records. Also note if any update records
// are high power (has a high power provider with an interval under a threshold).
for (UpdateRecord updateRecord : mUpdateRecords.values()) {
- LocationProviderManager provider = getLocationProviderLocked(
+ LocationProviderManager manager = getLocationProviderManager(
updateRecord.mProvider);
- if (provider == null) {
+ if (manager == null) {
continue;
}
- if (!provider.isUseableLocked() && !isSettingsExemptLocked(updateRecord)) {
+ if (!manager.isUseable() && !isSettingsExemptLocked(updateRecord)) {
continue;
}
requestingLocation = true;
- ProviderProperties properties = provider.getPropertiesLocked();
+ ProviderProperties properties = manager.getProperties();
if (properties != null
&& properties.mPowerRequirement == Criteria.POWER_HIGH
&& updateRecord.mRequest.getInterval() < HIGH_POWER_INTERVAL_MS) {
@@ -1432,7 +1357,7 @@ public class LocationManagerService extends ILocationManager.Stub {
String featureId, String listenerIdentifier) {
Objects.requireNonNull(listenerIdentifier);
- return mGnssManagerService == null ? false : mGnssManagerService.addGnssBatchingCallback(
+ return mGnssManagerService != null && mGnssManagerService.addGnssBatchingCallback(
callback, packageName, featureId, listenerIdentifier);
}
@@ -1443,7 +1368,7 @@ public class LocationManagerService extends ILocationManager.Stub {
@Override
public boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName) {
- return mGnssManagerService == null ? false : mGnssManagerService.startGnssBatch(periodNanos,
+ return mGnssManagerService != null && mGnssManagerService.startGnssBatch(periodNanos,
wakeOnFifoFull, packageName);
}
@@ -1454,35 +1379,14 @@ public class LocationManagerService extends ILocationManager.Stub {
@Override
public boolean stopGnssBatch() {
- return mGnssManagerService == null ? false : mGnssManagerService.stopGnssBatch();
- }
-
- @GuardedBy("mLock")
- private void addProviderLocked(LocationProviderManager provider) {
- Preconditions.checkState(getLocationProviderLocked(provider.getName()) == null);
-
- mProviders.add(provider);
-
- // it would be more correct to call this for all users, but we know this can only
- // affect the current user since providers are disabled for non-current users
- provider.onUseableChangedLocked(mCurrentUserId);
+ return mGnssManagerService != null && mGnssManagerService.stopGnssBatch();
}
- @GuardedBy("mLock")
- private void removeProviderLocked(LocationProviderManager provider) {
- if (mProviders.remove(provider)) {
- // it would be more correct to call this for all users, but we know this can only
- // affect the current user since providers are disabled for non-current users
- provider.onUseableChangedLocked(mCurrentUserId);
- }
- }
-
- @GuardedBy("mLock")
@Nullable
- private LocationProviderManager getLocationProviderLocked(String providerName) {
- for (LocationProviderManager provider : mProviders) {
- if (providerName.equals(provider.getName())) {
- return provider;
+ private LocationProviderManager getLocationProviderManager(String providerName) {
+ for (LocationProviderManager manager : mProviderManagers) {
+ if (providerName.equals(manager.getName())) {
+ return manager;
}
}
@@ -1531,12 +1435,12 @@ public class LocationManagerService extends ILocationManager.Stub {
// network and fused providers are ok with COARSE or FINE
return RESOLUTION_LEVEL_COARSE;
} else {
- for (LocationProviderManager lp : mProviders) {
+ for (LocationProviderManager lp : mProviderManagers) {
if (!lp.getName().equals(provider)) {
continue;
}
- ProviderProperties properties = lp.getPropertiesLocked();
+ ProviderProperties properties = lp.getProperties();
if (properties != null) {
if (properties.mRequiresSatellite) {
// provider requiring satellites require FINE permission
@@ -1587,11 +1491,9 @@ public class LocationManagerService extends ILocationManager.Stub {
case RESOLUTION_LEVEL_COARSE:
return AppOpsManager.OPSTR_COARSE_LOCATION;
case RESOLUTION_LEVEL_FINE:
- return AppOpsManager.OPSTR_FINE_LOCATION;
+ // fall through
case RESOLUTION_LEVEL_NONE:
- // The client is not allowed to get any location, so both FINE and COARSE ops will
- // be denied. Pick the most restrictive one to be safe.
- return AppOpsManager.OPSTR_FINE_LOCATION;
+ // fall through
default:
// Use the most restrictive ops if not sure.
return AppOpsManager.OPSTR_FINE_LOCATION;
@@ -1629,17 +1531,14 @@ public class LocationManagerService extends ILocationManager.Stub {
*/
@Override
public List<String> getAllProviders() {
- synchronized (mLock) {
- ArrayList<String> providers = new ArrayList<>(mProviders.size());
- for (LocationProviderManager provider : mProviders) {
- String name = provider.getName();
- if (FUSED_PROVIDER.equals(name)) {
- continue;
- }
- providers.add(name);
+ ArrayList<String> providers = new ArrayList<>(mProviderManagers.size());
+ for (LocationProviderManager manager : mProviderManagers) {
+ if (FUSED_PROVIDER.equals(manager.getName())) {
+ continue;
}
- return providers;
+ providers.add(manager.getName());
}
+ return providers;
}
/**
@@ -1651,21 +1550,21 @@ public class LocationManagerService extends ILocationManager.Stub {
public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
int allowedResolutionLevel = getCallerAllowedResolutionLevel();
synchronized (mLock) {
- ArrayList<String> providers = new ArrayList<>(mProviders.size());
- for (LocationProviderManager provider : mProviders) {
- String name = provider.getName();
+ ArrayList<String> providers = new ArrayList<>(mProviderManagers.size());
+ for (LocationProviderManager manager : mProviderManagers) {
+ String name = manager.getName();
if (FUSED_PROVIDER.equals(name)) {
continue;
}
if (allowedResolutionLevel < getMinimumResolutionLevelForProviderUseLocked(name)) {
continue;
}
- if (enabledOnly && !provider.isUseableLocked()) {
+ if (enabledOnly && !manager.isUseable()) {
continue;
}
if (criteria != null
&& !android.location.LocationProvider.propertiesMeetCriteria(
- name, provider.getPropertiesLocked(), criteria)) {
+ name, manager.getProperties(), criteria)) {
continue;
}
providers.add(name);
@@ -1702,15 +1601,15 @@ public class LocationManagerService extends ILocationManager.Stub {
}
@GuardedBy("mLock")
- private void updateProviderUseableLocked(LocationProviderManager provider) {
- boolean useable = provider.isUseableLocked();
+ private void updateProviderUseableLocked(LocationProviderManager manager) {
+ boolean useable = manager.isUseable();
ArrayList<Receiver> deadReceivers = null;
- ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider.getName());
+ ArrayList<UpdateRecord> records = mRecordsByProvider.get(manager.getName());
if (records != null) {
for (UpdateRecord record : records) {
- if (!isCurrentProfileLocked(
+ if (!mUserInfoStore.isCurrentUserOrProfile(
UserHandle.getUserId(record.mReceiver.mCallerIdentity.mUid))) {
continue;
}
@@ -1721,7 +1620,7 @@ public class LocationManagerService extends ILocationManager.Stub {
}
// Sends a notification message to the receiver
- if (!record.mReceiver.callProviderEnabledLocked(provider.getName(), useable)) {
+ if (!record.mReceiver.callProviderEnabledLocked(manager.getName(), useable)) {
if (deadReceivers == null) {
deadReceivers = new ArrayList<>();
}
@@ -1736,26 +1635,25 @@ public class LocationManagerService extends ILocationManager.Stub {
}
}
- applyRequirementsLocked(provider);
+ applyRequirementsLocked(manager);
}
@GuardedBy("mLock")
private void applyRequirementsLocked(String providerName) {
- LocationProviderManager provider = getLocationProviderLocked(providerName);
- if (provider != null) {
- applyRequirementsLocked(provider);
+ LocationProviderManager manager = getLocationProviderManager(providerName);
+ if (manager != null) {
+ applyRequirementsLocked(manager);
}
}
@GuardedBy("mLock")
- private void applyRequirementsLocked(LocationProviderManager provider) {
- ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider.getName());
- WorkSource worksource = new WorkSource();
- ProviderRequest providerRequest = new ProviderRequest();
+ private void applyRequirementsLocked(LocationProviderManager manager) {
+ ArrayList<UpdateRecord> records = mRecordsByProvider.get(manager.getName());
+ ProviderRequest.Builder providerRequest = new ProviderRequest.Builder();
// if provider is not active, it should not respond to requests
- if (mProviders.contains(provider) && records != null && !records.isEmpty()) {
+ if (mProviderManagers.contains(manager) && records != null && !records.isEmpty()) {
long backgroundThrottleInterval;
long identity = Binder.clearCallingIdentity();
@@ -1765,6 +1663,8 @@ public class LocationManagerService extends ILocationManager.Stub {
Binder.restoreCallingIdentity(identity);
}
+ ArrayList<LocationRequest> requests = new ArrayList<>(records.size());
+
final boolean isForegroundOnlyMode =
mBatterySaverMode == PowerManager.LOCATION_MODE_FOREGROUND_ONLY;
final boolean shouldThrottleRequests =
@@ -1772,9 +1672,9 @@ public class LocationManagerService extends ILocationManager.Stub {
== PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF
&& !mPowerManager.isInteractive();
// initialize the low power mode to true and set to false if any of the records requires
- providerRequest.lowPowerMode = true;
+ providerRequest.setLowPowerMode(true);
for (UpdateRecord record : records) {
- if (!isCurrentProfileLocked(
+ if (!mUserInfoStore.isCurrentUserOrProfile(
UserHandle.getUserId(record.mReceiver.mCallerIdentity.mUid))) {
continue;
}
@@ -1787,10 +1687,10 @@ public class LocationManagerService extends ILocationManager.Stub {
}
final boolean isBatterySaverDisablingLocation = shouldThrottleRequests
|| (isForegroundOnlyMode && !record.mIsForegroundUid);
- if (!provider.isUseableLocked() || isBatterySaverDisablingLocation) {
+ if (!manager.isUseable() || isBatterySaverDisablingLocation) {
if (isSettingsExemptLocked(record)) {
- providerRequest.locationSettingsIgnored = true;
- providerRequest.lowPowerMode = false;
+ providerRequest.setLocationSettingsIgnored(true);
+ providerRequest.setLowPowerMode(false);
} else {
continue;
}
@@ -1801,7 +1701,7 @@ public class LocationManagerService extends ILocationManager.Stub {
// if we're forcing location, don't apply any throttling
- if (!providerRequest.locationSettingsIgnored && !isThrottlingExemptLocked(
+ if (!providerRequest.isLocationSettingsIgnored() && !isThrottlingExemptLocked(
record.mReceiver.mCallerIdentity)) {
if (!record.mIsForegroundUid) {
interval = Math.max(interval, backgroundThrottleInterval);
@@ -1813,42 +1713,44 @@ public class LocationManagerService extends ILocationManager.Stub {
}
record.mRequest = locationRequest;
- providerRequest.locationRequests.add(locationRequest);
+ requests.add(locationRequest);
if (!locationRequest.isLowPowerMode()) {
- providerRequest.lowPowerMode = false;
+ providerRequest.setLowPowerMode(false);
}
- if (interval < providerRequest.interval) {
- providerRequest.reportLocation = true;
- providerRequest.interval = interval;
+ if (interval < providerRequest.getInterval()) {
+ providerRequest.setInterval(interval);
}
}
- if (providerRequest.reportLocation) {
+ providerRequest.setLocationRequests(requests);
+
+ if (providerRequest.getInterval() < Long.MAX_VALUE) {
// calculate who to blame for power
// This is somewhat arbitrary. We pick a threshold interval
// that is slightly higher that the minimum interval, and
// spread the blame across all applications with a request
// under that threshold.
- long thresholdInterval = (providerRequest.interval + 1000) * 3 / 2;
+ // TODO: overflow
+ long thresholdInterval = (providerRequest.getInterval() + 1000) * 3 / 2;
for (UpdateRecord record : records) {
- if (isCurrentProfileLocked(
+ if (mUserInfoStore.isCurrentUserOrProfile(
UserHandle.getUserId(record.mReceiver.mCallerIdentity.mUid))) {
LocationRequest locationRequest = record.mRequest;
// Don't assign battery blame for update records whose
// client has no permission to receive location data.
- if (!providerRequest.locationRequests.contains(locationRequest)) {
+ if (!providerRequest.getLocationRequests().contains(locationRequest)) {
continue;
}
if (locationRequest.getInterval() <= thresholdInterval) {
if (record.mReceiver.mWorkSource != null
&& isValidWorkSource(record.mReceiver.mWorkSource)) {
- worksource.add(record.mReceiver.mWorkSource);
+ providerRequest.getWorkSource().add(record.mReceiver.mWorkSource);
} else {
// Assign blame to caller if there's no WorkSource associated with
// the request or if it's invalid.
- worksource.add(
+ providerRequest.getWorkSource().add(
record.mReceiver.mCallerIdentity.mUid,
record.mReceiver.mCallerIdentity.mPackageName);
}
@@ -1858,7 +1760,7 @@ public class LocationManagerService extends ILocationManager.Stub {
}
}
- provider.setRequest(providerRequest, worksource);
+ manager.setRequest(providerRequest.build());
}
/**
@@ -2198,8 +2100,8 @@ public class LocationManagerService extends ILocationManager.Stub {
throw new IllegalArgumentException("provider name must not be null");
}
- LocationProviderManager provider = getLocationProviderLocked(name);
- if (provider == null) {
+ LocationProviderManager manager = getLocationProviderManager(name);
+ if (manager == null) {
throw new IllegalArgumentException("provider doesn't exist: " + name);
}
@@ -2217,7 +2119,7 @@ public class LocationManagerService extends ILocationManager.Stub {
oldRecord.disposeLocked(false);
}
- if (!provider.isUseableLocked() && !isSettingsExemptLocked(record)) {
+ if (!manager.isUseable() && !isSettingsExemptLocked(record)) {
// Notify the listener that updates are currently disabled - but only if the request
// does not ignore location settings
receiver.callProviderEnabledLocked(name, false);
@@ -2320,16 +2222,16 @@ public class LocationManagerService extends ILocationManager.Stub {
// or use the fused provider
String name = request.getProvider();
if (name == null) name = LocationManager.FUSED_PROVIDER;
- LocationProviderManager provider = getLocationProviderLocked(name);
- if (provider == null) return null;
+ LocationProviderManager manager = getLocationProviderManager(name);
+ if (manager == null) return null;
// only the current user or location providers may get location this way
- if (!isCurrentProfileLocked(UserHandle.getUserId(uid)) && !isProviderPackage(
- packageName)) {
+ if (!mUserInfoStore.isCurrentUserOrProfile(UserHandle.getUserId(uid))
+ && !isProviderPackage(packageName)) {
return null;
}
- if (!provider.isUseableLocked()) {
+ if (!manager.isUseable()) {
return null;
}
@@ -2450,19 +2352,19 @@ public class LocationManagerService extends ILocationManager.Stub {
"Access Fine Location permission not granted to inject Location");
synchronized (mLock) {
- LocationProviderManager provider = getLocationProviderLocked(location.getProvider());
- if (provider == null || !provider.isUseableLocked()) {
+ LocationProviderManager manager = getLocationProviderManager(location.getProvider());
+ if (manager == null || !manager.isUseable()) {
return false;
}
// NOTE: If last location is already available, location is not injected. If
// provider's normal source (like a GPS chipset) have already provided an output
// there is no need to inject this location.
- if (mLastLocation.get(provider.getName()) != null) {
+ if (mLastLocation.get(manager.getName()) != null) {
return false;
}
- updateLastLocationLocked(location, provider.getName());
+ updateLastLocationLocked(location, manager.getName());
return true;
}
}
@@ -2511,7 +2413,7 @@ public class LocationManagerService extends ILocationManager.Stub {
packageName,
request,
/* hasListener= */ false,
- intent != null,
+ true,
geofence,
mActivityManager.getPackageImportance(packageName));
}
@@ -2542,7 +2444,7 @@ public class LocationManagerService extends ILocationManager.Stub {
packageName,
/* LocationRequest= */ null,
/* hasListener= */ false,
- intent != null,
+ true,
geofence,
mActivityManager.getPackageImportance(packageName));
}
@@ -2555,7 +2457,7 @@ public class LocationManagerService extends ILocationManager.Stub {
@Override
public boolean registerGnssStatusCallback(IGnssStatusListener listener, String packageName,
String featureId) {
- return mGnssManagerService == null ? false : mGnssManagerService.registerGnssStatusCallback(
+ return mGnssManagerService != null && mGnssManagerService.registerGnssStatusCallback(
listener, packageName, featureId);
}
@@ -2569,9 +2471,8 @@ public class LocationManagerService extends ILocationManager.Stub {
String packageName, String featureId, String listenerIdentifier) {
Objects.requireNonNull(listenerIdentifier);
- return mGnssManagerService == null ? false
- : mGnssManagerService.addGnssMeasurementsListener(listener, packageName, featureId,
- listenerIdentifier);
+ return mGnssManagerService != null && mGnssManagerService.addGnssMeasurementsListener(
+ listener, packageName, featureId, listenerIdentifier);
}
@Override
@@ -2586,8 +2487,8 @@ public class LocationManagerService extends ILocationManager.Stub {
public void injectGnssMeasurementCorrections(
GnssMeasurementCorrections measurementCorrections, String packageName) {
if (mGnssManagerService != null) {
- mGnssManagerService.injectGnssMeasurementCorrections(
- measurementCorrections, packageName);
+ mGnssManagerService.injectGnssMeasurementCorrections(measurementCorrections,
+ packageName);
}
}
@@ -2602,9 +2503,8 @@ public class LocationManagerService extends ILocationManager.Stub {
String packageName, String featureId, String listenerIdentifier) {
Objects.requireNonNull(listenerIdentifier);
- return mGnssManagerService == null ? false
- : mGnssManagerService.addGnssNavigationMessageListener(listener, packageName,
- featureId, listenerIdentifier);
+ return mGnssManagerService != null && mGnssManagerService.addGnssNavigationMessageListener(
+ listener, packageName, featureId, listenerIdentifier);
}
@Override
@@ -2634,9 +2534,10 @@ public class LocationManagerService extends ILocationManager.Stub {
LocationStatsEnums.API_SEND_EXTRA_COMMAND,
providerName);
- LocationProviderManager provider = getLocationProviderLocked(providerName);
- if (provider != null) {
- provider.sendExtraCommand(command, extras);
+ LocationProviderManager manager = getLocationProviderManager(providerName);
+ if (manager != null) {
+ manager.sendExtraCommand(Binder.getCallingUid(), Binder.getCallingPid(), command,
+ extras);
}
mLocationUsageLogger.logLocationApiUsage(
@@ -2650,43 +2551,37 @@ public class LocationManagerService extends ILocationManager.Stub {
@Override
public boolean sendNiResponse(int notifId, int userResponse) {
- return mGnssManagerService == null ? false : mGnssManagerService.sendNiResponse(notifId,
+ return mGnssManagerService != null && mGnssManagerService.sendNiResponse(notifId,
userResponse);
}
@Override
public ProviderProperties getProviderProperties(String providerName) {
- synchronized (mLock) {
- LocationProviderManager provider = getLocationProviderLocked(providerName);
- if (provider == null) {
- return null;
- }
- return provider.getPropertiesLocked();
+ LocationProviderManager manager = getLocationProviderManager(providerName);
+ if (manager == null) {
+ return null;
}
+ return manager.getProperties();
}
@Override
public boolean isProviderPackage(String packageName) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_DEVICE_CONFIG,
Manifest.permission.READ_DEVICE_CONFIG + " permission required");
- synchronized (mLock) {
- for (LocationProviderManager provider : mProviders) {
- if (provider.getPackagesLocked().contains(packageName)) {
- return true;
- }
+ for (LocationProviderManager manager : mProviderManagers) {
+ if (manager.getPackages().contains(packageName)) {
+ return true;
}
- return false;
}
+ return false;
}
@Override
public List<String> getProviderPackages(String providerName) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_DEVICE_CONFIG,
Manifest.permission.READ_DEVICE_CONFIG + " permission required");
- synchronized (mLock) {
- LocationProviderManager provider = getLocationProviderLocked(providerName);
- return provider == null ? Collections.emptyList() : provider.getPackagesLocked();
- }
+ LocationProviderManager manager = getLocationProviderManager(providerName);
+ return manager == null ? Collections.emptyList() : new ArrayList<>(manager.getPackages());
}
@Override
@@ -2753,8 +2648,8 @@ public class LocationManagerService extends ILocationManager.Stub {
if (FUSED_PROVIDER.equals(providerName)) return false;
synchronized (mLock) {
- LocationProviderManager provider = getLocationProviderLocked(providerName);
- return provider != null && provider.isUseableLocked(userId);
+ LocationProviderManager manager = getLocationProviderManager(providerName);
+ return manager != null && manager.isUseable(userId);
}
}
@@ -2792,37 +2687,39 @@ public class LocationManagerService extends ILocationManager.Stub {
}
@GuardedBy("mLock")
- private void handleLocationChangedLocked(Location location, LocationProviderManager provider) {
- if (!mProviders.contains(provider)) {
+ private void handleLocationChangedLocked(Location location, LocationProviderManager manager) {
+ if (!mProviderManagers.contains(manager)) {
+ Log.w(TAG, "received location from unknown provider: " + manager.getName());
return;
}
if (!location.isComplete()) {
- Log.w(TAG, "Dropping incomplete location: " + location);
+ Log.w(TAG, "dropping incomplete location from " + manager.getName() + " provider: "
+ + location);
return;
}
- // only notify passive provider and update last location for locations that come from
- // useable providers
- if (provider.isUseableLocked()) {
- if (!provider.isPassiveLocked()) {
- mPassiveProvider.updateLocation(location);
- }
+ // notify passive provider
+ if (manager != mPassiveManager) {
+ mPassiveManager.updateLocation(new Location(location));
}
if (D) Log.d(TAG, "incoming location: " + location);
long now = SystemClock.elapsedRealtime();
- if (provider.isUseableLocked()) {
- updateLastLocationLocked(location, provider.getName());
+
+
+ // only update last location for locations that come from useable providers
+ if (manager.isUseable()) {
+ updateLastLocationLocked(location, manager.getName());
}
// Update last known coarse interval location if enough time has passed.
Location lastLocationCoarseInterval = mLastLocationCoarseInterval.get(
- provider.getName());
+ manager.getName());
if (lastLocationCoarseInterval == null) {
lastLocationCoarseInterval = new Location(location);
- if (provider.isUseableLocked()) {
- mLastLocationCoarseInterval.put(provider.getName(), lastLocationCoarseInterval);
+ if (manager.isUseable()) {
+ mLastLocationCoarseInterval.put(manager.getName(), lastLocationCoarseInterval);
}
}
long timeDeltaMs = TimeUnit.NANOSECONDS.toMillis(location.getElapsedRealtimeNanos()
@@ -2837,7 +2734,7 @@ public class LocationManagerService extends ILocationManager.Stub {
lastLocationCoarseInterval.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
// Skip if there are no UpdateRecords for this provider.
- ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider.getName());
+ ArrayList<UpdateRecord> records = mRecordsByProvider.get(manager.getName());
if (records == null || records.size() == 0) return;
// Fetch coarse location
@@ -2854,17 +2751,16 @@ public class LocationManagerService extends ILocationManager.Stub {
Receiver receiver = r.mReceiver;
boolean receiverDead = false;
- if (!provider.isUseableLocked() && !isSettingsExemptLocked(r)) {
+ if (!manager.isUseable() && !isSettingsExemptLocked(r)) {
continue;
}
int receiverUserId = UserHandle.getUserId(receiver.mCallerIdentity.mUid);
- if (!isCurrentProfileLocked(receiverUserId)
+ if (!mUserInfoStore.isCurrentUserOrProfile(receiverUserId)
&& !isProviderPackage(receiver.mCallerIdentity.mPackageName)) {
if (D) {
Log.d(TAG, "skipping loc update for background user " + receiverUserId +
- " (current user: " + mCurrentUserId + ", app: " +
- receiver.mCallerIdentity.mPackageName + ")");
+ " (app: " + receiver.mCallerIdentity.mPackageName + ")");
}
continue;
}
@@ -2949,7 +2845,7 @@ public class LocationManagerService extends ILocationManager.Stub {
for (UpdateRecord r : deadUpdateRecords) {
r.disposeLocked(true);
}
- applyRequirementsLocked(provider);
+ applyRequirementsLocked(manager);
}
}
@@ -3006,143 +2902,99 @@ public class LocationManagerService extends ILocationManager.Stub {
// Mock Providers
- private boolean canCallerAccessMockLocation(String opPackageName) {
- return mAppOps.checkOp(AppOpsManager.OP_MOCK_LOCATION, Binder.getCallingUid(),
- opPackageName) == AppOpsManager.MODE_ALLOWED;
- }
-
@Override
- public void addTestProvider(String name, ProviderProperties properties, String opPackageName) {
- if (!canCallerAccessMockLocation(opPackageName)) {
+ public void addTestProvider(String provider, ProviderProperties properties,
+ String packageName) {
+ if (mAppOps.checkOp(AppOpsManager.OP_MOCK_LOCATION, Binder.getCallingUid(), packageName)
+ != AppOpsManager.MODE_ALLOWED) {
return;
}
- if (PASSIVE_PROVIDER.equals(name)) {
- throw new IllegalArgumentException("Cannot mock the passive location provider");
- }
-
synchronized (mLock) {
- long identity = Binder.clearCallingIdentity();
- try {
- LocationProviderManager oldProvider = getLocationProviderLocked(name);
- if (oldProvider != null) {
- removeProviderLocked(oldProvider);
- }
-
- MockLocationProvider mockProviderManager = new MockLocationProvider(name);
- addProviderLocked(mockProviderManager);
- mockProviderManager.attachLocked(
- new MockProvider(mContext, mockProviderManager, properties));
- } finally {
- Binder.restoreCallingIdentity(identity);
+ LocationProviderManager manager = getLocationProviderManager(provider);
+ if (manager == null) {
+ manager = new LocationProviderManager(provider);
+ mProviderManagers.add(manager);
}
+
+ manager.setMockProvider(new MockProvider(mContext, properties));
}
}
@Override
- public void removeTestProvider(String name, String opPackageName) {
- if (!canCallerAccessMockLocation(opPackageName)) {
+ public void removeTestProvider(String provider, String packageName) {
+ if (mAppOps.checkOp(AppOpsManager.OP_MOCK_LOCATION, Binder.getCallingUid(), packageName)
+ != AppOpsManager.MODE_ALLOWED) {
return;
}
synchronized (mLock) {
- long identity = Binder.clearCallingIdentity();
- try {
- LocationProviderManager testProvider = getLocationProviderLocked(name);
- if (testProvider == null || !testProvider.isMock()) {
- return;
- }
-
- removeProviderLocked(testProvider);
-
- // reinstate real provider if available
- LocationProviderManager realProvider = null;
- for (LocationProviderManager provider : mRealProviders) {
- if (name.equals(provider.getName())) {
- realProvider = provider;
- break;
- }
- }
+ LocationProviderManager manager = getLocationProviderManager(provider);
+ if (manager == null) {
+ return;
+ }
- if (realProvider != null) {
- addProviderLocked(realProvider);
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
+ manager.setMockProvider(null);
+ if (!manager.hasProvider()) {
+ mProviderManagers.remove(manager);
+ mLastLocation.remove(manager.getName());
+ mLastLocationCoarseInterval.remove(manager.getName());
}
}
}
@Override
- public void setTestProviderLocation(String providerName, Location location,
- String opPackageName) {
- if (!canCallerAccessMockLocation(opPackageName)) {
+ public void setTestProviderLocation(String provider, Location location, String packageName) {
+ if (mAppOps.checkOp(AppOpsManager.OP_MOCK_LOCATION, Binder.getCallingUid(), packageName)
+ != AppOpsManager.MODE_ALLOWED) {
return;
}
- synchronized (mLock) {
- LocationProviderManager testProvider = getLocationProviderLocked(providerName);
- if (testProvider == null || !testProvider.isMock()) {
- throw new IllegalArgumentException("Provider \"" + providerName + "\" unknown");
- }
-
- String locationProvider = location.getProvider();
- if (!TextUtils.isEmpty(locationProvider) && !providerName.equals(locationProvider)) {
- // The location has an explicit provider that is different from the mock
- // provider name. The caller may be trying to fool us via b/33091107.
- EventLog.writeEvent(0x534e4554, "33091107", Binder.getCallingUid(),
- providerName + "!=" + location.getProvider());
- }
-
- ((MockLocationProvider) testProvider).setLocationLocked(location);
+ LocationProviderManager manager = getLocationProviderManager(provider);
+ if (manager == null) {
+ throw new IllegalArgumentException("provider doesn't exist: " + provider);
}
+
+ manager.setMockProviderLocation(location);
}
@Override
- public void setTestProviderEnabled(String providerName, boolean enabled, String opPackageName) {
- if (!canCallerAccessMockLocation(opPackageName)) {
+ public void setTestProviderEnabled(String provider, boolean enabled, String packageName) {
+ if (mAppOps.checkOp(AppOpsManager.OP_MOCK_LOCATION, Binder.getCallingUid(), packageName)
+ != AppOpsManager.MODE_ALLOWED) {
return;
}
- synchronized (mLock) {
- LocationProviderManager testProvider = getLocationProviderLocked(providerName);
- if (testProvider == null || !testProvider.isMock()) {
- throw new IllegalArgumentException("Provider \"" + providerName + "\" unknown");
- }
-
- ((MockLocationProvider) testProvider).setEnabledLocked(enabled);
+ LocationProviderManager manager = getLocationProviderManager(provider);
+ if (manager == null) {
+ throw new IllegalArgumentException("provider doesn't exist: " + provider);
}
+
+ manager.setMockProviderEnabled(enabled);
}
@Override
@NonNull
- public List<LocationRequest> getTestProviderCurrentRequests(String providerName,
- String opPackageName) {
- if (!canCallerAccessMockLocation(opPackageName)) {
+ public List<LocationRequest> getTestProviderCurrentRequests(String provider,
+ String packageName) {
+ if (mAppOps.checkOp(AppOpsManager.OP_MOCK_LOCATION, Binder.getCallingUid(), packageName)
+ != AppOpsManager.MODE_ALLOWED) {
return Collections.emptyList();
}
- synchronized (mLock) {
- LocationProviderManager testProvider = getLocationProviderLocked(providerName);
- if (testProvider == null || !testProvider.isMock()) {
- throw new IllegalArgumentException("Provider \"" + providerName + "\" unknown");
- }
-
- MockLocationProvider provider = (MockLocationProvider) testProvider;
- if (provider.mCurrentRequest == null) {
- return Collections.emptyList();
- }
- List<LocationRequest> requests = new ArrayList<>();
- for (LocationRequest request : provider.mCurrentRequest.locationRequests) {
- requests.add(new LocationRequest(request));
- }
- return requests;
+ LocationProviderManager manager = getLocationProviderManager(provider);
+ if (manager == null) {
+ throw new IllegalArgumentException("provider doesn't exist: " + provider);
}
+
+ return manager.getMockProviderRequests();
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) {
+ return;
+ }
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
@@ -3158,9 +3010,17 @@ public class LocationManagerService extends ILocationManager.Stub {
+ TimeUtils.logTimeOfDay(System.currentTimeMillis()));
ipw.println(", Current Elapsed Time: "
+ TimeUtils.formatDuration(SystemClock.elapsedRealtime()));
- ipw.println("Current user: " + mCurrentUserId + " " + Arrays.toString(
- mCurrentUserProfiles));
- ipw.println("Location Mode: " + isLocationEnabledForUser(mCurrentUserId));
+
+ ipw.println("User Info:");
+ ipw.increaseIndent();
+ mUserInfoStore.dump(fd, ipw, args);
+ ipw.decreaseIndent();
+
+ ipw.println("Location Settings:");
+ ipw.increaseIndent();
+ mSettingsStore.dump(fd, ipw, args);
+ ipw.decreaseIndent();
+
ipw.println("Battery Saver Location Mode: "
+ locationPowerSaveModeToString(mBatterySaverMode));
@@ -3227,24 +3087,19 @@ public class LocationManagerService extends ILocationManager.Stub {
ipw.decreaseIndent();
}
- ipw.println("Location Settings:");
- ipw.increaseIndent();
- mSettingsStore.dump(fd, ipw, args);
- ipw.decreaseIndent();
-
ipw.println("Location Providers:");
ipw.increaseIndent();
- for (LocationProviderManager provider : mProviders) {
- provider.dumpLocked(fd, ipw, args);
+ for (LocationProviderManager manager : mProviderManagers) {
+ manager.dump(fd, ipw, args);
}
ipw.decreaseIndent();
- }
- if (mGnssManagerService != null) {
- ipw.println("GNSS:");
- ipw.increaseIndent();
- mGnssManagerService.dump(fd, ipw, args);
- ipw.decreaseIndent();
+ if (mGnssManagerService != null) {
+ ipw.println("GNSS:");
+ ipw.increaseIndent();
+ mGnssManagerService.dump(fd, ipw, args);
+ ipw.decreaseIndent();
+ }
}
}
}
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index deff440aa0a6..7b4fd37f01c9 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -199,13 +199,14 @@ public class PackageWatchdog {
mSystemClock = clock;
mNumberOfNativeCrashPollsRemaining = NUMBER_OF_NATIVE_CRASH_POLLS;
loadFromFile();
+ sPackageWatchdog = this;
}
/** Creates or gets singleton instance of PackageWatchdog. */
public static PackageWatchdog getInstance(Context context) {
synchronized (PackageWatchdog.class) {
if (sPackageWatchdog == null) {
- sPackageWatchdog = new PackageWatchdog(context);
+ new PackageWatchdog(context);
}
return sPackageWatchdog;
}
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index fb1a962c0ef3..3dafc64391fd 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -18,8 +18,12 @@ package com.android.server;
import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
+import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.VersionedPackage;
import android.os.Build;
import android.os.Environment;
import android.os.FileUtils;
@@ -36,8 +40,12 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.StatsLog;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
+import com.android.server.PackageWatchdog.FailureReasons;
+import com.android.server.PackageWatchdog.PackageHealthObserver;
+import com.android.server.PackageWatchdog.PackageHealthObserverImpact;
import com.android.server.am.SettingsToPropertiesMapper;
import com.android.server.utils.FlagNamespaceUtils;
@@ -79,19 +87,30 @@ public class RescueParty {
@VisibleForTesting
static final long BOOT_TRIGGER_WINDOW_MILLIS = 600 * DateUtils.SECOND_IN_MILLIS;
@VisibleForTesting
- static final long PERSISTENT_APP_CRASH_TRIGGER_WINDOW_MILLIS = 30 * DateUtils.SECOND_IN_MILLIS;
- @VisibleForTesting
static final String TAG = "RescueParty";
+ private static final String NAME = "rescue-party-observer";
+
+
private static final String PROP_DISABLE_RESCUE = "persist.sys.disable_rescue";
private static final String PROP_RESCUE_BOOT_START = "sys.rescue_boot_start";
private static final String PROP_VIRTUAL_DEVICE = "ro.hardware.virtual_device";
+ private static final int PERSISTENT_MASK = ApplicationInfo.FLAG_PERSISTENT
+ | ApplicationInfo.FLAG_SYSTEM;
+
+
/** Threshold for boot loops */
private static final Threshold sBoot = new BootThreshold();
/** Threshold for app crash loops */
private static SparseArray<Threshold> sApps = new SparseArray<>();
+ /** Register the Rescue Party observer as a Package Watchdog health observer */
+ public static void registerHealthObserver(Context context) {
+ PackageWatchdog.getInstance(context).registerHealthObserver(
+ RescuePartyObserver.getInstance(context));
+ }
+
private static boolean isDisabled() {
// Check if we're explicitly enabled for testing
if (SystemProperties.getBoolean(PROP_ENABLE_RESCUE, false)) {
@@ -135,24 +154,6 @@ public class RescueParty {
}
/**
- * Take note of a persistent app or apex module crash. If we notice too many of these
- * events happening in rapid succession, we'll send out a rescue party.
- */
- public static void noteAppCrash(Context context, int uid) {
- if (isDisabled()) return;
- Threshold t = sApps.get(uid);
- if (t == null) {
- t = new AppThreshold(uid);
- sApps.put(uid, t);
- }
- if (t.incrementAndTest()) {
- t.reset();
- incrementRescueLevel(t.uid);
- executeRescueLevel(context);
- }
- }
-
- /**
* Check if we're currently attempting to reboot for a factory reset.
*/
public static boolean isAttemptingFactoryReset() {
@@ -171,11 +172,6 @@ public class RescueParty {
@VisibleForTesting
static void resetAllThresholds() {
sBoot.reset();
-
- for (int i = 0; i < sApps.size(); i++) {
- Threshold appThreshold = sApps.get(sApps.keyAt(i));
- appThreshold.reset();
- }
}
@VisibleForTesting
@@ -243,6 +239,28 @@ public class RescueParty {
FlagNamespaceUtils.NAMESPACE_NO_PACKAGE);
}
+ private static int mapRescueLevelToUserImpact(int rescueLevel) {
+ switch(rescueLevel) {
+ case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS:
+ case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES:
+ return PackageHealthObserverImpact.USER_IMPACT_LOW;
+ case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS:
+ case LEVEL_FACTORY_RESET:
+ return PackageHealthObserverImpact.USER_IMPACT_HIGH;
+ default:
+ return PackageHealthObserverImpact.USER_IMPACT_NONE;
+ }
+ }
+
+ private static int getPackageUid(Context context, String packageName) {
+ try {
+ return context.getPackageManager().getPackageUid(packageName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ // Since UIDs are always >= 0, this value means the UID could not be determined.
+ return -1;
+ }
+ }
+
private static void resetAllSettings(Context context, int mode) throws Exception {
// Try our best to reset all settings possible, and once finished
// rethrow any exception that we encountered
@@ -271,6 +289,89 @@ public class RescueParty {
}
/**
+ * Handle mitigation action for package failures. This observer will be register to Package
+ * Watchdog and will receive calls about package failures. This observer is persistent so it
+ * may choose to mitigate failures for packages it has not explicitly asked to observe.
+ */
+ public static class RescuePartyObserver implements PackageHealthObserver {
+
+ private Context mContext;
+
+ @GuardedBy("RescuePartyObserver.class")
+ static RescuePartyObserver sRescuePartyObserver;
+
+ private RescuePartyObserver(Context context) {
+ mContext = context;
+ }
+
+ /** Creates or gets singleton instance of RescueParty. */
+ public static RescuePartyObserver getInstance(Context context) {
+ synchronized (RescuePartyObserver.class) {
+ if (sRescuePartyObserver == null) {
+ sRescuePartyObserver = new RescuePartyObserver(context);
+ }
+ return sRescuePartyObserver;
+ }
+ }
+
+ @Override
+ public int onHealthCheckFailed(@Nullable VersionedPackage failedPackage,
+ @FailureReasons int failureReason) {
+ if (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH
+ || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING) {
+ int rescueLevel = MathUtils.constrain(
+ SystemProperties.getInt(PROP_RESCUE_LEVEL, LEVEL_NONE) + 1,
+ LEVEL_NONE, LEVEL_FACTORY_RESET);
+ return mapRescueLevelToUserImpact(rescueLevel);
+ } else {
+ return PackageHealthObserverImpact.USER_IMPACT_NONE;
+ }
+ }
+
+ @Override
+ public boolean execute(@Nullable VersionedPackage failedPackage,
+ @FailureReasons int failureReason) {
+ if (isDisabled()) {
+ return false;
+ }
+ if (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH
+ || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING) {
+ int triggerUid = getPackageUid(mContext, failedPackage.getPackageName());
+ incrementRescueLevel(triggerUid);
+ executeRescueLevel(mContext);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isPersistent() {
+ return true;
+ }
+
+ @Override
+ public boolean mayObservePackage(String packageName) {
+ PackageManager pm = mContext.getPackageManager();
+ try {
+ // A package is a Mainline module if this is non-null
+ if (pm.getModuleInfo(packageName, 0) != null) {
+ return true;
+ }
+ ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
+ return (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK;
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+ }
+
+ /**
* Threshold that can be triggered if a number of events occur within a
* window of time.
*/
@@ -349,27 +450,6 @@ public class RescueParty {
}
}
- /**
- * Specialization of {@link Threshold} for monitoring app crashes. It stores
- * counters in memory.
- */
- private static class AppThreshold extends Threshold {
- private int count;
- private long start;
-
- public AppThreshold(int uid) {
- // We're interested in TRIGGER_COUNT events in any
- // PERSISTENT_APP_CRASH_TRIGGER_WINDOW_MILLIS second period; apps crash pretty quickly
- // so we can keep a tight leash on them.
- super(uid, TRIGGER_COUNT, PERSISTENT_APP_CRASH_TRIGGER_WINDOW_MILLIS);
- }
-
- @Override public int getCount() { return count; }
- @Override public void setCount(int count) { this.count = count; }
- @Override public long getStart() { return start; }
- @Override public void setStart(long start) { this.start = start; }
- }
-
private static int[] getAllUserIds() {
int[] userIds = { UserHandle.USER_SYSTEM };
try {
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 3e6ccb572860..2b7745b7428a 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -83,7 +83,6 @@ import com.android.internal.app.IBatteryStats;
import com.android.internal.telephony.IOnSubscriptionsChangedListener;
import com.android.internal.telephony.IPhoneStateListener;
import com.android.internal.telephony.ITelephonyRegistry;
-import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.TelephonyPermissions;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
@@ -2194,6 +2193,17 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
private static final String PHONE_CONSTANTS_STATE_KEY = "state";
private static final String PHONE_CONSTANTS_SUBSCRIPTION_KEY = "subscription";
+ /**
+ * Broadcast Action: The phone's signal strength has changed. The intent will have the
+ * following extra values:
+ * phoneName - A string version of the phone name.
+ * asu - A numeric value for the signal strength.
+ * An ASU is 0-31 or -1 if unknown (for GSM, dBm = -113 - 2 * asu).
+ * The following special values are defined:
+ * 0 means "-113 dBm or less".31 means "-51 dBm or greater".
+ */
+ public static final String ACTION_SIGNAL_STRENGTH_CHANGED = "android.intent.action.SIG_STR";
+
private void broadcastServiceStateChanged(ServiceState state, int phoneId, int subId) {
long ident = Binder.clearCallingIdentity();
try {
@@ -2228,7 +2238,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
Binder.restoreCallingIdentity(ident);
}
- Intent intent = new Intent(TelephonyIntents.ACTION_SIGNAL_STRENGTH_CHANGED);
+ Intent intent = new Intent(ACTION_SIGNAL_STRENGTH_CHANGED);
Bundle data = new Bundle();
fillInSignalStrengthNotifierBundle(signalStrength, data);
intent.putExtras(data);
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index 4b48ef917744..143474bd5c94 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -413,6 +413,11 @@ public class AdbDebuggingManager {
case MESSAGE_ADB_CLEAR: {
Slog.d(TAG, "Received a request to clear the adb authorizations");
mConnectedKeys.clear();
+ // If the key store has not yet been instantiated then do so now; this avoids
+ // the unnecessary creation of the key store when adb is not enabled.
+ if (mAdbKeyStore == null) {
+ mAdbKeyStore = new AdbKeyStore();
+ }
mAdbKeyStore.deleteKeyStore();
cancelJobToUpdateAdbKeyStore();
break;
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index a58bd9bf32ff..e2a036aec172 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2068,7 +2068,7 @@ public final class ActiveServices {
+ " type=" + resolvedType + " callingUid=" + callingUid);
userId = mAm.mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
- ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE, "service",
+ ActivityManagerInternal.ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE, "service",
callingPackage);
ServiceMap smap = getServiceMapLocked(userId);
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 5e48dcf91676..8071f52037e6 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -33,8 +33,6 @@ import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
-import android.content.pm.ModuleInfo;
-import android.content.pm.PackageManager;
import android.content.pm.VersionedPackage;
import android.net.Uri;
import android.os.Binder;
@@ -56,7 +54,6 @@ import com.android.internal.app.ProcessMap;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.server.PackageWatchdog;
-import com.android.server.RescueParty;
import com.android.server.wm.WindowProcessController;
import java.io.FileDescriptor;
@@ -423,28 +420,6 @@ class AppErrors {
}
if (r != null) {
- boolean isApexModule = false;
- try {
- for (String androidPackage : r.getPackageList()) {
- ModuleInfo moduleInfo = mContext.getPackageManager().getModuleInfo(
- androidPackage, /*flags=*/ 0);
- if (moduleInfo != null) {
- isApexModule = true;
- break;
- }
- }
- } catch (IllegalStateException | PackageManager.NameNotFoundException e) {
- // Call to PackageManager#getModuleInfo() can result in NameNotFoundException or
- // IllegalStateException. In case they are thrown, there isn't much we can do
- // other than proceed with app crash handling.
- }
-
- if (r.isPersistent() || isApexModule) {
- // If a persistent app or apex module is stuck in a crash loop, the device isn't
- // very usable, so we want to consider sending out a rescue party.
- RescueParty.noteAppCrash(mContext, r.uid);
- }
-
mPackageWatchdog.onPackageFailure(r.getPackageListWithVersionCode(),
PackageWatchdog.FAILURE_REASON_APP_CRASH);
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index eb1ab3863624..2b207827a5ef 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -16,12 +16,14 @@
package com.android.server.am;
+import static android.Manifest.permission.INTERACT_ACROSS_PROFILES;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.app.ActivityManager.USER_OP_ERROR_IS_SYSTEM;
import static android.app.ActivityManager.USER_OP_ERROR_RELATED_USERS_CANNOT_STOP;
import static android.app.ActivityManager.USER_OP_IS_CURRENT;
import static android.app.ActivityManager.USER_OP_SUCCESS;
+import static android.app.ActivityManagerInternal.ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE;
import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE;
@@ -1641,9 +1643,10 @@ class UserController implements Handler.Callback {
if (callingUid != 0 && callingUid != SYSTEM_UID) {
final boolean allow;
+ final boolean isSameProfileGroup = isSameProfileGroup(callingUserId, targetUserId);
if (mInjector.isCallerRecents(callingUid)
&& callingUserId == getCurrentUserId()
- && isSameProfileGroup(callingUserId, targetUserId)) {
+ && isSameProfileGroup) {
// If the caller is Recents and it is running in the current user, we then allow it
// to access its profiles.
allow = true;
@@ -1654,6 +1657,9 @@ class UserController implements Handler.Callback {
} else if (allowMode == ALLOW_FULL_ONLY) {
// We require full access, sucks to be you.
allow = false;
+ } else if (canInteractWithAcrossProfilesPermission(
+ allowMode, isSameProfileGroup, callingPid, callingUid)) {
+ allow = true;
} else if (mInjector.checkComponentPermission(INTERACT_ACROSS_USERS, callingPid,
callingUid, -1, true) != PackageManager.PERMISSION_GRANTED) {
// If the caller does not have either permission, they are always doomed.
@@ -1661,10 +1667,11 @@ class UserController implements Handler.Callback {
} else if (allowMode == ALLOW_NON_FULL) {
// We are blanket allowing non-full access, you lucky caller!
allow = true;
- } else if (allowMode == ALLOW_NON_FULL_IN_PROFILE) {
+ } else if (allowMode == ALLOW_NON_FULL_IN_PROFILE
+ || allowMode == ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE) {
// We may or may not allow this depending on whether the two users are
// in the same profile.
- allow = isSameProfileGroup(callingUserId, targetUserId);
+ allow = isSameProfileGroup;
} else {
throw new IllegalArgumentException("Unknown mode: " + allowMode);
}
@@ -1690,6 +1697,11 @@ class UserController implements Handler.Callback {
if (allowMode != ALLOW_FULL_ONLY) {
builder.append(" or ");
builder.append(INTERACT_ACROSS_USERS);
+ if (isSameProfileGroup
+ && allowMode == ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE) {
+ builder.append(" or ");
+ builder.append(INTERACT_ACROSS_PROFILES);
+ }
}
String msg = builder.toString();
Slog.w(TAG, msg);
@@ -1710,6 +1722,19 @@ class UserController implements Handler.Callback {
return targetUserId;
}
+ private boolean canInteractWithAcrossProfilesPermission(
+ int allowMode, boolean isSameProfileGroup, int callingPid, int callingUid) {
+ if (allowMode != ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE) {
+ return false;
+ }
+ if (!isSameProfileGroup) {
+ return false;
+ }
+ return mInjector.checkComponentPermission(
+ INTERACT_ACROSS_PROFILES, callingPid, callingUid, /*owningUid= */-1,
+ /*exported= */true) == PackageManager.PERMISSION_GRANTED;
+ }
+
int unsafeConvertIncomingUser(@UserIdInt int userId) {
return (userId == UserHandle.USER_CURRENT || userId == UserHandle.USER_CURRENT_OR_SELF)
? getCurrentUserId(): userId;
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index d7ae5b5c91bd..e39d6d5389e3 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1305,21 +1305,13 @@ public final class DisplayManagerService extends SystemService {
}
private SurfaceControl.ScreenshotGraphicBuffer screenshotInternal(int displayId) {
- synchronized (mSyncRoot) {
- final IBinder token = getDisplayToken(displayId);
- if (token == null) {
- return null;
- }
- final LogicalDisplay logicalDisplay = mLogicalDisplays.get(displayId);
- if (logicalDisplay == null) {
- return null;
- }
-
- final DisplayInfo displayInfo = logicalDisplay.getDisplayInfoLocked();
- return SurfaceControl.screenshotToBufferWithSecureLayersUnsafe(token, new Rect(),
- displayInfo.getNaturalWidth(), displayInfo.getNaturalHeight(),
- false /* useIdentityTransform */, 0 /* rotation */);
+ final IBinder token = getDisplayToken(displayId);
+ if (token == null) {
+ return null;
}
+ return SurfaceControl.screenshotToBufferWithSecureLayersUnsafe(
+ token, new Rect(), 0 /* width */, 0 /* height */,
+ false /* useIdentityTransform */, 0 /* rotation */);
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/integrity/IntegrityFileManager.java b/services/core/java/com/android/server/integrity/IntegrityFileManager.java
index 31d4816e5d66..17a4b9c6c170 100644
--- a/services/core/java/com/android/server/integrity/IntegrityFileManager.java
+++ b/services/core/java/com/android/server/integrity/IntegrityFileManager.java
@@ -39,6 +39,7 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.util.Collections;
import java.util.List;
import java.util.Optional;
@@ -153,14 +154,19 @@ public class IntegrityFileManager {
throws IOException, RuleParseException {
synchronized (RULES_LOCK) {
// Try to identify indexes from the index file.
- List<RuleIndexRange> ruleReadingIndexes =
- mRuleIndexingController.identifyRulesToEvaluate(appInstallMetadata);
+ List<RuleIndexRange> ruleReadingIndexes;
+ try {
+ ruleReadingIndexes =
+ mRuleIndexingController.identifyRulesToEvaluate(appInstallMetadata);
+ } catch (Exception e) {
+ Slog.w(TAG, "Error identifying the rule indexes. Trying unindexed.", e);
+ ruleReadingIndexes = Collections.emptyList();
+ }
- // Read the rules based on the index information.
- // TODO(b/145493956): Provide the identified indexes to the rule reader.
+ // Read the rules based on the index information when available.
try (FileInputStream inputStream =
new FileInputStream(new File(mRulesDir, RULES_FILE))) {
- List<Rule> rules = mRuleParser.parse(inputStream);
+ List<Rule> rules = mRuleParser.parse(inputStream, ruleReadingIndexes);
return rules;
}
}
diff --git a/services/core/java/com/android/server/integrity/model/BitTrackedInputStream.java b/services/core/java/com/android/server/integrity/model/BitTrackedInputStream.java
new file mode 100644
index 000000000000..e555e3e5746e
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/model/BitTrackedInputStream.java
@@ -0,0 +1,69 @@
+/*
+ * 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.integrity.model;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * An input stream that tracks the total number read bytes since construction and allows moving
+ * fast forward to a certain byte any time during the execution.
+ *
+ * This class is used for efficient reading of rules based on the rule indexing.
+ */
+public class BitTrackedInputStream extends BitInputStream {
+
+ private static int sReadBitsCount;
+
+ /** Constructor with byte array. */
+ public BitTrackedInputStream(byte[] inputStream) {
+ super(inputStream);
+ sReadBitsCount = 0;
+ }
+
+ /** Constructor with input stream. */
+ public BitTrackedInputStream(InputStream inputStream) {
+ super(inputStream);
+ sReadBitsCount = 0;
+ }
+
+ /** Obtains an integer value of the next {@code numOfBits}. */
+ @Override
+ public int getNext(int numOfBits) throws IOException {
+ sReadBitsCount += numOfBits;
+ return super.getNext(numOfBits);
+ }
+
+ /** Returns the current cursor position showing the number of bits that are read. */
+ public int getReadBitsCount() {
+ return sReadBitsCount;
+ }
+
+ /**
+ * Sets the cursor to the specified byte location.
+ *
+ * Note that the integer parameter specifies the location in bytes -- not bits.
+ */
+ public void setCursorToByteLocation(int byteLocation) throws IOException {
+ int bitCountToRead = byteLocation * 8 - sReadBitsCount;
+ if (bitCountToRead < 0) {
+ throw new IllegalStateException("The byte position is already read.");
+ }
+ super.getNext(bitCountToRead);
+ sReadBitsCount = byteLocation * 8;
+ }
+}
diff --git a/services/core/java/com/android/server/integrity/serializer/ByteTrackedOutputStream.java b/services/core/java/com/android/server/integrity/model/ByteTrackedOutputStream.java
index 62815a9806ee..f575599e1c49 100644
--- a/services/core/java/com/android/server/integrity/serializer/ByteTrackedOutputStream.java
+++ b/services/core/java/com/android/server/integrity/model/ByteTrackedOutputStream.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.integrity.serializer;
+package com.android.server.integrity.model;
import java.io.IOException;
import java.io.OutputStream;
diff --git a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
index cbb6e4e8e06f..e744326c49db 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
@@ -37,71 +37,103 @@ import android.content.integrity.CompoundFormula;
import android.content.integrity.Formula;
import android.content.integrity.Rule;
-import com.android.server.integrity.model.BitInputStream;
+import com.android.server.integrity.model.BitTrackedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/** A helper class to parse rules into the {@link Rule} model from Binary representation. */
public class RuleBinaryParser implements RuleParser {
- private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
-
@Override
public List<Rule> parse(byte[] ruleBytes) throws RuleParseException {
try {
- BitInputStream bitInputStream = new BitInputStream(ruleBytes);
- return parseRules(bitInputStream);
+ BitTrackedInputStream bitTrackedInputStream = new BitTrackedInputStream(ruleBytes);
+ return parseRules(bitTrackedInputStream, /* indexRanges= */ Collections.emptyList());
} catch (Exception e) {
throw new RuleParseException(e.getMessage(), e);
}
}
@Override
- public List<Rule> parse(InputStream inputStream) throws RuleParseException {
+ public List<Rule> parse(InputStream inputStream, List<RuleIndexRange> indexRanges)
+ throws RuleParseException {
try {
- BitInputStream bitInputStream = new BitInputStream(inputStream);
- return parseRules(bitInputStream);
+ BitTrackedInputStream bitTrackedInputStream = new BitTrackedInputStream(inputStream);
+ return parseRules(bitTrackedInputStream, indexRanges);
} catch (Exception e) {
throw new RuleParseException(e.getMessage(), e);
}
}
- private List<Rule> parseRules(BitInputStream bitInputStream) throws IOException {
- List<Rule> parsedRules = new ArrayList<>();
+ private List<Rule> parseRules(
+ BitTrackedInputStream bitTrackedInputStream,
+ List<RuleIndexRange> indexRanges)
+ throws IOException {
// Read the rule binary file format version.
- bitInputStream.getNext(FORMAT_VERSION_BITS);
+ bitTrackedInputStream.getNext(FORMAT_VERSION_BITS);
+
+ return indexRanges.isEmpty()
+ ? parseAllRules(bitTrackedInputStream)
+ : parseIndexedRules(bitTrackedInputStream, indexRanges);
+ }
+
+ private List<Rule> parseAllRules(BitTrackedInputStream bitTrackedInputStream)
+ throws IOException {
+ List<Rule> parsedRules = new ArrayList<>();
+
+ while (bitTrackedInputStream.hasNext()) {
+ if (bitTrackedInputStream.getNext(SIGNAL_BIT) == 1) {
+ parsedRules.add(parseRule(bitTrackedInputStream));
+ }
+ }
+
+ return parsedRules;
+ }
+
+ private List<Rule> parseIndexedRules(
+ BitTrackedInputStream bitTrackedInputStream, List<RuleIndexRange> indexRanges)
+ throws IOException {
+ List<Rule> parsedRules = new ArrayList<>();
+
+ for (RuleIndexRange range : indexRanges) {
+ // Skip the rules that are not in the range.
+ bitTrackedInputStream.setCursorToByteLocation(range.getStartIndex());
- while (bitInputStream.hasNext()) {
- if (bitInputStream.getNext(SIGNAL_BIT) == 1) {
- parsedRules.add(parseRule(bitInputStream));
+ // Read the rules until we reach the end index.
+ while (bitTrackedInputStream.hasNext()
+ && bitTrackedInputStream.getReadBitsCount() < range.getEndIndex()) {
+ if (bitTrackedInputStream.getNext(SIGNAL_BIT) == 1) {
+ parsedRules.add(parseRule(bitTrackedInputStream));
+ }
}
}
return parsedRules;
}
- private Rule parseRule(BitInputStream bitInputStream) throws IOException {
- Formula formula = parseFormula(bitInputStream);
- int effect = bitInputStream.getNext(EFFECT_BITS);
+ private Rule parseRule(BitTrackedInputStream bitTrackedInputStream) throws IOException {
+ Formula formula = parseFormula(bitTrackedInputStream);
+ int effect = bitTrackedInputStream.getNext(EFFECT_BITS);
- if (bitInputStream.getNext(SIGNAL_BIT) != 1) {
+ if (bitTrackedInputStream.getNext(SIGNAL_BIT) != 1) {
throw new IllegalArgumentException("A rule must end with a '1' bit.");
}
return new Rule(formula, effect);
}
- private Formula parseFormula(BitInputStream bitInputStream) throws IOException {
- int separator = bitInputStream.getNext(SEPARATOR_BITS);
+ private Formula parseFormula(BitTrackedInputStream bitTrackedInputStream) throws IOException {
+ int separator = bitTrackedInputStream.getNext(SEPARATOR_BITS);
switch (separator) {
case ATOMIC_FORMULA_START:
- return parseAtomicFormula(bitInputStream);
+ return parseAtomicFormula(bitTrackedInputStream);
case COMPOUND_FORMULA_START:
- return parseCompoundFormula(bitInputStream);
+ return parseCompoundFormula(bitTrackedInputStream);
case COMPOUND_FORMULA_END:
return null;
default:
@@ -110,37 +142,40 @@ public class RuleBinaryParser implements RuleParser {
}
}
- private CompoundFormula parseCompoundFormula(BitInputStream bitInputStream) throws IOException {
- int connector = bitInputStream.getNext(CONNECTOR_BITS);
+ private CompoundFormula parseCompoundFormula(BitTrackedInputStream bitTrackedInputStream)
+ throws IOException {
+ int connector = bitTrackedInputStream.getNext(CONNECTOR_BITS);
List<Formula> formulas = new ArrayList<>();
- Formula parsedFormula = parseFormula(bitInputStream);
+ Formula parsedFormula = parseFormula(bitTrackedInputStream);
while (parsedFormula != null) {
formulas.add(parsedFormula);
- parsedFormula = parseFormula(bitInputStream);
+ parsedFormula = parseFormula(bitTrackedInputStream);
}
return new CompoundFormula(connector, formulas);
}
- private AtomicFormula parseAtomicFormula(BitInputStream bitInputStream) throws IOException {
- int key = bitInputStream.getNext(KEY_BITS);
- int operator = bitInputStream.getNext(OPERATOR_BITS);
+ private AtomicFormula parseAtomicFormula(BitTrackedInputStream bitTrackedInputStream)
+ throws IOException {
+ int key = bitTrackedInputStream.getNext(KEY_BITS);
+ int operator = bitTrackedInputStream.getNext(OPERATOR_BITS);
switch (key) {
case AtomicFormula.PACKAGE_NAME:
case AtomicFormula.APP_CERTIFICATE:
case AtomicFormula.INSTALLER_NAME:
case AtomicFormula.INSTALLER_CERTIFICATE:
- boolean isHashedValue = bitInputStream.getNext(IS_HASHED_BITS) == 1;
- int valueSize = bitInputStream.getNext(VALUE_SIZE_BITS);
- String stringValue = getStringValue(bitInputStream, valueSize, isHashedValue);
+ boolean isHashedValue = bitTrackedInputStream.getNext(IS_HASHED_BITS) == 1;
+ int valueSize = bitTrackedInputStream.getNext(VALUE_SIZE_BITS);
+ String stringValue = getStringValue(bitTrackedInputStream, valueSize,
+ isHashedValue);
return new AtomicFormula.StringAtomicFormula(key, stringValue, isHashedValue);
case AtomicFormula.VERSION_CODE:
- int intValue = getIntValue(bitInputStream);
+ int intValue = getIntValue(bitTrackedInputStream);
return new AtomicFormula.IntAtomicFormula(key, operator, intValue);
case AtomicFormula.PRE_INSTALLED:
- boolean booleanValue = getBooleanValue(bitInputStream);
+ boolean booleanValue = getBooleanValue(bitTrackedInputStream);
return new AtomicFormula.BooleanAtomicFormula(key, booleanValue);
default:
throw new IllegalArgumentException(String.format("Unknown key: %d", key));
diff --git a/services/core/java/com/android/server/integrity/parser/RuleParser.java b/services/core/java/com/android/server/integrity/parser/RuleParser.java
index 81783d5c7324..a8e9f6134759 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleParser.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleParser.java
@@ -28,5 +28,6 @@ public interface RuleParser {
List<Rule> parse(byte[] ruleBytes) throws RuleParseException;
/** Parse rules from an input stream. */
- List<Rule> parse(InputStream inputStream) throws RuleParseException;
+ List<Rule> parse(InputStream inputStream, List<RuleIndexRange> ruleIndexRanges)
+ throws RuleParseException;
}
diff --git a/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java b/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java
index d405583442bd..497be8424286 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java
@@ -62,7 +62,8 @@ public final class RuleXmlParser implements RuleParser {
}
@Override
- public List<Rule> parse(InputStream inputStream) throws RuleParseException {
+ public List<Rule> parse(InputStream inputStream, List<RuleIndexRange> indexRanges)
+ throws RuleParseException {
try {
XmlPullParser xmlPullParser = Xml.newPullParser();
xmlPullParser.setInput(inputStream, StandardCharsets.UTF_8.name());
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
index b8791c3c3489..f964d4cf2724 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
@@ -42,6 +42,7 @@ import android.content.integrity.Rule;
import com.android.internal.util.Preconditions;
import com.android.server.integrity.IntegrityUtils;
import com.android.server.integrity.model.BitOutputStream;
+import com.android.server.integrity.model.ByteTrackedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -109,7 +110,8 @@ public class RuleBinarySerializer implements RuleSerializer {
}
private void serializeRuleFileMetadata(Optional<Integer> formatVersion,
- ByteTrackedOutputStream outputStream) throws IOException {
+ ByteTrackedOutputStream outputStream)
+ throws IOException {
int formatVersionValue = formatVersion.orElse(DEFAULT_FORMAT_VERSION);
BitOutputStream bitOutputStream = new BitOutputStream();
diff --git a/services/core/java/com/android/server/location/AbstractLocationProvider.java b/services/core/java/com/android/server/location/AbstractLocationProvider.java
index ccfc98e2291b..ed6a759409d4 100644
--- a/services/core/java/com/android/server/location/AbstractLocationProvider.java
+++ b/services/core/java/com/android/server/location/AbstractLocationProvider.java
@@ -16,11 +16,11 @@
package com.android.server.location;
+import android.annotation.Nullable;
import android.content.Context;
import android.location.Location;
import android.os.Binder;
import android.os.Bundle;
-import android.os.WorkSource;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
@@ -29,127 +29,336 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.UnaryOperator;
/**
- * Location Manager's interface for location providers. Always starts as disabled.
+ * Base class for all location providers.
*
* @hide
*/
public abstract class AbstractLocationProvider {
/**
- * Interface for communicating from a location provider back to the location service.
+ * Interface for listening to location providers.
*/
- public interface LocationProviderManager {
+ public interface Listener {
/**
- * May be called to inform the location service of a change in this location provider's
- * enabled/disabled state.
+ * Called when a provider's state changes. May be invoked from any thread. Will be
+ * invoked with a cleared binder identity.
*/
- void onSetEnabled(boolean enabled);
+ void onStateChanged(State oldState, State newState);
/**
- * May be called to inform the location service of a change in this location provider's
- * properties.
+ * Called when a provider has a new location available. May be invoked from any thread. Will
+ * be invoked with a cleared binder identity.
*/
- void onSetProperties(ProviderProperties properties);
+ void onReportLocation(Location location);
/**
- * May be called to inform the location service that this provider has a new location
- * available.
+ * Called when a provider has a new location available. May be invoked from any thread. Will
+ * be invoked with a cleared binder identity.
*/
- void onReportLocation(Location location);
+ void onReportLocation(List<Location> locations);
+ }
+
+ /**
+ * Holds a representation of the public state of a provider.
+ */
+ public static final class State {
/**
- * May be called to inform the location service that this provider has a new location
- * available.
+ * Default state value for a location provider that is disabled with no properties and an
+ * empty provider package list.
*/
- void onReportLocation(List<Location> locations);
+ public static final State EMPTY_STATE = new State(false, null,
+ Collections.emptySet());
+
+ /**
+ * The provider's enabled state.
+ */
+ public final boolean enabled;
+
+ /**
+ * The provider's properties.
+ */
+ @Nullable public final ProviderProperties properties;
+
+ /**
+ * The provider's package name list - provider packages may be afforded special privileges.
+ */
+ public final Set<String> providerPackageNames;
+
+ private State(boolean enabled, ProviderProperties properties,
+ Set<String> providerPackageNames) {
+ this.enabled = enabled;
+ this.properties = properties;
+ this.providerPackageNames = Objects.requireNonNull(providerPackageNames);
+ }
+
+ private State withEnabled(boolean enabled) {
+ if (enabled == this.enabled) {
+ return this;
+ } else {
+ return new State(enabled, properties, providerPackageNames);
+ }
+ }
+
+ private State withProperties(ProviderProperties properties) {
+ if (properties.equals(this.properties)) {
+ return this;
+ } else {
+ return new State(enabled, properties, providerPackageNames);
+ }
+ }
+
+ private State withProviderPackageNames(Set<String> providerPackageNames) {
+ if (providerPackageNames.equals(this.providerPackageNames)) {
+ return this;
+ } else {
+ return new State(enabled, properties, providerPackageNames);
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof State)) {
+ return false;
+ }
+ State state = (State) o;
+ return enabled == state.enabled && properties == state.properties
+ && providerPackageNames.equals(state.providerPackageNames);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(enabled, properties, providerPackageNames);
+ }
+ }
+
+ // combines listener and state information so that they can be updated atomically with respect
+ // to each other and an ordering established.
+ private static class InternalState {
+ @Nullable public final Listener listener;
+ public final State state;
+
+ private InternalState(@Nullable Listener listener, State state) {
+ this.listener = listener;
+ this.state = state;
+ }
+
+ private InternalState withListener(Listener listener) {
+ if (listener == this.listener) {
+ return this;
+ } else {
+ return new InternalState(listener, state);
+ }
+ }
+
+ private InternalState withState(State state) {
+ if (state.equals(this.state)) {
+ return this;
+ } else {
+ return new InternalState(listener, state);
+ }
+ }
+
+ private InternalState withState(UnaryOperator<State> operator) {
+ return withState(operator.apply(state));
+ }
}
protected final Context mContext;
- private final LocationProviderManager mLocationProviderManager;
+ protected final Executor mExecutor;
+
+ // we use a lock-free implementation to update state to ensure atomicity between updating the
+ // provider state and setting the listener, so that the state updates a listener sees are
+ // consistent with when the listener was set (a listener should not see any updates that occur
+ // before it was set, and should not miss any updates that occur after it was set).
+ private final AtomicReference<InternalState> mInternalState;
- protected AbstractLocationProvider(
- Context context, LocationProviderManager locationProviderManager) {
+ protected AbstractLocationProvider(Context context, Executor executor) {
+ this(context, executor, Collections.singleton(context.getPackageName()));
+ }
+
+ protected AbstractLocationProvider(Context context, Executor executor,
+ Set<String> packageNames) {
mContext = context;
- mLocationProviderManager = locationProviderManager;
+ mExecutor = executor;
+ mInternalState = new AtomicReference<>(
+ new InternalState(null, State.EMPTY_STATE.withProviderPackageNames(packageNames)));
}
/**
- * Call this method to report a change in provider enabled/disabled status. May be called from
- * any thread.
+ * Sets the listener and returns the state at the moment the listener was set. The listener can
+ * expect to receive all state updates from after this point.
*/
- protected void setEnabled(boolean enabled) {
- long identity = Binder.clearCallingIdentity();
- try {
- mLocationProviderManager.onSetEnabled(enabled);
- } finally {
- Binder.restoreCallingIdentity(identity);
+ State setListener(@Nullable Listener listener) {
+ return mInternalState.updateAndGet(
+ internalState -> internalState.withListener(listener)).state;
+ }
+
+ /**
+ * Retrieves the state of the provider.
+ */
+ State getState() {
+ return mInternalState.get().state;
+ }
+
+ /**
+ * Sets the state of the provider to the new state.
+ */
+ void setState(State newState) {
+ InternalState oldInternalState = mInternalState.getAndUpdate(
+ internalState -> internalState.withState(newState));
+ if (newState.equals(oldInternalState.state)) {
+ return;
+ }
+
+ // we know that we only updated the state, so the listener for the old state is the same as
+ // the listener for the new state.
+ if (oldInternalState.listener != null) {
+ long identity = Binder.clearCallingIdentity();
+ try {
+ oldInternalState.listener.onStateChanged(oldInternalState.state, newState);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
+ private void setState(UnaryOperator<State> operator) {
+ InternalState oldInternalState = mInternalState.getAndUpdate(
+ internalState -> internalState.withState(operator));
+
+ // recreate the new state from our knowledge of the old state - unfortunately may result in
+ // an extra allocation, but oh well...
+ State newState = operator.apply(oldInternalState.state);
+
+ if (newState.equals(oldInternalState.state)) {
+ return;
+ }
+
+ // we know that we only updated the state, so the listener for the old state is the same as
+ // the listener for the new state.
+ if (oldInternalState.listener != null) {
+ long identity = Binder.clearCallingIdentity();
+ try {
+ oldInternalState.listener.onStateChanged(oldInternalState.state, newState);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
}
/**
- * Call this method to report a change in provider properties. May be called from
- * any thread.
+ * The current enabled state of this provider.
+ */
+ protected boolean isEnabled() {
+ return mInternalState.get().state.enabled;
+ }
+
+ /**
+ * The current provider properties of this provider.
+ */
+ @Nullable
+ protected ProviderProperties getProperties() {
+ return mInternalState.get().state.properties;
+ }
+
+ /**
+ * The current package set of this provider.
+ */
+ protected Set<String> getProviderPackages() {
+ return mInternalState.get().state.providerPackageNames;
+ }
+
+ /**
+ * Call this method to report a change in provider enabled/disabled status.
+ */
+ protected void setEnabled(boolean enabled) {
+ setState(state -> state.withEnabled(enabled));
+ }
+
+ /**
+ * Call this method to report a change in provider properties.
*/
protected void setProperties(ProviderProperties properties) {
- long identity = Binder.clearCallingIdentity();
- try {
- mLocationProviderManager.onSetProperties(properties);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ setState(state -> state.withProperties(properties));
+ }
+
+ /**
+ * Call this method to report a change in provider packages.
+ */
+ protected void setPackageNames(Set<String> packageNames) {
+ setState(state -> state.withProviderPackageNames(packageNames));
}
/**
- * Call this method to report a new location. May be called from any thread.
+ * Call this method to report a new location.
*/
protected void reportLocation(Location location) {
- long identity = Binder.clearCallingIdentity();
- try {
- mLocationProviderManager.onReportLocation(location);
- } finally {
- Binder.restoreCallingIdentity(identity);
+ Listener listener = mInternalState.get().listener;
+ if (listener != null) {
+ long identity = Binder.clearCallingIdentity();
+ try {
+ listener.onReportLocation(location);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
}
/**
- * Call this method to report a new location. May be called from any thread.
+ * Call this method to report a new location.
*/
protected void reportLocation(List<Location> locations) {
- long identity = Binder.clearCallingIdentity();
- try {
- mLocationProviderManager.onReportLocation(locations);
- } finally {
- Binder.restoreCallingIdentity(identity);
+ Listener listener = mInternalState.get().listener;
+ if (listener != null) {
+ long identity = Binder.clearCallingIdentity();
+ try {
+ listener.onReportLocation(locations);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
}
/**
- * Invoked by the location service to return a list of packages currently associated with this
- * provider. May be called from any thread.
+ * Sets a new request and worksource for the provider.
*/
- public List<String> getProviderPackages() {
- return Collections.singletonList(mContext.getPackageName());
+ public final void setRequest(ProviderRequest request) {
+ // all calls into the provider must be moved onto the provider thread to prevent deadlock
+ mExecutor.execute(() -> onSetRequest(request));
}
/**
- * Invoked by the location service to deliver a new request for fulfillment to the provider.
- * Replaces any previous requests completely. Will always be invoked from the location service
- * thread with a cleared binder identity.
+ * Always invoked on the provider executor.
*/
- public abstract void onSetRequest(ProviderRequest request, WorkSource source);
+ protected abstract void onSetRequest(ProviderRequest request);
+
+ /**
+ * Sends an extra command to the provider for it to interpret as it likes.
+ */
+ public final void sendExtraCommand(int uid, int pid, String command, Bundle extras) {
+ // all calls into the provider must be moved onto the provider thread to prevent deadlock
+ mExecutor.execute(() -> onExtraCommand(uid, pid, command, extras));
+ }
/**
- * Invoked by the location service to deliver a custom command to this provider. Will always be
- * invoked from the location service thread with a cleared binder identity.
+ * Always invoked on the provider executor.
*/
- public void onSendExtraCommand(int uid, int pid, String command, Bundle extras) {}
+ protected void onExtraCommand(int uid, int pid, String command, Bundle extras) {}
/**
- * Invoked by the location service to dump debug or log information. May be invoked from any
- * thread.
+ * Dumps debug or log information. May be invoked from any thread.
*/
public abstract void dump(FileDescriptor fd, PrintWriter pw, String[] args);
}
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index d8561b697caa..15cf190952d1 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -43,6 +43,7 @@ import android.os.BatteryStats;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.Looper;
import android.os.Message;
import android.os.PersistableBundle;
@@ -113,8 +114,15 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
private static final ProviderProperties PROPERTIES = new ProviderProperties(
- true, true, false, false, true, true, true,
- Criteria.POWER_HIGH, Criteria.ACCURACY_FINE);
+ /* requiresNetwork = */false,
+ /* requiresSatellite = */true,
+ /* requiresCell = */false,
+ /* hasMonetaryCost = */false,
+ /* supportAltitude = */true,
+ /* supportsSpeed = */true,
+ /* supportsBearing = */true,
+ Criteria.POWER_HIGH,
+ Criteria.ACCURACY_FINE);
// these need to match GnssPositionMode enum in IGnss.hal
private static final int GPS_POSITION_MODE_STANDALONE = 0;
@@ -616,13 +624,12 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
}
}
- public GnssLocationProvider(Context context, LocationProviderManager locationProviderManager,
- Looper looper) {
- super(context, locationProviderManager);
+ public GnssLocationProvider(Context context, Handler handler) {
+ super(context, new HandlerExecutor(handler));
ensureInitialized();
- mLooper = looper;
+ mLooper = handler.getLooper();
// Create a wake lock
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
@@ -639,7 +646,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_TIMEOUT), 0);
mNetworkConnectivityHandler = new GnssNetworkConnectivityHandler(context,
- GnssLocationProvider.this::onNetworkAvailable, looper);
+ GnssLocationProvider.this::onNetworkAvailable, mLooper);
// App ops service to keep track of who is accessing the GPS
mAppOps = mContext.getSystemService(AppOpsManager.class);
@@ -649,7 +656,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
BatteryStats.SERVICE_NAME));
// Construct internal handler
- mHandler = new ProviderHandler(looper);
+ mHandler = new ProviderHandler(mLooper);
// Load GPS configuration and register listeners in the background:
// some operations, such as opening files and registering broadcast receivers, can take a
@@ -693,10 +700,10 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
};
mGnssMetrics = new GnssMetrics(mBatteryStats);
- mNtpTimeHelper = new NtpTimeHelper(mContext, looper, this);
+ mNtpTimeHelper = new NtpTimeHelper(mContext, mLooper, this);
GnssSatelliteBlacklistHelper gnssSatelliteBlacklistHelper =
new GnssSatelliteBlacklistHelper(mContext,
- looper, this);
+ mLooper, this);
mHandler.post(gnssSatelliteBlacklistHelper::updateSatelliteBlacklist);
mGnssBatchingProvider = new GnssBatchingProvider();
mGnssGeofenceProvider = new GnssGeofenceProvider();
@@ -1047,8 +1054,8 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
}
@Override
- public void onSetRequest(ProviderRequest request, WorkSource source) {
- sendMessage(SET_REQUEST, 0, new GpsRequest(request, source));
+ public void onSetRequest(ProviderRequest request) {
+ sendMessage(SET_REQUEST, 0, new GpsRequest(request, request.workSource));
}
private void handleSetRequest(ProviderRequest request, WorkSource source) {
@@ -1185,7 +1192,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
}
@Override
- public void onSendExtraCommand(int uid, int pid, String command, Bundle extras) {
+ public void onExtraCommand(int uid, int pid, String command, Bundle extras) {
long identity = Binder.clearCallingIdentity();
try {
@@ -2064,10 +2071,8 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
}
/**
- * This method is bound to {@link #GnssLocationProvider(Context, LocationProviderManager,
- * Looper)}.
- * It is in charge of loading properties and registering for events that will be posted to
- * this handler.
+ * This method is bound to the constructor. It is in charge of loading properties and
+ * registering for events that will be posted to this handler.
*/
private void handleInitialize() {
// class_init_native() already initializes the GNSS service handle during class loading.
diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java
index 694f14904668..8a149afa6238 100644
--- a/services/core/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/core/java/com/android/server/location/LocationProviderProxy.java
@@ -23,12 +23,13 @@ import android.content.Context;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.WorkSource;
+import android.util.ArraySet;
import android.util.Log;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.location.ILocationProvider;
import com.android.internal.location.ILocationProviderManager;
import com.android.internal.location.ProviderProperties;
@@ -39,10 +40,8 @@ import com.android.server.ServiceWatcher;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
/**
* Proxy for ILocationProvider implementations.
@@ -52,59 +51,64 @@ public class LocationProviderProxy extends AbstractLocationProvider {
private static final String TAG = "LocationProviderProxy";
private static final boolean D = LocationManagerService.D;
- // used to ensure that updates to mProviderPackages are atomic
- private final Object mProviderPackagesLock = new Object();
-
- // used to ensure that updates to mRequest and mWorkSource are atomic
- private final Object mRequestLock = new Object();
+ private static final int MAX_ADDITIONAL_PACKAGES = 2;
private final ILocationProviderManager.Stub mManager = new ILocationProviderManager.Stub() {
// executed on binder thread
@Override
public void onSetAdditionalProviderPackages(List<String> packageNames) {
- LocationProviderProxy.this.onSetAdditionalProviderPackages(packageNames);
+ int maxCount = Math.min(MAX_ADDITIONAL_PACKAGES, packageNames.size()) + 1;
+ ArraySet<String> allPackages = new ArraySet<>(maxCount);
+ allPackages.add(mServiceWatcher.getCurrentPackageName());
+ for (String packageName : packageNames) {
+ if (packageNames.size() >= maxCount) {
+ return;
+ }
+
+ try {
+ mContext.getPackageManager().getPackageInfo(packageName, MATCH_SYSTEM_ONLY);
+ allPackages.add(packageName);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, mServiceWatcher + " specified unknown additional provider package: "
+ + packageName);
+ }
+ }
+
+ setPackageNames(allPackages);
}
// executed on binder thread
@Override
public void onSetEnabled(boolean enabled) {
- LocationProviderProxy.this.setEnabled(enabled);
+ setEnabled(enabled);
}
// executed on binder thread
@Override
public void onSetProperties(ProviderProperties properties) {
- LocationProviderProxy.this.setProperties(properties);
+ setProperties(properties);
}
// executed on binder thread
@Override
public void onReportLocation(Location location) {
- LocationProviderProxy.this.reportLocation(location);
+ reportLocation(location);
}
};
private final ServiceWatcher mServiceWatcher;
- @GuardedBy("mProviderPackagesLock")
- private final CopyOnWriteArrayList<String> mProviderPackages = new CopyOnWriteArrayList<>();
-
- @GuardedBy("mRequestLock")
- @Nullable
- private ProviderRequest mRequest;
- @GuardedBy("mRequestLock")
- private WorkSource mWorkSource;
+ @Nullable private ProviderRequest mRequest;
/**
* Creates a new LocationProviderProxy and immediately begins binding to the best applicable
* service.
*/
@Nullable
- public static LocationProviderProxy createAndBind(
- Context context, LocationProviderManager locationProviderManager, String action,
+ public static LocationProviderProxy createAndBind(Context context, String action,
int overlaySwitchResId, int defaultServicePackageNameResId,
int initialPackageNamesResId) {
- LocationProviderProxy proxy = new LocationProviderProxy(context, locationProviderManager,
+ LocationProviderProxy proxy = new LocationProviderProxy(context, FgThread.getHandler(),
action, overlaySwitchResId, defaultServicePackageNameResId,
initialPackageNamesResId);
if (proxy.bind()) {
@@ -114,14 +118,13 @@ public class LocationProviderProxy extends AbstractLocationProvider {
}
}
- private LocationProviderProxy(Context context, LocationProviderManager locationProviderManager,
- String action, int overlaySwitchResId, int defaultServicePackageNameResId,
+ private LocationProviderProxy(Context context, Handler handler, String action,
+ int overlaySwitchResId, int defaultServicePackageNameResId,
int initialPackageNamesResId) {
- super(context, locationProviderManager);
+ super(context, new HandlerExecutor(handler), Collections.emptySet());
mServiceWatcher = new ServiceWatcher(context, TAG, action, overlaySwitchResId,
- defaultServicePackageNameResId, initialPackageNamesResId,
- FgThread.getHandler()) {
+ defaultServicePackageNameResId, initialPackageNamesResId, handler) {
@Override
protected void onBind() {
@@ -130,14 +133,11 @@ public class LocationProviderProxy extends AbstractLocationProvider {
@Override
protected void onUnbind() {
- resetProviderPackages(Collections.emptyList());
- setEnabled(false);
- setProperties(null);
+ setState(State.EMPTY_STATE);
}
};
mRequest = null;
- mWorkSource = new WorkSource();
}
private boolean bind() {
@@ -148,77 +148,34 @@ public class LocationProviderProxy extends AbstractLocationProvider {
ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
if (D) Log.d(TAG, "applying state to connected service " + mServiceWatcher);
- resetProviderPackages(Collections.emptyList());
+ setPackageNames(Collections.singleton(mServiceWatcher.getCurrentPackageName()));
service.setLocationProviderManager(mManager);
- synchronized (mRequestLock) {
- if (mRequest != null) {
- service.setRequest(mRequest, mWorkSource);
- }
- }
- }
-
- @Override
- public List<String> getProviderPackages() {
- synchronized (mProviderPackagesLock) {
- return mProviderPackages;
+ if (mRequest != null) {
+ service.setRequest(mRequest, mRequest.workSource);
}
}
@Override
- public void onSetRequest(ProviderRequest request, WorkSource source) {
- synchronized (mRequestLock) {
- mRequest = request;
- mWorkSource = source;
- }
+ public void onSetRequest(ProviderRequest request) {
mServiceWatcher.runOnBinder(binder -> {
+ mRequest = request;
ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
- service.setRequest(request, source);
+ service.setRequest(request, request.workSource);
});
}
@Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("service=" + mServiceWatcher);
- synchronized (mProviderPackagesLock) {
- if (mProviderPackages.size() > 1) {
- pw.println("additional packages=" + mProviderPackages);
- }
- }
- }
-
- @Override
- public void onSendExtraCommand(int uid, int pid, String command, Bundle extras) {
+ public void onExtraCommand(int uid, int pid, String command, Bundle extras) {
mServiceWatcher.runOnBinder(binder -> {
ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
service.sendExtraCommand(command, extras);
});
}
- private void onSetAdditionalProviderPackages(List<String> packageNames) {
- resetProviderPackages(packageNames);
- }
-
- private void resetProviderPackages(List<String> additionalPackageNames) {
- ArrayList<String> permittedPackages = new ArrayList<>(additionalPackageNames.size());
- for (String packageName : additionalPackageNames) {
- try {
- mContext.getPackageManager().getPackageInfo(packageName, MATCH_SYSTEM_ONLY);
- permittedPackages.add(packageName);
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, mServiceWatcher + " specified unknown additional provider package: "
- + packageName);
- }
- }
-
- synchronized (mProviderPackagesLock) {
- mProviderPackages.clear();
- String myPackage = mServiceWatcher.getCurrentPackageName();
- if (myPackage != null) {
- mProviderPackages.add(myPackage);
- mProviderPackages.addAll(permittedPackages);
- }
- }
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("service=" + mServiceWatcher);
}
}
diff --git a/services/core/java/com/android/server/location/LocationSettingsStore.java b/services/core/java/com/android/server/location/LocationSettingsStore.java
index f625452975c0..0e8720ebb08f 100644
--- a/services/core/java/com/android/server/location/LocationSettingsStore.java
+++ b/services/core/java/com/android/server/location/LocationSettingsStore.java
@@ -16,6 +16,8 @@
package com.android.server.location;
+import static android.location.LocationManager.FUSED_PROVIDER;
+import static android.location.LocationManager.PASSIVE_PROVIDER;
import static android.provider.Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS;
import static android.provider.Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST;
import static android.provider.Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS;
@@ -28,6 +30,7 @@ import android.app.ActivityManager;
import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
+import android.os.Binder;
import android.os.Handler;
import android.os.UserHandle;
import android.provider.Settings;
@@ -248,6 +251,9 @@ public class LocationSettingsStore {
DEFAULT_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS);
}
+ /**
+ * Retrieve maximum age of the last location.
+ */
public long getMaxLastLocationAgeMs() {
return Settings.Global.getLong(
mContext.getContentResolver(),
@@ -256,6 +262,29 @@ public class LocationSettingsStore {
}
/**
+ * Set a value for the deprecated LOCATION_PROVIDERS_ALLOWED setting. This is used purely for
+ * backwards compatibility for old clients, and may be removed in the future.
+ */
+ public void setLocationProviderAllowed(String provider, boolean enabled, int userId) {
+ // fused and passive provider never get public updates for legacy reasons
+ if (FUSED_PROVIDER.equals(provider) || PASSIVE_PROVIDER.equals(provider)) {
+ return;
+ }
+
+ long identity = Binder.clearCallingIdentity();
+ try {
+ // update LOCATION_PROVIDERS_ALLOWED for best effort backwards compatibility
+ Settings.Secure.putStringForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+ (enabled ? "+" : "-") + provider,
+ userId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
* Dump info for debugging.
*/
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
diff --git a/services/core/java/com/android/server/location/MockProvider.java b/services/core/java/com/android/server/location/MockProvider.java
index 472876bfd86a..60c9fc12c201 100644
--- a/services/core/java/com/android/server/location/MockProvider.java
+++ b/services/core/java/com/android/server/location/MockProvider.java
@@ -19,7 +19,6 @@ package com.android.server.location;
import android.annotation.Nullable;
import android.content.Context;
import android.location.Location;
-import android.os.WorkSource;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
@@ -34,41 +33,33 @@ import java.io.PrintWriter;
*/
public class MockProvider extends AbstractLocationProvider {
- private boolean mEnabled;
@Nullable private Location mLocation;
- public MockProvider(Context context,
- LocationProviderManager locationProviderManager, ProviderProperties properties) {
- super(context, locationProviderManager);
-
- mEnabled = true;
- mLocation = null;
-
+ public MockProvider(Context context, ProviderProperties properties) {
+ // using a direct executor is only acceptable because this class is so simple it is trivial
+ // to verify that it does not acquire any locks or re-enter LMS from callbacks
+ super(context, Runnable::run);
setProperties(properties);
}
/** Sets the enabled state of this mock provider. */
- public void setEnabled(boolean enabled) {
- mEnabled = enabled;
- super.setEnabled(enabled);
+ public void setProviderEnabled(boolean enabled) {
+ setEnabled(enabled);
}
/** Sets the location to report for this mock provider. */
- public void setLocation(Location l) {
- mLocation = new Location(l);
- if (!mLocation.isFromMockProvider()) {
- mLocation.setIsFromMockProvider(true);
- }
- if (mEnabled) {
- reportLocation(mLocation);
- }
+ public void setProviderLocation(Location l) {
+ Location location = new Location(l);
+ location.setIsFromMockProvider(true);
+ mLocation = location;
+ reportLocation(location);
}
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("last location=" + mLocation);
+ pw.println("last mock location=" + mLocation);
}
@Override
- public void onSetRequest(ProviderRequest request, WorkSource source) {}
+ public void onSetRequest(ProviderRequest request) {}
}
diff --git a/services/core/java/com/android/server/location/MockableLocationProvider.java b/services/core/java/com/android/server/location/MockableLocationProvider.java
new file mode 100644
index 000000000000..f50dfe7edbb7
--- /dev/null
+++ b/services/core/java/com/android/server/location/MockableLocationProvider.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.location.Location;
+import android.os.Bundle;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.location.ProviderRequest;
+import com.android.internal.util.Preconditions;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Represents a location provider that may switch between a mock implementation and a real
+ * implementation. Requires owners to provide a lock object that will be used internally and held
+ * for the duration of all listener callbacks. Owners are reponsible for ensuring this cannot lead
+ * to deadlock.
+ *
+ * In order to ensure deadlock does not occur, the owner must validate that the ONLY lock which can
+ * be held BOTH when calling into this class AND when receiving a callback from this class is the
+ * lock given to this class via the constructor. Holding any other lock is ok as long as there is no
+ * possibility that it can be obtained within both codepaths.
+ *
+ * Holding the given lock guarantees atomicity of any operations on this class for the duration.
+ *
+ * @hide
+ */
+public class MockableLocationProvider extends AbstractLocationProvider {
+
+ private final Object mOwnerLock;
+
+ @GuardedBy("mOwnerLock")
+ @Nullable private AbstractLocationProvider mProvider;
+ @GuardedBy("mOwnerLock")
+ @Nullable private AbstractLocationProvider mRealProvider;
+ @GuardedBy("mOwnerLock")
+ @Nullable private MockProvider mMockProvider;
+
+ @GuardedBy("mOwnerLock")
+ private ProviderRequest mRequest;
+
+ /**
+ * The given lock object will be held any time the listener is invoked, and may also be acquired
+ * and released during the course of invoking any public methods. Holding the given lock ensures
+ * that provider state cannot change except as result of an explicit call by the owner of the
+ * lock into this class. The client is reponsible for ensuring this cannot cause deadlock.
+ *
+ * The client should expect that it may being to receive callbacks as soon as this constructor
+ * is invoked.
+ */
+ public MockableLocationProvider(Context context, Object ownerLock, Listener listener) {
+ // using a direct executor is acceptable because all inbound calls are delegated to the
+ // actual provider implementations which will use their own executors
+ super(context, Runnable::run, Collections.emptySet());
+ mOwnerLock = ownerLock;
+ mRequest = ProviderRequest.EMPTY_REQUEST;
+
+ setListener(listener);
+ }
+
+ /**
+ * Returns the current provider implementation. May be null if there is no current
+ * implementation.
+ */
+ @Nullable
+ public AbstractLocationProvider getProvider() {
+ synchronized (mOwnerLock) {
+ return mProvider;
+ }
+ }
+
+ /**
+ * Sets the real provider implementation, replacing any previous real provider implementation.
+ * May cause an inline invocation of {@link Listener#onStateChanged(State, State)} if this
+ * results in a state change.
+ */
+ public void setRealProvider(@Nullable AbstractLocationProvider provider) {
+ synchronized (mOwnerLock) {
+ if (mRealProvider == provider) {
+ return;
+ }
+
+ mRealProvider = provider;
+ if (!isMock()) {
+ setProviderLocked(mRealProvider);
+ }
+ }
+ }
+
+ /**
+ * Sets the mock provider implementation, replacing any previous mock provider implementation.
+ * Mock implementations are always used instead of real implementations if set. May cause an
+ * inline invocation of {@link Listener#onStateChanged(State, State)} if this results in a
+ * state change.
+ */
+ public void setMockProvider(@Nullable MockProvider provider) {
+ synchronized (mOwnerLock) {
+ if (mMockProvider == provider) {
+ return;
+ }
+
+ mMockProvider = provider;
+ if (mMockProvider != null) {
+ setProviderLocked(mMockProvider);
+ } else {
+ setProviderLocked(mRealProvider);
+ }
+ }
+ }
+
+ @GuardedBy("mOwnerLock")
+ private void setProviderLocked(@Nullable AbstractLocationProvider provider) {
+ if (mProvider == provider) {
+ return;
+ }
+
+ AbstractLocationProvider oldProvider = mProvider;
+ mProvider = provider;
+
+ if (oldProvider != null) {
+ // do this after switching the provider - so even if the old provider is using a direct
+ // executor, if it re-enters this class within setRequest(), it will be ignored
+ oldProvider.setListener(null);
+ oldProvider.setRequest(ProviderRequest.EMPTY_REQUEST);
+ }
+
+ State newState;
+ if (mProvider != null) {
+ newState = mProvider.setListener(new ListenerWrapper(mProvider));
+ } else {
+ newState = State.EMPTY_STATE;
+ }
+
+ ProviderRequest oldRequest = mRequest;
+ setState(newState);
+
+ if (mProvider != null && oldRequest == mRequest) {
+ mProvider.setRequest(mRequest);
+ }
+ }
+
+ /**
+ * Returns true if the current active provider implementation is the mock implementation, and
+ * false otherwise.
+ */
+ public boolean isMock() {
+ synchronized (mOwnerLock) {
+ return mMockProvider != null && mProvider == mMockProvider;
+ }
+ }
+
+ /**
+ * Sets the mock provider implementation's enabled state. Will throw an exception if the mock
+ * provider is not currently the active implementation.
+ */
+ public void setMockProviderEnabled(boolean enabled) {
+ synchronized (mOwnerLock) {
+ Preconditions.checkState(isMock());
+ mMockProvider.setProviderEnabled(enabled);
+ }
+ }
+ /**
+ * Sets the mock provider implementation's location. Will throw an exception if the mock
+ * provider is not currently the active implementation.
+ */
+ public void setMockProviderLocation(Location location) {
+ synchronized (mOwnerLock) {
+ Preconditions.checkState(isMock());
+ mMockProvider.setProviderLocation(location);
+ }
+ }
+
+ @Override
+ public State getState() {
+ return super.getState();
+ }
+
+ /**
+ * Returns the current location request.
+ */
+ public ProviderRequest getCurrentRequest() {
+ synchronized (mOwnerLock) {
+ return mRequest;
+ }
+ }
+
+ protected void onSetRequest(ProviderRequest request) {
+ synchronized (mOwnerLock) {
+ if (request == mRequest) {
+ return;
+ }
+
+ mRequest = request;
+
+ if (mProvider != null) {
+ mProvider.setRequest(request);
+ }
+ }
+ }
+
+ protected void onExtraCommand(int uid, int pid, String command, Bundle extras) {
+ synchronized (mOwnerLock) {
+ if (mProvider != null) {
+ mProvider.sendExtraCommand(uid, pid, command, extras);
+ }
+ }
+ }
+
+ /**
+ * Dumps the current provider implementation.
+ */
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ AbstractLocationProvider provider;
+ synchronized (mOwnerLock) {
+ provider = mProvider;
+ pw.println("request=" + mRequest);
+ }
+
+ if (provider != null) {
+ // dump outside the lock in case the provider wants to acquire its own locks, and since
+ // the default provider dump behavior does not move things onto the provider thread...
+ provider.dump(fd, pw, args);
+ }
+ }
+
+ // ensures that callbacks from the incorrect provider are never visible to clients - this
+ // requires holding the owner's lock for the duration of the callback
+ private class ListenerWrapper implements Listener {
+
+ private final AbstractLocationProvider mListenerProvider;
+
+ private ListenerWrapper(AbstractLocationProvider listenerProvider) {
+ mListenerProvider = listenerProvider;
+ }
+
+ @Override
+ public final void onStateChanged(State oldState, State newState) {
+ synchronized (mOwnerLock) {
+ if (mListenerProvider != mProvider) {
+ return;
+ }
+
+ setState(newState);
+ }
+ }
+
+ @Override
+ public final void onReportLocation(Location location) {
+ synchronized (mOwnerLock) {
+ if (mListenerProvider != mProvider) {
+ return;
+ }
+
+ reportLocation(location);
+ }
+ }
+
+ @Override
+ public final void onReportLocation(List<Location> locations) {
+ synchronized (mOwnerLock) {
+ if (mListenerProvider != mProvider) {
+ return;
+ }
+
+ reportLocation(locations);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/location/PassiveProvider.java b/services/core/java/com/android/server/location/PassiveProvider.java
index 639b1eb1ed5e..b33877069d70 100644
--- a/services/core/java/com/android/server/location/PassiveProvider.java
+++ b/services/core/java/com/android/server/location/PassiveProvider.java
@@ -19,7 +19,6 @@ package com.android.server.location;
import android.content.Context;
import android.location.Criteria;
import android.location.Location;
-import android.os.WorkSource;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
@@ -37,13 +36,22 @@ import java.io.PrintWriter;
public class PassiveProvider extends AbstractLocationProvider {
private static final ProviderProperties PROPERTIES = new ProviderProperties(
- false, false, false, false, false, false, false,
- Criteria.POWER_LOW, Criteria.ACCURACY_COARSE);
+ /* requiresNetwork = */false,
+ /* requiresSatellite = */false,
+ /* requiresCell = */false,
+ /* hasMonetaryCost = */false,
+ /* supportsAltitude = */false,
+ /* supportsSpeed = */false,
+ /* supportsBearing = */false,
+ Criteria.POWER_LOW,
+ Criteria.ACCURACY_COARSE);
- private boolean mReportLocation;
+ private volatile boolean mReportLocation;
- public PassiveProvider(Context context, LocationProviderManager locationProviderManager) {
- super(context, locationProviderManager);
+ public PassiveProvider(Context context) {
+ // using a direct executor is only acceptable because this class is so simple it is trivial
+ // to verify that it does not acquire any locks or re-enter LMS from callbacks
+ super(context, Runnable::run);
mReportLocation = false;
@@ -52,7 +60,7 @@ public class PassiveProvider extends AbstractLocationProvider {
}
@Override
- public void onSetRequest(ProviderRequest request, WorkSource source) {
+ public void onSetRequest(ProviderRequest request) {
mReportLocation = request.reportLocation;
}
@@ -63,7 +71,5 @@ public class PassiveProvider extends AbstractLocationProvider {
}
@Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("report location=" + mReportLocation);
- }
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {}
}
diff --git a/services/core/java/com/android/server/location/UserInfoStore.java b/services/core/java/com/android/server/location/UserInfoStore.java
new file mode 100644
index 000000000000..550f51c7de58
--- /dev/null
+++ b/services/core/java/com/android/server/location/UserInfoStore.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.UserInfo;
+import android.os.Build;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.Preconditions;
+import com.android.server.FgThread;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Provides accessors and listeners for all user info.
+ */
+public class UserInfoStore {
+
+ /**
+ * Listener for current user changes.
+ */
+ public interface UserChangedListener {
+ /**
+ * Called when the current user changes.
+ */
+ void onUserChanged(@UserIdInt int oldUserId, @UserIdInt int newUserId);
+ }
+
+ private final Context mContext;
+ private final CopyOnWriteArrayList<UserChangedListener> mListeners;
+
+ @GuardedBy("this")
+ @Nullable
+ private UserManager mUserManager;
+
+ @GuardedBy("this")
+ @UserIdInt
+ private int mCurrentUserId;
+
+ @GuardedBy("this")
+ @UserIdInt
+ private int mCachedParentUserId;
+ @GuardedBy("this")
+ private int[] mCachedProfileUserIds;
+
+ public UserInfoStore(Context context) {
+ mContext = context;
+ mListeners = new CopyOnWriteArrayList<>();
+
+ mCurrentUserId = UserHandle.USER_NULL;
+ mCachedParentUserId = UserHandle.USER_NULL;
+ mCachedProfileUserIds = new int[]{UserHandle.USER_NULL};
+ }
+
+ /** Called when system is ready. */
+ public synchronized void onSystemReady() {
+ if (mUserManager != null) {
+ return;
+ }
+
+ mUserManager = mContext.getSystemService(UserManager.class);
+
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
+ intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
+ intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
+
+ mContext.registerReceiverAsUser(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action == null) {
+ return;
+ }
+ switch (action) {
+ case Intent.ACTION_USER_SWITCHED:
+ int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
+ UserHandle.USER_NULL);
+ if (userId != UserHandle.USER_NULL) {
+ onUserChanged(userId);
+ }
+ break;
+ case Intent.ACTION_MANAGED_PROFILE_ADDED:
+ case Intent.ACTION_MANAGED_PROFILE_REMOVED:
+ onUserProfilesChanged();
+ break;
+ }
+ }
+ }, UserHandle.ALL, intentFilter, null, FgThread.getHandler());
+
+ mCurrentUserId = ActivityManager.getCurrentUser();
+ }
+
+ /**
+ * Adds a listener for user changed events.
+ */
+ public void addListener(UserChangedListener listener) {
+ mListeners.add(listener);
+ }
+
+ /**
+ * Removes a listener for user changed events.
+ */
+ public void removeListener(UserChangedListener listener) {
+ mListeners.remove(listener);
+ }
+
+ private void onUserChanged(@UserIdInt int newUserId) {
+ int oldUserId;
+ synchronized (this) {
+ if (newUserId == mCurrentUserId) {
+ return;
+ }
+
+ oldUserId = mCurrentUserId;
+ mCurrentUserId = newUserId;
+ }
+
+ for (UserChangedListener listener : mListeners) {
+ listener.onUserChanged(oldUserId, newUserId);
+ }
+ }
+
+ private synchronized void onUserProfilesChanged() {
+ // this intent is only sent to the current user
+ if (mCachedParentUserId == mCurrentUserId) {
+ mCachedParentUserId = UserHandle.USER_NULL;
+ mCachedProfileUserIds = null;
+ }
+ }
+
+ /**
+ * Returns the user id of the current user.
+ */
+ @UserIdInt
+ public synchronized int getCurrentUserId() {
+ return mCurrentUserId;
+ }
+
+ /**
+ * Returns true if the given user id is either the current user or a profile of the current
+ * user.
+ */
+ public synchronized boolean isCurrentUserOrProfile(@UserIdInt int userId) {
+ return userId == mCurrentUserId || ArrayUtils.contains(
+ getProfileUserIdsForParentUser(mCurrentUserId), userId);
+ }
+
+ /**
+ * Returns the parent user id of the given user id, or the user id itself if the user id either
+ * is a parent or has no profiles.
+ */
+ @UserIdInt
+ public synchronized int getParentUserId(@UserIdInt int userId) {
+ int parentUserId;
+ if (userId == mCachedParentUserId || ArrayUtils.contains(mCachedProfileUserIds, userId)) {
+ parentUserId = mCachedParentUserId;
+ } else {
+ Preconditions.checkState(mUserManager != null);
+
+ UserInfo userInfo = mUserManager.getProfileParent(userId);
+ if (userInfo != null) {
+ parentUserId = userInfo.id;
+ } else {
+ // getProfileParent() returns null if the userId is already the parent...
+ parentUserId = userId;
+ }
+
+ // force profiles into cache
+ getProfileUserIdsForParentUser(parentUserId);
+ }
+
+ return parentUserId;
+ }
+
+ @GuardedBy("this")
+ private int[] getProfileUserIdsForParentUser(@UserIdInt int parentUserId) {
+ Preconditions.checkState(mUserManager != null);
+
+ if (Build.IS_DEBUGGABLE) {
+ Preconditions.checkArgument(mUserManager.getProfileParent(parentUserId) == null);
+ }
+
+ if (parentUserId != mCachedParentUserId) {
+ mCachedParentUserId = parentUserId;
+ mCachedProfileUserIds = mUserManager.getProfileIdsWithDisabled(parentUserId);
+ }
+
+ return mCachedProfileUserIds;
+ }
+
+ /**
+ * Dump info for debugging.
+ */
+ public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("Current User: " + mCurrentUserId + " " + Arrays.toString(
+ getProfileUserIdsForParentUser(mCurrentUserId)));
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index d2e54f9cd64c..46ea9d11d1dc 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -25,11 +25,13 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserManager;
import android.util.Slog;
+import android.util.StatsLog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.RebootEscrowListener;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -109,20 +111,50 @@ class RebootEscrowManager {
}
void loadRebootEscrowDataIfAvailable() {
+ List<UserInfo> users = mUserManager.getUsers();
+ List<UserInfo> rebootEscrowUsers = new ArrayList<>();
+ for (UserInfo user : users) {
+ if (mCallbacks.isUserSecure(user.id) && mStorage.hasRebootEscrow(user.id)) {
+ rebootEscrowUsers.add(user);
+ }
+ }
+
+ if (rebootEscrowUsers.isEmpty()) {
+ return;
+ }
+
+ SecretKeySpec escrowKey = getAndClearRebootEscrowKey();
+ if (escrowKey == null) {
+ Slog.w(TAG, "Had reboot escrow data for users, but no key; removing escrow storage.");
+ for (UserInfo user : users) {
+ mStorage.removeRebootEscrow(user.id);
+ }
+ StatsLog.write(StatsLog.REBOOT_ESCROW_RECOVERY_REPORTED, false);
+ return;
+ }
+
+ boolean allUsersUnlocked = true;
+ for (UserInfo user : rebootEscrowUsers) {
+ allUsersUnlocked &= restoreRebootEscrowForUser(user.id, escrowKey);
+ }
+ StatsLog.write(StatsLog.REBOOT_ESCROW_RECOVERY_REPORTED, allUsersUnlocked);
+ }
+
+ private SecretKeySpec getAndClearRebootEscrowKey() {
IRebootEscrow rebootEscrow = mInjector.getRebootEscrow();
if (rebootEscrow == null) {
- return;
+ return null;
}
- final SecretKeySpec escrowKey;
try {
byte[] escrowKeyBytes = rebootEscrow.retrieveKey();
if (escrowKeyBytes == null) {
- return;
+ Slog.w(TAG, "Had reboot escrow data for users, but could not retrieve key");
+ return null;
} else if (escrowKeyBytes.length != 32) {
Slog.e(TAG, "IRebootEscrow returned key of incorrect size "
+ escrowKeyBytes.length);
- return;
+ return null;
}
// Make sure we didn't get the null key.
@@ -132,29 +164,22 @@ class RebootEscrowManager {
}
if (zero == 0) {
Slog.w(TAG, "IRebootEscrow returned an all-zeroes key");
- return;
+ return null;
}
// Overwrite the existing key with the null key
rebootEscrow.storeKey(new byte[32]);
- escrowKey = RebootEscrowData.fromKeyBytes(escrowKeyBytes);
+ return RebootEscrowData.fromKeyBytes(escrowKeyBytes);
} catch (RemoteException e) {
Slog.w(TAG, "Could not retrieve escrow data");
- return;
- }
-
- List<UserInfo> users = mUserManager.getUsers();
- for (UserInfo user : users) {
- if (mCallbacks.isUserSecure(user.id)) {
- restoreRebootEscrowForUser(user.id, escrowKey);
- }
+ return null;
}
}
- private void restoreRebootEscrowForUser(@UserIdInt int userId, SecretKeySpec escrowKey) {
+ private boolean restoreRebootEscrowForUser(@UserIdInt int userId, SecretKeySpec escrowKey) {
if (!mStorage.hasRebootEscrow(userId)) {
- return;
+ return false;
}
try {
@@ -165,9 +190,11 @@ class RebootEscrowManager {
mCallbacks.onRebootEscrowRestored(escrowData.getSpVersion(),
escrowData.getSyntheticPassword(), userId);
+ return true;
} catch (IOException e) {
Slog.w(TAG, "Could not load reboot escrow data for user " + userId, e);
}
+ return false;
}
void callToRebootEscrowIfNeeded(@UserIdInt int userId, byte spVersion,
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
index 29338ba06dc2..52750f392d5b 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
@@ -20,6 +20,7 @@ import static android.security.keystore.recovery.KeyChainProtectionParams.TYPE_L
import android.content.Context;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.security.Scrypt;
import android.security.keystore.recovery.KeyChainProtectionParams;
import android.security.keystore.recovery.KeyChainSnapshot;
@@ -163,16 +164,28 @@ public class KeySyncTask implements Runnable {
}
private void syncKeys() throws RemoteException {
+ int generation = mPlatformKeyManager.getGenerationId(mUserId);
if (mCredentialType == LockPatternUtils.CREDENTIAL_TYPE_NONE) {
// Application keys for the user will not be available for sync.
Log.w(TAG, "Credentials are not set for user " + mUserId);
- int generation = mPlatformKeyManager.getGenerationId(mUserId);
- mPlatformKeyManager.invalidatePlatformKey(mUserId, generation);
+ if (generation < PlatformKeyManager.MIN_GENERATION_ID_FOR_UNLOCKED_DEVICE_REQUIRED
+ || mUserId != UserHandle.USER_SYSTEM) {
+ // Only invalidate keys with legacy protection param.
+ mPlatformKeyManager.invalidatePlatformKey(mUserId, generation);
+ }
return;
}
if (isCustomLockScreen()) {
- Log.w(TAG, "Unsupported credential type " + mCredentialType + "for user " + mUserId);
- mRecoverableKeyStoreDb.invalidateKeysForUserIdOnCustomScreenLock(mUserId);
+ Log.w(TAG, "Unsupported credential type " + mCredentialType + " for user " + mUserId);
+ // Keys will be synced when user starts using non custom screen lock.
+ if (generation < PlatformKeyManager.MIN_GENERATION_ID_FOR_UNLOCKED_DEVICE_REQUIRED
+ || mUserId != UserHandle.USER_SYSTEM) {
+ mRecoverableKeyStoreDb.invalidateKeysForUserIdOnCustomScreenLock(mUserId);
+ }
+ return;
+ }
+ if (mPlatformKeyManager.isDeviceLocked(mUserId) && mUserId == UserHandle.USER_SYSTEM) {
+ Log.w(TAG, "Can't sync keys for locked user " + mUserId);
return;
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
index 0ad6c2a69556..0761cde825b6 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
@@ -67,8 +67,9 @@ import javax.crypto.spec.GCMParameterSpec;
* @hide
*/
public class PlatformKeyManager {
- private static final String TAG = "PlatformKeyManager";
+ static final int MIN_GENERATION_ID_FOR_UNLOCKED_DEVICE_REQUIRED = 1000000;
+ private static final String TAG = "PlatformKeyManager";
private static final String KEY_ALGORITHM = "AES";
private static final int KEY_SIZE_BITS = 256;
private static final String KEY_ALIAS_PREFIX =
@@ -131,14 +132,14 @@ public class PlatformKeyManager {
/**
* Returns {@code true} if the platform key is available. A platform key won't be available if
- * the user has not set up a lock screen.
+ * device is locked.
*
* @param userId The ID of the user to whose lock screen the platform key must be bound.
*
* @hide
*/
- public boolean isAvailable(int userId) {
- return mContext.getSystemService(KeyguardManager.class).isDeviceSecure(userId);
+ public boolean isDeviceLocked(int userId) {
+ return mContext.getSystemService(KeyguardManager.class).isDeviceLocked(userId);
}
/**
@@ -169,7 +170,6 @@ public class PlatformKeyManager {
* @param userId The ID of the user to whose lock screen the platform key must be bound.
* @throws NoSuchAlgorithmException if AES is unavailable - should never happen.
* @throws KeyStoreException if there is an error in AndroidKeyStore.
- * @throws InsecureUserException if the user does not have a lock screen set.
* @throws IOException if there was an issue with local database update.
* @throws RemoteException if there was an issue communicating with {@link IGateKeeperService}.
*
@@ -177,13 +177,8 @@ public class PlatformKeyManager {
*/
@VisibleForTesting
void regenerate(int userId)
- throws NoSuchAlgorithmException, KeyStoreException, InsecureUserException, IOException,
+ throws NoSuchAlgorithmException, KeyStoreException, IOException,
RemoteException {
- if (!isAvailable(userId)) {
- throw new InsecureUserException(String.format(
- Locale.US, "%d does not have a lock screen set.", userId));
- }
-
int generationId = getGenerationId(userId);
int nextId;
if (generationId == -1) {
@@ -192,6 +187,7 @@ public class PlatformKeyManager {
invalidatePlatformKey(userId, generationId);
nextId = generationId + 1;
}
+ generationId = Math.max(generationId, MIN_GENERATION_ID_FOR_UNLOCKED_DEVICE_REQUIRED);
generateAndLoadKey(userId, nextId);
}
@@ -203,7 +199,6 @@ public class PlatformKeyManager {
* @throws KeyStoreException if there was an AndroidKeyStore error.
* @throws UnrecoverableKeyException if the key could not be recovered.
* @throws NoSuchAlgorithmException if AES is unavailable - should never occur.
- * @throws InsecureUserException if the user does not have a lock screen set.
* @throws IOException if there was an issue with local database update.
* @throws RemoteException if there was an issue communicating with {@link IGateKeeperService}.
*
@@ -211,7 +206,7 @@ public class PlatformKeyManager {
*/
public PlatformEncryptionKey getEncryptKey(int userId)
throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException,
- InsecureUserException, IOException, RemoteException {
+ IOException, RemoteException {
init(userId);
try {
// Try to see if the decryption key is still accessible before using the encryption key.
@@ -234,12 +229,11 @@ public class PlatformKeyManager {
* @throws KeyStoreException if there was an AndroidKeyStore error.
* @throws UnrecoverableKeyException if the key could not be recovered.
* @throws NoSuchAlgorithmException if AES is unavailable - should never occur.
- * @throws InsecureUserException if the user does not have a lock screen set.
*
* @hide
*/
private PlatformEncryptionKey getEncryptKeyInternal(int userId) throws KeyStoreException,
- UnrecoverableKeyException, NoSuchAlgorithmException, InsecureUserException {
+ UnrecoverableKeyException, NoSuchAlgorithmException {
int generationId = getGenerationId(userId);
String alias = getEncryptAlias(userId, generationId);
if (!isKeyLoaded(userId, generationId)) {
@@ -258,7 +252,6 @@ public class PlatformKeyManager {
* @throws KeyStoreException if there was an AndroidKeyStore error.
* @throws UnrecoverableKeyException if the key could not be recovered.
* @throws NoSuchAlgorithmException if AES is unavailable - should never occur.
- * @throws InsecureUserException if the user does not have a lock screen set.
* @throws IOException if there was an issue with local database update.
* @throws RemoteException if there was an issue communicating with {@link IGateKeeperService}.
*
@@ -266,7 +259,7 @@ public class PlatformKeyManager {
*/
public PlatformDecryptionKey getDecryptKey(int userId)
throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException,
- InsecureUserException, IOException, RemoteException {
+ IOException, RemoteException {
init(userId);
try {
PlatformDecryptionKey decryptionKey = getDecryptKeyInternal(userId);
@@ -288,12 +281,11 @@ public class PlatformKeyManager {
* @throws KeyStoreException if there was an AndroidKeyStore error.
* @throws UnrecoverableKeyException if the key could not be recovered.
* @throws NoSuchAlgorithmException if AES is unavailable - should never occur.
- * @throws InsecureUserException if the user does not have a lock screen set.
*
* @hide
*/
private PlatformDecryptionKey getDecryptKeyInternal(int userId) throws KeyStoreException,
- UnrecoverableKeyException, NoSuchAlgorithmException, InsecureUserException {
+ UnrecoverableKeyException, NoSuchAlgorithmException {
int generationId = getGenerationId(userId);
String alias = getDecryptAlias(userId, generationId);
if (!isKeyLoaded(userId, generationId)) {
@@ -340,13 +332,8 @@ public class PlatformKeyManager {
* @hide
*/
void init(int userId)
- throws KeyStoreException, NoSuchAlgorithmException, InsecureUserException, IOException,
+ throws KeyStoreException, NoSuchAlgorithmException, IOException,
RemoteException {
- if (!isAvailable(userId)) {
- throw new InsecureUserException(String.format(
- Locale.US, "%d does not have a lock screen set.", userId));
- }
-
int generationId = getGenerationId(userId);
if (isKeyLoaded(userId, generationId)) {
Log.i(TAG, String.format(
@@ -363,6 +350,7 @@ public class PlatformKeyManager {
generationId++;
}
+ generationId = Math.max(generationId, MIN_GENERATION_ID_FOR_UNLOCKED_DEVICE_REQUIRED);
generateAndLoadKey(userId, generationId);
}
@@ -440,12 +428,16 @@ public class PlatformKeyManager {
KeyProtection.Builder decryptionKeyProtection =
new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT)
- .setUserAuthenticationRequired(true)
- .setUserAuthenticationValidityDurationSeconds(
- USER_AUTHENTICATION_VALIDITY_DURATION_SECONDS)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE);
- if (userId != UserHandle.USER_SYSTEM) {
+ // Skip UserAuthenticationRequired for main user
+ if (userId == UserHandle.USER_SYSTEM) {
+ decryptionKeyProtection.setUnlockedDeviceRequired(true);
+ } else {
+ // With setUnlockedDeviceRequired, KeyStore thinks that device is locked .
+ decryptionKeyProtection.setUserAuthenticationRequired(true);
+ decryptionKeyProtection.setUserAuthenticationValidityDurationSeconds(
+ USER_AUTHENTICATION_VALIDITY_DURATION_SECONDS);
// Bind decryption key to secondary profile lock screen secret.
long secureUserId = getGateKeeperService().getSecureUserId(userId);
// TODO(b/124095438): Propagate this failure instead of silently failing.
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index 383d5cf326c0..6d97ed7a69a7 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -19,7 +19,6 @@ package com.android.server.locksettings.recoverablekeystore;
import static android.security.keystore.recovery.RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT;
import static android.security.keystore.recovery.RecoveryController.ERROR_DECRYPTION_FAILED;
import static android.security.keystore.recovery.RecoveryController.ERROR_DOWNGRADE_CERTIFICATE;
-import static android.security.keystore.recovery.RecoveryController.ERROR_INSECURE_USER;
import static android.security.keystore.recovery.RecoveryController.ERROR_INVALID_CERTIFICATE;
import static android.security.keystore.recovery.RecoveryController.ERROR_INVALID_KEY_FORMAT;
import static android.security.keystore.recovery.RecoveryController.ERROR_NO_SNAPSHOT_PENDING;
@@ -46,7 +45,6 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.HexDump;
-import com.android.internal.util.Preconditions;
import com.android.server.locksettings.recoverablekeystore.certificate.CertParsingException;
import com.android.server.locksettings.recoverablekeystore.certificate.CertUtils;
import com.android.server.locksettings.recoverablekeystore.certificate.CertValidationException;
@@ -76,8 +74,9 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
-import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
import javax.crypto.AEADBadTagException;
@@ -89,13 +88,14 @@ import javax.crypto.AEADBadTagException;
*/
public class RecoverableKeyStoreManager {
private static final String TAG = "RecoverableKeyStoreMgr";
+ private static final long SYNC_DELAY_MILLIS = 2000;
private static RecoverableKeyStoreManager mInstance;
private final Context mContext;
private final RecoverableKeyStoreDb mDatabase;
private final RecoverySessionStorage mRecoverySessionStorage;
- private final ExecutorService mExecutorService;
+ private final ScheduledExecutorService mExecutorService;
private final RecoverySnapshotListenersStorage mListenersStorage;
private final RecoverableKeyGenerator mRecoverableKeyGenerator;
private final RecoverySnapshotStorage mSnapshotStorage;
@@ -136,7 +136,7 @@ public class RecoverableKeyStoreManager {
context.getApplicationContext(),
db,
new RecoverySessionStorage(),
- Executors.newSingleThreadExecutor(),
+ Executors.newScheduledThreadPool(1),
snapshotStorage,
new RecoverySnapshotListenersStorage(),
platformKeyManager,
@@ -152,7 +152,7 @@ public class RecoverableKeyStoreManager {
Context context,
RecoverableKeyStoreDb recoverableKeyStoreDb,
RecoverySessionStorage recoverySessionStorage,
- ExecutorService executorService,
+ ScheduledExecutorService executorService,
RecoverySnapshotStorage snapshotStorage,
RecoverySnapshotListenersStorage listenersStorage,
PlatformKeyManager platformKeyManager,
@@ -724,8 +724,6 @@ public class RecoverableKeyStoreManager {
throw new RuntimeException(e);
} catch (KeyStoreException | UnrecoverableKeyException | IOException e) {
throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
- } catch (InsecureUserException e) {
- throw new ServiceSpecificException(ERROR_INSECURE_USER, e.getMessage());
}
try {
@@ -793,8 +791,6 @@ public class RecoverableKeyStoreManager {
throw new RuntimeException(e);
} catch (KeyStoreException | UnrecoverableKeyException | IOException e) {
throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
- } catch (InsecureUserException e) {
- throw new ServiceSpecificException(ERROR_INSECURE_USER, e.getMessage());
}
try {
@@ -915,7 +911,7 @@ public class RecoverableKeyStoreManager {
int storedHashType, @NonNull byte[] credential, int userId) {
// So as not to block the critical path unlocking the phone, defer to another thread.
try {
- mExecutorService.execute(KeySyncTask.newInstance(
+ mExecutorService.schedule(KeySyncTask.newInstance(
mContext,
mDatabase,
mSnapshotStorage,
@@ -923,7 +919,10 @@ public class RecoverableKeyStoreManager {
userId,
storedHashType,
credential,
- /*credentialUpdated=*/ false));
+ /*credentialUpdated=*/ false),
+ SYNC_DELAY_MILLIS,
+ TimeUnit.MILLISECONDS
+ );
} catch (NoSuchAlgorithmException e) {
Log.wtf(TAG, "Should never happen - algorithm unavailable for KeySync", e);
} catch (KeyStoreException e) {
@@ -947,7 +946,7 @@ public class RecoverableKeyStoreManager {
int userId) {
// So as not to block the critical path unlocking the phone, defer to another thread.
try {
- mExecutorService.execute(KeySyncTask.newInstance(
+ mExecutorService.schedule(KeySyncTask.newInstance(
mContext,
mDatabase,
mSnapshotStorage,
@@ -955,7 +954,10 @@ public class RecoverableKeyStoreManager {
userId,
storedHashType,
credential,
- /*credentialUpdated=*/ true));
+ /*credentialUpdated=*/ true),
+ SYNC_DELAY_MILLIS,
+ TimeUnit.MILLISECONDS
+ );
} catch (NoSuchAlgorithmException e) {
Log.wtf(TAG, "Should never happen - algorithm unavailable for KeySync", e);
} catch (KeyStoreException e) {
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index 9ca302edd204..9c9a4121830f 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -47,11 +47,11 @@ abstract class MediaRoute2Provider {
public abstract void requestCreateSession(String packageName, String routeId,
String routeType, long requestId);
- public abstract void releaseSession(int sessionId);
+ public abstract void releaseSession(String sessionId);
- public abstract void selectRoute(int sessionId, String routeId);
- public abstract void deselectRoute(int sessionId, String routeId);
- public abstract void transferToRoute(int sessionId, String routeId);
+ public abstract void selectRoute(String sessionId, String routeId);
+ public abstract void deselectRoute(String sessionId, String routeId);
+ public abstract void transferToRoute(String sessionId, String routeId);
public abstract void sendControlRequest(String routeId, Intent request);
public abstract void requestSetVolume(String routeId, int volume);
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
index 5cc2b16e073a..635983575226 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
@@ -86,7 +86,7 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv
}
@Override
- public void releaseSession(int sessionId) {
+ public void releaseSession(String sessionId) {
if (mConnectionReady) {
mActiveConnection.releaseSession(sessionId);
updateBinding();
@@ -94,21 +94,21 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv
}
@Override
- public void selectRoute(int sessionId, String routeId) {
+ public void selectRoute(String sessionId, String routeId) {
if (mConnectionReady) {
mActiveConnection.selectRoute(sessionId, routeId);
}
}
@Override
- public void deselectRoute(int sessionId, String routeId) {
+ public void deselectRoute(String sessionId, String routeId) {
if (mConnectionReady) {
mActiveConnection.deselectRoute(sessionId, routeId);
}
}
@Override
- public void transferToRoute(int sessionId, String routeId) {
+ public void transferToRoute(String sessionId, String routeId) {
if (mConnectionReady) {
mActiveConnection.transferToRoute(sessionId, routeId);
}
@@ -302,6 +302,11 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv
+ mComponentName);
return;
}
+
+ sessionInfo = new RouteSessionInfo.Builder(sessionInfo)
+ .setProviderId(getUniqueId())
+ .build();
+
mCallback.onSessionInfoChanged(this, sessionInfo);
}
@@ -355,7 +360,7 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv
}
}
- public void releaseSession(int sessionId) {
+ public void releaseSession(String sessionId) {
try {
mProvider.releaseSession(sessionId);
} catch (RemoteException ex) {
@@ -363,7 +368,7 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv
}
}
- public void selectRoute(int sessionId, String routeId) {
+ public void selectRoute(String sessionId, String routeId) {
try {
mProvider.selectRoute(sessionId, routeId);
} catch (RemoteException ex) {
@@ -371,7 +376,7 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv
}
}
- public void deselectRoute(int sessionId, String routeId) {
+ public void deselectRoute(String sessionId, String routeId) {
try {
mProvider.deselectRoute(sessionId, routeId);
} catch (RemoteException ex) {
@@ -379,7 +384,7 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv
}
}
- public void transferToRoute(int sessionId, String routeId) {
+ public void transferToRoute(String sessionId, String routeId) {
try {
mProvider.transferToRoute(sessionId, routeId);
} catch (RemoteException ex) {
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index b48243279d9e..487ab5201278 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -16,6 +16,9 @@
package com.android.server.media;
+import static android.media.MediaRouter2Utils.getOriginalId;
+import static android.media.MediaRouter2Utils.getProviderId;
+
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import android.annotation.NonNull;
@@ -197,6 +200,9 @@ class MediaRouter2ServiceImpl {
MediaRoute2Info route) {
Objects.requireNonNull(client, "client must not be null");
Objects.requireNonNull(route, "route must not be null");
+ if (TextUtils.isEmpty(uniqueSessionId)) {
+ throw new IllegalArgumentException("uniqueSessionId must not be empty");
+ }
final long token = Binder.clearCallingIdentity();
try {
@@ -213,6 +219,9 @@ class MediaRouter2ServiceImpl {
MediaRoute2Info route) {
Objects.requireNonNull(client, "client must not be null");
Objects.requireNonNull(route, "route must not be null");
+ if (TextUtils.isEmpty(uniqueSessionId)) {
+ throw new IllegalArgumentException("uniqueSessionId must not be empty");
+ }
final long token = Binder.clearCallingIdentity();
try {
@@ -228,6 +237,9 @@ class MediaRouter2ServiceImpl {
MediaRoute2Info route) {
Objects.requireNonNull(client, "client must not be null");
Objects.requireNonNull(route, "route must not be null");
+ if (TextUtils.isEmpty(uniqueSessionId)) {
+ throw new IllegalArgumentException("uniqueSessionId must not be empty");
+ }
final long token = Binder.clearCallingIdentity();
try {
@@ -241,6 +253,9 @@ class MediaRouter2ServiceImpl {
public void releaseSession(IMediaRouter2Client client, String uniqueSessionId) {
Objects.requireNonNull(client, "client must not be null");
+ if (TextUtils.isEmpty(uniqueSessionId)) {
+ throw new IllegalArgumentException("uniqueSessionId must not be empty");
+ }
final long token = Binder.clearCallingIdentity();
try {
@@ -607,7 +622,7 @@ class MediaRouter2ServiceImpl {
}
long uniqueRequestId = toUniqueRequestId(managerRecord.mClientId, requestId);
if (clientRecord != null && managerRecord.mTrusted) {
- //TODO: select category properly
+ //TODO: select route type properly
requestCreateSessionLocked(clientRecord.mClient, route,
route.getRouteTypes().get(0), uniqueRequestId);
}
@@ -1002,8 +1017,7 @@ class MediaRouter2ServiceImpl {
if (provider == null) {
return;
}
- provider.selectRoute(RouteSessionInfo.getSessionId(uniqueSessionId),
- route.getOriginalId());
+ provider.selectRoute(getOriginalId(uniqueSessionId), route.getOriginalId());
}
private void deselectRouteOnHandler(@NonNull Client2Record clientRecord,
@@ -1019,8 +1033,7 @@ class MediaRouter2ServiceImpl {
if (provider == null) {
return;
}
- provider.deselectRoute(RouteSessionInfo.getSessionId(uniqueSessionId),
- route.getOriginalId());
+ provider.deselectRoute(getOriginalId(uniqueSessionId), route.getOriginalId());
}
private void transferToRouteOnHandler(@NonNull Client2Record clientRecord,
@@ -1036,7 +1049,7 @@ class MediaRouter2ServiceImpl {
if (provider == null) {
return;
}
- provider.transferToRoute(RouteSessionInfo.getSessionId(uniqueSessionId),
+ provider.transferToRoute(getOriginalId(uniqueSessionId),
route.getOriginalId());
}
@@ -1068,9 +1081,9 @@ class MediaRouter2ServiceImpl {
return false;
}
- final Integer sessionId = RouteSessionInfo.getSessionId(uniqueSessionId);
+ final String sessionId = getOriginalId(uniqueSessionId);
if (sessionId == null) {
- Slog.w(TAG, "Failed to get int session id from unique session id. "
+ Slog.w(TAG, "Failed to get original session id from unique session id. "
+ "uniqueSessionId=" + uniqueSessionId);
return false;
}
@@ -1093,14 +1106,14 @@ class MediaRouter2ServiceImpl {
return;
}
- final String providerId = RouteSessionInfo.getProviderId(uniqueSessionId);
+ final String providerId = getProviderId(uniqueSessionId);
if (providerId == null) {
Slog.w(TAG, "Ignoring releasing session with invalid unique session ID. "
+ "uniqueSessionId=" + uniqueSessionId);
return;
}
- final Integer sessionId = RouteSessionInfo.getSessionId(uniqueSessionId);
+ final String sessionId = getOriginalId(uniqueSessionId);
if (sessionId == null) {
Slog.w(TAG, "Ignoring releasing session with invalid unique session ID. "
+ "uniqueSessionId=" + uniqueSessionId + " providerId=" + providerId);
@@ -1146,15 +1159,15 @@ class MediaRouter2ServiceImpl {
}
String originalRouteId = matchingRequest.mRoute.getId();
- String originalCategory = matchingRequest.mRouteType;
+ String originalRouteType = matchingRequest.mRouteType;
Client2Record client2Record = matchingRequest.mClientRecord;
if (!sessionInfo.getSelectedRoutes().contains(originalRouteId)
- || !TextUtils.equals(originalCategory,
+ || !TextUtils.equals(originalRouteType,
sessionInfo.getRouteType())) {
Slog.w(TAG, "Created session doesn't match the original request."
+ " originalRouteId=" + originalRouteId
- + ", originalCategory=" + originalCategory + ", requestId=" + requestId
+ + ", originalRouteType=" + originalRouteType + ", requestId=" + requestId
+ ", sessionInfo=" + sessionInfo);
notifySessionCreationFailed(matchingRequest.mClientRecord,
toClientRequestId(requestId));
@@ -1164,41 +1177,34 @@ class MediaRouter2ServiceImpl {
// Succeeded
notifySessionCreated(matchingRequest.mClientRecord,
sessionInfo, toClientRequestId(requestId));
- mSessionToClientMap.put(sessionInfo.getUniqueSessionId(), client2Record);
+ mSessionToClientMap.put(sessionInfo.getId(), client2Record);
// TODO: Tell managers for the session creation
}
private void onSessionInfoChangedOnHandler(@NonNull MediaRoute2Provider provider,
@NonNull RouteSessionInfo sessionInfo) {
- RouteSessionInfo sessionInfoWithProviderId = new RouteSessionInfo.Builder(sessionInfo)
- .setProviderId(provider.getUniqueId())
- .build();
Client2Record client2Record = mSessionToClientMap.get(
- sessionInfoWithProviderId.getUniqueSessionId());
+ sessionInfo.getId());
if (client2Record == null) {
- Slog.w(TAG, "No matching client found for session=" + sessionInfoWithProviderId);
+ Slog.w(TAG, "No matching client found for session=" + sessionInfo);
// TODO: Tell managers for the session update
return;
}
- notifySessionInfoChanged(client2Record, sessionInfoWithProviderId);
+ notifySessionInfoChanged(client2Record, sessionInfo);
// TODO: Tell managers for the session update
}
private void onSessionReleasedOnHandler(@NonNull MediaRoute2Provider provider,
@NonNull RouteSessionInfo sessionInfo) {
- RouteSessionInfo sessionInfoWithProviderId = new RouteSessionInfo.Builder(sessionInfo)
- .setProviderId(provider.getUniqueId())
- .build();
- Client2Record client2Record = mSessionToClientMap.get(
- sessionInfoWithProviderId.getUniqueSessionId());
+ Client2Record client2Record = mSessionToClientMap.get(sessionInfo.getId());
if (client2Record == null) {
- Slog.w(TAG, "No matching client found for session=" + sessionInfoWithProviderId);
+ Slog.w(TAG, "No matching client found for session=" + sessionInfo);
// TODO: Tell managers for the session release
return;
}
- notifySessionReleased(client2Record, sessionInfoWithProviderId);
+ notifySessionReleased(client2Record, sessionInfo);
// TODO: Tell managers for the session release
}
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index daf603012391..0ea4e63231d4 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -48,8 +48,8 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
static final String BLUETOOTH_ROUTE_ID = "BLUETOOTH_ROUTE";
// TODO: Move these to a proper place
- public static final String CATEGORY_LIVE_AUDIO = "android.media.intent.category.LIVE_AUDIO";
- public static final String CATEGORY_LIVE_VIDEO = "android.media.intent.category.LIVE_VIDEO";
+ public static final String TYPE_LIVE_AUDIO = "android.media.intent.route.TYPE_LIVE_AUDIO";
+ public static final String TYPE_LIVE_VIDEO = "android.media.intent.route.TYPE_LIVE_VIDEO";
private final AudioManager mAudioManager;
private final IAudioService mAudioService;
@@ -97,22 +97,22 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
}
@Override
- public void releaseSession(int sessionId) {
+ public void releaseSession(String sessionId) {
// Do nothing
}
@Override
- public void selectRoute(int sessionId, String routeId) {
+ public void selectRoute(String sessionId, String routeId) {
//TODO: implement method
}
@Override
- public void deselectRoute(int sessionId, String routeId) {
+ public void deselectRoute(String sessionId, String routeId) {
//TODO: implement method
}
@Override
- public void transferToRoute(int sessionId, String routeId) {
+ public void transferToRoute(String sessionId, String routeId) {
//TODO: implement method
}
@@ -141,8 +141,8 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
: MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
.setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
.setVolume(mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC))
- .addRouteType(CATEGORY_LIVE_AUDIO)
- .addRouteType(CATEGORY_LIVE_VIDEO)
+ .addRouteType(TYPE_LIVE_AUDIO)
+ .addRouteType(TYPE_LIVE_VIDEO)
.build();
AudioRoutesInfo newAudioRoutes = null;
@@ -181,8 +181,8 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
: MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
.setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
.setVolume(mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC))
- .addRouteType(CATEGORY_LIVE_AUDIO)
- .addRouteType(CATEGORY_LIVE_VIDEO)
+ .addRouteType(TYPE_LIVE_AUDIO)
+ .addRouteType(TYPE_LIVE_VIDEO)
.build();
if (!TextUtils.equals(newRoutes.bluetoothName, mCurAudioRoutesInfo.bluetoothName)) {
@@ -193,7 +193,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
mCurAudioRoutesInfo.bluetoothName)
.setDescription(mContext.getResources().getText(
R.string.bluetooth_a2dp_audio_route_name).toString())
- .addRouteType(CATEGORY_LIVE_AUDIO)
+ .addRouteType(TYPE_LIVE_AUDIO)
.build();
} else {
mBluetoothA2dpRoute = null;
diff --git a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
index d66fd574ef4a..da2ca9b0cfde 100644
--- a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
@@ -43,7 +43,8 @@ public class NotificationChannelExtractor implements NotificationSignalExtractor
}
record.updateNotificationChannel(mConfig.getNotificationChannel(record.sbn.getPackageName(),
- record.sbn.getUid(), record.getChannel().getId(), false));
+ record.sbn.getUid(), record.getChannel().getId(),
+ record.getNotification().getShortcutId(), false));
return null;
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index a4f4d079df27..0e08033e0f68 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -25,6 +25,7 @@ import static android.app.Notification.FLAG_INSISTENT;
import static android.app.Notification.FLAG_NO_CLEAR;
import static android.app.Notification.FLAG_ONGOING_EVENT;
import static android.app.Notification.FLAG_ONLY_ALERT_ONCE;
+import static android.app.NotificationChannel.CONVERSATION_CHANNEL_ID_FORMAT;
import static android.app.NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED;
import static android.app.NotificationManager.ACTION_AUTOMATIC_ZEN_RULE_STATUS_CHANGED;
import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED;
@@ -236,6 +237,7 @@ 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.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.internal.util.function.TriPredicate;
import com.android.server.DeviceIdleInternal;
@@ -1008,12 +1010,14 @@ public class NotificationManagerService extends SystemService {
if (clearEffects) {
clearEffects();
}
+ mAssistants.onPanelRevealed(items);
}
@Override
public void onPanelHidden() {
MetricsLogger.hidden(getContext(), MetricsEvent.NOTIFICATION_PANEL);
EventLogTags.writeNotificationPanelHidden();
+ mAssistants.onPanelHidden();
}
@Override
@@ -1060,6 +1064,7 @@ public class NotificationManagerService extends SystemService {
reportSeen(r);
}
r.setVisibility(true, nv.rank, nv.count);
+ mAssistants.notifyAssistantVisibilityChangedLocked(r.sbn, true);
boolean isHun = (nv.location
== NotificationVisibility.NotificationLocation.LOCATION_FIRST_HEADS_UP);
// hasBeenVisiblyExpanded must be called after updating the expansion state of
@@ -1078,6 +1083,7 @@ public class NotificationManagerService extends SystemService {
NotificationRecord r = mNotificationsByKey.get(nv.key);
if (r == null) continue;
r.setVisibility(false, nv.rank, nv.count);
+ mAssistants.notifyAssistantVisibilityChangedLocked(r.sbn, false);
nv.recycle();
}
}
@@ -2219,8 +2225,8 @@ public class NotificationManagerService extends SystemService {
maybeNotifyChannelOwner(pkg, uid, preUpdate, channel);
if (!fromListener) {
- final NotificationChannel modifiedChannel =
- mPreferencesHelper.getNotificationChannel(pkg, uid, channel.getId(), false);
+ final NotificationChannel modifiedChannel = mPreferencesHelper.getNotificationChannel(
+ pkg, uid, channel.getId(), false);
mListeners.notifyNotificationChannelChanged(
pkg, UserHandle.getUserHandleForUid(uid),
modifiedChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
@@ -2481,7 +2487,7 @@ public class NotificationManagerService extends SystemService {
.setUid(r.sbn.getUid())
.setChannelId(r.getChannel().getId())
.setChannelName(r.getChannel().getName().toString())
- .setPostedTimeMs(r.sbn.getPostTime())
+ .setPostedTimeMs(System.currentTimeMillis())
.setTitle(getHistoryTitle(r.getNotification()))
.setText(getHistoryText(
r.sbn.getPackageContext(getContext()), r.getNotification()))
@@ -3017,21 +3023,43 @@ public class NotificationManagerService extends SystemService {
@Override
public void createNotificationChannels(String pkg,
- ParceledListSlice channelsList) throws RemoteException {
+ ParceledListSlice channelsList) {
checkCallerIsSystemOrSameApp(pkg);
createNotificationChannelsImpl(pkg, Binder.getCallingUid(), channelsList);
}
@Override
public void createNotificationChannelsForPackage(String pkg, int uid,
- ParceledListSlice channelsList) throws RemoteException {
- checkCallerIsSystem();
+ ParceledListSlice channelsList) {
+ enforceSystemOrSystemUI("only system can call this");
createNotificationChannelsImpl(pkg, uid, channelsList);
}
@Override
+ public void createConversationNotificationChannelForPackage(String pkg, int uid,
+ NotificationChannel parentChannel, String conversationId) {
+ enforceSystemOrSystemUI("only system can call this");
+ Preconditions.checkNotNull(parentChannel);
+ Preconditions.checkNotNull(conversationId);
+ String parentId = parentChannel.getId();
+ NotificationChannel conversationChannel = parentChannel;
+ conversationChannel.setId(String.format(
+ CONVERSATION_CHANNEL_ID_FORMAT, parentId, conversationId));
+ conversationChannel.setConversationId(parentId, conversationId);
+ createNotificationChannelsImpl(
+ pkg, uid, new ParceledListSlice(Arrays.asList(conversationChannel)));
+ }
+
+ @Override
public NotificationChannel getNotificationChannel(String callingPkg, int userId,
String targetPkg, String channelId) {
+ return getConversationNotificationChannel(
+ callingPkg, userId, targetPkg, channelId, null);
+ }
+
+ @Override
+ public NotificationChannel getConversationNotificationChannel(String callingPkg, int userId,
+ String targetPkg, String channelId, String conversationId) {
if (canNotifyAsPackage(callingPkg, targetPkg, userId)
|| isCallingUidSystem()) {
int targetUid = -1;
@@ -3041,7 +3069,8 @@ public class NotificationManagerService extends SystemService {
/* ignore */
}
return mPreferencesHelper.getNotificationChannel(
- targetPkg, targetUid, channelId, false /* includeDeleted */);
+ targetPkg, targetUid, channelId, conversationId,
+ false /* includeDeleted */);
}
throw new SecurityException("Pkg " + callingPkg
+ " cannot read channels for " + targetPkg + " in " + userId);
@@ -3072,6 +3101,30 @@ public class NotificationManagerService extends SystemService {
}
@Override
+ public void deleteConversationNotificationChannels(String pkg, int uid,
+ String conversationId) {
+ checkCallerIsSystem();
+ final int callingUid = Binder.getCallingUid();
+ List<NotificationChannel> channels =
+ mPreferencesHelper.getNotificationChannelsByConversationId(
+ pkg, uid, conversationId);
+ if (!channels.isEmpty()) {
+ for (NotificationChannel nc : channels) {
+ cancelAllNotificationsInt(MY_UID, MY_PID, pkg, nc.getId(), 0, 0, true,
+ UserHandle.getUserId(callingUid), REASON_CHANNEL_BANNED, null);
+ mPreferencesHelper.deleteNotificationChannel(pkg, callingUid, nc.getId());
+ mListeners.notifyNotificationChannelChanged(pkg,
+ UserHandle.getUserHandleForUid(callingUid),
+ mPreferencesHelper.getNotificationChannel(
+ pkg, callingUid, nc.getId(), true),
+ NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
+ }
+ handleSavePolicyFile();
+ }
+ }
+
+
+ @Override
public NotificationChannelGroup getNotificationChannelGroup(String pkg, String groupId) {
checkCallerIsSystemOrSameApp(pkg);
return mPreferencesHelper.getNotificationChannelGroupWithChannels(
@@ -5203,7 +5256,8 @@ public class NotificationManagerService extends SystemService {
channelId = (new Notification.TvExtender(notification)).getChannelId();
}
final NotificationChannel channel = mPreferencesHelper.getNotificationChannel(pkg,
- notificationUid, channelId, false /* includeDeleted */);
+ notificationUid, channelId, notification.getShortcutId(),
+ false /* includeDeleted */);
if (channel == null) {
final String noChannelStr = "No Channel found for "
+ "pkg=" + pkg
@@ -8296,6 +8350,32 @@ public class NotificationManagerService extends SystemService {
}
}
+ protected void onPanelRevealed(int items) {
+ for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
+ mHandler.post(() -> {
+ final INotificationListener assistant = (INotificationListener) info.service;
+ try {
+ assistant.onPanelRevealed(items);
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "unable to notify assistant (panel revealed): " + info, ex);
+ }
+ });
+ }
+ }
+
+ protected void onPanelHidden() {
+ for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
+ mHandler.post(() -> {
+ final INotificationListener assistant = (INotificationListener) info.service;
+ try {
+ assistant.onPanelHidden();
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "unable to notify assistant (panel hidden): " + info, ex);
+ }
+ });
+ }
+ }
+
boolean hasUserSet(int userId) {
synchronized (mLock) {
return mUserSetMap.getOrDefault(userId, false);
@@ -8363,6 +8443,24 @@ public class NotificationManagerService extends SystemService {
}
@GuardedBy("mNotificationLock")
+ void notifyAssistantVisibilityChangedLocked(
+ final StatusBarNotification sbn,
+ final boolean isVisible) {
+ final String key = sbn.getKey();
+ Slog.d(TAG, "notifyAssistantVisibilityChangedLocked: " + key);
+ notifyAssistantLocked(
+ sbn,
+ false /* sameUserOnly */,
+ (assistant, sbnHolder) -> {
+ try {
+ assistant.onNotificationVisibilityChanged(key, isVisible);
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "unable to notify assistant (visible): " + assistant, ex);
+ }
+ });
+ }
+
+ @GuardedBy("mNotificationLock")
void notifyAssistantExpansionChangedLocked(
final StatusBarNotification sbn,
final boolean isUserAction,
diff --git a/services/core/java/com/android/server/notification/OWNERS b/services/core/java/com/android/server/notification/OWNERS
new file mode 100644
index 000000000000..5a19656b36a6
--- /dev/null
+++ b/services/core/java/com/android/server/notification/OWNERS
@@ -0,0 +1,4 @@
+dsandler@android.com
+juliacr@google.com
+beverlyt@google.com
+pixel@google.com
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index cdb0a17ada0a..92fcb7f99f9c 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -770,6 +770,13 @@ public class PreferencesHelper implements RankingConfig {
channel.setShowBadge(false);
}
channel.setOriginalImportance(channel.getImportance());
+
+ // validate parent
+ if (channel.getParentChannelId() != null) {
+ Preconditions.checkArgument(r.channels.containsKey(channel.getParentChannelId()),
+ "Tried to create a conversation channel without a preexisting parent");
+ }
+
r.channels.put(channel.getId(), channel);
if (channel.canBypassDnd() != mAreChannelsBypassingDnd) {
updateChannelsBypassingDnd(mContext.getUserId());
@@ -851,6 +858,13 @@ public class PreferencesHelper implements RankingConfig {
public NotificationChannel getNotificationChannel(String pkg, int uid, String channelId,
boolean includeDeleted) {
Objects.requireNonNull(pkg);
+ return getNotificationChannel(pkg, uid, channelId, null, includeDeleted);
+ }
+
+ @Override
+ public NotificationChannel getNotificationChannel(String pkg, int uid, String channelId,
+ String conversationId, boolean includeDeleted) {
+ Preconditions.checkNotNull(pkg);
synchronized (mPackagePreferences) {
PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
if (r == null) {
@@ -859,14 +873,51 @@ public class PreferencesHelper implements RankingConfig {
if (channelId == null) {
channelId = NotificationChannel.DEFAULT_CHANNEL_ID;
}
- final NotificationChannel nc = r.channels.get(channelId);
- if (nc != null && (includeDeleted || !nc.isDeleted())) {
- return nc;
+ if (conversationId == null) {
+ final NotificationChannel nc = r.channels.get(channelId);
+ if (nc != null && (includeDeleted || !nc.isDeleted())) {
+ return nc;
+ }
+ } else {
+ // look for an automatically created conversation specific channel
+ return findConversationChannel(r, channelId, conversationId, includeDeleted);
}
return null;
}
}
+ private NotificationChannel findConversationChannel(PackagePreferences p, String parentId,
+ String conversationId, boolean includeDeleted) {
+ for (NotificationChannel nc : p.channels.values()) {
+ if (conversationId.equals(nc.getConversationId())
+ && parentId.equals(nc.getParentChannelId())
+ && (includeDeleted || !nc.isDeleted())) {
+ return nc;
+ }
+ }
+ return null;
+ }
+
+ public List<NotificationChannel> getNotificationChannelsByConversationId(String pkg, int uid,
+ String conversationId) {
+ Preconditions.checkNotNull(pkg);
+ Preconditions.checkNotNull(conversationId);
+ List<NotificationChannel> channels = new ArrayList<>();
+ synchronized (mPackagePreferences) {
+ PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
+ if (r == null) {
+ return channels;
+ }
+ for (NotificationChannel nc : r.channels.values()) {
+ if (conversationId.equals(nc.getConversationId())
+ && !nc.isDeleted()) {
+ channels.add(nc);
+ }
+ }
+ return channels;
+ }
+ }
+
@Override
public void deleteNotificationChannel(String pkg, int uid, String channelId) {
synchronized (mPackagePreferences) {
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index 7816f3619023..4b044c13ecc6 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -41,10 +41,15 @@ public interface RankingConfig {
int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty);
boolean createNotificationChannel(String pkg, int uid, NotificationChannel channel,
boolean fromTargetApp, boolean hasDndAccess);
- void updateNotificationChannel(String pkg, int uid, NotificationChannel channel, boolean fromUser);
- NotificationChannel getNotificationChannel(String pkg, int uid, String channelId, boolean includeDeleted);
+ void updateNotificationChannel(String pkg, int uid, NotificationChannel channel,
+ boolean fromUser);
+ NotificationChannel getNotificationChannel(String pkg, int uid, String channelId,
+ boolean includeDeleted);
+ NotificationChannel getNotificationChannel(String pkg, int uid, String channelId,
+ String conversationId, boolean includeDeleted);
void deleteNotificationChannel(String pkg, int uid, String channelId);
void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId);
void permanentlyDeleteNotificationChannels(String pkg, int uid);
- ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid, boolean includeDeleted);
+ ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid,
+ boolean includeDeleted);
}
diff --git a/services/core/java/com/android/server/om/TEST_MAPPING b/services/core/java/com/android/server/om/TEST_MAPPING
new file mode 100644
index 000000000000..52163a09e387
--- /dev/null
+++ b/services/core/java/com/android/server/om/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.om."
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 307a07bb09a2..a009183f7ecb 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -32,10 +32,13 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
+import android.content.pm.parsing.AndroidPackage;
import android.os.Environment;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.sysprop.ApexProperties;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Singleton;
import android.util.Slog;
@@ -44,15 +47,20 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.IndentingPrintWriter;
+import com.google.android.collect.Lists;
+
import java.io.File;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
import java.util.stream.Collectors;
/**
@@ -97,12 +105,27 @@ abstract class ApexManager {
* Minimal information about APEX mount points and the original APEX package they refer to.
*/
static class ActiveApexInfo {
+ @Nullable public final String apexModuleName;
public final File apexDirectory;
- public final File preinstalledApexPath;
+ public final File preInstalledApexPath;
+
+ private ActiveApexInfo(File apexDirectory, File preInstalledApexPath) {
+ this(null, apexDirectory, preInstalledApexPath);
+ }
- private ActiveApexInfo(File apexDirectory, File preinstalledApexPath) {
+ private ActiveApexInfo(@Nullable String apexModuleName, File apexDirectory,
+ File preInstalledApexPath) {
+ this.apexModuleName = apexModuleName;
this.apexDirectory = apexDirectory;
- this.preinstalledApexPath = preinstalledApexPath;
+ this.preInstalledApexPath = preInstalledApexPath;
+ }
+
+ private ActiveApexInfo(ApexInfo apexInfo) {
+ this(
+ apexInfo.moduleName,
+ new File(Environment.getApexDirectory() + File.separator
+ + apexInfo.moduleName),
+ new File(apexInfo.preinstalledModulePath));
}
}
@@ -232,6 +255,17 @@ abstract class ApexManager {
abstract boolean uninstallApex(String apexPackagePath);
/**
+ * Registers an APK package as an embedded apk of apex.
+ */
+ abstract void registerApkInApex(AndroidPackage pkg);
+
+ /**
+ * Returns list of {@code packageName} of apks inside the given apex.
+ * @param apexPackageName Package name of the apk container of apex
+ */
+ abstract List<String> getApksInApex(String apexPackageName);
+
+ /**
* Dumps various state information to the provided {@link PrintWriter} object.
*
* @param pw the {@link PrintWriter} object to send information to.
@@ -255,16 +289,33 @@ abstract class ApexManager {
static class ApexManagerImpl extends ApexManager {
private final IApexService mApexService;
private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private Set<ActiveApexInfo> mActiveApexInfosCache;
+
/**
- * A map from {@code APEX packageName} to the {@Link PackageInfo} generated from the {@code
- * AndroidManifest.xml}
- *
- * <p>Note that key of this map is {@code packageName} field of the corresponding {@code
- * AndroidManifest.xml}.
- */
+ * Contains the list of {@code packageName}s of apks-in-apex for given
+ * {@code apexModuleName}. See {@link #mPackageNameToApexModuleName} to understand the
+ * difference between {@code packageName} and {@code apexModuleName}.
+ */
+ @GuardedBy("mLock")
+ private Map<String, List<String>> mApksInApex = new ArrayMap<>();
+
@GuardedBy("mLock")
private List<PackageInfo> mAllPackagesCache;
+ /**
+ * An APEX is a file format that delivers the apex-payload wrapped in an apk container. The
+ * apk container has a reference name, called {@code packageName}, which is found inside the
+ * {@code AndroidManifest.xml}. The apex payload inside the container also has a reference
+ * name, called {@code apexModuleName}, which is found in {@code apex_manifest.json} file.
+ *
+ * {@link #mPackageNameToApexModuleName} contains the mapping from {@code packageName} of
+ * the apk container to {@code apexModuleName} of the apex-payload inside.
+ */
+ @GuardedBy("mLock")
+ private Map<String, String> mPackageNameToApexModuleName;
+
ApexManagerImpl(IApexService apexService) {
mApexService = apexService;
}
@@ -291,18 +342,25 @@ abstract class ApexManager {
@Override
List<ActiveApexInfo> getActiveApexInfos() {
- try {
- return Arrays.stream(mApexService.getActivePackages())
- .map(apexInfo -> new ActiveApexInfo(
- new File(
- Environment.getApexDirectory() + File.separator
- + apexInfo.moduleName),
- new File(apexInfo.preinstalledModulePath))).collect(
- Collectors.toList());
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to retrieve packages from apexservice", e);
+ synchronized (mLock) {
+ if (mActiveApexInfosCache == null) {
+ try {
+ mActiveApexInfosCache = new ArraySet<>();
+ final ApexInfo[] activePackages = mApexService.getActivePackages();
+ for (int i = 0; i < activePackages.length; i++) {
+ ApexInfo apexInfo = activePackages[i];
+ mActiveApexInfosCache.add(new ActiveApexInfo(apexInfo));
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to retrieve packages from apexservice", e);
+ }
+ }
+ if (mActiveApexInfosCache != null) {
+ return new ArrayList<>(mActiveApexInfosCache);
+ } else {
+ return Collections.emptyList();
+ }
}
- return Collections.emptyList();
}
@Override
@@ -325,6 +383,7 @@ abstract class ApexManager {
}
try {
mAllPackagesCache = new ArrayList<>();
+ mPackageNameToApexModuleName = new HashMap<>();
HashSet<String> activePackagesSet = new HashSet<>();
HashSet<String> factoryPackagesSet = new HashSet<>();
final ApexInfo[] allPkgs = mApexService.getAllPackages();
@@ -350,6 +409,7 @@ abstract class ApexManager {
final PackageInfo packageInfo =
PackageParser.generatePackageInfo(pkg, ai, flags);
mAllPackagesCache.add(packageInfo);
+ mPackageNameToApexModuleName.put(packageInfo.packageName, ai.moduleName);
if (ai.isActive) {
if (activePackagesSet.contains(packageInfo.packageName)) {
throw new IllegalStateException(
@@ -366,7 +426,6 @@ abstract class ApexManager {
}
factoryPackagesSet.add(packageInfo.packageName);
}
-
}
} catch (RemoteException re) {
Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString());
@@ -533,6 +592,37 @@ abstract class ApexManager {
}
}
+ @Override
+ void registerApkInApex(AndroidPackage pkg) {
+ synchronized (mLock) {
+ final Iterator<ActiveApexInfo> it = mActiveApexInfosCache.iterator();
+ while (it.hasNext()) {
+ final ActiveApexInfo aai = it.next();
+ if (pkg.getBaseCodePath().startsWith(aai.apexDirectory.getAbsolutePath())) {
+ List<String> apks = mApksInApex.get(aai.apexModuleName);
+ if (apks == null) {
+ apks = Lists.newArrayList();
+ mApksInApex.put(aai.apexModuleName, apks);
+ }
+ apks.add(pkg.getPackageName());
+ }
+ }
+ }
+ }
+
+ @Override
+ List<String> getApksInApex(String apexPackageName) {
+ // TODO(b/142712057): Avoid calling populateAllPackagesCacheIfNeeded during boot.
+ populateAllPackagesCacheIfNeeded();
+ synchronized (mLock) {
+ String moduleName = mPackageNameToApexModuleName.get(apexPackageName);
+ if (moduleName == null) {
+ return Collections.emptyList();
+ }
+ return mApksInApex.getOrDefault(moduleName, Collections.emptyList());
+ }
+ }
+
/**
* Dump information about the packages contained in a particular cache
* @param packagesCache the cache to print information about.
@@ -614,7 +704,6 @@ abstract class ApexManager {
* updating APEX packages.
*/
private static final class ApexManagerFlattenedApex extends ApexManager {
-
@Override
List<ActiveApexInfo> getActiveApexInfos() {
// There is no apexd running in case of flattened apex
@@ -721,6 +810,16 @@ abstract class ApexManager {
}
@Override
+ void registerApkInApex(AndroidPackage pkg) {
+ // No-op
+ }
+
+ @Override
+ List<String> getApksInApex(String apexPackageName) {
+ return Collections.emptyList();
+ }
+
+ @Override
void dump(PrintWriter pw, String packageName) {
// No-op
}
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index b25e1e2160c3..ed7139991937 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -15,35 +15,45 @@
*/
package com.android.server.pm;
+import static android.app.AppOpsManager.OP_INTERACT_ACROSS_PROFILES;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+import android.Manifest;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
+import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.IApplicationThread;
import android.app.admin.DevicePolicyEventLogger;
+import android.app.admin.DevicePolicyManagerInternal;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ICrossProfileApps;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.os.Binder;
+import android.os.IBinder;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.stats.devicepolicy.DevicePolicyEnums;
import android.text.TextUtils;
+import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
+import com.android.internal.app.IAppOpsService;
+import com.android.internal.util.ArrayUtils;
import com.android.server.LocalServices;
+import com.android.server.appop.AppOpsService;
import com.android.server.wm.ActivityTaskManagerInternal;
import java.util.ArrayList;
@@ -55,6 +65,9 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
private Context mContext;
private Injector mInjector;
+ private AppOpsService mAppOpsService;
+ private final DevicePolicyManagerInternal mDpmi;
+ private final IPackageManager mIpm;
public CrossProfileAppsServiceImpl(Context context) {
this(context, new InjectorImpl(context));
@@ -64,6 +77,8 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
CrossProfileAppsServiceImpl(Context context, Injector injector) {
mContext = context;
mInjector = injector;
+ mIpm = AppGlobals.getPackageManager();
+ mDpmi = LocalServices.getService(DevicePolicyManagerInternal.class);
}
@Override
@@ -153,6 +168,63 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
userId);
}
+ @Override
+ public boolean canRequestInteractAcrossProfiles(String callingPackage) {
+ Objects.requireNonNull(callingPackage);
+ verifyCallingPackage(callingPackage);
+
+ final List<UserHandle> targetUserProfiles = getTargetUserProfilesUnchecked(
+ callingPackage, mInjector.getCallingUserId());
+ if (targetUserProfiles.isEmpty()) {
+ return false;
+ }
+
+ if (!hasRequestedAppOpPermission(
+ AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), callingPackage)) {
+ return false;
+ }
+ return isCrossProfilePackageWhitelisted(callingPackage);
+ }
+
+ private boolean hasRequestedAppOpPermission(String permission, String packageName) {
+ try {
+ String[] packages = mIpm.getAppOpPermissionPackages(permission);
+ return ArrayUtils.contains(packages, packageName);
+ } catch (RemoteException exc) {
+ Slog.e(TAG, "PackageManager dead. Cannot get permission info");
+ return false;
+ }
+ }
+
+ @Override
+ public boolean canInteractAcrossProfiles(String callingPackage) {
+ Objects.requireNonNull(callingPackage);
+ verifyCallingPackage(callingPackage);
+
+ final List<UserHandle> targetUserProfiles = getTargetUserProfilesUnchecked(
+ callingPackage, mInjector.getCallingUserId());
+ if (targetUserProfiles.isEmpty()) {
+ return false;
+ }
+
+ final int callingUid = mInjector.getCallingUid();
+ return isPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS_FULL, callingUid)
+ || isPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS, callingUid)
+ || isPermissionGranted(Manifest.permission.INTERACT_ACROSS_PROFILES, callingUid)
+ || AppOpsManager.MODE_ALLOWED == getAppOpsService().noteOperation(
+ OP_INTERACT_ACROSS_PROFILES, callingUid, callingPackage, /* featureId= */ null,
+ /*shouldCollectAsyncNotedOp= */false, /*message= */null);
+ }
+
+ private boolean isCrossProfilePackageWhitelisted(String packageName) {
+ final long ident = mInjector.clearCallingIdentity();
+ try {
+ return mDpmi.getAllCrossProfilePackages().contains(packageName);
+ } finally {
+ mInjector.restoreCallingIdentity(ident);
+ }
+ }
+
private List<UserHandle> getTargetUserProfilesUnchecked(
String callingPackage, @UserIdInt int callingUserId) {
final long ident = mInjector.clearCallingIdentity();
@@ -239,6 +311,19 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
mInjector.getAppOpsManager().checkPackage(mInjector.getCallingUid(), callingPackage);
}
+ private static boolean isPermissionGranted(String permission, int uid) {
+ return PackageManager.PERMISSION_GRANTED == ActivityManager.checkComponentPermission(
+ permission, uid, /* owningUid= */-1, /* exported= */ true);
+ }
+
+ private AppOpsService getAppOpsService() {
+ if (mAppOpsService == null) {
+ IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
+ mAppOpsService = (AppOpsService) IAppOpsService.Stub.asInterface(b);
+ }
+ return mAppOpsService;
+ }
+
private static class InjectorImpl implements Injector {
private Context mContext;
diff --git a/services/core/java/com/android/server/pm/InstallSource.java b/services/core/java/com/android/server/pm/InstallSource.java
index 0541797523a4..6684e3f8973e 100644
--- a/services/core/java/com/android/server/pm/InstallSource.java
+++ b/services/core/java/com/android/server/pm/InstallSource.java
@@ -18,6 +18,8 @@ package com.android.server.pm;
import android.annotation.Nullable;
+import com.android.internal.util.Preconditions;
+
import java.util.Objects;
/**
@@ -29,16 +31,27 @@ final class InstallSource {
* An instance of InstallSource representing an absence of knowledge of the source of
* a package. Used in preference to null.
*/
- static final InstallSource EMPTY = new InstallSource(null, null, null, false);
+ static final InstallSource EMPTY = new InstallSource(null, null, null, false, false, null);
/** We also memoize this case because it is common - all un-updated system apps. */
- private static final InstallSource EMPTY_ORPHANED = new InstallSource(null, null, null, true);
+ private static final InstallSource EMPTY_ORPHANED = new InstallSource(
+ null, null, null, true, false, null);
- /** The package that requested the installation, if known. */
+ /**
+ * The package that requested the installation, if known. May not correspond to a currently
+ * installed package if {@link #isInitiatingPackageUninstalled} is true.
+ */
@Nullable
final String initiatingPackageName;
/**
+ * The signing details of the initiating package, if known. Always null if
+ * {@link #initiatingPackageName} is null.
+ */
+ @Nullable
+ final PackageSignatures initiatingPackageSignatures;
+
+ /**
* The package on behalf of which the initiating package requested the installation, if any.
* For example if a downloaded APK is installed via the Package Installer this could be the
* app that performed the download. This value is provided by the initiating package and not
@@ -57,76 +70,120 @@ final class InstallSource {
/** Indicates if the package that was the installerPackageName has been uninstalled. */
final boolean isOrphaned;
+ /**
+ * Indicates if the package in initiatingPackageName has been uninstalled. Always false if
+ * {@link #initiatingPackageName} is null.
+ */
+ final boolean isInitiatingPackageUninstalled;
+
+ static InstallSource create(@Nullable String initiatingPackageName,
+ @Nullable String originatingPackageName, @Nullable String installerPackageName) {
+ return create(initiatingPackageName, originatingPackageName, installerPackageName,
+ false, false);
+ }
+
static InstallSource create(@Nullable String initiatingPackageName,
@Nullable String originatingPackageName, @Nullable String installerPackageName,
- boolean isOrphaned) {
+ boolean isOrphaned, boolean isInitiatingPackageUninstalled) {
return createInternal(
intern(initiatingPackageName),
intern(originatingPackageName),
intern(installerPackageName),
- isOrphaned);
+ isOrphaned, isInitiatingPackageUninstalled, null);
}
private static InstallSource createInternal(@Nullable String initiatingPackageName,
@Nullable String originatingPackageName, @Nullable String installerPackageName,
- boolean isOrphaned) {
+ boolean isOrphaned, boolean isInitiatingPackageUninstalled,
+ @Nullable PackageSignatures initiatingPackageSignatures) {
if (initiatingPackageName == null && originatingPackageName == null
- && installerPackageName == null) {
+ && installerPackageName == null && initiatingPackageSignatures == null
+ && !isInitiatingPackageUninstalled) {
return isOrphaned ? EMPTY_ORPHANED : EMPTY;
}
return new InstallSource(initiatingPackageName, originatingPackageName,
- installerPackageName, isOrphaned);
+ installerPackageName, isOrphaned, isInitiatingPackageUninstalled,
+ initiatingPackageSignatures
+ );
}
private InstallSource(@Nullable String initiatingPackageName,
@Nullable String originatingPackageName, @Nullable String installerPackageName,
- boolean isOrphaned) {
+ boolean isOrphaned, boolean isInitiatingPackageUninstalled,
+ @Nullable PackageSignatures initiatingPackageSignatures) {
+ if (initiatingPackageName == null) {
+ Preconditions.checkArgument(initiatingPackageSignatures == null);
+ Preconditions.checkArgument(!isInitiatingPackageUninstalled);
+ }
this.initiatingPackageName = initiatingPackageName;
this.originatingPackageName = originatingPackageName;
this.installerPackageName = installerPackageName;
this.isOrphaned = isOrphaned;
+ this.isInitiatingPackageUninstalled = isInitiatingPackageUninstalled;
+ this.initiatingPackageSignatures = initiatingPackageSignatures;
}
/**
- * Return an InstallSource the same as this one except with the specified installerPackageName.
+ * Return an InstallSource the same as this one except with the specified
+ * {@link #installerPackageName}.
*/
- InstallSource setInstallerPackage(String installerPackageName) {
+ InstallSource setInstallerPackage(@Nullable String installerPackageName) {
if (Objects.equals(installerPackageName, this.installerPackageName)) {
return this;
}
return createInternal(initiatingPackageName, originatingPackageName,
- intern(installerPackageName), isOrphaned);
+ intern(installerPackageName), isOrphaned, isInitiatingPackageUninstalled,
+ initiatingPackageSignatures
+ );
}
/**
- * Return an InstallSource the same as this one except with the specified value for isOrphaned.
+ * Return an InstallSource the same as this one except with the specified value for
+ * {@link #isOrphaned}.
*/
InstallSource setIsOrphaned(boolean isOrphaned) {
if (isOrphaned == this.isOrphaned) {
return this;
}
return createInternal(initiatingPackageName, originatingPackageName, installerPackageName,
- isOrphaned);
+ isOrphaned, isInitiatingPackageUninstalled, initiatingPackageSignatures);
}
/**
- * Return an InstallSource the same as this one except it does not refer to the specified
- * installer package name (which is being uninstalled).
+ * Return an InstallSource the same as this one except with the specified
+ * {@link #initiatingPackageSignatures}.
*/
- InstallSource removeInstallerPackage(String packageName) {
+ InstallSource setInitiatingPackageSignatures(@Nullable PackageSignatures signatures) {
+ if (signatures == initiatingPackageSignatures) {
+ return this;
+ }
+ return createInternal(initiatingPackageName, originatingPackageName, installerPackageName,
+ isOrphaned, isInitiatingPackageUninstalled, signatures);
+ }
+
+ /**
+ * Return an InstallSource the same as this one updated to reflect that the specified installer
+ * package name has been uninstalled.
+ */
+ InstallSource removeInstallerPackage(@Nullable String packageName) {
if (packageName == null) {
return this;
}
boolean modified = false;
- String initiatingPackageName = this.initiatingPackageName;
+ boolean isInitiatingPackageUninstalled = this.isInitiatingPackageUninstalled;
String originatingPackageName = this.originatingPackageName;
String installerPackageName = this.installerPackageName;
boolean isOrphaned = this.isOrphaned;
- if (packageName.equals(initiatingPackageName)) {
- initiatingPackageName = null;
- modified = true;
+ if (packageName.equals(this.initiatingPackageName)) {
+ if (!isInitiatingPackageUninstalled) {
+ // In this case we deliberately do not clear the package name (and signatures).
+ // We allow an app to retrieve details of its own install initiator even after
+ // it has been uninstalled.
+ isInitiatingPackageUninstalled = true;
+ modified = true;
+ }
}
if (packageName.equals(originatingPackageName)) {
originatingPackageName = null;
@@ -141,8 +198,9 @@ final class InstallSource {
if (!modified) {
return this;
}
+
return createInternal(initiatingPackageName, originatingPackageName, installerPackageName,
- isOrphaned);
+ isOrphaned, isInitiatingPackageUninstalled, initiatingPackageSignatures);
}
@Nullable
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index e2dfa126225f..6331dd46c035 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -188,7 +188,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
}
};
- public PackageInstallerService(Context context, PackageManagerService pm, ApexManager am) {
+ public PackageInstallerService(Context context, PackageManagerService pm) {
mContext = context;
mPm = pm;
mPermissionManager = LocalServices.getService(PermissionManagerServiceInternal.class);
@@ -206,9 +206,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
mSessionsDir = new File(Environment.getDataSystemDirectory(), "install_sessions");
mSessionsDir.mkdirs();
- mApexManager = am;
-
- mStagingManager = new StagingManager(this, am, context);
+ mApexManager = ApexManager.getInstance();
+ mStagingManager = new StagingManager(this, context);
}
boolean okToSendBroadcasts() {
@@ -635,7 +634,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
}
}
InstallSource installSource = InstallSource.create(installerPackageName,
- originatingPackageName, requestedInstallerPackageName, false);
+ originatingPackageName, requestedInstallerPackageName);
session = new PackageInstallerSession(mInternalCallback, mContext, mPm, this,
mInstallThread.getLooper(), mStagingManager, sessionId, userId, callingUid,
installSource, params, createdMillis,
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 4dcdf7ea0ecc..165bdebe070f 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -1475,7 +1475,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
mInstallerUid = uid;
- mInstallSource = InstallSource.create(packageName, null, packageName, false);
+ mInstallSource = InstallSource.create(packageName, null, packageName);
}
} catch (PackageManager.NameNotFoundException e) {
onSessionTransferStatus(statusReceiver, packageName,
@@ -2413,16 +2413,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@Override
public void addFile(String name, long lengthBytes, byte[] metadata) {
- if (mIncrementalFileStorages != null) {
- try {
- mIncrementalFileStorages.addFile(new InstallationFile(name, lengthBytes, metadata));
- //TODO(b/136132412): merge incremental and callback installation schemes
- return;
- } catch (IOException ex) {
- throw new IllegalStateException(
- "Failed to add and configure Incremental File: " + name, ex);
- }
- }
if (!isDataLoaderInstallation()) {
throw new IllegalStateException(
"Cannot add files to non-data loader installation session.");
@@ -2435,7 +2425,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
synchronized (mLock) {
assertCallerIsOwnerOrRootLocked();
assertPreparedAndNotSealedLocked("addFile");
-
mFiles.add(FileInfo.added(name, lengthBytes, metadata));
}
}
@@ -2486,7 +2475,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
*/
private void prepareDataLoader()
throws PackageManagerException, StreamingException {
- if (!isStreamingInstallation()) {
+ if (!isDataLoaderInstallation()) {
return;
}
@@ -2500,6 +2489,18 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
file -> file.name.substring(
0, file.name.length() - REMOVE_MARKER_EXTENSION.length())).collect(
Collectors.toList());
+ if (mIncrementalFileStorages != null) {
+ for (InstallationFile file : addedFiles) {
+ try {
+ mIncrementalFileStorages.addFile(file);
+ } catch (IOException ex) {
+ // TODO(b/146080380): add incremental-specific error code
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Failed to add and configure Incremental File: " + file.getName(), ex);
+ }
+ }
+ return;
+ }
final FileSystemConnector connector = new FileSystemConnector(addedFiles);
@@ -3138,7 +3139,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
InstallSource installSource = InstallSource.create(installInitiatingPackageName,
- installOriginatingPackageName, installerPackageName, false);
+ installOriginatingPackageName, installerPackageName);
return new PackageInstallerSession(callback, context, pm, sessionProvider,
installerThread, stagingManager, sessionId, userId, installerUid,
installSource, params, createdMillis, stageDir, stageCid, fileInfosArray,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 6bd9c4847a77..17870ebe9957 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -492,6 +492,7 @@ public class PackageManagerService extends IPackageManager.Stub
static final int SCAN_AS_PRODUCT = 1 << 20;
static final int SCAN_AS_SYSTEM_EXT = 1 << 21;
static final int SCAN_AS_ODM = 1 << 22;
+ static final int SCAN_AS_APK_IN_APEX = 1 << 23;
@IntDef(flag = true, prefix = { "SCAN_" }, value = {
SCAN_NO_DEX,
@@ -2589,6 +2590,9 @@ public class PackageManagerService extends IPackageManager.Stub
& (SCAN_AS_VENDOR | SCAN_AS_ODM | SCAN_AS_PRODUCT | SCAN_AS_SYSTEM_EXT)) != 0) {
return true;
}
+ if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0) {
+ return true;
+ }
return false;
}
@@ -3332,7 +3336,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- mInstallerService = new PackageInstallerService(mContext, this, mApexManager);
+ mInstallerService = new PackageInstallerService(mContext, this);
final Pair<ComponentName, String> instantAppResolverComponent =
getInstantAppResolverLPr();
if (instantAppResolverComponent != null) {
@@ -5344,8 +5348,9 @@ public class PackageManagerService extends IPackageManager.Stub
if (!mUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForComponent(flags, userId);
- mPermissionManager.enforceCrossUserPermission(callingUid, userId,
- false /* requireFullPermission */, false /* checkShell */, "get service info");
+ mPermissionManager.enforceCrossUserOrProfilePermission(
+ callingUid, userId, false /* requireFullPermission */, false /* checkShell */,
+ "get service info");
synchronized (mLock) {
ParsedService s = mComponentResolver.getService(component);
if (DEBUG_PACKAGE_INFO) Log.v(
@@ -7795,8 +7800,10 @@ public class PackageManagerService extends IPackageManager.Stub
String resolvedType, int flags, int userId, int callingUid,
boolean includeInstantApps) {
if (!mUserManager.exists(userId)) return Collections.emptyList();
- mPermissionManager.enforceCrossUserPermission(callingUid, userId,
- false /*requireFullPermission*/, false /*checkShell*/,
+ mPermissionManager.enforceCrossUserOrProfilePermission(callingUid,
+ userId,
+ false /*requireFullPermission*/,
+ false /*checkShell*/,
"query intent receivers");
final String instantAppPkgName = getInstantAppPackageName(callingUid);
flags = updateFlagsForResolve(flags, userId, callingUid, includeInstantApps);
@@ -11710,6 +11717,9 @@ public class PackageManagerService extends IPackageManager.Stub
mSettings.insertPackageSettingLPw(pkgSetting, pkg);
// Add the new setting to mPackages
mPackages.put(pkg.getAppInfoPackageName(), pkg);
+ if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0) {
+ mApexManager.registerApkInApex(pkg);
+ }
// Add the package's KeySets to the global KeySetManagerService
KeySetManagerService ksms = mSettings.mKeySetManagerService;
@@ -15158,10 +15168,10 @@ public class PackageManagerService extends IPackageManager.Stub
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
final String pkgName = pkg.getPackageName();
- final InstallSource installSource = installArgs.installSource;
- final String installerPackageName = installSource.installerPackageName;
final int[] installedForUsers = res.origUsers;
final int installReason = installArgs.installReason;
+ InstallSource installSource = installArgs.installSource;
+ final String installerPackageName = installSource.installerPackageName;
if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + pkg.getCodePath());
synchronized (mLock) {
@@ -15207,6 +15217,14 @@ public class PackageManagerService extends IPackageManager.Stub
ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, installerPackageName);
}
+ if (installSource.initiatingPackageName != null) {
+ final PackageSetting ips = mSettings.mPackages.get(
+ installSource.initiatingPackageName);
+ if (ips != null) {
+ installSource = installSource.setInitiatingPackageSignatures(
+ ips.signatures);
+ }
+ }
ps.setInstallSource(installSource);
mSettings.addInstallerPackageNames(installSource);
@@ -17749,10 +17767,10 @@ public class PackageManagerService extends IPackageManager.Stub
ApexManager.ActiveApexInfo apexInfo) {
for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) {
SystemPartition sp = SYSTEM_PARTITIONS.get(i);
- if (apexInfo.preinstalledApexPath.getAbsolutePath().startsWith(
+ if (apexInfo.preInstalledApexPath.getAbsolutePath().startsWith(
sp.folder.getAbsolutePath())) {
- return new SystemPartition(apexInfo.apexDirectory, sp.scanFlag,
- false /* hasOverlays */);
+ return new SystemPartition(apexInfo.apexDirectory,
+ sp.scanFlag | SCAN_AS_APK_IN_APEX, false /* hasOverlays */);
}
}
return null;
@@ -19372,7 +19390,7 @@ public class PackageManagerService extends IPackageManager.Stub
// PermissionController manages default home directly.
return false;
}
- mPermissionManager.setDefaultHome(currentPackageName, userId, (successful) -> {
+ mPermissionManager.setDefaultHome(packageName, userId, (successful) -> {
if (successful) {
postPreferredActivityChangedBroadcast(userId);
}
@@ -19976,19 +19994,25 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- // All installSource strings are interned, so == is ok here
- if (installSource.initiatingPackageName == installSource.installerPackageName) {
- // The installer and initiator will often be the same, and when they are
- // we can skip doing the same check again.
- initiatingPackageName = installerPackageName;
+ if (installSource.isInitiatingPackageUninstalled) {
+ // TODO(b/146555198) Allow the app itself to see the info
+ // (at least for non-instant apps)
+ initiatingPackageName = null;
} else {
- initiatingPackageName = installSource.initiatingPackageName;
- final PackageSetting ps = mSettings.mPackages.get(initiatingPackageName);
- if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) {
- initiatingPackageName = null;
+ // All installSource strings are interned, so == is ok here
+ if (installSource.initiatingPackageName == installSource.installerPackageName) {
+ // The installer and initiator will often be the same, and when they are
+ // we can skip doing the same check again.
+ initiatingPackageName = installerPackageName;
+ } else {
+ initiatingPackageName = installSource.initiatingPackageName;
+ final PackageSetting ps = mSettings.mPackages.get(initiatingPackageName);
+ if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) {
+ initiatingPackageName = null;
+ }
}
-
}
+
originatingPackageName = installSource.originatingPackageName;
if (originatingPackageName != null) {
final PackageSetting ps = mSettings.mPackages.get(originatingPackageName);
@@ -20108,8 +20132,7 @@ public class PackageManagerService extends IPackageManager.Stub
// Disable any carrier apps. We do this very early in boot to prevent the apps from being
// disabled after already being started.
CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(), this,
- mPermissionManagerService, mContext.getContentResolver(),
- UserHandle.USER_SYSTEM);
+ mPermissionManagerService, UserHandle.USER_SYSTEM, mContext);
disableSkuSpecificApps();
@@ -22777,7 +22800,7 @@ public class PackageManagerService extends IPackageManager.Stub
ArrayList<String> systemPackageNames = new ArrayList<>(pkgNames.length);
for (String pkgName: pkgNames) {
- synchronized (mPackages) {
+ synchronized (mLock) {
if (pkgName == null) {
continue;
}
@@ -23438,6 +23461,11 @@ public class PackageManagerService extends IPackageManager.Stub
}
@Override
+ public List<String> getApksInApex(String apexPackageName) {
+ return PackageManagerService.this.mApexManager.getApksInApex(apexPackageName);
+ }
+
+ @Override
public void uninstallApex(String packageName, long versionCode, int userId,
IntentSender intentSender, int flags) {
final int callerUid = Binder.getCallingUid();
@@ -23618,7 +23646,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Nullable
public PackageSetting getPackageSetting(String packageName) {
- synchronized (mPackages) {
+ synchronized (mLock) {
packageName = resolveInternalPackageNameLPr(
packageName, PackageManager.VERSION_CODE_HIGHEST);
return mSettings.mPackages.get(packageName);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index f9a336166825..ec84b51577f9 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -2819,6 +2819,9 @@ public final class Settings {
if (installSource.initiatingPackageName != null) {
serializer.attribute(null, "installInitiator", installSource.initiatingPackageName);
}
+ if (installSource.isInitiatingPackageUninstalled) {
+ serializer.attribute(null, "installInitiatorUninstalled", "true");
+ }
if (installSource.originatingPackageName != null) {
serializer.attribute(null, "installOriginator", installSource.originatingPackageName);
}
@@ -2836,6 +2839,11 @@ public final class Settings {
pkg.signatures.writeXml(serializer, "sigs", mPastSignatures);
+ if (installSource.initiatingPackageSignatures != null) {
+ installSource.initiatingPackageSignatures.writeXml(
+ serializer, "install-initiator-sigs", mPastSignatures);
+ }
+
writePermissionsLPr(serializer, pkg.getPermissionsState().getInstallPermissionStates());
writeSigningKeySetLPr(serializer, pkg.keySetData);
@@ -3571,6 +3579,7 @@ public final class Settings {
String isOrphaned = null;
String installOriginatingPackageName = null;
String installInitiatingPackageName = null;
+ String installInitiatorUninstalled = null;
String volumeUuid = null;
String categoryHintString = null;
String updateAvailable = null;
@@ -3616,6 +3625,8 @@ public final class Settings {
isOrphaned = parser.getAttributeValue(null, "isOrphaned");
installInitiatingPackageName = parser.getAttributeValue(null, "installInitiator");
installOriginatingPackageName = parser.getAttributeValue(null, "installOriginator");
+ installInitiatorUninstalled = parser.getAttributeValue(null,
+ "installInitiatorUninstalled");
volumeUuid = parser.getAttributeValue(null, "volumeUuid");
categoryHintString = parser.getAttributeValue(null, "categoryHint");
if (categoryHintString != null) {
@@ -3772,7 +3783,8 @@ public final class Settings {
packageSetting.uidError = "true".equals(uidError);
InstallSource installSource = InstallSource.create(
installInitiatingPackageName, installOriginatingPackageName,
- installerPackageName, "true".equals(isOrphaned));
+ installerPackageName, "true".equals(isOrphaned),
+ "true".equals(installInitiatorUninstalled));
packageSetting.installSource = installSource;
packageSetting.volumeUuid = volumeUuid;
packageSetting.categoryHint = categoryHint;
@@ -3849,6 +3861,11 @@ public final class Settings {
mKeySetRefs.put(id, 1);
}
packageSetting.keySetData.addDefinedKeySet(id, alias);
+ } else if (tagName.equals("install-initiator-sigs")) {
+ final PackageSignatures signatures = new PackageSignatures();
+ signatures.readXml(parser, mPastSignatures);
+ packageSetting.installSource =
+ packageSetting.installSource.setInitiatingPackageSignatures(signatures);
} else if (tagName.equals(TAG_DOMAIN_VERIFICATION)) {
readDomainVerificationLPw(parser, packageSetting);
} else {
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 9e462cd529bb..2265d010216e 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -33,11 +33,13 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.PackageParser.SigningDetails;
import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
import android.content.pm.ParceledListSlice;
+import android.content.pm.parsing.AndroidPackage;
import android.content.rollback.IRollbackManager;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
@@ -50,6 +52,8 @@ import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.os.UserManagerInternal;
import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
import android.util.IntArray;
@@ -61,6 +65,7 @@ import android.util.apk.ApkSignatureVerifier;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageHelper;
import com.android.internal.os.BackgroundThread;
+import com.android.server.LocalServices;
import java.io.File;
import java.io.IOException;
@@ -93,10 +98,11 @@ public class StagingManager {
@GuardedBy("mStagedSessions")
private final SparseIntArray mSessionRollbackIds = new SparseIntArray();
- StagingManager(PackageInstallerService pi, ApexManager am, Context context) {
+ StagingManager(PackageInstallerService pi, Context context) {
mPi = pi;
- mApexManager = am;
mContext = context;
+
+ mApexManager = ApexManager.getInstance();
mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mPreRebootVerificationHandler = new PreRebootVerificationHandler(
BackgroundThread.get().getLooper());
@@ -334,6 +340,88 @@ public class StagingManager {
return PackageHelper.getStorageManager().needsCheckpoint();
}
+ /**
+ * Apks inside apex are not installed using apk-install flow. They are scanned from the system
+ * directory directly by PackageManager, as such, RollbackManager need to handle their data
+ * separately here.
+ */
+ private void snapshotAndRestoreApkInApexUserData(PackageInstallerSession session) {
+ // We want to process apks inside apex. So current session needs to contain apex.
+ if (!sessionContainsApex(session)) {
+ return;
+ }
+
+ boolean doSnapshotOrRestore =
+ (session.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0
+ || session.params.installReason == PackageManager.INSTALL_REASON_ROLLBACK;
+ if (!doSnapshotOrRestore) {
+ return;
+ }
+
+ // Find all the apex sessions that needs processing
+ List<PackageInstallerSession> apexSessions = new ArrayList<>();
+ if (session.isMultiPackage()) {
+ List<PackageInstallerSession> childrenSessions = new ArrayList<>();
+ synchronized (mStagedSessions) {
+ for (int childSessionId : session.getChildSessionIds()) {
+ PackageInstallerSession childSession = mStagedSessions.get(childSessionId);
+ if (childSession != null) {
+ childrenSessions.add(childSession);
+ }
+ }
+ }
+ for (PackageInstallerSession childSession : childrenSessions) {
+ if (sessionContainsApex(childSession)) {
+ apexSessions.add(childSession);
+ }
+ }
+ } else {
+ apexSessions.add(session);
+ }
+
+ // For each apex, process the apks inside it
+ for (PackageInstallerSession apexSession : apexSessions) {
+ List<String> apksInApex = mApexManager.getApksInApex(apexSession.getPackageName());
+ for (String apk: apksInApex) {
+ snapshotAndRestoreApkInApexUserData(apk);
+ }
+ }
+ }
+
+ private void snapshotAndRestoreApkInApexUserData(String packageName) {
+ IRollbackManager rm = IRollbackManager.Stub.asInterface(
+ ServiceManager.getService(Context.ROLLBACK_SERVICE));
+
+ PackageManagerInternal mPmi = LocalServices.getService(PackageManagerInternal.class);
+ AndroidPackage pkg = mPmi.getPackage(packageName);
+ if (pkg == null) {
+ Slog.e(TAG, "Could not find package: " + packageName
+ + "for snapshotting/restoring user data.");
+ return;
+ }
+ final String seInfo = pkg.getSeInfo();
+ final UserManagerInternal um = LocalServices.getService(UserManagerInternal.class);
+ final int[] allUsers = um.getUserIds();
+
+ int appId = -1;
+ long ceDataInode = -1;
+ final PackageSetting ps = (PackageSetting) mPmi.getPackageSetting(packageName);
+ if (ps != null && rm != null) {
+ appId = ps.appId;
+ ceDataInode = ps.getCeDataInode(UserHandle.USER_SYSTEM);
+ // NOTE: We ignore the user specified in the InstallParam because we know this is
+ // an update, and hence need to restore data for all installed users.
+ final int[] installedUsers = ps.queryInstalledUsers(allUsers, true);
+
+ try {
+ rm.snapshotAndRestoreUserData(packageName, installedUsers, appId, ceDataInode,
+ seInfo, 0 /*token*/);
+ } catch (RemoteException re) {
+ Slog.e(TAG, "Error snapshotting/restoring user data: " + re);
+ }
+ }
+ }
+
private void resumeSession(@NonNull PackageInstallerSession session) {
Slog.d(TAG, "Resuming session " + session.sessionId);
@@ -407,6 +495,7 @@ public class StagingManager {
abortCheckpoint();
return;
}
+ snapshotAndRestoreApkInApexUserData(session);
Slog.i(TAG, "APEX packages in session " + session.sessionId
+ " were successfully activated. Proceeding with APK packages, if any");
}
@@ -529,7 +618,7 @@ public class StagingManager {
Arrays.stream(session.getChildSessionIds())
// Retrieve cached sessions matching ids.
.mapToObj(i -> mStagedSessions.get(i))
- // Filter only the ones containing APKs.s
+ // Filter only the ones containing APKs.
.filter(childSession -> !isApexSession(childSession))
.collect(Collectors.toList());
}
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 d921f313eb48..d468cd981b52 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -4005,21 +4005,128 @@ public class PermissionManagerService extends IPermissionManager.Stub {
PackageManagerServiceUtils.enforceShellRestriction(mUserManagerInt,
UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId);
}
- if (!requirePermissionWhenSameUser && userId == UserHandle.getUserId(callingUid)) return;
- if (callingUid != Process.SYSTEM_UID && callingUid != Process.ROOT_UID) {
- if (requireFullPermission) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
- } else {
- try {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
- } catch (SecurityException se) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS, message);
- }
- }
+ final int callingUserId = UserHandle.getUserId(callingUid);
+ if (hasCrossUserPermission(
+ callingUid, callingUserId, userId, requireFullPermission,
+ requirePermissionWhenSameUser)) {
+ return;
+ }
+ String errorMessage = buildInvalidCrossUserPermissionMessage(
+ message, requireFullPermission);
+ Slog.w(TAG, errorMessage);
+ throw new SecurityException(errorMessage);
+ }
+
+ /**
+ * Checks if the request is from the system or an app that has the appropriate cross-user
+ * permissions defined as follows:
+ * <ul>
+ * <li>INTERACT_ACROSS_USERS_FULL if {@code requireFullPermission} is true.</li>
+ * <li>INTERACT_ACROSS_USERS if the given {@userId} is in a different profile group
+ * to the caller.</li>
+ * <li>Otherwise, INTERACT_ACROSS_PROFILES if the given {@userId} is in the same profile group
+ * as the caller.</li>
+ * </ul>
+ *
+ * @param checkShell whether to prevent shell from access if there's a debugging restriction
+ * @param message the message to log on security exception
+ */
+ private void enforceCrossUserOrProfilePermission(int callingUid, int userId,
+ boolean requireFullPermission, boolean checkShell,
+ String message) {
+ if (userId < 0) {
+ throw new IllegalArgumentException("Invalid userId " + userId);
+ }
+ if (checkShell) {
+ PackageManagerServiceUtils.enforceShellRestriction(mUserManagerInt,
+ UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId);
+ }
+ final int callingUserId = UserHandle.getUserId(callingUid);
+ if (hasCrossUserPermission(callingUid, callingUserId, userId, requireFullPermission,
+ /*requirePermissionWhenSameUser= */ false)) {
+ return;
+ }
+ final boolean isSameProfileGroup = isSameProfileGroup(callingUserId, userId);
+ if (isSameProfileGroup
+ && hasPermission(android.Manifest.permission.INTERACT_ACROSS_PROFILES)) {
+ return;
}
+ String errorMessage = buildInvalidCrossUserOrProfilePermissionMessage(
+ message, requireFullPermission, isSameProfileGroup);
+ Slog.w(TAG, errorMessage);
+ throw new SecurityException(errorMessage);
+ }
+
+ private boolean hasCrossUserPermission(
+ int callingUid, int callingUserId, int userId, boolean requireFullPermission,
+ boolean requirePermissionWhenSameUser) {
+ if (!requirePermissionWhenSameUser && userId == callingUserId) {
+ return true;
+ }
+ if (callingUid == Process.SYSTEM_UID || callingUid == Process.ROOT_UID) {
+ return true;
+ }
+ if (requireFullPermission) {
+ return hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ }
+ return hasPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ || hasPermission(Manifest.permission.INTERACT_ACROSS_USERS);
+ }
+
+ private boolean hasPermission(String permission) {
+ return mContext.checkCallingOrSelfPermission(permission)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return UserManagerService.getInstance().isSameProfileGroup(callerUserId, userId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ private static String buildInvalidCrossUserPermissionMessage(
+ String message, boolean requireFullPermission) {
+ StringBuilder builder = new StringBuilder();
+ if (message != null) {
+ builder.append(message);
+ builder.append(": ");
+ }
+ builder.append("Requires ");
+ builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ if (requireFullPermission) {
+ builder.append(".");
+ return builder.toString();
+ }
+ builder.append(" or ");
+ builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS);
+ builder.append(".");
+ return builder.toString();
+ }
+
+ private static String buildInvalidCrossUserOrProfilePermissionMessage(
+ String message, boolean requireFullPermission, boolean isSameProfileGroup) {
+ StringBuilder builder = new StringBuilder();
+ if (message != null) {
+ builder.append(message);
+ builder.append(": ");
+ }
+ builder.append("Requires ");
+ builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ if (requireFullPermission) {
+ builder.append(".");
+ return builder.toString();
+ }
+ builder.append(" or ");
+ builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS);
+ if (isSameProfileGroup) {
+ builder.append(" or ");
+ builder.append(android.Manifest.permission.INTERACT_ACROSS_PROFILES);
+ }
+ builder.append(".");
+ return builder.toString();
}
@GuardedBy({"mSettings.mLock", "mLock"})
@@ -4215,6 +4322,17 @@ public class PermissionManagerService extends IPermissionManager.Stub {
PermissionManagerService.this.enforceCrossUserPermission(callingUid, userId,
requireFullPermission, checkShell, requirePermissionWhenSameUser, message);
}
+
+ @Override
+ public void enforceCrossUserOrProfilePermission(int callingUid, int userId,
+ boolean requireFullPermission, boolean checkShell, String message) {
+ PermissionManagerService.this.enforceCrossUserOrProfilePermission(callingUid,
+ userId,
+ requireFullPermission,
+ checkShell,
+ message);
+ }
+
@Override
public void enforceGrantRevokeRuntimePermissionPermissions(String message) {
PermissionManagerService.this.enforceGrantRevokeRuntimePermissionPermissions(message);
@@ -4453,8 +4571,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
@Override
public void onNewUserCreated(int userId) {
+ mDefaultPermissionGrantPolicy.grantDefaultPermissions(userId);
synchronized (mLock) {
- mDefaultPermissionGrantPolicy.grantDefaultPermissions(userId);
// NOTE: This adds UPDATE_PERMISSIONS_REPLACE_PKG
PermissionManagerService.this.updateAllPermissions(
StorageManager.UUID_PRIVATE_INTERNAL, true, 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 0f22619fafa6..58a9f42f372d 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -271,6 +271,15 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager
*/
public abstract void enforceCrossUserPermission(int callingUid, int userId,
boolean requireFullPermission, boolean checkShell, @NonNull String message);
+
+ /**
+ * Similar to {@link #enforceCrossUserPermission(int, int, boolean, boolean, String)}
+ * but also allows INTERACT_ACROSS_PROFILES permission if calling user and {@code userId} are
+ * in the same profile group.
+ */
+ public abstract void enforceCrossUserOrProfilePermission(int callingUid, int userId,
+ boolean requireFullPermission, boolean checkShell, @NonNull String message);
+
/**
* @see #enforceCrossUserPermission(int, int, boolean, boolean, String)
* @param requirePermissionWhenSameUser When {@code true}, still require the cross user
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index e7269a6d9498..a86c8d7545b1 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -58,6 +58,7 @@ import android.util.SparseBooleanArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
+import com.android.internal.infra.AndroidFuture;
import com.android.internal.util.IntPair;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.FgThread;
@@ -68,7 +69,7 @@ import com.android.server.policy.PermissionPolicyInternal.OnInitializedCallback;
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
/**
* This is a permission policy that governs over all permission mechanism
@@ -280,7 +281,7 @@ public final class PermissionPolicyService extends SystemService {
if (DEBUG) Slog.i(LOG_TAG, "defaultPermsWereGrantedSinceBoot(" + userId + ")");
// Now call into the permission controller to apply policy around permissions
- final CountDownLatch latch = new CountDownLatch(1);
+ final AndroidFuture<Boolean> future = new AndroidFuture<>();
// We need to create a local manager that does not schedule work on the main
// there as we are on the main thread and want to block until the work is
@@ -290,22 +291,22 @@ public final class PermissionPolicyService extends SystemService {
getUserContext(getContext(), UserHandle.of(userId)),
FgThread.getHandler());
permissionControllerManager.grantOrUpgradeDefaultRuntimePermissions(
- FgThread.getExecutor(),
- (Boolean success) -> {
- if (!success) {
+ FgThread.getExecutor(), successful -> {
+ if (successful) {
+ future.complete(null);
+ } else {
// We are in an undefined state now, let us crash and have
// rescue party suggest a wipe to recover to a good one.
- final String message = "Error granting/upgrading runtime permissions";
+ final String message = "Error granting/upgrading runtime permissions"
+ + " for user " + userId;
Slog.wtf(LOG_TAG, message);
- throw new IllegalStateException(message);
+ future.completeExceptionally(new IllegalStateException(message));
}
- latch.countDown();
- }
- );
+ });
try {
- latch.await();
- } catch (InterruptedException e) {
- /* ignore */
+ future.get();
+ } catch (InterruptedException | ExecutionException e) {
+ throw new IllegalStateException(e);
}
permissionControllerManager.updateUserSensitive();
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index 88c1564fdb60..9f592b85a5e6 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -62,7 +62,7 @@ class Rollback {
private static final String TAG = "RollbackManager";
- @IntDef(flag = true, prefix = { "ROLLBACK_STATE_" }, value = {
+ @IntDef(prefix = { "ROLLBACK_STATE_" }, value = {
ROLLBACK_STATE_ENABLING,
ROLLBACK_STATE_AVAILABLE,
ROLLBACK_STATE_COMMITTED,
@@ -92,6 +92,19 @@ class Rollback {
*/
static final int ROLLBACK_STATE_DELETED = 4;
+ @IntDef(flag = true, prefix = { "MATCH_" }, value = {
+ MATCH_APK_IN_APEX,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface RollbackInfoFlags {}
+
+ /**
+ * {@link RollbackInfo} flag: include {@code RollbackInfo} packages that are apk-in-apex.
+ * These packages do not have their own sessions. They are embedded in an apex which has a
+ * session id.
+ */
+ static final int MATCH_APK_IN_APEX = 1;
+
/**
* The session ID for the staged session if this rollback data represents a staged session,
* {@code -1} otherwise.
@@ -323,8 +336,8 @@ class Rollback {
new VersionedPackage(packageName, newVersion),
new VersionedPackage(packageName, installedVersion),
new IntArray() /* pendingBackups */, new ArrayList<>() /* pendingRestores */,
- isApex, new IntArray(), new SparseLongArray() /* ceSnapshotInodes */,
- rollbackDataPolicy);
+ isApex, false /* isApkInApex */, new IntArray(),
+ new SparseLongArray() /* ceSnapshotInodes */, rollbackDataPolicy);
synchronized (mLock) {
info.getPackages().add(packageRollbackInfo);
@@ -334,6 +347,30 @@ class Rollback {
}
/**
+ * Enables this rollback for the provided apk-in-apex.
+ *
+ * @return boolean True if the rollback was enabled successfully for the specified package.
+ */
+ boolean enableForPackageInApex(String packageName, long installedVersion,
+ int rollbackDataPolicy) {
+ // TODO(b/142712057): Extract the new version number of apk-in-apex
+ // The new version for the apk-in-apex is set to 0 for now. If the package is then further
+ // updated via non-staged install flow, then RollbackManagerServiceImpl#onPackageReplaced()
+ // will be called and this rollback will be deleted. Other ways of package update have not
+ // been handled yet.
+ PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(
+ new VersionedPackage(packageName, 0 /* newVersion */),
+ new VersionedPackage(packageName, installedVersion),
+ new IntArray() /* pendingBackups */, new ArrayList<>() /* pendingRestores */,
+ false /* isApex */, true /* isApkInApex */, new IntArray(),
+ new SparseLongArray() /* ceSnapshotInodes */, rollbackDataPolicy);
+ synchronized (mLock) {
+ info.getPackages().add(packageRollbackInfo);
+ }
+ return true;
+ }
+
+ /**
* Snapshots user data for the provided package and user ids. Does nothing if this rollback is
* not in the ENABLING state.
*/
@@ -428,6 +465,11 @@ class Rollback {
parentSessionId);
for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) {
+ if (pkgRollbackInfo.isApkInApex()) {
+ // No need to issue a downgrade install request for apk-in-apex. It will
+ // be rolled back when its parent apex is downgraded.
+ continue;
+ }
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
String installerPackageName = mInstallerPackageName;
@@ -453,7 +495,8 @@ class Rollback {
this, pkgRollbackInfo.getPackageName());
if (packageCodePaths == null) {
sendFailure(context, statusReceiver, RollbackManager.STATUS_FAILURE,
- "Backup copy of package inaccessible");
+ "Backup copy of package: "
+ + pkgRollbackInfo.getPackageName() + " is inaccessible");
return;
}
@@ -696,9 +739,30 @@ class Rollback {
}
}
- int getPackageCount() {
+ /**
+ * Returns the number of {@link PackageRollbackInfo} we are storing in this {@link Rollback}
+ * instance. By default, this method does not include apk-in-apex package in the count.
+ *
+ * @param flags Apk-in-apex packages can be included in the count by passing
+ * {@link Rollback#MATCH_APK_IN_APEX}
+ *
+ * @return Counts number of {@link PackageRollbackInfo} stored in the {@link Rollback}
+ * according to {@code flags} passed
+ */
+ int getPackageCount(@RollbackInfoFlags int flags) {
synchronized (mLock) {
- return info.getPackages().size();
+ List<PackageRollbackInfo> packages = info.getPackages();
+ if ((flags & MATCH_APK_IN_APEX) != 0) {
+ return packages.size();
+ }
+
+ int packagesWithoutApkInApex = 0;
+ for (PackageRollbackInfo rollbackInfo : packages) {
+ if (!rollbackInfo.isApkInApex()) {
+ packagesWithoutApkInApex++;
+ }
+ }
+ return packagesWithoutApkInApex;
}
}
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index e29d1a765d69..8f8a5c4b14e9 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -891,9 +891,36 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
}
ApplicationInfo appInfo = pkgInfo.applicationInfo;
- return rollback.enableForPackage(packageName, newPackage.versionCode,
+ boolean success = rollback.enableForPackage(packageName, newPackage.versionCode,
pkgInfo.getLongVersionCode(), isApex, appInfo.sourceDir,
appInfo.splitSourceDirs, session.rollbackDataPolicy);
+ if (!success) {
+ return success;
+ }
+
+ if (isApex) {
+ // Check if this apex contains apks inside it. If true, then they should be added as
+ // a RollbackPackageInfo into this rollback
+ final PackageManagerInternal pmi = LocalServices.getService(
+ PackageManagerInternal.class);
+ List<String> apksInApex = pmi.getApksInApex(packageName);
+ for (String apkInApex : apksInApex) {
+ // Get information about the currently installed package.
+ final PackageInfo apkPkgInfo;
+ try {
+ apkPkgInfo = getPackageInfo(apkInApex);
+ } catch (PackageManager.NameNotFoundException e) {
+ // TODO: Support rolling back fresh package installs rather than
+ // fail here. Test this case.
+ Slog.e(TAG, apkInApex + " is not installed");
+ return false;
+ }
+ success = rollback.enableForPackageInApex(
+ apkInApex, apkPkgInfo.getLongVersionCode(), session.rollbackDataPolicy);
+ if (!success) return success;
+ }
+ }
+ return true;
}
@Override
@@ -907,9 +934,13 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
getHandler().post(() -> {
snapshotUserDataInternal(packageName, userIds);
restoreUserDataInternal(packageName, userIds, appId, seInfo);
- final PackageManagerInternal pmi = LocalServices.getService(
- PackageManagerInternal.class);
- pmi.finishPackageInstall(token, false);
+ // When this method is called as part of the install flow, a positive token number is
+ // passed to it. Need to notify the PackageManager when we are done.
+ if (token > 0) {
+ final PackageManagerInternal pmi = LocalServices.getService(
+ PackageManagerInternal.class);
+ pmi.finishPackageInstall(token, false);
+ }
});
}
@@ -1195,7 +1226,11 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
return null;
}
- if (rollback.getPackageCount() != newRollback.getPackageSessionIdCount()) {
+ // We are checking if number of packages (excluding apk-in-apex) we enabled for rollback is
+ // equal to the number of sessions we are installing, to ensure we didn't skip enabling
+ // of any sessions. If we successfully enable an apex, then we can assume we enabled
+ // rollback for the embedded apk-in-apex, if any.
+ if (rollback.getPackageCount(0 /*flags*/) != newRollback.getPackageSessionIdCount()) {
Slog.e(TAG, "Failed to enable rollback for all packages in session.");
rollback.delete(mAppDataRollbackHelper);
return null;
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index df75a29edd79..bbcd0def05a8 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -341,6 +341,7 @@ class RollbackStore {
json.put("pendingRestores", convertToJsonArray(pendingRestores));
json.put("isApex", info.isApex());
+ json.put("isApkInApex", info.isApkInApex());
// Field is named 'installedUsers' for legacy reasons.
json.put("installedUsers", convertToJsonArray(snapshottedUsers));
@@ -364,6 +365,7 @@ class RollbackStore {
json.getJSONArray("pendingRestores"));
final boolean isApex = json.getBoolean("isApex");
+ final boolean isApkInApex = json.getBoolean("isApkInApex");
// Field is named 'installedUsers' for legacy reasons.
final IntArray snapshottedUsers = convertToIntArray(json.getJSONArray("installedUsers"));
@@ -375,8 +377,8 @@ class RollbackStore {
PackageManager.RollbackDataPolicy.RESTORE);
return new PackageRollbackInfo(versionRolledBackFrom, versionRolledBackTo,
- pendingBackups, pendingRestores, isApex, snapshottedUsers, ceSnapshotInodes,
- rollbackDataPolicy);
+ pendingBackups, pendingRestores, isApex, isApkInApex, snapshottedUsers,
+ ceSnapshotInodes, rollbackDataPolicy);
}
private static JSONArray versionedPackagesToJson(List<VersionedPackage> packages)
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
index 0b89646bbed1..8385f406f13d 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.hardware.audio.common.V2_0.Uuid;
import android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback;
import android.hardware.soundtrigger.V2_3.ISoundTriggerHw;
+import android.hardware.soundtrigger.V2_3.Properties;
import android.media.audio.common.AudioConfig;
import android.media.audio.common.AudioOffloadInfo;
import android.media.soundtrigger_middleware.ConfidenceLevel;
@@ -69,6 +70,13 @@ class ConversionUtil {
return aidlProperties;
}
+ static @NonNull SoundTriggerModuleProperties hidl2aidlProperties(
+ @NonNull Properties hidlProperties) {
+ SoundTriggerModuleProperties aidlProperties = hidl2aidlProperties(hidlProperties.base);
+ aidlProperties.supportedModelArch = hidlProperties.supportedModelArch;
+ return aidlProperties;
+ }
+
static @NonNull
String hidl2aidlUuid(@NonNull Uuid hidlUuid) {
if (hidlUuid.node == null || hidlUuid.node.length != 6) {
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/Hw2CompatUtil.java b/services/core/java/com/android/server/soundtrigger_middleware/Hw2CompatUtil.java
index f0a0d8305bc6..dbf91a984bda 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/Hw2CompatUtil.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/Hw2CompatUtil.java
@@ -74,4 +74,12 @@ class Hw2CompatUtil {
config_2_0.data = HidlMemoryUtil.hidlMemoryToByteList(config.data);
return config_2_0;
}
+
+ static android.hardware.soundtrigger.V2_3.Properties convertProperties_2_0_to_2_3(
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties properties) {
+ android.hardware.soundtrigger.V2_3.Properties properties_2_3 =
+ new android.hardware.soundtrigger.V2_3.Properties();
+ properties_2_3.base = properties;
+ return properties_2_3;
+ }
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHw2.java b/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHw2.java
index 81252c9a8c14..2f024a50a276 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHw2.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHw2.java
@@ -16,7 +16,6 @@
package com.android.server.soundtrigger_middleware;
-import android.hardware.soundtrigger.V2_3.ISoundTriggerHw;
import android.hardware.soundtrigger.V2_3.ModelParameterRange;
import android.hidl.base.V1_0.IBase;
import android.os.IHwBinder;
@@ -54,9 +53,10 @@ import android.os.IHwBinder;
*/
public interface ISoundTriggerHw2 {
/**
- * @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#getProperties(android.hardware.soundtrigger.V2_0.ISoundTriggerHw.getPropertiesCallback
+ * @see android.hardware.soundtrigger.V2_3.ISoundTriggerHw#getPropertiesEx(
+ * android.hardware.soundtrigger.V2_3.ISoundTriggerHw.getPropertiesExCallback)
*/
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.Properties getProperties();
+ android.hardware.soundtrigger.V2_3.Properties getProperties();
/**
* @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#loadSoundModel_2_1(android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel,
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
index 4a852c4b68e8..3354c561b57a 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
@@ -112,18 +112,23 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
}
@Override
- public android.hardware.soundtrigger.V2_1.ISoundTriggerHw.Properties getProperties() {
+ public android.hardware.soundtrigger.V2_3.Properties getProperties() {
try {
AtomicInteger retval = new AtomicInteger(-1);
- AtomicReference<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.Properties>
+ AtomicReference<android.hardware.soundtrigger.V2_3.Properties>
properties =
new AtomicReference<>();
- as2_0().getProperties(
- (r, p) -> {
- retval.set(r);
- properties.set(p);
- });
- handleHalStatus(retval.get(), "getProperties");
+ try {
+ as2_3().getProperties_2_3(
+ (r, p) -> {
+ retval.set(r);
+ properties.set(p);
+ });
+ } catch (NotSupported e) {
+ // Fall-back to the 2.0 version:
+ return getProperties_2_0();
+ }
+ handleHalStatus(retval.get(), "getProperties_2_3");
return properties.get();
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
@@ -312,6 +317,21 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
return as2_0().interfaceDescriptor();
}
+ private android.hardware.soundtrigger.V2_3.Properties getProperties_2_0()
+ throws RemoteException {
+ AtomicInteger retval = new AtomicInteger(-1);
+ AtomicReference<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties>
+ properties =
+ new AtomicReference<>();
+ as2_0().getProperties(
+ (r, p) -> {
+ retval.set(r);
+ properties.set(p);
+ });
+ handleHalStatus(retval.get(), "getProperties");
+ return Hw2CompatUtil.convertProperties_2_0_to_2_3(properties.get());
+ }
+
private int loadSoundModel_2_0(
android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel soundModel,
Callback callback, int cookie)
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
index 987c05fd8c9d..5a06a2c4b388 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
@@ -20,6 +20,7 @@ import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.PermissionChecker;
import android.hardware.soundtrigger.V2_0.ISoundTriggerHw;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
@@ -32,6 +33,7 @@ import android.media.soundtrigger_middleware.RecognitionEvent;
import android.media.soundtrigger_middleware.RecognitionStatus;
import android.media.soundtrigger_middleware.SoundModel;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
+import android.media.soundtrigger_middleware.Status;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.util.Log;
@@ -223,23 +225,48 @@ public class SoundTriggerMiddlewareService extends ISoundTriggerMiddlewareServic
}
/**
- * Throws a {@link SecurityException} if caller doesn't have the right permissions to use this
- * service.
+ * Throws a {@link SecurityException} if caller permanently doesn't have the given permission,
+ * or a {@link ServiceSpecificException} with a {@link Status#TEMPORARY_PERMISSION_DENIED} if
+ * caller temporarily doesn't have the right permissions to use this service.
*/
private void checkPermissions() {
- mContext.enforceCallingOrSelfPermission(Manifest.permission.RECORD_AUDIO,
- "Caller must have the android.permission.RECORD_AUDIO permission.");
- mContext.enforceCallingOrSelfPermission(Manifest.permission.CAPTURE_AUDIO_HOTWORD,
- "Caller must have the android.permission.CAPTURE_AUDIO_HOTWORD permission.");
+ enforcePermission(Manifest.permission.RECORD_AUDIO);
+ enforcePermission(Manifest.permission.CAPTURE_AUDIO_HOTWORD);
}
/**
- * Throws a {@link SecurityException} if caller doesn't have the right permissions to preempt
- * active sound trigger sessions.
+ * Throws a {@link SecurityException} if caller permanently doesn't have the given permission,
+ * or a {@link ServiceSpecificException} with a {@link Status#TEMPORARY_PERMISSION_DENIED} if
+ * caller temporarily doesn't have the right permissions to preempt active sound trigger
+ * sessions.
*/
private void checkPreemptPermissions() {
- mContext.enforceCallingOrSelfPermission(Manifest.permission.PREEMPT_SOUND_TRIGGER,
- "Caller must have the android.permission.PREEMPT_SOUND_TRIGGER permission.");
+ enforcePermission(Manifest.permission.PREEMPT_SOUND_TRIGGER);
+ }
+
+ /**
+ * Throws a {@link SecurityException} if caller permanently doesn't have the given permission,
+ * or a {@link ServiceSpecificException} with a {@link Status#TEMPORARY_PERMISSION_DENIED} if
+ * caller temporarily doesn't have the given permission.
+ *
+ * @param permission The permission to check.
+ */
+ private void enforcePermission(String permission) {
+ final int status = PermissionChecker.checkCallingOrSelfPermissionForPreflight(mContext,
+ permission);
+ switch (status) {
+ case PermissionChecker.PERMISSION_GRANTED:
+ return;
+ case PermissionChecker.PERMISSION_DENIED:
+ throw new SecurityException(
+ String.format("Caller must have the %s permission.", permission));
+ case PermissionChecker.PERMISSION_DENIED_APP_OP:
+ throw new ServiceSpecificException(Status.TEMPORARY_PERMISSION_DENIED,
+ String.format("Caller must have the %s permission.", permission));
+ default:
+ throw new InternalServerError(
+ new RuntimeException("Unexpected perimission check result."));
+ }
}
/** State of a sound model. */
diff --git a/services/core/java/com/android/server/stats/StatsPullAtomService.java b/services/core/java/com/android/server/stats/StatsPullAtomService.java
index e367f284b39a..a75f1c210bc0 100644
--- a/services/core/java/com/android/server/stats/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/StatsPullAtomService.java
@@ -16,11 +16,166 @@
package com.android.server.stats;
+import static android.app.AppOpsManager.OP_FLAGS_ALL_TRUSTED;
+import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
+import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
+import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
+import static android.os.Process.getUidForPid;
+import static android.os.storage.VolumeInfo.TYPE_PRIVATE;
+import static android.os.storage.VolumeInfo.TYPE_PUBLIC;
+
+import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
+import static com.android.server.stats.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs;
+import static com.android.server.stats.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs;
+import static com.android.server.stats.ProcfsMemoryUtil.forEachPid;
+import static com.android.server.stats.ProcfsMemoryUtil.readCmdlineFromProcfs;
+import static com.android.server.stats.ProcfsMemoryUtil.readMemorySnapshotFromProcfs;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManagerInternal;
+import android.app.AlarmManager;
+import android.app.AlarmManager.OnAlarmListener;
+import android.app.AppOpsManager;
+import android.app.AppOpsManager.HistoricalOps;
+import android.app.AppOpsManager.HistoricalOpsRequest;
+import android.app.AppOpsManager.HistoricalPackageOps;
+import android.app.AppOpsManager.HistoricalUidOps;
+import android.app.INotificationManager;
+import android.app.ProcessMemoryState;
+import android.app.StatsManager;
+import android.app.StatsManager.PullAtomMetadata;
+import android.bluetooth.BluetoothActivityEnergyInfo;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.UidTraffic;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PermissionInfo;
+import android.content.pm.UserInfo;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.FingerprintManager;
+import android.net.ConnectivityManager;
+import android.net.INetworkStatsService;
+import android.net.Network;
+import android.net.NetworkRequest;
+import android.net.NetworkStats;
+import android.net.wifi.WifiManager;
+import android.os.BatteryStats;
+import android.os.BatteryStatsInternal;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.CoolingDevice;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.IPullAtomCallback;
+import android.os.IStatsCompanionService;
+import android.os.IStatsd;
+import android.os.IStoraged;
+import android.os.IThermalEventListener;
+import android.os.IThermalService;
+import android.os.Looper;
+import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.StatFs;
+import android.os.StatsLogEventWrapper;
+import android.os.SynchronousResultReceiver;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.Temperature;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.connectivity.WifiActivityEnergyInfo;
+import android.os.storage.DiskInfo;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+import android.provider.Settings;
+import android.stats.storage.StorageEnums;
+import android.telephony.ModemActivityInfo;
+import android.telephony.TelephonyManager;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
import android.util.Slog;
+import android.util.StatsEvent;
+import android.util.StatsLog;
+import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.procstats.IProcessStats;
+import com.android.internal.app.procstats.ProcessStats;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.os.BatterySipper;
+import com.android.internal.os.BatteryStatsHelper;
+import com.android.internal.os.BinderCallsStats.ExportedCallStat;
+import com.android.internal.os.KernelCpuSpeedReader;
+import com.android.internal.os.KernelCpuThreadReader;
+import com.android.internal.os.KernelCpuThreadReaderDiff;
+import com.android.internal.os.KernelCpuThreadReaderSettingsObserver;
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader;
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader;
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader;
+import com.android.internal.os.KernelWakelockReader;
+import com.android.internal.os.KernelWakelockStats;
+import com.android.internal.os.LooperStats;
+import com.android.internal.os.PowerProfile;
+import com.android.internal.os.ProcessCpuTracker;
+import com.android.internal.os.StoragedUidIoStatsReader;
+import com.android.internal.util.DumpUtils;
+import com.android.server.BinderCallsStatsService;
+import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.SystemServiceManager;
+import com.android.server.am.MemoryStatUtil.MemoryStat;
+import com.android.server.notification.NotificationManagerService;
+import com.android.server.role.RoleManagerInternal;
+import com.android.server.stats.IonMemoryUtil.IonAllocations;
+import com.android.server.stats.ProcfsMemoryUtil.MemorySnapshot;
+import com.android.server.storage.DiskStatsFileLogger;
+import com.android.server.storage.DiskStatsLoggingService;
+
+import com.google.android.collect.Sets;
+
+import libcore.io.IoUtils;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
/**
* SystemService containing PullAtomCallbacks that are registered with statsd.
@@ -31,13 +186,24 @@ public class StatsPullAtomService extends SystemService {
private static final String TAG = "StatsPullAtomService";
private static final boolean DEBUG = true;
+ private final Object mNetworkStatsLock = new Object();
+ @GuardedBy("mNetworkStatsLock")
+ private INetworkStatsService mNetworkStatsService;
+ private final Object mThermalLock = new Object();
+ @GuardedBy("mThermalLock")
+ private IThermalService mThermalService;
+
+ private final Context mContext;
+ private StatsManager mStatsManager;
+
public StatsPullAtomService(Context context) {
super(context);
+ mContext = context;
}
@Override
public void onStart() {
- // No op.
+ mStatsManager = (StatsManager) mContext.getSystemService(Context.STATS_MANAGER);
}
@Override
@@ -54,5 +220,621 @@ public class StatsPullAtomService extends SystemService {
if (DEBUG) {
Slog.d(TAG, "Registering all pullers with statsd");
}
+ registerWifiBytesTransfer();
+ registerWifiBytesTransferBackground();
+ registerMobileBytesTransfer();
+ registerMobileBytesTransferBackground();
+ registerBluetoothBytesTransfer();
+ registerKernelWakelock();
+ registerCpuTimePerFreq();
+ registerCpuTimePerUid();
+ registerCpuTimePerUidFreq();
+ registerCpuActiveTime();
+ registerCpuClusterTime();
+ registerWifiActivityInfo();
+ registerModemActivityInfo();
+ registerBluetoothActivityInfo();
+ registerSystemElapsedRealtime();
+ registerSystemUptime();
+ registerRemainingBatteryCapacity();
+ registerFullBatteryCapacity();
+ registerBatteryVoltage();
+ registerBatteryLevel();
+ registerBatteryCycleCount();
+ registerProcessMemoryState();
+ registerProcessMemoryHighWaterMark();
+ registerProcessMemorySnapshot();
+ registerSystemIonHeapSize();
+ registerProcessSystemIonHeapSize();
+ registerTemperature();
+ registerCoolingDevice();
+ registerBinderCalls();
+ registerBinderCallsExceptions();
+ registerLooperStats();
+ registerDiskStats();
+ registerDirectoryUsage();
+ registerAppSize();
+ registerCategorySize();
+ registerNumFingerprintsEnrolled();
+ registerNumFacesEnrolled();
+ registerProcStats();
+ registerProcStatsPkgProc();
+ registerDiskIO();
+ registerPowerProfile();
+ registerProcessCpuTime();
+ registerCpuTimePerThreadFreq();
+ registerDeviceCalculatedPowerUse();
+ registerDeviceCalculatedPowerBlameUid();
+ registerDeviceCalculatedPowerBlameOther();
+ registerDebugElapsedClock();
+ registerDebugFailingElapsedClock();
+ registerBuildInformation();
+ registerRoleHolder();
+ registerDangerousPermissionState();
+ registerTimeZoneDataInfo();
+ registerExternalStorageInfo();
+ registerAppsOnExternalStorageInfo();
+ registerFaceSettings();
+ registerAppOps();
+ registerNotificationRemoteViews();
+ registerDangerousPermissionState();
+ registerDangerousPermissionStateSampled();
+ }
+
+ private INetworkStatsService getINetworkStatsService() {
+ synchronized (mNetworkStatsLock) {
+ if (mNetworkStatsService == null) {
+ mNetworkStatsService = INetworkStatsService.Stub.asInterface(
+ ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
+ if (mNetworkStatsService != null) {
+ try {
+ mNetworkStatsService.asBinder().linkToDeath(() -> {
+ synchronized (mNetworkStatsLock) {
+ mNetworkStatsService = null;
+ }
+ }, /* flags */ 0);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "linkToDeath with NetworkStatsService failed", e);
+ mNetworkStatsService = null;
+ }
+ }
+
+ }
+ return mNetworkStatsService;
+ }
+ }
+
+ private IThermalService getIThermalService() {
+ synchronized (mThermalLock) {
+ if (mThermalService == null) {
+ mThermalService = IThermalService.Stub.asInterface(
+ ServiceManager.getService(Context.THERMAL_SERVICE));
+ if (mThermalService != null) {
+ try {
+ mThermalService.asBinder().linkToDeath(() -> {
+ synchronized (mThermalLock) {
+ mThermalService = null;
+ }
+ }, /* flags */ 0);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "linkToDeath with thermalService failed", e);
+ mThermalService = null;
+ }
+ }
+ }
+ return mThermalService;
+ }
+ }
+ private void registerWifiBytesTransfer() {
+ int tagId = StatsLog.WIFI_BYTES_TRANSFER;
+ PullAtomMetadata metaData = PullAtomMetadata.newBuilder()
+ .setAdditiveFields(new int[] {2, 3, 4, 5}).build();
+ mStatsManager.registerPullAtomCallback(
+ tagId,
+ metaData,
+ (atomTag, data) -> pullWifiBytesTransfer(atomTag, data),
+ Executors.newSingleThreadExecutor()
+ );
+ }
+
+ private int pullWifiBytesTransfer(int atomTag, List<StatsEvent> pulledData) {
+ INetworkStatsService networkStatsService = getINetworkStatsService();
+ if (networkStatsService == null) {
+ Slog.e(TAG, "NetworkStats Service is not available!");
+ return StatsManager.PULL_SKIP;
+ }
+ long token = Binder.clearCallingIdentity();
+ try {
+ // TODO: Consider caching the following call to get BatteryStatsInternal.
+ BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
+ String[] ifaces = bs.getWifiIfaces();
+ if (ifaces.length == 0) {
+ return StatsManager.PULL_SKIP;
+ }
+ // Combine all the metrics per Uid into one record.
+ NetworkStats stats = networkStatsService.getDetailedUidStats(ifaces).groupedByUid();
+ addNetworkStats(atomTag, pulledData, stats, false);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Pulling netstats for wifi bytes has error", e);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return StatsManager.PULL_SUCCESS;
+ }
+
+ private void addNetworkStats(
+ int tag, List<StatsEvent> ret, NetworkStats stats, boolean withFGBG) {
+ int size = stats.size();
+ NetworkStats.Entry entry = new NetworkStats.Entry(); // For recycling
+ for (int j = 0; j < size; j++) {
+ stats.getValues(j, entry);
+ StatsEvent.Builder e = StatsEvent.newBuilder();
+ e.setAtomId(tag);
+ e.writeInt(entry.uid);
+ if (withFGBG) {
+ e.writeInt(entry.set);
+ }
+ e.writeLong(entry.rxBytes);
+ e.writeLong(entry.rxPackets);
+ e.writeLong(entry.txBytes);
+ e.writeLong(entry.txPackets);
+ ret.add(e.build());
+ }
+ }
+
+ private void registerWifiBytesTransferBackground() {
+ // No op.
+ }
+
+ private void pullWifiBytesTransferBackground() {
+ // No op.
+ }
+
+ private void registerMobileBytesTransfer() {
+ // No op.
+ }
+
+ private void pullMobileBytesTransfer() {
+ // No op.
+ }
+
+ private void registerMobileBytesTransferBackground() {
+ // No op.
+ }
+
+ private void pullMobileBytesTransferBackground() {
+ // No op.
+ }
+
+ private void registerBluetoothBytesTransfer() {
+ // No op.
+ }
+
+ private void pullBluetoothBytesTransfer() {
+ // No op.
+ }
+
+ private void registerKernelWakelock() {
+ // No op.
+ }
+
+ private void pullKernelWakelock() {
+ // No op.
+ }
+
+ private void registerCpuTimePerFreq() {
+ // No op.
+ }
+
+ private void pullCpuTimePerFreq() {
+ // No op.
+ }
+
+ private void registerCpuTimePerUid() {
+ // No op.
+ }
+
+ private void pullCpuTimePerUid() {
+ // No op.
+ }
+
+ private void registerCpuTimePerUidFreq() {
+ // No op.
+ }
+
+ private void pullCpuTimeperUidFreq() {
+ // No op.
+ }
+
+ private void registerCpuActiveTime() {
+ // No op.
+ }
+
+ private void pullCpuActiveTime() {
+ // No op.
+ }
+
+ private void registerCpuClusterTime() {
+ // No op.
+ }
+
+ private int pullCpuClusterTime() {
+ return 0;
+ }
+
+ private void registerWifiActivityInfo() {
+ // No op.
+ }
+
+ private void pullWifiActivityInfo() {
+ // No op.
+ }
+
+ private void registerModemActivityInfo() {
+ // No op.
+ }
+
+ private void pullModemActivityInfo() {
+ // No op.
+ }
+
+ private void registerBluetoothActivityInfo() {
+ // No op.
+ }
+
+ private void pullBluetoothActivityInfo() {
+ // No op.
+ }
+
+ private void registerSystemElapsedRealtime() {
+ // No op.
+ }
+
+ private void pullSystemElapsedRealtime() {
+ // No op.
+ }
+
+ private void registerSystemUptime() {
+ // No op.
+ }
+
+ private void pullSystemUptime() {
+ // No op.
+ }
+
+ private void registerRemainingBatteryCapacity() {
+ // No op.
+ }
+
+ private void pullRemainingBatteryCapacity() {
+ // No op.
+ }
+
+ private void registerFullBatteryCapacity() {
+ // No op.
+ }
+
+ private void pullFullBatteryCapacity() {
+ // No op.
+ }
+
+ private void registerBatteryVoltage() {
+ // No op.
+ }
+
+ private void pullBatteryVoltage() {
+ // No op.
+ }
+
+ private void registerBatteryLevel() {
+ // No op.
+ }
+
+ private void pullBatteryLevel() {
+ // No op.
+ }
+
+ private void registerBatteryCycleCount() {
+ // No op.
+ }
+
+ private void pullBatteryCycleCount() {
+ // No op.
+ }
+
+ private void registerProcessMemoryState() {
+ // No op.
+ }
+
+ private void pullProcessMemoryState() {
+ // No op.
+ }
+
+ private void registerProcessMemoryHighWaterMark() {
+ // No op.
+ }
+
+ private void pullProcessMemoryHighWaterMark() {
+ // No op.
+ }
+
+ private void registerProcessMemorySnapshot() {
+ // No op.
+ }
+
+ private void pullProcessMemorySnapshot() {
+ // No op.
+ }
+
+ private void registerSystemIonHeapSize() {
+ // No op.
+ }
+
+ private void pullSystemIonHeapSize() {
+ // No op.
+ }
+
+ private void registerProcessSystemIonHeapSize() {
+ // No op.
+ }
+
+ private void pullProcessSystemIonHeapSize() {
+ // No op.
+ }
+
+ private void registerTemperature() {
+ // No op.
+ }
+
+ private void pullTemperature() {
+ // No op.
+ }
+
+ private void registerCoolingDevice() {
+ // No op.
+ }
+
+ private void pullCooldownDevice() {
+ // No op.
+ }
+
+ private void registerBinderCalls() {
+ // No op.
+ }
+
+ private void pullBinderCalls() {
+ // No op.
+ }
+
+ private void registerBinderCallsExceptions() {
+ // No op.
+ }
+
+ private void pullBinderCallsExceptions() {
+ // No op.
+ }
+
+ private void registerLooperStats() {
+ // No op.
+ }
+
+ private void pullLooperStats() {
+ // No op.
+ }
+
+ private void registerDiskStats() {
+ // No op.
+ }
+
+ private void pullDiskStats() {
+ // No op.
+ }
+
+ private void registerDirectoryUsage() {
+ // No op.
+ }
+
+ private void pullDirectoryUsage() {
+ // No op.
+ }
+
+ private void registerAppSize() {
+ // No op.
+ }
+
+ private void pullAppSize() {
+ // No op.
+ }
+
+ private void registerCategorySize() {
+ // No op.
+ }
+
+ private void pullCategorySize() {
+ // No op.
+ }
+
+ private void registerNumFingerprintsEnrolled() {
+ // No op.
+ }
+
+ private void pullNumFingerprintsEnrolled() {
+ // No op.
+ }
+
+ private void registerNumFacesEnrolled() {
+ // No op.
+ }
+
+ private void pullNumFacesEnrolled() {
+ // No op.
+ }
+
+ private void registerProcStats() {
+ // No op.
+ }
+
+ private void pullProcStats() {
+ // No op.
+ }
+
+ private void registerProcStatsPkgProc() {
+ // No op.
+ }
+
+ private void pullProcStatsPkgProc() {
+ // No op.
+ }
+
+ private void registerDiskIO() {
+ // No op.
+ }
+
+ private void pullDiskIO() {
+ // No op.
+ }
+
+ private void registerPowerProfile() {
+ // No op.
+ }
+
+ private void pullPowerProfile() {
+ // No op.
+ }
+
+ private void registerProcessCpuTime() {
+ // No op.
+ }
+
+ private void pullProcessCpuTime() {
+ // No op.
+ }
+
+ private void registerCpuTimePerThreadFreq() {
+ // No op.
+ }
+
+ private void pullCpuTimePerThreadFreq() {
+ // No op.
+ }
+
+ private void registerDeviceCalculatedPowerUse() {
+ // No op.
+ }
+
+ private void pullDeviceCalculatedPowerUse() {
+ // No op.
+ }
+
+ private void registerDeviceCalculatedPowerBlameUid() {
+ // No op.
+ }
+
+ private void pullDeviceCalculatedPowerBlameUid() {
+ // No op.
+ }
+
+ private void registerDeviceCalculatedPowerBlameOther() {
+ // No op.
+ }
+
+ private void pullDeviceCalculatedPowerBlameOther() {
+ // No op.
+ }
+
+ private void registerDebugElapsedClock() {
+ // No op.
+ }
+
+ private void pullDebugElapsedClock() {
+ // No op.
+ }
+
+ private void registerDebugFailingElapsedClock() {
+ // No op.
+ }
+
+ private void pullDebugFailingElapsedClock() {
+ // No op.
+ }
+
+ private void registerBuildInformation() {
+ // No op.
+ }
+
+ private void pullBuildInformation() {
+ // No op.
+ }
+
+ private void registerRoleHolder() {
+ // No op.
+ }
+
+ private void pullRoleHolder() {
+ // No op.
+ }
+
+ private void registerDangerousPermissionState() {
+ // No op.
+ }
+
+ private void pullDangerousPermissionState() {
+ // No op.
+ }
+
+ private void registerTimeZoneDataInfo() {
+ // No op.
+ }
+
+ private void pullTimeZoneDataInfo() {
+ // No op.
+ }
+
+ private void registerExternalStorageInfo() {
+ // No op.
+ }
+
+ private void pullExternalStorageInfo() {
+ // No op.
+ }
+
+ private void registerAppsOnExternalStorageInfo() {
+ // No op.
+ }
+
+ private void pullAppsOnExternalStorageInfo() {
+ // No op.
+ }
+
+ private void registerFaceSettings() {
+ // No op.
+ }
+
+ private void pullRegisterFaceSettings() {
+ // No op.
+ }
+
+ private void registerAppOps() {
+ // No op.
+ }
+
+ private void pullAppOps() {
+ // No op.
+ }
+
+ private void registerNotificationRemoteViews() {
+ // No op.
+ }
+
+ private void pullNotificationRemoteViews() {
+ // No op.
+ }
+
+ private void registerDangerousPermissionStateSampled() {
+ // No op.
+ }
+
+ private void pullDangerousPermissionStateSampled() {
+ // No op.
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index c95943904d1f..9fd3ea4fc090 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -163,6 +163,7 @@ import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
+import android.view.ITaskOrganizer;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -803,6 +804,16 @@ class ActivityStack extends WindowContainer<WindowContainer> implements BoundsAn
setWindowingMode(windowingMode, false /* animate */, false /* showRecents */,
false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */,
false /* creating */);
+
+ windowingMode = getWindowingMode();
+ /*
+ * Different windowing modes may be managed by different task organizers. If
+ * getTaskOrganizer returns null, we still call transferToTaskOrganizer to
+ * make sure we clear it.
+ */
+ final ITaskOrganizer org =
+ mWmService.mAtmService.mTaskOrganizerController.getTaskOrganizer(windowingMode);
+ transferToTaskOrganizer(org);
}
/**
@@ -1650,6 +1661,33 @@ class ActivityStack extends WindowContainer<WindowContainer> implements BoundsAn
}
/**
+ * Indicate whether the first task in this stack is controlled by a TaskOrganizer. We aren't
+ * expecting to use the TaskOrganizer in multiple task per stack scenarios so checking
+ * the first one is ok.
+ */
+ boolean isControlledByTaskOrganizer() {
+ return getChildCount() > 0 && getTopMostTask().mTaskOrganizer != null;
+ }
+
+ private static void transferSingleTaskToOrganizer(Task tr, ITaskOrganizer organizer) {
+ tr.setTaskOrganizer(organizer);
+ }
+
+ /**
+ * Transfer control of the leashes and IWindowContainers to the given ITaskOrganizer.
+ * This will (or shortly there-after) invoke the taskAppeared callbacks.
+ * If the tasks had a previous TaskOrganizer, setTaskOrganizer will take care of
+ * emitting the taskVanished callbacks.
+ */
+ void transferToTaskOrganizer(ITaskOrganizer organizer) {
+ final PooledConsumer c = PooledLambda.obtainConsumer(
+ ActivityStack::transferSingleTaskToOrganizer,
+ PooledLambda.__(Task.class), organizer);
+ forAllTasks(c);
+ c.recycle();
+ }
+
+ /**
* Returns true if the stack should be visible.
*
* @param starting The currently starting activity or null if there is none.
@@ -3577,6 +3615,15 @@ class ActivityStack extends WindowContainer<WindowContainer> implements BoundsAn
void animateResizePinnedStack(Rect toBounds, Rect sourceHintBounds, int animationDuration,
boolean fromFullscreen) {
if (!inPinnedWindowingMode()) return;
+
+ /**
+ * TODO(b/146594635): Remove all PIP animation code from WM once SysUI handles animation.
+ * If this PIP Task is controlled by a TaskOrganizer, the animation occurs entirely
+ * on the TaskOrganizer side, so we just hand over the leash without doing any animation.
+ * We have to be careful to not schedule the enter-pip callback as the TaskOrganizer
+ * needs to have flexibility to schedule that at an appropriate point in the animation.
+ */
+ if (isControlledByTaskOrganizer()) return;
if (toBounds == null /* toFullscreen */) {
final Configuration parentConfig = getParent().getConfiguration();
final ActivityRecord top = topRunningNonOverlayTaskActivity();
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index fd871bdc1ba2..aa90248b97f8 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -83,6 +83,7 @@ import static com.android.server.wm.Task.LOCK_TASK_AUTH_WHITELISTED;
import static com.android.server.wm.Task.REPARENT_KEEP_STACK_AT_FRONT;
import static com.android.server.wm.Task.REPARENT_LEAVE_STACK_IN_PLACE;
import static com.android.server.wm.Task.REPARENT_MOVE_STACK_TO_FRONT;
+import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
@@ -2079,7 +2080,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
ArrayList<ActivityRecord> readyToStopActivities = null;
for (int i = mStoppingActivities.size() - 1; i >= 0; --i) {
final ActivityRecord s = mStoppingActivities.get(i);
- final boolean animating = s.isAnimating(TRANSITION);
+ final boolean animating = s.isAnimating(TRANSITION | PARENTS);
if (DEBUG_STATES) Slog.v(TAG, "Stopping " + s + ": nowVisible=" + s.nowVisible
+ " animating=" + animating + " finishing=" + s.finishing);
@@ -2547,6 +2548,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
final PooledConsumer c = PooledLambda.obtainConsumer(
ActivityRecord::updatePictureInPictureMode,
PooledLambda.__(ActivityRecord.class), targetStackBounds, forceUpdate);
+ task.getStack().setBounds(targetStackBounds);
task.forAllActivities(c);
c.recycle();
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 474c5c960eb0..ded603c9fd77 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -226,6 +226,7 @@ import android.util.StatsLog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import android.view.IRecentsAnimationRunner;
+import android.view.ITaskOrganizer;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
import android.view.WindowContainerTransaction;
@@ -662,6 +663,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
private FontScaleSettingObserver mFontScaleSettingObserver;
+ /**
+ * Stores the registration and state of TaskOrganizers in use.
+ */
+ TaskOrganizerController mTaskOrganizerController =
+ new TaskOrganizerController(this, mGlobalLock);
+
private int mDeviceOwnerUid = Process.INVALID_UID;
private final class FontScaleSettingObserver extends ContentObserver {
@@ -1271,6 +1278,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
.execute();
}
+ @Override
+ public final void registerTaskOrganizer(ITaskOrganizer organizer, int windowingMode) {
+ enforceCallerIsRecentsOrHasPermission(
+ MANAGE_ACTIVITY_STACKS, "registerTaskOrganizer()");
+ synchronized (mGlobalLock) {
+ mTaskOrganizerController.registerTaskOrganizer(organizer, windowingMode);
+ }
+ }
@Override
public IBinder requestStartActivityPermissionToken(IBinder delegatorToken) {
@@ -3319,6 +3334,18 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
}
+ private void applyWindowContainerChange(ConfigurationContainer cc,
+ WindowContainerTransaction.Change c) {
+ sanitizeAndApplyConfigChange(cc, c);
+
+ Rect enterPipBounds = c.getEnterPipBounds();
+ if (enterPipBounds != null) {
+ Task tr = (Task) cc;
+ mStackSupervisor.updatePictureInPictureMode(tr,
+ enterPipBounds, true);
+ }
+ }
+
@Override
public void applyContainerTransaction(WindowContainerTransaction t) {
mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "applyContainerTransaction()");
@@ -3335,7 +3362,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
entries.next();
final ConfigurationContainer cc = ConfigurationContainer.RemoteToken.fromBinder(
entry.getKey()).getContainer();
- sanitizeAndApplyConfigChange(cc, entry.getValue());
+ applyWindowContainerChange(cc, entry.getValue());
}
}
} finally {
@@ -4057,7 +4084,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
throw new IllegalArgumentException("Stack: " + stack
+ " doesn't support animated resize.");
}
- if (animate) {
+ /**
+ * TODO(b/146594635): Remove all PIP animation code from WM
+ * once SysUI handles animation. Don't even try to animate TaskOrganized tasks.
+ */
+ if (animate && !stack.isControlledByTaskOrganizer()) {
stack.animateResizePinnedStack(null /* destBounds */,
null /* sourceHintBounds */, animationDuration,
false /* fromFullscreen */);
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 09111d07e8c1..014cb76c0064 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -436,10 +436,9 @@ public class AppTransition implements Dump {
mNextAppTransition = TRANSIT_UNSET;
mNextAppTransitionFlags = 0;
setAppTransitionState(APP_STATE_RUNNING);
- final AnimationAdapter topOpeningAnim =
- (topOpeningApp != null && topOpeningApp.getAnimatingContainer() != null)
- ? topOpeningApp.getAnimatingContainer().getAnimation()
- : null;
+ final WindowContainer wc =
+ topOpeningApp != null ? topOpeningApp.getAnimatingContainer() : null;
+ final AnimationAdapter topOpeningAnim = wc != null ? wc.getAnimation() : null;
int redoLayout = notifyAppTransitionStartingLocked(transit,
topOpeningAnim != null ? topOpeningAnim.getDurationHint() : 0,
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index dd3365c900d7..d0310f1a7607 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -666,4 +666,8 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
return sb.toString();
}
}
+
+ RemoteToken getRemoteToken() {
+ return mRemoteToken;
+ }
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index f9ad03f40723..9c62e9970b48 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -2061,7 +2061,8 @@ public class DisplayPolicy {
cf.set(displayFrames.mRestricted);
}
applyStableConstraints(sysUiFl, fl, cf, displayFrames);
- if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
+ if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_NONE
+ && adjust != SOFT_INPUT_ADJUST_NOTHING) {
vf.set(displayFrames.mCurrent);
} else {
vf.set(cf);
@@ -2138,7 +2139,8 @@ public class DisplayPolicy {
applyStableConstraints(sysUiFl, fl, cf, displayFrames);
- if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
+ if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_NONE
+ && adjust != SOFT_INPUT_ADJUST_NOTHING) {
vf.set(displayFrames.mCurrent);
} else {
vf.set(cf);
@@ -2179,7 +2181,8 @@ public class DisplayPolicy {
cf.set(displayFrames.mContent);
df.set(displayFrames.mContent);
}
- if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
+ if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_NONE
+ && adjust != SOFT_INPUT_ADJUST_NOTHING) {
vf.set(displayFrames.mCurrent);
} else {
vf.set(cf);
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index c4b67d76607e..091f66c0b19a 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -279,6 +279,19 @@ final class InputMonitor {
// we avoid reintroducing this concept by just choosing one of them here.
inputWindowHandle.surfaceInset = child.getAttrs().surfaceInsets.left;
+ /**
+ * If the window is in a TaskManaged by a TaskOrganizer then most cropping
+ * will be applied using the SurfaceControl hierarchy from the Organizer.
+ * This means we need to make sure that these changes in crop are reflected
+ * in the input windows, and so ensure this flag is set so that
+ * the input crop always reflects the surface hierarchy.
+ * we may have some issues with modal-windows, but I guess we can
+ * cross that bridge when we come to implementing full-screen TaskOrg
+ */
+ if (child.getTask() != null && child.getTask().isControlledByTaskOrganizer()) {
+ inputWindowHandle.replaceTouchableRegionWithCrop(null /* Use this surfaces crop */);
+ }
+
if (child.mGlobalScale != 1) {
// If we are scaling the window, input coordinates need
// to be inversely scaled to map from what is on screen
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index a13383d3991e..5a591ecd4746 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -129,6 +129,7 @@ class InsetsSourceProvider {
if (win == null) {
setServerVisible(false);
mSource.setFrame(new Rect());
+ mSource.setVisibleFrame(null);
} else if (mControllable) {
mWin.setControllableInsetProvider(this);
if (mControlTarget != null) {
@@ -160,6 +161,15 @@ class InsetsSourceProvider {
mTmpRect.inset(mWin.mGivenContentInsets);
}
mSource.setFrame(mTmpRect);
+
+ if (mWin.mGivenVisibleInsets.left != 0 || mWin.mGivenVisibleInsets.top != 0
+ || mWin.mGivenVisibleInsets.right != 0 || mWin.mGivenVisibleInsets.bottom != 0) {
+ mTmpRect.set(mWin.getFrameLw());
+ mTmpRect.inset(mWin.mGivenVisibleInsets);
+ mSource.setVisibleFrame(mTmpRect);
+ } else {
+ mSource.setVisibleFrame(null);
+ }
}
/**
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index a7bf6600d7b5..c3e815d10dda 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2168,12 +2168,18 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
mService.continueWindowLayout();
}
+ // TODO(b/146594635): Remove all PIP animation code from WM once SysUI handles animation.
// Notify the pinned stack controller to prepare the PiP animation, expect callback
- // delivered from SystemUI to WM to start the animation.
- final PinnedStackController pinnedStackController =
+ // delivered from SystemUI to WM to start the animation. Unless we are using
+ // the TaskOrganizer in which case the animation will be entirely handled
+ // on that side.
+ if (mService.mTaskOrganizerController.getTaskOrganizer(WINDOWING_MODE_PINNED)
+ == null) {
+ final PinnedStackController pinnedStackController =
display.mDisplayContent.getPinnedStackController();
- pinnedStackController.prepareAnimation(sourceHintBounds, aspectRatio,
- null /* stackBounds */);
+ pinnedStackController.prepareAnimation(sourceHintBounds, aspectRatio,
+ null /* stackBounds */);
+ }
// TODO: revisit the following statement after the animation is moved from WM to SysUI.
// Update the visibility of all activities after the they have been reparented to the new
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index eaa0ea72452a..399c5d3ae45f 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -16,12 +16,8 @@
package com.android.server.wm;
-import static com.android.server.wm.AnimationSpecProto.ROTATE;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.server.wm.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC;
-import static com.android.server.wm.RotationAnimationSpecProto.DURATION_MS;
-import static com.android.server.wm.RotationAnimationSpecProto.END_LUMA;
-import static com.android.server.wm.RotationAnimationSpecProto.START_LUMA;
import static com.android.server.wm.ScreenRotationAnimationProto.ANIMATION_RUNNING;
import static com.android.server.wm.ScreenRotationAnimationProto.STARTED;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -29,9 +25,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
import static com.android.server.wm.WindowStateAnimator.WINDOW_FREEZE_LAYER;
-import android.animation.ArgbEvaluator;
import android.content.Context;
-import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
@@ -46,9 +40,7 @@ import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.Transformation;
-import com.android.internal.R;
import com.android.server.protolog.common.ProtoLog;
-import com.android.server.wm.utils.RotationAnimationUtils;
import java.io.PrintWriter;
@@ -68,10 +60,10 @@ import java.io.PrintWriter;
* animation first rotate the new content into the old orientation to then be able to
* animate to the new orientation
*
- * <li> The Background color frame: <p>
- * To have the animation seem more seamless, we add a color transitioning background behind the
- * exiting and entering layouts. We compute the brightness of the start and end
- * layouts and transition from the two brightness values as grayscale underneath the animation
+ * <li> The exiting Blackframe: <p>
+ * Because the change of orientation might change the width and height of the content (i.e
+ * when rotating from portrait to landscape) we "crop" the new content using black frames
+ * around the screenshot so the new content does not go beyond the screenshot's bounds
*
* <li> The entering Blackframe: <p>
* The enter Blackframe is similar to the exit Blackframe but is only used when a custom
@@ -89,6 +81,8 @@ class ScreenRotationAnimation {
*/
private static final int SCREEN_FREEZE_LAYER_BASE = WINDOW_FREEZE_LAYER + TYPE_LAYER_MULTIPLIER;
private static final int SCREEN_FREEZE_LAYER_ENTER = SCREEN_FREEZE_LAYER_BASE;
+ private static final int SCREEN_FREEZE_LAYER_SCREENSHOT = SCREEN_FREEZE_LAYER_BASE + 1;
+ private static final int SCREEN_FREEZE_LAYER_EXIT = SCREEN_FREEZE_LAYER_BASE + 2;
private final Context mContext;
private final DisplayContent mDisplayContent;
@@ -96,18 +90,16 @@ class ScreenRotationAnimation {
private final Transformation mRotateExitTransformation = new Transformation();
private final Transformation mRotateEnterTransformation = new Transformation();
// Complete transformations being applied.
+ private final Transformation mExitTransformation = new Transformation();
private final Transformation mEnterTransformation = new Transformation();
+ private final Matrix mFrameInitialMatrix = new Matrix();
private final Matrix mSnapshotInitialMatrix = new Matrix();
+ private final Matrix mSnapshotFinalMatrix = new Matrix();
+ private final Matrix mExitFrameFinalMatrix = new Matrix();
private final WindowManagerService mService;
- /** Only used for custom animations and not screen rotation. */
private SurfaceControl mEnterBlackFrameLayer;
- /** This layer contains the actual screenshot that is to be faded out. */
- private SurfaceControl mScreenshotLayer;
- /**
- * Only used for screen rotation and not custom animations. Layered behind all other layers
- * to avoid showing any "empty" spots
- */
- private SurfaceControl mBackColorSurface;
+ private SurfaceControl mRotationLayer;
+ private SurfaceControl mSurfaceControl;
private BlackFrame mEnteringBlackFrame;
private int mWidth, mHeight;
@@ -128,11 +120,8 @@ class ScreenRotationAnimation {
private boolean mFinishAnimReady;
private long mFinishAnimStartTime;
private boolean mForceDefaultOrientation;
+ private BlackFrame mExitingBlackFrame;
private SurfaceRotationAnimationController mSurfaceRotationAnimationController;
- /** Intensity of light/whiteness of the layout before rotation occurs. */
- private float mStartLuma;
- /** Intensity of light/whiteness of the layout after rotation occurs. */
- private float mEndLuma;
public ScreenRotationAnimation(Context context, DisplayContent displayContent,
boolean fixedToUserRotation, boolean isSecure, WindowManagerService service) {
@@ -173,15 +162,9 @@ class ScreenRotationAnimation {
final SurfaceControl.Transaction t = mService.mTransactionFactory.get();
try {
- mBackColorSurface = displayContent.makeChildSurface(null)
- .setName("BackColorSurface")
- .setColorLayer()
- .build();
-
- mScreenshotLayer = displayContent.makeOverlay()
+ mRotationLayer = displayContent.makeOverlay()
.setName("RotationLayer")
- .setBufferSize(mWidth, mHeight)
- .setSecure(isSecure)
+ .setContainerLayer()
.build();
mEnterBlackFrameLayer = displayContent.makeOverlay()
@@ -189,21 +172,26 @@ class ScreenRotationAnimation {
.setContainerLayer()
.build();
+ mSurfaceControl = mService.makeSurfaceBuilder(null)
+ .setName("ScreenshotSurface")
+ .setParent(mRotationLayer)
+ .setBufferSize(mWidth, mHeight)
+ .setSecure(isSecure)
+ .build();
+
// In case display bounds change, screenshot buffer and surface may mismatch so set a
// scaling mode.
SurfaceControl.Transaction t2 = mService.mTransactionFactory.get();
- t2.setOverrideScalingMode(mScreenshotLayer, Surface.SCALING_MODE_SCALE_TO_WINDOW);
+ t2.setOverrideScalingMode(mSurfaceControl, Surface.SCALING_MODE_SCALE_TO_WINDOW);
t2.apply(true /* sync */);
// Capture a screenshot into the surface we just created.
final int displayId = display.getDisplayId();
final Surface surface = mService.mSurfaceFactory.get();
- surface.copyFrom(mScreenshotLayer);
+ surface.copyFrom(mSurfaceControl);
SurfaceControl.ScreenshotGraphicBuffer gb =
mService.mDisplayManagerInternal.screenshot(displayId);
if (gb != null) {
- mStartLuma = RotationAnimationUtils.getAvgBorderLuma(gb.getGraphicBuffer(),
- gb.getColorSpace());
try {
surface.attachAndQueueBufferWithColorSpace(gb.getGraphicBuffer(),
gb.getColorSpace());
@@ -214,15 +202,13 @@ class ScreenRotationAnimation {
// screenshot surface we display it in also has FLAG_SECURE so that
// the user can not screenshot secure layers via the screenshot surface.
if (gb.containsSecureLayers()) {
- t.setSecure(mScreenshotLayer, true);
+ t.setSecure(mSurfaceControl, true);
}
- t.setLayer(mScreenshotLayer, SCREEN_FREEZE_LAYER_BASE);
- t.reparent(mBackColorSurface, displayContent.getSurfaceControl());
- t.setLayer(mBackColorSurface, -1);
- t.setColor(mBackColorSurface, new float[]{mStartLuma, mStartLuma, mStartLuma});
- t.setAlpha(mBackColorSurface, 1);
- t.show(mScreenshotLayer);
- t.show(mBackColorSurface);
+ t.setLayer(mRotationLayer, SCREEN_FREEZE_LAYER_BASE);
+ t.setLayer(mSurfaceControl, SCREEN_FREEZE_LAYER_SCREENSHOT);
+ t.setAlpha(mSurfaceControl, 0);
+ t.show(mRotationLayer);
+ t.show(mSurfaceControl);
} else {
Slog.w(TAG, "Unable to take screenshot of display " + displayId);
}
@@ -232,11 +218,32 @@ class ScreenRotationAnimation {
}
ProtoLog.i(WM_SHOW_SURFACE_ALLOC,
- " FREEZE %s: CREATE", mScreenshotLayer);
+ " FREEZE %s: CREATE", mSurfaceControl);
setRotation(t, originalRotation);
t.apply();
}
+ private static void createRotationMatrix(int rotation, int width, int height,
+ Matrix outMatrix) {
+ switch (rotation) {
+ case Surface.ROTATION_0:
+ outMatrix.reset();
+ break;
+ case Surface.ROTATION_90:
+ outMatrix.setRotate(90, 0, 0);
+ outMatrix.postTranslate(height, 0);
+ break;
+ case Surface.ROTATION_180:
+ outMatrix.setRotate(180, 0, 0);
+ outMatrix.postTranslate(width, height);
+ break;
+ case Surface.ROTATION_270:
+ outMatrix.setRotate(270, 0, 0);
+ outMatrix.postTranslate(0, width);
+ break;
+ }
+ }
+
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(STARTED, mStarted);
@@ -245,11 +252,11 @@ class ScreenRotationAnimation {
}
boolean hasScreenshot() {
- return mScreenshotLayer != null;
+ return mSurfaceControl != null;
}
private void setRotationTransform(SurfaceControl.Transaction t, Matrix matrix) {
- if (mScreenshotLayer == null) {
+ if (mRotationLayer == null) {
return;
}
matrix.getValues(mTmpFloats);
@@ -260,19 +267,24 @@ class ScreenRotationAnimation {
x -= mCurrentDisplayRect.left;
y -= mCurrentDisplayRect.top;
}
- t.setPosition(mScreenshotLayer, x, y);
- t.setMatrix(mScreenshotLayer,
+ t.setPosition(mRotationLayer, x, y);
+ t.setMatrix(mRotationLayer,
mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
- t.setAlpha(mScreenshotLayer, (float) 1.0);
- t.show(mScreenshotLayer);
+ t.setAlpha(mSurfaceControl, (float) 1.0);
+ t.setAlpha(mRotationLayer, (float) 1.0);
+ t.show(mRotationLayer);
}
public void printTo(String prefix, PrintWriter pw) {
- pw.print(prefix); pw.print("mSurface="); pw.print(mScreenshotLayer);
+ pw.print(prefix); pw.print("mSurface="); pw.print(mSurfaceControl);
pw.print(" mWidth="); pw.print(mWidth);
pw.print(" mHeight="); pw.println(mHeight);
+ pw.print(prefix); pw.print("mExitingBlackFrame="); pw.println(mExitingBlackFrame);
+ if (mExitingBlackFrame != null) {
+ mExitingBlackFrame.printTo(prefix + " ", pw);
+ }
pw.print(prefix);
pw.print("mEnteringBlackFrame=");
pw.println(mEnteringBlackFrame);
@@ -291,10 +303,20 @@ class ScreenRotationAnimation {
pw.print(" "); mRotateExitTransformation.printShortString(pw); pw.println();
pw.print(prefix); pw.print("mRotateEnterAnimation="); pw.print(mRotateEnterAnimation);
pw.print(" "); mRotateEnterTransformation.printShortString(pw); pw.println();
+ pw.print(prefix); pw.print("mExitTransformation=");
+ mExitTransformation.printShortString(pw); pw.println();
pw.print(prefix); pw.print("mEnterTransformation=");
mEnterTransformation.printShortString(pw); pw.println();
+ pw.print(prefix); pw.print("mFrameInitialMatrix=");
+ mFrameInitialMatrix.printShortString(pw);
+ pw.println();
pw.print(prefix); pw.print("mSnapshotInitialMatrix=");
- mSnapshotInitialMatrix.printShortString(pw);pw.println();
+ mSnapshotInitialMatrix.printShortString(pw);
+ pw.print(" mSnapshotFinalMatrix="); mSnapshotFinalMatrix.printShortString(pw);
+ pw.println();
+ pw.print(prefix); pw.print("mExitFrameFinalMatrix=");
+ mExitFrameFinalMatrix.printShortString(pw);
+ pw.println();
pw.print(prefix); pw.print("mForceDefaultOrientation="); pw.print(mForceDefaultOrientation);
if (mForceDefaultOrientation) {
pw.print(" mOriginalDisplayRect="); pw.print(mOriginalDisplayRect.toShortString());
@@ -309,7 +331,7 @@ class ScreenRotationAnimation {
// to the snapshot to make it stay in the same original position
// with the current screen rotation.
int delta = DisplayContent.deltaRotation(rotation, Surface.ROTATION_0);
- RotationAnimationUtils.createRotationMatrix(delta, mWidth, mHeight, mSnapshotInitialMatrix);
+ createRotationMatrix(delta, mWidth, mHeight, mSnapshotInitialMatrix);
setRotationTransform(t, mSnapshotInitialMatrix);
}
@@ -319,7 +341,7 @@ class ScreenRotationAnimation {
*/
private boolean startAnimation(SurfaceControl.Transaction t, long maxAnimationDuration,
float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim) {
- if (mScreenshotLayer == null) {
+ if (mSurfaceControl == null) {
// Can't do animation.
return false;
}
@@ -332,58 +354,89 @@ class ScreenRotationAnimation {
// Figure out how the screen has moved from the original rotation.
int delta = DisplayContent.deltaRotation(mCurRotation, mOriginalRotation);
+ mRotateAlphaAnimation = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.screen_rotate_alpha);
final boolean customAnim;
if (exitAnim != 0 && enterAnim != 0) {
customAnim = true;
mRotateExitAnimation = AnimationUtils.loadAnimation(mContext, exitAnim);
mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext, enterAnim);
- mRotateAlphaAnimation = AnimationUtils.loadAnimation(mContext,
- R.anim.screen_rotate_alpha);
} else {
customAnim = false;
- switch (delta) { /* Counter-Clockwise Rotations */
+ switch (delta) {
case Surface.ROTATION_0:
mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
- R.anim.screen_rotate_0_exit);
+ com.android.internal.R.anim.screen_rotate_0_exit);
mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
- R.anim.screen_rotate_0_enter);
+ com.android.internal.R.anim.screen_rotate_0_enter);
break;
case Surface.ROTATION_90:
mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
- R.anim.screen_rotate_plus_90_exit);
+ com.android.internal.R.anim.screen_rotate_plus_90_exit);
mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
- R.anim.screen_rotate_plus_90_enter);
+ com.android.internal.R.anim.screen_rotate_plus_90_enter);
break;
case Surface.ROTATION_180:
mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
- R.anim.screen_rotate_180_exit);
+ com.android.internal.R.anim.screen_rotate_180_exit);
mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
- R.anim.screen_rotate_180_enter);
+ com.android.internal.R.anim.screen_rotate_180_enter);
break;
case Surface.ROTATION_270:
mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
- R.anim.screen_rotate_minus_90_exit);
+ com.android.internal.R.anim.screen_rotate_minus_90_exit);
mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
- R.anim.screen_rotate_minus_90_enter);
+ com.android.internal.R.anim.screen_rotate_minus_90_enter);
break;
}
}
+ // Initialize the animations. This is a hack, redefining what "parent"
+ // means to allow supplying the last and next size. In this definition
+ // "%p" is the original (let's call it "previous") size, and "%" is the
+ // screen's current/new size.
+ mRotateEnterAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
mRotateExitAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
+ mAnimRunning = false;
+ mFinishAnimReady = false;
+ mFinishAnimStartTime = -1;
+
mRotateExitAnimation.restrictDuration(maxAnimationDuration);
mRotateExitAnimation.scaleCurrentDuration(animationScale);
- mRotateEnterAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
mRotateEnterAnimation.restrictDuration(maxAnimationDuration);
mRotateEnterAnimation.scaleCurrentDuration(animationScale);
+ mRotateAlphaAnimation.restrictDuration(maxAnimationDuration);
+ mRotateAlphaAnimation.scaleCurrentDuration(animationScale);
- mAnimRunning = false;
- mFinishAnimReady = false;
- mFinishAnimStartTime = -1;
-
- if (customAnim) {
- mRotateAlphaAnimation.restrictDuration(maxAnimationDuration);
- mRotateAlphaAnimation.scaleCurrentDuration(animationScale);
+ if (!customAnim && mExitingBlackFrame == null) {
+ try {
+ // Compute the transformation matrix that must be applied
+ // the the black frame to make it stay in the initial position
+ // before the new screen rotation. This is different than the
+ // snapshot transformation because the snapshot is always based
+ // of the native orientation of the screen, not the orientation
+ // we were last in.
+ createRotationMatrix(delta, mOriginalWidth, mOriginalHeight, mFrameInitialMatrix);
+
+ final Rect outer;
+ final Rect inner;
+ if (mForceDefaultOrientation) {
+ // Going from a smaller Display to a larger Display, add curtains to sides
+ // or top and bottom. Going from a larger to smaller display will result in
+ // no BlackSurfaces being constructed.
+ outer = mCurrentDisplayRect;
+ inner = mOriginalDisplayRect;
+ } else {
+ outer = new Rect(-mWidth, -mHeight, mWidth * 2, mHeight * 2);
+ inner = new Rect(0, 0, mWidth, mHeight);
+ }
+ mExitingBlackFrame = new BlackFrame(mService.mTransactionFactory, t, outer, inner,
+ SCREEN_FREEZE_LAYER_EXIT, mDisplayContent, mForceDefaultOrientation,
+ mRotationLayer);
+ } catch (OutOfResourcesException e) {
+ Slog.w(TAG, "Unable to allocate black surface", e);
+ }
}
if (customAnim && mEnteringBlackFrame == null) {
@@ -398,12 +451,7 @@ class ScreenRotationAnimation {
}
}
- if (customAnim) {
- mSurfaceRotationAnimationController.startCustomAnimation();
- } else {
- mSurfaceRotationAnimationController.startScreenRotationAnimation();
- }
-
+ mSurfaceRotationAnimationController.startAnimation();
return true;
}
@@ -412,13 +460,11 @@ class ScreenRotationAnimation {
*/
public boolean dismiss(SurfaceControl.Transaction t, long maxAnimationDuration,
float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim) {
- if (mScreenshotLayer == null) {
+ if (mSurfaceControl == null) {
// Can't do animation.
return false;
}
if (!mStarted) {
- mEndLuma = RotationAnimationUtils.getLumaOfSurfaceControl(mDisplayContent.getDisplay(),
- mDisplayContent.getWindowingLayer());
startAnimation(t, maxAnimationDuration, animationScale, finalWidth, finalHeight,
exitAnim, enterAnim);
}
@@ -434,28 +480,28 @@ class ScreenRotationAnimation {
mSurfaceRotationAnimationController.cancel();
mSurfaceRotationAnimationController = null;
}
-
- if (mScreenshotLayer != null) {
- ProtoLog.i(WM_SHOW_SURFACE_ALLOC, " FREEZE %s: DESTROY", mScreenshotLayer);
+ if (mSurfaceControl != null) {
+ ProtoLog.i(WM_SHOW_SURFACE_ALLOC, " FREEZE %s: DESTROY", mSurfaceControl);
+ mSurfaceControl = null;
SurfaceControl.Transaction t = mService.mTransactionFactory.get();
- if (mScreenshotLayer.isValid()) {
- t.remove(mScreenshotLayer);
+ if (mRotationLayer != null) {
+ if (mRotationLayer.isValid()) {
+ t.remove(mRotationLayer);
+ }
+ mRotationLayer = null;
}
- mScreenshotLayer = null;
-
if (mEnterBlackFrameLayer != null) {
if (mEnterBlackFrameLayer.isValid()) {
t.remove(mEnterBlackFrameLayer);
}
mEnterBlackFrameLayer = null;
}
- if (mBackColorSurface != null) {
- t.remove(mBackColorSurface);
- mBackColorSurface = null;
- }
t.apply();
}
-
+ if (mExitingBlackFrame != null) {
+ mExitingBlackFrame.kill();
+ mExitingBlackFrame = null;
+ }
if (mEnteringBlackFrame != null) {
mEnteringBlackFrame.kill();
mEnteringBlackFrame = null;
@@ -491,28 +537,18 @@ class ScreenRotationAnimation {
* Utility class that runs a {@link ScreenRotationAnimation} on the {@link
* SurfaceAnimationRunner}.
* <p>
- * The rotation animation supports both screen rotation and custom animations
- *
- * For custom animations:
- * <ul>
- * <li>
- * The screenshot layer which has an added animation of it's alpha channel
- * ("screen_rotate_alpha") and that will be applied along with the custom animation.
- * </li>
- * <li> A device layer that is animated with the provided custom animation </li>
- * </ul>
- *
- * For screen rotation:
+ * The rotation animation is divided into the following hierarchy:
* <ul>
- * <li> A rotation layer that is both rotated and faded out during a single animation </li>
- * <li> A device layer that is both rotated and faded in during a single animation </li>
- * <li> A background color layer that transitions colors behind the first two layers </li>
- * </ul>
- *
+ * <li> A first rotation layer, containing the blackframes. This layer is animated by the
+ * "screen_rotate_X_exit" that applies a scale and rotate and where X is value of the rotation.
+ * <ul>
+ * <li> A child layer containing the screenshot on which is added an animation of it's
+ * alpha channel ("screen_rotate_alpha") and that will rotate with his parent layer.</li>
+ * </ul>
+ * <li> A second rotation layer used when custom animations are passed in
* {@link ScreenRotationAnimation#startAnimation(
* SurfaceControl.Transaction, long, float, int, int, int, int)}.
* </ul>
- *
* <p>
* Thus an {@link LocalAnimationAdapter.AnimationSpec} is created for each of
* this three {@link SurfaceControl}s which then delegates the animation to the
@@ -520,35 +556,22 @@ class ScreenRotationAnimation {
*/
class SurfaceRotationAnimationController {
private SurfaceAnimator mDisplayAnimator;
+ private SurfaceAnimator mEnterBlackFrameAnimator;
private SurfaceAnimator mScreenshotRotationAnimator;
private SurfaceAnimator mRotateScreenAnimator;
- private SurfaceAnimator mEnterBlackFrameAnimator;
-
- void startCustomAnimation() {
- try {
- mService.mSurfaceAnimationRunner.deferStartingAnimations();
- mRotateScreenAnimator = startScreenshotAlphaAnimation();
- mDisplayAnimator = startDisplayRotation();
- if (mEnteringBlackFrame != null) {
- mEnterBlackFrameAnimator = startEnterBlackFrameAnimation();
- }
- } finally {
- mService.mSurfaceAnimationRunner.continueStartingAnimations();
- }
- }
/**
* Start the rotation animation of the display and the screenshot on the
* {@link SurfaceAnimationRunner}.
*/
- void startScreenRotationAnimation() {
- try {
- mService.mSurfaceAnimationRunner.deferStartingAnimations();
- mDisplayAnimator = startDisplayRotation();
+ void startAnimation() {
+ mRotateScreenAnimator = startScreenshotAlphaAnimation();
+ mDisplayAnimator = startDisplayRotation();
+ if (mExitingBlackFrame != null) {
mScreenshotRotationAnimator = startScreenshotRotationAnimation();
- startColorAnimation();
- } finally {
- mService.mSurfaceAnimationRunner.continueStartingAnimations();
+ }
+ if (mEnteringBlackFrame != null) {
+ mEnterBlackFrameAnimator = startEnterBlackFrameAnimation();
}
}
@@ -573,8 +596,8 @@ class ScreenRotationAnimation {
private SurfaceAnimator startScreenshotAlphaAnimation() {
return startAnimation(initializeBuilder()
- .setSurfaceControl(mScreenshotLayer)
- .setAnimationLeashParent(mDisplayContent.getOverlayLayer())
+ .setSurfaceControl(mSurfaceControl)
+ .setAnimationLeashParent(mRotationLayer)
.setWidth(mWidth)
.setHeight(mHeight)
.build(),
@@ -593,67 +616,13 @@ class ScreenRotationAnimation {
private SurfaceAnimator startScreenshotRotationAnimation() {
return startAnimation(initializeBuilder()
- .setSurfaceControl(mScreenshotLayer)
+ .setSurfaceControl(mRotationLayer)
.setAnimationLeashParent(mDisplayContent.getOverlayLayer())
.build(),
createWindowAnimationSpec(mRotateExitAnimation),
this::onAnimationEnd);
}
-
- /**
- * Applies the color change from {@link #mStartLuma} to {@link #mEndLuma} as a
- * grayscale color
- */
- private void startColorAnimation() {
- int colorTransitionMs = mContext.getResources().getInteger(
- R.integer.config_screen_rotation_color_transition);
- final SurfaceAnimationRunner runner = mService.mSurfaceAnimationRunner;
- final float[] rgbTmpFloat = new float[3];
- final int startColor = Color.rgb(mStartLuma, mStartLuma, mStartLuma);
- final int endColor = Color.rgb(mEndLuma, mEndLuma, mEndLuma);
- final long duration = colorTransitionMs * (long) mService.getCurrentAnimatorScale();
- final ArgbEvaluator va = ArgbEvaluator.getInstance();
- runner.startAnimation(
- new LocalAnimationAdapter.AnimationSpec() {
- @Override
- public long getDuration() {
- return duration;
- }
-
- @Override
- public void apply(SurfaceControl.Transaction t, SurfaceControl leash,
- long currentPlayTime) {
- float fraction = (float)currentPlayTime / (float)getDuration();
- int color = (Integer) va.evaluate(fraction, startColor, endColor);
- Color middleColor = Color.valueOf(color);
- rgbTmpFloat[0] = middleColor.red();
- rgbTmpFloat[1] = middleColor.green();
- rgbTmpFloat[2] = middleColor.blue();
- if (leash.isValid()) {
- t.setColor(leash, rgbTmpFloat);
- }
- }
-
- @Override
- public void dump(PrintWriter pw, String prefix) {
- pw.println(prefix + "startLuma=" + mStartLuma
- + " endLuma=" + mEndLuma
- + " durationMs=" + colorTransitionMs);
- }
-
- @Override
- public void dumpDebugInner(ProtoOutputStream proto) {
- final long token = proto.start(ROTATE);
- proto.write(START_LUMA, mStartLuma);
- proto.write(END_LUMA, mEndLuma);
- proto.write(DURATION_MS, colorTransitionMs);
- proto.end(token);
- }
- },
- mBackColorSurface, mDisplayContent.getPendingTransaction(), null);
- }
-
private WindowAnimationSpec createWindowAnimationSpec(Animation mAnimation) {
return new WindowAnimationSpec(mAnimation, new Point(0, 0) /* position */,
false /* canSkipFirstFrame */, 0 /* WindowCornerRadius */);
@@ -677,6 +646,7 @@ class ScreenRotationAnimation {
LocalAnimationAdapter localAnimationAdapter = new LocalAnimationAdapter(
animationSpec, mService.mSurfaceAnimationRunner);
+
animator.startAnimation(mDisplayContent.getPendingTransaction(),
localAnimationAdapter, false);
return animator;
@@ -722,6 +692,7 @@ class ScreenRotationAnimation {
if (mEnterBlackFrameAnimator != null) {
mEnterBlackFrameAnimator.cancelAnimation();
}
+
if (mScreenshotRotationAnimator != null) {
mScreenshotRotationAnimator.cancelAnimation();
}
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
index 5633b6be87b9..50cea2ed52cc 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
@@ -78,10 +78,6 @@ class SurfaceAnimationRunner {
@GuardedBy("mLock")
private boolean mAnimationStartDeferred;
- /**
- * There should only ever be one instance of this class. Usual spot for it is with
- * {@link WindowManagerService}
- */
SurfaceAnimationRunner(Supplier<Transaction> transactionFactory,
PowerManagerInternal powerManagerInternal) {
this(null /* callbackProvider */, null /* animatorFactory */,
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 9a140daad417..5cb7091bbed0 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -117,9 +117,11 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
+import android.graphics.Point;
import android.graphics.Rect;
import android.os.Debug;
import android.os.IBinder;
+import android.os.Parcel;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
@@ -130,6 +132,7 @@ import android.util.DisplayMetrics;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
+import android.view.ITaskOrganizer;
import android.view.RemoteAnimationTarget;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -425,6 +428,14 @@ class Task extends WindowContainer<WindowContainer> {
}
/**
+ * The TaskOrganizer which is delegated presentation of this task. If set the Task will
+ * emit an IWindowContainer (allowing access to it's SurfaceControl leash) to the organizers
+ * taskAppeared callback, and emit a taskRemoved callback when the Task is vanished.
+ */
+ ITaskOrganizer mTaskOrganizer;
+
+
+ /**
* Don't use constructor directly. Use {@link #create(ActivityTaskManagerService, int,
* ActivityInfo, Intent, TaskDescription)} instead.
*/
@@ -445,6 +456,22 @@ class Task extends WindowContainer<WindowContainer> {
_voiceSession, _voiceInteractor, stack);
}
+ class TaskToken extends RemoteToken {
+ TaskToken(ConfigurationContainer container) {
+ super(container);
+ }
+
+ @Override
+ public SurfaceControl getLeash() {
+ // We need to copy the SurfaceControl instead of returning the original
+ // because the Parcel FLAGS PARCELABLE_WRITE_RETURN_VALUE cause SurfaceControls
+ // to release themselves.
+ SurfaceControl sc = new SurfaceControl();
+ sc.copyFrom(getSurfaceControl());
+ return sc;
+ }
+ }
+
/** Don't use constructor directly. This is only used by XML parser. */
Task(ActivityTaskManagerService atmService, int _taskId, Intent _intent,
Intent _affinityIntent, String _affinity, String _rootAffinity,
@@ -469,7 +496,7 @@ class Task extends WindowContainer<WindowContainer> {
mTaskDescription = _lastTaskDescription;
// Tasks have no set orientation value (including SCREEN_ORIENTATION_UNSPECIFIED).
setOrientation(SCREEN_ORIENTATION_UNSET);
- mRemoteToken = new RemoteToken(this);
+ mRemoteToken = new TaskToken(this);
affinityIntent = _affinityIntent;
affinity = _affinity;
rootAffinity = _rootAffinity;
@@ -2179,6 +2206,10 @@ class Task extends WindowContainer<WindowContainer> {
void removeImmediately() {
if (DEBUG_STACK) Slog.i(TAG, "removeTask: removing taskId=" + mTaskId);
EventLogTags.writeWmTaskRemoved(mTaskId, "removeTask");
+
+ // If applicable let the TaskOrganizer know the Task is vanishing.
+ setTaskOrganizer(null);
+
super.removeImmediately();
}
@@ -2567,6 +2598,12 @@ class Task extends WindowContainer<WindowContainer> {
}
boolean shouldAnimate() {
+ /**
+ * Animations are handled by the TaskOrganizer implementation.
+ */
+ if (isControlledByTaskOrganizer()) {
+ return false;
+ }
// Don't animate while the task runs recents animation but only if we are in the mode
// where we cancel with deferred screenshot, which means that the controller has
// transformed the task.
@@ -3444,4 +3481,91 @@ class Task extends WindowContainer<WindowContainer> {
XmlUtils.skipCurrentTag(in);
}
}
+
+ boolean isControlledByTaskOrganizer() {
+ return mTaskOrganizer != null;
+ }
+
+ @Override
+ protected void reparentSurfaceControl(SurfaceControl.Transaction t, SurfaceControl newParent) {
+ /**
+ * Avoid yanking back control from the TaskOrganizer, which has presumably reparented the
+ * Surface in to its own hierarchy.
+ */
+ if (isControlledByTaskOrganizer()) {
+ return;
+ }
+ super.reparentSurfaceControl(t, newParent);
+ }
+
+ private void sendTaskAppeared() {
+ if (mSurfaceControl != null && mTaskOrganizer != null) {
+ mAtmService.mTaskOrganizerController.onTaskAppeared(mTaskOrganizer, this);
+ }
+ }
+
+ private void sendTaskVanished() {
+ if (mTaskOrganizer != null) {
+ mAtmService.mTaskOrganizerController.onTaskVanished(mTaskOrganizer, this);
+ }
+ }
+
+ void setTaskOrganizer(ITaskOrganizer organizer) {
+ // Let the old organizer know it has lost control.
+ if (mTaskOrganizer != null) {
+ sendTaskVanished();
+ }
+ mTaskOrganizer = organizer;
+ sendTaskAppeared();
+ }
+
+ // Called on Binder death.
+ void taskOrganizerDied() {
+ mTaskOrganizer = null;
+ }
+
+ @Override
+ void setSurfaceControl(SurfaceControl sc) {
+ super.setSurfaceControl(sc);
+ // If the TaskOrganizer was set before we created the SurfaceControl, we need to
+ // emit the callbacks now.
+ sendTaskAppeared();
+ }
+
+ @Override
+ public void updateSurfacePosition() {
+ // Avoid fighting with the TaskOrganizer over Surface position.
+ if (isControlledByTaskOrganizer()) {
+ getPendingTransaction().setPosition(mSurfaceControl, 0, 0);
+ scheduleAnimation();
+ return;
+ } else {
+ super.updateSurfacePosition();
+ }
+ }
+
+ @Override
+ void getRelativeDisplayedPosition(Point outPos) {
+ // In addition to updateSurfacePosition, we keep other code that sets
+ // position from fighting with the TaskOrganizer
+ if (isControlledByTaskOrganizer()) {
+ outPos.set(0, 0);
+ return;
+ }
+ super.getRelativeDisplayedPosition(outPos);
+ }
+
+ @Override
+ public void setWindowingMode(int windowingMode) {
+ super.setWindowingMode(windowingMode);
+ windowingMode = getWindowingMode();
+ /*
+ * Different windowing modes may be managed by different task organizers. If
+ * getTaskOrganizer returns null, we still call transferToTaskOrganizer to
+ * make sure we clear it.
+ */
+ final ITaskOrganizer org =
+ mAtmService.mTaskOrganizerController.getTaskOrganizer(windowingMode);
+ setTaskOrganizer(org);
+ }
}
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
new file mode 100644
index 000000000000..283be4010677
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+import android.view.ITaskOrganizer;
+import android.view.SurfaceControl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * Stores the TaskOrganizers associated with a given windowing mode and
+ * their associated state.
+ */
+class TaskOrganizerController {
+ private static final String TAG = "TaskOrganizerController";
+
+ private WindowManagerGlobalLock mGlobalLock;
+
+ private class DeathRecipient implements IBinder.DeathRecipient {
+ int mWindowingMode;
+ ITaskOrganizer mTaskOrganizer;
+
+ DeathRecipient(ITaskOrganizer organizer, int windowingMode) {
+ mTaskOrganizer = organizer;
+ mWindowingMode = windowingMode;
+ }
+
+ @Override
+ public void binderDied() {
+ synchronized (mGlobalLock) {
+ final TaskOrganizerState state = mTaskOrganizerStates.get(mTaskOrganizer);
+ for (int i = 0; i < state.mOrganizedTasks.size(); i++) {
+ state.mOrganizedTasks.get(i).taskOrganizerDied();
+ }
+ mTaskOrganizerStates.remove(mTaskOrganizer);
+ if (mTaskOrganizersForWindowingMode.get(mWindowingMode) == mTaskOrganizer) {
+ mTaskOrganizersForWindowingMode.remove(mWindowingMode);
+ }
+ }
+ }
+ };
+
+ class TaskOrganizerState {
+ ITaskOrganizer mOrganizer;
+ DeathRecipient mDeathRecipient;
+
+ ArrayList<Task> mOrganizedTasks = new ArrayList<>();
+
+ void addTask(Task t) {
+ mOrganizedTasks.add(t);
+ }
+
+ void removeTask(Task t) {
+ mOrganizedTasks.remove(t);
+ }
+
+ TaskOrganizerState(ITaskOrganizer organizer, DeathRecipient deathRecipient) {
+ mOrganizer = organizer;
+ mDeathRecipient = deathRecipient;
+ }
+ };
+
+
+ final HashMap<Integer, TaskOrganizerState> mTaskOrganizersForWindowingMode = new HashMap();
+ final HashMap<ITaskOrganizer, TaskOrganizerState> mTaskOrganizerStates = new HashMap();
+
+ final HashMap<Integer, ITaskOrganizer> mTaskOrganizersByPendingSyncId = new HashMap();
+
+ final ActivityTaskManagerService mService;
+
+ TaskOrganizerController(ActivityTaskManagerService atm, WindowManagerGlobalLock lock) {
+ mService = atm;
+ mGlobalLock = lock;
+ }
+
+ private void clearIfNeeded(int windowingMode) {
+ final TaskOrganizerState oldState = mTaskOrganizersForWindowingMode.get(windowingMode);
+ if (oldState != null) {
+ oldState.mOrganizer.asBinder().unlinkToDeath(oldState.mDeathRecipient, 0);
+ }
+ }
+
+ /**
+ * Register a TaskOrganizer to manage tasks as they enter the given windowing mode.
+ * If there was already a TaskOrganizer for this windowing mode it will be evicted
+ * and receive taskVanished callbacks in the process.
+ */
+ void registerTaskOrganizer(ITaskOrganizer organizer, int windowingMode) {
+ if (windowingMode != WINDOWING_MODE_PINNED) {
+ throw new UnsupportedOperationException(
+ "As of now only Pinned windowing mode is supported for registerTaskOrganizer");
+
+ }
+ clearIfNeeded(windowingMode);
+ DeathRecipient dr = new DeathRecipient(organizer, windowingMode);
+ try {
+ organizer.asBinder().linkToDeath(dr, 0);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "TaskOrganizer failed to register death recipient");
+ }
+
+ final TaskOrganizerState state = new TaskOrganizerState(organizer, dr);
+ mTaskOrganizersForWindowingMode.put(windowingMode, state);
+
+ mTaskOrganizerStates.put(organizer, state);
+ }
+
+ ITaskOrganizer getTaskOrganizer(int windowingMode) {
+ final TaskOrganizerState state = mTaskOrganizersForWindowingMode.get(windowingMode);
+ if (state == null) {
+ return null;
+ }
+ return state.mOrganizer;
+ }
+
+ private void sendTaskAppeared(ITaskOrganizer organizer, Task task) {
+ try {
+ organizer.taskAppeared(task.getRemoteToken(), task.getTaskInfo());
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception sending taskAppeared callback" + e);
+ }
+ }
+
+ private void sendTaskVanished(ITaskOrganizer organizer, Task task) {
+ try {
+ organizer.taskVanished(task.getRemoteToken());
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception sending taskVanished callback" + e);
+ }
+ }
+
+ void onTaskAppeared(ITaskOrganizer organizer, Task task) {
+ TaskOrganizerState state = mTaskOrganizerStates.get(organizer);
+
+ state.addTask(task);
+ sendTaskAppeared(organizer, task);
+ }
+
+ void onTaskVanished(ITaskOrganizer organizer, Task task) {
+ final TaskOrganizerState state = mTaskOrganizerStates.get(organizer);
+ sendTaskVanished(organizer, task);
+
+ // This could trigger TaskAppeared for other tasks in the same stack so make sure
+ // we do this AFTER sending taskVanished.
+ state.removeTask(task);
+ }
+}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index cefef37a1363..f3880fa5dd09 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -342,7 +342,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
if (mSurfaceControl == null) {
// If we don't yet have a surface, but we now have a parent, we should
// build a surface.
- mSurfaceControl = makeSurface().build();
+ setSurfaceControl(makeSurface().build());
getPendingTransaction().show(mSurfaceControl);
updateSurfacePosition();
} else {
@@ -496,7 +496,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
mParent.getPendingTransaction().merge(getPendingTransaction());
}
- mSurfaceControl = null;
+ setSurfaceControl(null);
mLastSurfacePosition.set(0, 0);
scheduleAnimation();
}
@@ -777,7 +777,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
* otherwise.
*/
boolean isWaitingForTransitionStart() {
- return getActivity(app -> app.isWaitingForTransitionStart()) != null;
+ return false;
}
/**
@@ -2209,4 +2209,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
return mParent.getDimmer();
}
+
+ void setSurfaceControl(SurfaceControl sc) {
+ mSurfaceControl = sc;
+ }
}
diff --git a/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java b/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java
deleted file mode 100644
index 94f66768d5ef..000000000000
--- a/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.utils;
-
-import android.graphics.Bitmap;
-import android.graphics.ColorSpace;
-import android.graphics.GraphicBuffer;
-import android.graphics.Matrix;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.view.Display;
-import android.view.Surface;
-import android.view.SurfaceControl;
-
-
-/** Helper functions for the {@link com.android.server.wm.ScreenRotationAnimation} class*/
-public class RotationAnimationUtils {
-
- /**
- * Converts the provided {@link GraphicBuffer} and converts it to a bitmap to then sample the
- * luminance at the borders of the bitmap
- * @return the average luminance of all the pixels at the borders of the bitmap
- */
- public static float getAvgBorderLuma(GraphicBuffer graphicBuffer, ColorSpace colorSpace) {
- Bitmap hwBitmap = Bitmap.wrapHardwareBuffer(graphicBuffer, colorSpace);
- if (hwBitmap == null) {
- return 0;
- }
-
- Bitmap swaBitmap = hwBitmap.copy(Bitmap.Config.ARGB_8888, false);
- float totalLuma = 0;
- int height = swaBitmap.getHeight();
- int width = swaBitmap.getWidth();
- int i;
- for (i = 0; i < width; i++) {
- totalLuma += swaBitmap.getColor(i, 0).luminance();
- totalLuma += swaBitmap.getColor(i, height - 1).luminance();
- }
- for (i = 0; i < height; i++) {
- totalLuma += swaBitmap.getColor(0, i).luminance();
- totalLuma += swaBitmap.getColor(width - 1, i).luminance();
- }
- return totalLuma / (2 * width + 2 * height);
- }
-
- /**
- * Gets the average border luma by taking a screenshot of the {@param surfaceControl}.
- * @see #getAvgBorderLuma(GraphicBuffer, ColorSpace)
- */
- public static float getLumaOfSurfaceControl(Display display, SurfaceControl surfaceControl) {
- if (surfaceControl == null) {
- return 0;
- }
-
- Point size = new Point();
- display.getSize(size);
- Rect crop = new Rect(0, 0, size.x, size.y);
- SurfaceControl.ScreenshotGraphicBuffer buffer =
- SurfaceControl.captureLayers(surfaceControl, crop, 1);
- return RotationAnimationUtils.getAvgBorderLuma(buffer.getGraphicBuffer(),
- buffer.getColorSpace());
- }
-
- public static void createRotationMatrix(int rotation, int width, int height, Matrix outMatrix) {
- switch (rotation) {
- case Surface.ROTATION_0:
- outMatrix.reset();
- break;
- case Surface.ROTATION_90:
- outMatrix.setRotate(90, 0, 0);
- outMatrix.postTranslate(height, 0);
- break;
- case Surface.ROTATION_180:
- outMatrix.setRotate(180, 0, 0);
- outMatrix.postTranslate(width, height);
- break;
- case Surface.ROTATION_270:
- outMatrix.setRotate(270, 0, 0);
- outMatrix.postTranslate(0, width);
- break;
- }
- }
-}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 1ad6e86bae84..03969b01f3f9 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -13,6 +13,7 @@ cc_library_static {
],
srcs: [
+ ":graphicsstats_proto",
"BroadcastRadio/JavaRef.cpp",
"BroadcastRadio/NativeCallbackThread.cpp",
"BroadcastRadio/BroadcastRadioService.cpp",
@@ -103,6 +104,11 @@ cc_defaults {
"libinputflinger",
"libinputflinger_base",
"libinputservice",
+ "libprotobuf-cpp-lite",
+ "libprotoutil",
+ "libstatspull",
+ "libstatssocket",
+ "libstatslog",
"libschedulerservicehidl",
"libsensorservice",
"libsensorservicehidl",
diff --git a/services/core/jni/com_android_server_GraphicsStatsService.cpp b/services/core/jni/com_android_server_GraphicsStatsService.cpp
index d1d253b94713..9353fbd71214 100644
--- a/services/core/jni/com_android_server_GraphicsStatsService.cpp
+++ b/services/core/jni/com_android_server_GraphicsStatsService.cpp
@@ -23,6 +23,16 @@
#include <nativehelper/ScopedUtfChars.h>
#include <JankTracker.h>
#include <service/GraphicsStatsService.h>
+#include <stats_pull_atom_callback.h>
+#include <stats_event.h>
+#include <statslog.h>
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+#include <android/util/ProtoOutputStream.h>
+#include "android/graphics/Utils.h"
+#include "core_jni_helpers.h"
+#include "protos/graphicsstats.pb.h"
+#include <cstring>
+#include <memory>
namespace android {
@@ -77,6 +87,20 @@ static void finishDump(JNIEnv*, jobject, jlong dumpPtr) {
GraphicsStatsService::finishDump(dump);
}
+static jlong finishDumpInMemory(JNIEnv* env, jobject, jlong dumpPtr) {
+ GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
+ std::vector<uint8_t>* result = new std::vector<uint8_t>();
+ GraphicsStatsService::finishDumpInMemory(dump,
+ [](void* buffer, int bufferOffset, int bufferSize, int totalSize, void* param1, void* param2) {
+ std::vector<uint8_t>* outBuffer = reinterpret_cast<std::vector<uint8_t>*>(param2);
+ if (outBuffer->size() < totalSize) {
+ outBuffer->resize(totalSize);
+ }
+ std::memcpy(outBuffer->data() + bufferOffset, buffer, bufferSize);
+ }, env, result);
+ return reinterpret_cast<jlong>(result);
+}
+
static void saveBuffer(JNIEnv* env, jobject clazz, jstring jpath, jstring jpackage,
jlong versionCode, jlong startTime, jlong endTime, jbyteArray jdata) {
ScopedByteArrayRO buffer(env, jdata);
@@ -93,19 +117,173 @@ static void saveBuffer(JNIEnv* env, jobject clazz, jstring jpath, jstring jpacka
GraphicsStatsService::saveBuffer(path, package, versionCode, startTime, endTime, data);
}
+static jobject gGraphicsStatsServiceObject = nullptr;
+static jmethodID gGraphicsStatsService_pullGraphicsStatsMethodID;
+
+static JNIEnv* getJNIEnv() {
+ JavaVM* vm = AndroidRuntime::getJavaVM();
+ JNIEnv* env = nullptr;
+ if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+ if (vm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) {
+ LOG_ALWAYS_FATAL("Failed to AttachCurrentThread!");
+ }
+ }
+ return env;
+}
+
+using namespace google::protobuf;
+
+// Field ids taken from FrameTimingHistogram message in atoms.proto
+#define TIME_MILLIS_BUCKETS_FIELD_NUMBER 1
+#define FRAME_COUNTS_FIELD_NUMBER 2
+
+static void writeCpuHistogram(stats_event* event,
+ const uirenderer::protos::GraphicsStatsProto& stat) {
+ util::ProtoOutputStream proto;
+ for (int bucketIndex = 0; bucketIndex < stat.histogram_size(); bucketIndex++) {
+ auto& bucket = stat.histogram(bucketIndex);
+ proto.write(android::util::FIELD_TYPE_INT32 | android::util::FIELD_COUNT_REPEATED |
+ TIME_MILLIS_BUCKETS_FIELD_NUMBER /* field id */,
+ (int)bucket.render_millis());
+ }
+ for (int bucketIndex = 0; bucketIndex < stat.histogram_size(); bucketIndex++) {
+ auto& bucket = stat.histogram(bucketIndex);
+ proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED |
+ FRAME_COUNTS_FIELD_NUMBER /* field id */,
+ (long long)bucket.frame_count());
+ }
+ std::vector<uint8_t> outVector;
+ proto.serializeToVector(&outVector);
+ stats_event_write_byte_array(event, outVector.data(), outVector.size());
+}
+
+static void writeGpuHistogram(stats_event* event,
+ const uirenderer::protos::GraphicsStatsProto& stat) {
+ util::ProtoOutputStream proto;
+ for (int bucketIndex = 0; bucketIndex < stat.gpu_histogram_size(); bucketIndex++) {
+ auto& bucket = stat.gpu_histogram(bucketIndex);
+ proto.write(android::util::FIELD_TYPE_INT32 | android::util::FIELD_COUNT_REPEATED |
+ TIME_MILLIS_BUCKETS_FIELD_NUMBER /* field id */,
+ (int)bucket.render_millis());
+ }
+ for (int bucketIndex = 0; bucketIndex < stat.gpu_histogram_size(); bucketIndex++) {
+ auto& bucket = stat.gpu_histogram(bucketIndex);
+ proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED |
+ FRAME_COUNTS_FIELD_NUMBER /* field id */,
+ (long long)bucket.frame_count());
+ }
+ std::vector<uint8_t> outVector;
+ proto.serializeToVector(&outVector);
+ stats_event_write_byte_array(event, outVector.data(), outVector.size());
+}
+
+// graphicsStatsPullCallback is invoked by statsd service to pull GRAPHICS_STATS atom.
+static bool graphicsStatsPullCallback(int32_t atom_tag, pulled_stats_event_list* data,
+ const void* cookie) {
+ JNIEnv* env = getJNIEnv();
+ if (!env) {
+ return false;
+ }
+ if (gGraphicsStatsServiceObject == nullptr) {
+ ALOGE("Failed to get graphicsstats service");
+ return false;
+ }
+
+ for (bool lastFullDay : {true, false}) {
+ jlong jdata = (jlong) env->CallLongMethod(
+ gGraphicsStatsServiceObject,
+ gGraphicsStatsService_pullGraphicsStatsMethodID,
+ (jboolean)(lastFullDay ? JNI_TRUE : JNI_FALSE));
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ ALOGE("Failed to invoke graphicsstats service");
+ return false;
+ }
+ if (!jdata) {
+ // null means data is not available for that day.
+ continue;
+ }
+ android::uirenderer::protos::GraphicsStatsServiceDumpProto serviceDump;
+ std::vector<uint8_t>* buffer = reinterpret_cast<std::vector<uint8_t>*>(jdata);
+ std::unique_ptr<std::vector<uint8_t>> bufferRelease(buffer);
+ int dataSize = buffer->size();
+ if (!dataSize) {
+ // Data is not available for that day.
+ continue;
+ }
+ io::ArrayInputStream input{buffer->data(), dataSize};
+ bool success = serviceDump.ParseFromZeroCopyStream(&input);
+ if (!success) {
+ ALOGW("Parse failed on GraphicsStatsPuller error='%s' dataSize='%d'",
+ serviceDump.InitializationErrorString().c_str(), dataSize);
+ return false;
+ }
+
+ for (int stat_index = 0; stat_index < serviceDump.stats_size(); stat_index++) {
+ auto& stat = serviceDump.stats(stat_index);
+ stats_event* event = add_stats_event_to_pull_data(data);
+ stats_event_set_atom_id(event, android::util::GRAPHICS_STATS);
+ stats_event_write_string8(event, stat.package_name().c_str());
+ stats_event_write_int64(event, (int64_t)stat.version_code());
+ stats_event_write_int64(event, (int64_t)stat.stats_start());
+ stats_event_write_int64(event, (int64_t)stat.stats_end());
+ stats_event_write_int32(event, (int32_t)stat.pipeline());
+ stats_event_write_int32(event, (int32_t)stat.summary().total_frames());
+ stats_event_write_int32(event, (int32_t)stat.summary().missed_vsync_count());
+ stats_event_write_int32(event, (int32_t)stat.summary().high_input_latency_count());
+ stats_event_write_int32(event, (int32_t)stat.summary().slow_ui_thread_count());
+ stats_event_write_int32(event, (int32_t)stat.summary().slow_bitmap_upload_count());
+ stats_event_write_int32(event, (int32_t)stat.summary().slow_draw_count());
+ stats_event_write_int32(event, (int32_t)stat.summary().missed_deadline_count());
+ writeCpuHistogram(event, stat);
+ writeGpuHistogram(event, stat);
+ // TODO: fill in UI mainline module version, when the feature is available.
+ stats_event_write_int64(event, (int64_t)0);
+ stats_event_write_bool(event, !lastFullDay);
+ stats_event_build(event);
+ }
+ }
+ return true;
+}
+
+// Register a puller for GRAPHICS_STATS atom with the statsd service.
+static void nativeInit(JNIEnv* env, jobject javaObject) {
+ gGraphicsStatsServiceObject = env->NewGlobalRef(javaObject);
+ pull_atom_metadata metadata = {.cool_down_ns = 10 * 1000000, // 10 milliseconds
+ .timeout_ns = 2 * NS_PER_SEC, // 2 seconds
+ .additive_fields = nullptr,
+ .additive_fields_size = 0};
+ register_stats_pull_atom_callback(android::util::GRAPHICS_STATS, &graphicsStatsPullCallback,
+ &metadata, nullptr);
+}
+
+static void nativeDestructor(JNIEnv* env, jobject javaObject) {
+ //TODO: Unregister the puller callback when a new API is available.
+ env->DeleteGlobalRef(gGraphicsStatsServiceObject);
+ gGraphicsStatsServiceObject = nullptr;
+}
+
static const JNINativeMethod sMethods[] = {
{ "nGetAshmemSize", "()I", (void*) getAshmemSize },
{ "nCreateDump", "(IZ)J", (void*) createDump },
{ "nAddToDump", "(JLjava/lang/String;Ljava/lang/String;JJJ[B)V", (void*) addToDump },
{ "nAddToDump", "(JLjava/lang/String;)V", (void*) addFileToDump },
{ "nFinishDump", "(J)V", (void*) finishDump },
+ { "nFinishDumpInMemory", "(J)J", (void*) finishDumpInMemory },
{ "nSaveBuffer", "(Ljava/lang/String;Ljava/lang/String;JJJ[B)V", (void*) saveBuffer },
+ { "nativeInit", "()V", (void*) nativeInit },
+ { "nativeDestructor", "()V", (void*)nativeDestructor }
};
int register_android_server_GraphicsStatsService(JNIEnv* env)
{
+ jclass graphicsStatsService_class = FindClassOrDie(env,
+ "com/android/server/GraphicsStatsService");
+ gGraphicsStatsService_pullGraphicsStatsMethodID = GetMethodIDOrDie(env,
+ graphicsStatsService_class, "pullGraphicsStats", "(Z)J");
return jniRegisterNativeMethods(env, "com/android/server/GraphicsStatsService",
sMethods, NELEM(sMethods));
}
-} // namespace android \ No newline at end of file
+} // namespace android
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 6504e3188a0d..acb6bea53a62 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -129,7 +129,6 @@ using android::hardware::hidl_vec;
using android::hardware::hidl_string;
using android::hardware::hidl_death_recipient;
-using android::hardware::gnss::V1_0::GnssConstellationType;
using android::hardware::gnss::V1_0::GnssLocationFlags;
using android::hardware::gnss::V1_0::IAGnssRilCallback;
using android::hardware::gnss::V1_0::IGnssGeofenceCallback;
@@ -149,6 +148,8 @@ using android::hardware::gnss::measurement_corrections::V1_0::ReflectingPlane;
using android::hidl::base::V1_0::IBase;
+using GnssConstellationType_V1_0 = android::hardware::gnss::V1_0::GnssConstellationType;
+using GnssConstellationType_V2_0 = android::hardware::gnss::V2_0::GnssConstellationType;
using GnssLocation_V1_0 = android::hardware::gnss::V1_0::GnssLocation;
using GnssLocation_V2_0 = android::hardware::gnss::V2_0::GnssLocation;
using IGnss_V1_0 = android::hardware::gnss::V1_0::IGnss;
@@ -161,6 +162,7 @@ using IGnssCallback_V2_1 = android::hardware::gnss::V2_1::IGnssCallback;
using IGnssConfiguration_V1_0 = android::hardware::gnss::V1_0::IGnssConfiguration;
using IGnssConfiguration_V1_1 = android::hardware::gnss::V1_1::IGnssConfiguration;
using IGnssConfiguration_V2_0 = android::hardware::gnss::V2_0::IGnssConfiguration;
+using IGnssConfiguration_V2_1 = android::hardware::gnss::V2_1::IGnssConfiguration;
using IGnssDebug_V1_0 = android::hardware::gnss::V1_0::IGnssDebug;
using IGnssDebug_V2_0 = android::hardware::gnss::V2_0::IGnssDebug;
using IGnssMeasurement_V1_0 = android::hardware::gnss::V1_0::IGnssMeasurement;
@@ -221,6 +223,7 @@ sp<IGnssDebug_V2_0> gnssDebugIface_V2_0 = nullptr;
sp<IGnssConfiguration_V1_0> gnssConfigurationIface = nullptr;
sp<IGnssConfiguration_V1_1> gnssConfigurationIface_V1_1 = nullptr;
sp<IGnssConfiguration_V2_0> gnssConfigurationIface_V2_0 = nullptr;
+sp<IGnssConfiguration_V2_1> gnssConfigurationIface_V2_1 = nullptr;
sp<IGnssNi> gnssNiIface = nullptr;
sp<IGnssMeasurement_V1_0> gnssMeasurementIface = nullptr;
sp<IGnssMeasurement_V1_1> gnssMeasurementIface_V1_1 = nullptr;
@@ -1888,7 +1891,17 @@ static void android_location_GnssLocationProvider_init_once(JNIEnv* env, jclass
gnssNiIface = gnssNi;
}
- if (gnssHal_V2_0 != nullptr) {
+ if (gnssHal_V2_1 != nullptr) {
+ auto gnssConfiguration = gnssHal_V2_1->getExtensionGnssConfiguration_2_1();
+ if (!gnssConfiguration.isOk()) {
+ ALOGD("Unable to get a handle to GnssConfiguration_V2_1");
+ } else {
+ gnssConfigurationIface_V2_1 = gnssConfiguration;
+ gnssConfigurationIface_V2_0 = gnssConfigurationIface_V2_1;
+ gnssConfigurationIface_V1_1 = gnssConfigurationIface_V2_1;
+ gnssConfigurationIface = gnssConfigurationIface_V2_1;
+ }
+ } else if (gnssHal_V2_0 != nullptr) {
auto gnssConfiguration = gnssHal_V2_0->getExtensionGnssConfiguration_2_0();
if (!gnssConfiguration.isOk()) {
ALOGD("Unable to get a handle to GnssConfiguration_V2_0");
@@ -1962,7 +1975,11 @@ static jboolean android_location_GnssNetworkConnectivityHandler_is_agps_ril_supp
static jobject android_location_GnssConfiguration_get_gnss_configuration_version(
JNIEnv* env, jclass /* jclazz */) {
jint major, minor;
- if (gnssConfigurationIface_V2_0 != nullptr) {
+ if (gnssConfigurationIface_V2_1 != nullptr) {
+ major = 2;
+ minor = 1;
+ }
+ else if (gnssConfigurationIface_V2_0 != nullptr) {
major = 2;
minor = 0;
} else if (gnssConfigurationIface_V1_1 != nullptr) {
@@ -2768,7 +2785,7 @@ static jboolean
SingleSatCorrection singleSatCorrection = {
.singleSatCorrectionFlags = corrFlags,
- .constellation = static_cast<GnssConstellationType>(constType),
+ .constellation = static_cast<GnssConstellationType_V1_0>(constType),
.svid = static_cast<uint16_t>(satId),
.carrierFrequencyHz = carrierFreqHz,
.probSatIsLos = probSatIsLos,
@@ -2863,8 +2880,8 @@ static jboolean android_location_GnssConfiguration_set_supl_version(JNIEnv*,
static jboolean android_location_GnssConfiguration_set_supl_es(JNIEnv*,
jobject,
jint suplEs) {
- if (gnssConfigurationIface_V2_0 != nullptr) {
- ALOGI("Config parameter SUPL_ES is deprecated in IGnssConfiguration.hal version 2.0.");
+ if (gnssConfigurationIface_V2_0 != nullptr || gnssConfigurationIface_V2_1 != nullptr) {
+ ALOGI("Config parameter SUPL_ES is deprecated in IGnssConfiguration.hal version 2.0 and higher.");
return JNI_FALSE;
}
@@ -2892,7 +2909,7 @@ static jboolean android_location_GnssConfiguration_set_supl_mode(JNIEnv*,
static jboolean android_location_GnssConfiguration_set_gps_lock(JNIEnv*,
jobject,
jint gpsLock) {
- if (gnssConfigurationIface_V2_0 != nullptr) {
+ if (gnssConfigurationIface_V2_0 != nullptr || gnssConfigurationIface_V2_1 != nullptr) {
ALOGI("Config parameter GPS_LOCK is deprecated in IGnssConfiguration.hal version 2.0.");
return JNI_FALSE;
}
@@ -2932,7 +2949,7 @@ static jboolean android_location_GnssConfiguration_set_gnss_pos_protocol_select(
static jboolean android_location_GnssConfiguration_set_satellite_blacklist(
JNIEnv* env, jobject, jintArray constellations, jintArray sv_ids) {
- if (gnssConfigurationIface_V1_1 == nullptr) {
+ if (gnssConfigurationIface_V1_1 == nullptr && gnssConfigurationIface_V2_1 == nullptr) {
ALOGI("IGnssConfiguration interface does not support satellite blacklist.");
return JNI_FALSE;
}
@@ -2955,11 +2972,24 @@ static jboolean android_location_GnssConfiguration_set_satellite_blacklist(
return JNI_FALSE;
}
+ if (gnssConfigurationIface_V2_1 != nullptr) {
+ hidl_vec<IGnssConfiguration_V2_1::BlacklistedSource> sources;
+ sources.resize(length);
+
+ for (int i = 0; i < length; i++) {
+ sources[i].constellation = static_cast<GnssConstellationType_V2_0>(constellation_array[i]);
+ sources[i].svid = sv_id_array[i];
+ }
+
+ auto result = gnssConfigurationIface_V2_1->setBlacklist_2_1(sources);
+ return checkHidlReturn(result, "IGnssConfiguration_V2_1 setBlacklist_2_1() failed.");
+ }
+
hidl_vec<IGnssConfiguration_V1_1::BlacklistedSource> sources;
sources.resize(length);
for (int i = 0; i < length; i++) {
- sources[i].constellation = static_cast<GnssConstellationType>(constellation_array[i]);
+ sources[i].constellation = static_cast<GnssConstellationType_V1_0>(constellation_array[i]);
sources[i].svid = sv_id_array[i];
}
diff --git a/services/devicepolicy/Android.bp b/services/devicepolicy/Android.bp
index 380ee942af98..cdbe77a3d64c 100644
--- a/services/devicepolicy/Android.bp
+++ b/services/devicepolicy/Android.bp
@@ -18,8 +18,3 @@ java_library_static {
"compat-changeid-annotation-processor",
],
}
-
-platform_compat_config {
- name: "services-devicepolicy-platform-compat-config",
- src: ":services.devicepolicy",
-}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 2a08f5c2de12..d5ff2802499c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -11723,6 +11723,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return mStateCache;
}
+ @Override
+ public List<String> getAllCrossProfilePackages() {
+ return DevicePolicyManagerService.this.getAllCrossProfilePackages();
+ }
+
}
private Intent createShowAdminSupportIntent(ComponentName admin, int userId) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index bfec51c92651..b6a8ca447213 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -751,6 +751,7 @@ public final class SystemServer {
// Now that we have the bare essentials of the OS up and running, take
// note that we just booted, which might send out a rescue party if
// we're stuck in a runtime restart loop.
+ RescueParty.registerHealthObserver(mSystemContext);
RescueParty.noteBoot(mSystemContext);
// Manages LEDs and display backlight so we need it to bring up the display.
diff --git a/services/net/java/android/net/ip/IpClientCallbacks.java b/services/net/java/android/net/ip/IpClientCallbacks.java
index 61cd88aac921..c93e5c5e4759 100644
--- a/services/net/java/android/net/ip/IpClientCallbacks.java
+++ b/services/net/java/android/net/ip/IpClientCallbacks.java
@@ -17,6 +17,7 @@
package android.net.ip;
import android.net.DhcpResults;
+import android.net.DhcpResultsParcelable;
import android.net.Layer2PacketParcelable;
import android.net.LinkProperties;
@@ -69,6 +70,18 @@ public class IpClientCallbacks {
public void onNewDhcpResults(DhcpResults dhcpResults) {}
/**
+ * Callback called when new DHCP results are available.
+ *
+ * <p>This is purely advisory and not an indication of provisioning success or failure. This is
+ * only here for callers that want to expose DHCPv4 results to other APIs
+ * (e.g., WifiInfo#setInetAddress).
+ *
+ * <p>DHCPv4 or static IPv4 configuration failure or success can be determined by whether or not
+ * the passed-in DhcpResults object is null.
+ */
+ public void onNewDhcpResults(DhcpResultsParcelable dhcpResults) {}
+
+ /**
* Indicates that provisioning was successful.
*/
public void onProvisioningSuccess(LinkProperties newLp) {}
diff --git a/services/net/java/android/net/ip/IpClientUtil.java b/services/net/java/android/net/ip/IpClientUtil.java
index 4d60e6239376..7f723b1c232b 100644
--- a/services/net/java/android/net/ip/IpClientUtil.java
+++ b/services/net/java/android/net/ip/IpClientUtil.java
@@ -119,6 +119,7 @@ public class IpClientUtil {
@Override
public void onNewDhcpResults(DhcpResultsParcelable dhcpResults) {
mCb.onNewDhcpResults(fromStableParcelable(dhcpResults));
+ mCb.onNewDhcpResults(dhcpResults);
}
@Override
diff --git a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
index 8632ca4c2898..8b2f15c2babb 100644
--- a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -81,7 +81,10 @@ import org.robolectric.shadows.ShadowLooper;
import org.robolectric.shadows.ShadowPackageManager;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.List;
/**
@@ -1238,13 +1241,49 @@ public class UserBackupManagerServiceTest {
assertThat(service.getAncestralSerialNumber()).isEqualTo(testSerialNumber2);
}
+ /**
+ * Test that {@link UserBackupManagerService#dump()} for system user does not prefix dump with
+ * "User 0:".
+ */
+ @Test
+ public void testDump_forSystemUser_DoesNotHaveUserPrefix() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService service =
+ BackupManagerServiceTestUtils.createUserBackupManagerServiceAndRunTasks(
+ UserHandle.USER_SYSTEM,
+ mContext,
+ mBackupThread,
+ mBaseStateDir,
+ mDataDir,
+ mTransportManager);
+
+ StringWriter dump = new StringWriter();
+ service.dump(new FileDescriptor(), new PrintWriter(dump), new String[0]);
+
+ assertThat(dump.toString()).startsWith("Backup Manager is ");
+ }
+
+ /**
+ * Test that {@link UserBackupManagerService#dump()} for non-system user prefixes dump with
+ * "User <userid>:".
+ */
+ @Test
+ public void testDump_forNonSystemUser_HasUserPrefix() throws Exception {
+ mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+ UserBackupManagerService service = createUserBackupManagerServiceAndRunTasks();
+
+ StringWriter dump = new StringWriter();
+ service.dump(new FileDescriptor(), new PrintWriter(dump), new String[0]);
+
+ assertThat(dump.toString()).startsWith("User " + USER_ID + ":" + "Backup Manager is ");
+ }
+
private File createTestFile() throws IOException {
File testFile = new File(mContext.getFilesDir(), "test");
testFile.createNewFile();
return testFile;
}
-
/**
* We can't mock the void method {@link #schedule(Context, long, BackupManagerConstants)} so we
* extend {@link ShadowKeyValueBackupJob} and throw an exception at the end of the method.
diff --git a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
index 0d6020c3d06d..30d89d31ba64 100644
--- a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
@@ -32,6 +32,7 @@ import static org.mockito.ArgumentMatchers.isNull;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.pm.VersionedPackage;
import android.os.RecoverySystem;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -39,6 +40,8 @@ import android.provider.DeviceConfig;
import android.provider.Settings;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.server.PackageWatchdog.PackageHealthObserverImpact;
+import com.android.server.RescueParty.RescuePartyObserver;
import com.android.server.am.SettingsToPropertiesMapper;
import com.android.server.utils.FlagNamespaceUtils;
@@ -57,13 +60,15 @@ import java.util.HashMap;
* Test RescueParty.
*/
public class RescuePartyTest {
- private static final int PERSISTENT_APP_UID = 12;
private static final long CURRENT_NETWORK_TIME_MILLIS = 0L;
private static final String FAKE_NATIVE_NAMESPACE1 = "native1";
private static final String FAKE_NATIVE_NAMESPACE2 = "native2";
private static final String[] FAKE_RESET_NATIVE_NAMESPACES =
{FAKE_NATIVE_NAMESPACE1, FAKE_NATIVE_NAMESPACE2};
+ private static VersionedPackage sFailingPackage = new VersionedPackage("com.package.name", 1);
+ private static final String PROP_DISABLE_RESCUE = "persist.sys.disable_rescue";
+
private MockitoSession mSession;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
@@ -182,25 +187,25 @@ public class RescuePartyTest {
@Test
public void testPersistentAppCrashDetectionWithExecutionForAllRescueLevels() {
- notePersistentAppCrash(RescueParty.TRIGGER_COUNT);
+ notePersistentAppCrash();
verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS);
assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS,
SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
- notePersistentAppCrash(RescueParty.TRIGGER_COUNT);
+ notePersistentAppCrash();
verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES);
assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES,
SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
- notePersistentAppCrash(RescueParty.TRIGGER_COUNT);
+ notePersistentAppCrash();
verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS);
assertEquals(RescueParty.LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS,
SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
- notePersistentAppCrash(RescueParty.TRIGGER_COUNT);
+ notePersistentAppCrash();
verify(() -> RecoverySystem.rebootPromptAndWipeUserData(mMockContext, RescueParty.TAG));
assertEquals(RescueParty.LEVEL_FACTORY_RESET,
@@ -221,20 +226,6 @@ public class RescuePartyTest {
}
@Test
- public void testPersistentAppCrashDetectionWithWrongInterval() {
- notePersistentAppCrash(RescueParty.TRIGGER_COUNT - 1);
-
- // last persistent app crash is just outside of the boot loop detection window
- doReturn(CURRENT_NETWORK_TIME_MILLIS
- + RescueParty.PERSISTENT_APP_CRASH_TRIGGER_WINDOW_MILLIS + 1)
- .when(() -> RescueParty.getElapsedRealtime());
- notePersistentAppCrash(/*numTimes=*/1);
-
- assertEquals(RescueParty.LEVEL_NONE,
- SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
- }
-
- @Test
public void testBootLoopDetectionWithProperInterval() {
noteBoot(RescueParty.TRIGGER_COUNT - 1);
@@ -249,21 +240,6 @@ public class RescuePartyTest {
}
@Test
- public void testPersistentAppCrashDetectionWithProperInterval() {
- notePersistentAppCrash(RescueParty.TRIGGER_COUNT - 1);
-
- // last persistent app crash is just inside of the boot loop detection window
- doReturn(CURRENT_NETWORK_TIME_MILLIS
- + RescueParty.PERSISTENT_APP_CRASH_TRIGGER_WINDOW_MILLIS)
- .when(() -> RescueParty.getElapsedRealtime());
- notePersistentAppCrash(/*numTimes=*/1);
-
- verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS);
- assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS,
- SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
- }
-
- @Test
public void testBootLoopDetectionWithWrongTriggerCount() {
noteBoot(RescueParty.TRIGGER_COUNT - 1);
assertEquals(RescueParty.LEVEL_NONE,
@@ -271,13 +247,6 @@ public class RescuePartyTest {
}
@Test
- public void testPersistentAppCrashDetectionWithWrongTriggerCount() {
- notePersistentAppCrash(RescueParty.TRIGGER_COUNT - 1);
- assertEquals(RescueParty.LEVEL_NONE,
- SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE));
- }
-
- @Test
public void testIsAttemptingFactoryReset() {
noteBoot(RescueParty.TRIGGER_COUNT * 4);
@@ -319,6 +288,77 @@ public class RescuePartyTest {
FAKE_NATIVE_NAMESPACE2, /*makeDefault=*/true));
}
+ @Test
+ public void testExplicitlyEnablingAndDisablingRescue() {
+ SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false));
+ SystemProperties.set(PROP_DISABLE_RESCUE, Boolean.toString(true));
+ assertEquals(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage,
+ PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING), false);
+
+ SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true));
+ assertTrue(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage,
+ PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING));
+ }
+
+ @Test
+ public void testHealthCheckLevels() {
+ RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
+
+ // Ensure that no action is taken for cases where the failure reason is unknown
+ SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString(
+ RescueParty.LEVEL_FACTORY_RESET));
+ assertEquals(observer.onHealthCheckFailed(null, PackageWatchdog.FAILURE_REASON_UNKNOWN),
+ PackageHealthObserverImpact.USER_IMPACT_NONE);
+
+ /*
+ For the following cases, ensure that the returned user impact corresponds with the user
+ impact of the next available rescue level, not the current one.
+ */
+ SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString(
+ RescueParty.LEVEL_NONE));
+ assertEquals(observer.onHealthCheckFailed(null,
+ PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING),
+ PackageHealthObserverImpact.USER_IMPACT_LOW);
+
+ SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString(
+ RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS));
+ assertEquals(observer.onHealthCheckFailed(null,
+ PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING),
+ PackageHealthObserverImpact.USER_IMPACT_LOW);
+
+
+ SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString(
+ RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES));
+ assertEquals(observer.onHealthCheckFailed(null,
+ PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING),
+ PackageHealthObserverImpact.USER_IMPACT_HIGH);
+
+
+ SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString(
+ RescueParty.LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS));
+ assertEquals(observer.onHealthCheckFailed(null,
+ PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING),
+ PackageHealthObserverImpact.USER_IMPACT_HIGH);
+
+
+ SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString(
+ RescueParty.LEVEL_FACTORY_RESET));
+ assertEquals(observer.onHealthCheckFailed(null,
+ PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING),
+ PackageHealthObserverImpact.USER_IMPACT_HIGH);
+ }
+
+ @Test
+ public void testRescueLevelIncrementsWhenExecuted() {
+ RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
+ SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString(
+ RescueParty.LEVEL_NONE));
+ observer.execute(sFailingPackage,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH);
+ assertEquals(SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, -1),
+ RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS);
+ }
+
private void verifySettingsResets(int resetMode) {
verify(() -> Settings.Global.resetToDefaultsAsUser(mMockContentResolver, null,
resetMode, UserHandle.USER_SYSTEM));
@@ -332,9 +372,8 @@ public class RescuePartyTest {
}
}
- private void notePersistentAppCrash(int numTimes) {
- for (int i = 0; i < numTimes; i++) {
- RescueParty.noteAppCrash(mMockContext, PERSISTENT_APP_UID);
- }
+ private void notePersistentAppCrash() {
+ RescuePartyObserver.getInstance(mMockContext).execute(new VersionedPackage(
+ "com.package.name", 1), PackageWatchdog.FAILURE_REASON_UNKNOWN);
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/UserInfoStoreTest.java b/services/tests/mockingservicestests/src/com/android/server/location/UserInfoStoreTest.java
new file mode 100644
index 000000000000..06fb10257a37
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/UserInfoStoreTest.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.location;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.UserInfo;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.quality.Strictness;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class UserInfoStoreTest {
+
+ private static final int USER1_ID = 1;
+ private static final int USER1_MANAGED_ID = 11;
+ private static final int[] USER1_PROFILES = new int[]{USER1_ID, USER1_MANAGED_ID};
+ private static final int USER2_ID = 2;
+ private static final int USER2_MANAGED_ID = 12;
+ private static final int[] USER2_PROFILES = new int[]{USER2_ID, USER2_MANAGED_ID};
+
+ @Mock private Context mContext;
+ @Mock private UserManager mUserManager;
+
+ private StaticMockitoSession mMockingSession;
+ private List<BroadcastReceiver> mBroadcastReceivers = new ArrayList<>();
+
+ private UserInfoStore mStore;
+
+ @Before
+ public void setUp() {
+ mMockingSession = mockitoSession()
+ .initMocks(this)
+ .spyStatic(ActivityManager.class)
+ .strictness(Strictness.WARN)
+ .startMocking();
+
+ doReturn(mUserManager).when(mContext).getSystemService(UserManager.class);
+ doAnswer(invocation -> {
+ mBroadcastReceivers.add(invocation.getArgument(0));
+ return null;
+ }).when(mContext).registerReceiverAsUser(any(BroadcastReceiver.class), any(
+ UserHandle.class), any(IntentFilter.class), isNull(), any(Handler.class));
+ doReturn(USER1_PROFILES).when(mUserManager).getProfileIdsWithDisabled(USER1_ID);
+ doReturn(USER2_PROFILES).when(mUserManager).getProfileIdsWithDisabled(USER2_ID);
+ doReturn(new UserInfo(USER1_ID, "", 0)).when(mUserManager).getProfileParent(
+ USER1_MANAGED_ID);
+ doReturn(new UserInfo(USER2_ID, "", 0)).when(mUserManager).getProfileParent(
+ USER2_MANAGED_ID);
+
+ doReturn(USER1_ID).when(ActivityManager::getCurrentUser);
+
+ mStore = new UserInfoStore(mContext);
+ mStore.onSystemReady();
+ }
+
+ @After
+ public void tearDown() {
+ if (mMockingSession != null) {
+ mMockingSession.finishMocking();
+ }
+ }
+
+ private void switchUser(int userId) {
+ doReturn(userId).when(ActivityManager::getCurrentUser);
+ Intent intent = new Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE,
+ userId);
+ for (BroadcastReceiver broadcastReceiver : mBroadcastReceivers) {
+ broadcastReceiver.onReceive(mContext, intent);
+ }
+ }
+
+ @Test
+ public void testListeners() {
+ UserInfoStore.UserChangedListener listener = mock(UserInfoStore.UserChangedListener.class);
+ mStore.addListener(listener);
+
+ switchUser(USER1_ID);
+ verify(listener, never()).onUserChanged(anyInt(), anyInt());
+
+ switchUser(USER2_ID);
+ verify(listener).onUserChanged(USER1_ID, USER2_ID);
+
+ switchUser(USER1_ID);
+ verify(listener).onUserChanged(USER2_ID, USER1_ID);
+ }
+
+ @Test
+ public void testCurrentUser() {
+ assertThat(mStore.getCurrentUserId()).isEqualTo(USER1_ID);
+
+ switchUser(USER2_ID);
+
+ assertThat(mStore.getCurrentUserId()).isEqualTo(USER2_ID);
+
+ switchUser(USER1_ID);
+
+ assertThat(mStore.getCurrentUserId()).isEqualTo(USER1_ID);
+ }
+
+ @Test
+ public void testIsCurrentUserOrProfile() {
+ assertThat(mStore.isCurrentUserOrProfile(USER1_ID)).isTrue();
+ assertThat(mStore.isCurrentUserOrProfile(USER1_MANAGED_ID)).isTrue();
+ assertThat(mStore.isCurrentUserOrProfile(USER2_ID)).isFalse();
+ assertThat(mStore.isCurrentUserOrProfile(USER2_MANAGED_ID)).isFalse();
+
+ switchUser(USER2_ID);
+
+ assertThat(mStore.isCurrentUserOrProfile(USER1_ID)).isFalse();
+ assertThat(mStore.isCurrentUserOrProfile(USER2_ID)).isTrue();
+ assertThat(mStore.isCurrentUserOrProfile(USER1_MANAGED_ID)).isFalse();
+ assertThat(mStore.isCurrentUserOrProfile(USER2_MANAGED_ID)).isTrue();
+ }
+
+ @Test
+ public void testGetParentUserId() {
+ assertThat(mStore.getParentUserId(USER1_ID)).isEqualTo(USER1_ID);
+ assertThat(mStore.getParentUserId(USER1_MANAGED_ID)).isEqualTo(USER1_ID);
+ assertThat(mStore.getParentUserId(USER2_ID)).isEqualTo(USER2_ID);
+ assertThat(mStore.getParentUserId(USER2_MANAGED_ID)).isEqualTo(USER2_ID);
+
+ switchUser(USER2_ID);
+
+ assertThat(mStore.getParentUserId(USER1_ID)).isEqualTo(USER1_ID);
+ assertThat(mStore.getParentUserId(USER2_ID)).isEqualTo(USER2_ID);
+ assertThat(mStore.getParentUserId(USER1_MANAGED_ID)).isEqualTo(USER1_ID);
+ assertThat(mStore.getParentUserId(USER2_MANAGED_ID)).isEqualTo(USER2_ID);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
index 1829fb79699f..2fb2021de200 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
@@ -29,6 +29,7 @@ import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
@@ -56,7 +57,6 @@ import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
-import android.net.wifi.WifiSsid;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
@@ -182,8 +182,8 @@ public class NetworkScoreServiceTest {
}
private ScanResult createScanResult(String ssid, String bssid) {
- ScanResult result = new ScanResult();
- result.wifiSsid = WifiSsid.createFromAsciiEncoded(ssid);
+ ScanResult result = mock(ScanResult.class);
+ result.SSID = ssid;
result.BSSID = bssid;
return result;
}
@@ -794,7 +794,7 @@ public class NetworkScoreServiceTest {
@Test
public void testScanResultsScoreCacheFilter_invalidScanResults() throws Exception {
List<ScanResult> invalidScanResults = Lists.newArrayList(
- new ScanResult(),
+ mock(ScanResult.class),
createScanResult("", SCORED_NETWORK.networkKey.wifiKey.bssid),
createScanResult(WifiManager.UNKNOWN_SSID, SCORED_NETWORK.networkKey.wifiKey.bssid),
createScanResult(SSID, null),
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index d11d98766b01..591c3a385e23 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -85,6 +85,7 @@ import android.view.accessibility.IAccessibilityInteractionConnection;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection;
+import com.android.server.accessibility.test.MessageCapturingHandler;
import com.android.server.wm.WindowManagerInternal;
import org.junit.Before;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
new file mode 100644
index 000000000000..75239db92121
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility;
+
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.Manifest;
+import android.app.PendingIntent;
+import android.app.RemoteAction;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Icon;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.server.LocalServices;
+import com.android.server.accessibility.AccessibilityManagerService.AccessibilityDisplayListener;
+import com.android.server.wm.ActivityTaskManagerInternal;
+import com.android.server.wm.WindowManagerInternal;
+
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * APCT tests for {@link AccessibilityManagerService}.
+ */
+public class AccessibilityManagerServiceTest extends AndroidTestCase {
+ private static final String TAG = "A11Y_MANAGER_SERVICE_TEST";
+ private static final int ACTION_ID = 20;
+ private static final String LABEL = "label";
+ private static final String INTENT_ACTION = "TESTACTION";
+ private static final String DESCRIPTION = "description";
+ private static final PendingIntent TEST_PENDING_INTENT = PendingIntent.getBroadcast(
+ InstrumentationRegistry.getTargetContext(), 0, new Intent(INTENT_ACTION), 0);
+ private static final RemoteAction TEST_ACTION = new RemoteAction(
+ Icon.createWithContentUri("content://test"),
+ LABEL,
+ DESCRIPTION,
+ TEST_PENDING_INTENT);
+ private static final AccessibilityAction NEW_ACCESSIBILITY_ACTION =
+ new AccessibilityAction(ACTION_ID, LABEL);
+
+ @Mock private PackageManager mMockPackageManager;
+ @Mock private WindowManagerInternal mMockWindowManagerService;
+ @Mock private AccessibilitySecurityPolicy mMockSecurityPolicy;
+ @Mock private SystemActionPerformer mMockSystemActionPerformer;
+ @Mock private AccessibilityWindowManager mMockA11yWindowManager;
+ @Mock private AccessibilityDisplayListener mMockA11yDisplayListener;
+ @Mock private ActivityTaskManagerInternal mMockActivityTaskManagerInternal;
+
+ private AccessibilityManagerService mA11yms;
+
+ @Override
+ protected void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ LocalServices.removeServiceForTest(WindowManagerInternal.class);
+ LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
+ LocalServices.addService(WindowManagerInternal.class, mMockWindowManagerService);
+ LocalServices.addService(
+ ActivityTaskManagerInternal.class, mMockActivityTaskManagerInternal);
+ mA11yms = new AccessibilityManagerService(
+ InstrumentationRegistry.getContext(),
+ mMockPackageManager,
+ mMockSecurityPolicy,
+ mMockSystemActionPerformer,
+ mMockA11yWindowManager,
+ mMockA11yDisplayListener);
+ }
+
+ @SmallTest
+ public void testRegisterSystemActionWithoutPermission() throws Exception {
+ doThrow(SecurityException.class).when(mMockSecurityPolicy).enforceCallingPermission(
+ Manifest.permission.MANAGE_ACCESSIBILITY,
+ AccessibilityManagerService.FUNCTION_REGISTER_SYSTEM_ACTION);
+
+ try {
+ mA11yms.registerSystemAction(TEST_ACTION, ACTION_ID);
+ fail();
+ } catch (SecurityException expected) {
+ }
+ verify(mMockSystemActionPerformer, never()).registerSystemAction(ACTION_ID, TEST_ACTION);
+ }
+
+ @SmallTest
+ public void testRegisterSystemAction() throws Exception {
+ mA11yms.registerSystemAction(TEST_ACTION, ACTION_ID);
+ verify(mMockSystemActionPerformer).registerSystemAction(ACTION_ID, TEST_ACTION);
+ }
+
+ @SmallTest
+ public void testUnregisterSystemActionWithoutPermission() throws Exception {
+ doThrow(SecurityException.class).when(mMockSecurityPolicy).enforceCallingPermission(
+ Manifest.permission.MANAGE_ACCESSIBILITY,
+ AccessibilityManagerService.FUNCTION_UNREGISTER_SYSTEM_ACTION);
+
+ try {
+ mA11yms.unregisterSystemAction(ACTION_ID);
+ fail();
+ } catch (SecurityException expected) {
+ }
+ verify(mMockSystemActionPerformer, never()).unregisterSystemAction(ACTION_ID);
+ }
+
+ @SmallTest
+ public void testUnregisterSystemAction() throws Exception {
+ mA11yms.unregisterSystemAction(ACTION_ID);
+ verify(mMockSystemActionPerformer).unregisterSystemAction(ACTION_ID);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index 99dd9a12eb72..2ce70b6f0889 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -44,6 +44,7 @@ import android.os.UserHandle;
import android.testing.DexmakerShareClassLoaderRule;
import android.view.Display;
+import com.android.server.accessibility.test.MessageCapturingHandler;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
index 67075edb0143..9db5a080c093 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
@@ -53,6 +53,7 @@ import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection;
+import com.android.server.accessibility.test.MessageCapturingHandler;
import com.android.server.wm.WindowManagerInternal;
import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java
index 44a514f7623c..96ae102e53f3 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java
@@ -31,6 +31,8 @@ import android.accessibilityservice.FingerprintGestureController;
import android.accessibilityservice.FingerprintGestureController.FingerprintGestureCallback;
import android.accessibilityservice.IAccessibilityServiceConnection;
+import com.android.server.accessibility.test.MessageCapturingHandler;
+
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java
index de7bc443b8c5..30d00ad716e6 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java
@@ -31,6 +31,7 @@ import android.hardware.fingerprint.IFingerprintService;
import android.view.KeyEvent;
import com.android.server.accessibility.FingerprintGestureDispatcher.FingerprintGestureClient;
+import com.android.server.accessibility.test.MessageCapturingHandler;
import org.junit.After;
import org.junit.Before;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java b/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
index 23ce483be107..41235560dc91 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java
@@ -44,6 +44,7 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.accessibility.KeyEventDispatcher.KeyEventFilter;
+import com.android.server.accessibility.test.MessageCapturingHandler;
import com.android.server.policy.WindowManagerPolicy;
import org.hamcrest.Description;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java
index 322653b4115c..78e651b7a3c1 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java
@@ -33,6 +33,7 @@ import android.view.KeyEvent;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.accessibility.test.MessageCapturingHandler;
import com.android.server.policy.WindowManagerPolicy;
import org.hamcrest.Description;
@@ -212,4 +213,4 @@ public class KeyboardInterceptorTest {
description.appendText("Matches key event");
}
}
-} \ No newline at end of file
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java
index 773b8778c7bf..82c6498bd9be 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java
@@ -47,6 +47,7 @@ import android.view.MagnificationSpec;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.accessibility.test.MessageCapturingHandler;
import com.android.server.wm.WindowManagerInternal;
import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
index 36e854ca77cd..1ac4a8ed96d0 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
@@ -54,6 +54,7 @@ import android.view.accessibility.AccessibilityEvent;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.accessibility.test.MessageCapturingHandler;
import com.android.server.accessibility.utils.MotionEventMatcher;
import org.hamcrest.Description;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
index 8da927dcb4ab..c8baca610bdc 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
@@ -37,6 +37,7 @@ import android.hardware.display.DisplayManager;
import android.os.IBinder;
import android.view.accessibility.AccessibilityEvent;
+import com.android.server.accessibility.test.MessageCapturingHandler;
import com.android.server.wm.WindowManagerInternal;
import org.junit.After;
diff --git a/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java b/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java
index d4182f3d31e2..5a1ad8655ab0 100644
--- a/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java
@@ -672,6 +672,30 @@ public final class AdbDebuggingManagerTest {
connectionTime2, mKeyStore.getLastConnectionTime(TEST_KEY_2));
}
+ @Test
+ public void testClearAuthorizationsBeforeAdbEnabled() throws Exception {
+ // The adb key store is not instantiated until adb is enabled; however if the user attempts
+ // to clear the adb authorizations when adb is disabled after a boot a NullPointerException
+ // was thrown as deleteKeyStore is invoked against the key store. This test ensures the
+ // key store can be successfully cleared when adb is disabled.
+ mHandler = mManager.new AdbDebuggingHandler(FgThread.get().getLooper());
+
+ clearKeyStore();
+ }
+
+ @Test
+ public void testClearAuthorizationsDeletesKeyFiles() throws Exception {
+ mAdbKeyFile.createNewFile();
+ mAdbKeyXmlFile.createNewFile();
+
+ clearKeyStore();
+
+ assertFalse("The adb key file should have been deleted after revocation of the grants",
+ mAdbKeyFile.exists());
+ assertFalse("The adb xml key file should have been deleted after revocation of the grants",
+ mAdbKeyXmlFile.exists());
+ }
+
/**
* Runs an adb test with the provided configuration.
*
diff --git a/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java
index 2326dfda7e12..d44476ed971d 100644
--- a/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -27,6 +27,7 @@ import static junit.framework.Assert.fail;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
@@ -59,6 +60,7 @@ import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -83,6 +85,8 @@ public class BackupManagerServiceTest {
@Mock
private UserBackupManagerService mUserBackupManagerService;
@Mock
+ private UserBackupManagerService mNonSystemUserBackupManagerService;
+ @Mock
private Context mContextMock;
@Mock
private PrintWriter mPrintWriterMock;
@@ -105,7 +109,7 @@ public class BackupManagerServiceTest {
mUserServices = new SparseArray<>();
mUserServices.append(UserHandle.USER_SYSTEM, mUserBackupManagerService);
- mUserServices.append(NON_USER_SYSTEM, mUserBackupManagerService);
+ mUserServices.append(NON_USER_SYSTEM, mNonSystemUserBackupManagerService);
when(mUserManagerMock.getUserInfo(UserHandle.USER_SYSTEM)).thenReturn(mUserInfoMock);
when(mUserManagerMock.getUserInfo(NON_USER_SYSTEM)).thenReturn(mUserInfoMock);
@@ -512,6 +516,26 @@ public class BackupManagerServiceTest {
mService.dump(mFileDescriptorStub, mPrintWriterMock, new String[0]);
verifyNoMoreInteractions(mUserBackupManagerService);
+ verifyNoMoreInteractions(mNonSystemUserBackupManagerService);
+ }
+
+ /**
+ * Test that {@link BackupManagerService#dump()} dumps system user information before non-system
+ * user information.
+ */
+
+ @Test
+ public void testDump_systemUserFirst() {
+ String[] args = new String[0];
+ mService.dumpWithoutCheckingPermission(mFileDescriptorStub, mPrintWriterMock, args);
+
+ InOrder inOrder =
+ inOrder(mUserBackupManagerService, mNonSystemUserBackupManagerService);
+ inOrder.verify(mUserBackupManagerService)
+ .dump(mFileDescriptorStub, mPrintWriterMock, args);
+ inOrder.verify(mNonSystemUserBackupManagerService)
+ .dump(mFileDescriptorStub, mPrintWriterMock, args);
+ inOrder.verifyNoMoreInteractions();
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/integrity/model/BitTrackedInputStreamTest.java b/services/tests/servicestests/src/com/android/server/integrity/model/BitTrackedInputStreamTest.java
new file mode 100644
index 000000000000..1eb5eb51504a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/integrity/model/BitTrackedInputStreamTest.java
@@ -0,0 +1,142 @@
+/*
+ * 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.integrity.model;
+
+import static com.android.server.integrity.model.ComponentBitSize.ATOMIC_FORMULA_START;
+import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_END;
+import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_START;
+import static com.android.server.integrity.model.ComponentBitSize.CONNECTOR_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.EFFECT_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.KEY_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.SEPARATOR_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
+import static com.android.server.integrity.utils.TestUtils.getBits;
+import static com.android.server.integrity.utils.TestUtils.getBytes;
+import static com.android.server.integrity.utils.TestUtils.getValueBits;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.content.integrity.AtomicFormula;
+import android.content.integrity.CompoundFormula;
+import android.content.integrity.Rule;
+
+import com.android.server.integrity.parser.BinaryFileOperations;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.IOException;
+
+@RunWith(JUnit4.class)
+public class BitTrackedInputStreamTest {
+ private static final String COMPOUND_FORMULA_START_BITS =
+ getBits(COMPOUND_FORMULA_START, SEPARATOR_BITS);
+ private static final String COMPOUND_FORMULA_END_BITS =
+ getBits(COMPOUND_FORMULA_END, SEPARATOR_BITS);
+ private static final String ATOMIC_FORMULA_START_BITS =
+ getBits(ATOMIC_FORMULA_START, SEPARATOR_BITS);
+ private static final String NOT = getBits(CompoundFormula.NOT, CONNECTOR_BITS);
+ private static final String PACKAGE_NAME = getBits(AtomicFormula.PACKAGE_NAME, KEY_BITS);
+ private static final String EQ = getBits(AtomicFormula.EQ, OPERATOR_BITS);
+ private static final String DENY = getBits(Rule.DENY, EFFECT_BITS);
+
+ private static final String IS_NOT_HASHED = "0";
+ private static final String START_BIT = "1";
+ private static final String END_BIT = "1";
+
+ @Test
+ public void testBitOperationsCountBitsCorrectly() throws IOException {
+ String packageName = "com.test.app";
+ byte[] testInput =
+ getBytes(
+ START_BIT
+ + COMPOUND_FORMULA_START_BITS
+ + NOT
+ + ATOMIC_FORMULA_START_BITS
+ + PACKAGE_NAME
+ + EQ
+ + IS_NOT_HASHED
+ + getBits(packageName.length(), VALUE_SIZE_BITS)
+ + getValueBits(packageName)
+ + COMPOUND_FORMULA_END_BITS
+ + DENY
+ + END_BIT);
+
+ BitTrackedInputStream bitTrackedInputStream = new BitTrackedInputStream(testInput);
+
+ // Right after construction, the read bits count should be 0.
+ assertThat(bitTrackedInputStream.getReadBitsCount()).isEqualTo(0);
+
+ // Get next 10 bits should result with 10 bits read.
+ bitTrackedInputStream.getNext(10);
+ assertThat(bitTrackedInputStream.getReadBitsCount()).isEqualTo(10);
+
+ // When we move the cursor 8 bytes, we should point to 64 bits.
+ bitTrackedInputStream.setCursorToByteLocation(8);
+ assertThat(bitTrackedInputStream.getReadBitsCount()).isEqualTo(64);
+
+ // Read until the end and the total size of the input stream should be available.
+ while (bitTrackedInputStream.hasNext()) {
+ bitTrackedInputStream.getNext(1);
+ }
+ assertThat(bitTrackedInputStream.getReadBitsCount()).isEqualTo(128);
+ }
+
+ @Test
+ public void testBitInputStreamOperationsStillWork() throws IOException {
+ String packageName = "com.test.app";
+ byte[] testInput =
+ getBytes(
+ IS_NOT_HASHED
+ + getBits(packageName.length(), VALUE_SIZE_BITS)
+ + getValueBits(packageName));
+
+ BitTrackedInputStream bitTrackedInputStream = new BitTrackedInputStream(testInput);
+ assertThat(bitTrackedInputStream.getReadBitsCount()).isEqualTo(0);
+
+ // Read until the string parameter.
+ String stringValue = BinaryFileOperations.getStringValue(bitTrackedInputStream);
+
+ // Verify that the read bytes are counted.
+ assertThat(stringValue).isEqualTo(packageName);
+ assertThat(bitTrackedInputStream.getReadBitsCount()).isGreaterThan(0);
+ }
+
+ @Test
+ public void testBitTrackedInputStream_moveCursorForwardFailsIfAlreadyRead() throws IOException {
+ String packageName = "com.test.app";
+ byte[] testInput =
+ getBytes(
+ IS_NOT_HASHED
+ + getBits(packageName.length(), VALUE_SIZE_BITS)
+ + getValueBits(packageName));
+
+ BitTrackedInputStream bitTrackedInputStream = new BitTrackedInputStream(testInput);
+
+ // Read more than two bytes.
+ bitTrackedInputStream.getNext(20);
+
+ // Ask to move the cursor to the second byte.
+ assertThrows(
+ IllegalStateException.class,
+ () -> bitTrackedInputStream.setCursorToByteLocation(2));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/ByteTrackedOutputStreamTest.java b/services/tests/servicestests/src/com/android/server/integrity/model/ByteTrackedOutputStreamTest.java
index 5ecb8b5c8169..c7cc343dd77e 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/serializer/ByteTrackedOutputStreamTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/model/ByteTrackedOutputStreamTest.java
@@ -14,12 +14,10 @@
* limitations under the License.
*/
-package com.android.server.integrity.serializer;
+package com.android.server.integrity.model;
import static com.google.common.truth.Truth.assertThat;
-import com.android.server.integrity.model.BitOutputStream;
-
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java
index 9cc0ed85a044..51f5c755754c 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java
@@ -48,6 +48,7 @@ import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -97,8 +98,10 @@ public class RuleBinaryParserTest {
private static final byte[] DEFAULT_FORMAT_VERSION_BYTES =
getBytes(getBits(DEFAULT_FORMAT_VERSION, FORMAT_VERSION_BITS));
+ private static final List<RuleIndexRange> NO_INDEXING = Collections.emptyList();
+
@Test
- public void testBinaryStream_validCompoundFormula() throws Exception {
+ public void testBinaryStream_validCompoundFormula_noIndexing() throws Exception {
String packageName = "com.test.app";
String ruleBits =
START_BIT
@@ -131,13 +134,13 @@ public class RuleBinaryParserTest {
/* isHashedValue= */ false))),
Rule.DENY);
- List<Rule> rules = binaryParser.parse(inputStream);
+ List<Rule> rules = binaryParser.parse(inputStream, NO_INDEXING);
assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
}
@Test
- public void testBinaryString_validCompoundFormula_notConnector() throws Exception {
+ public void testBinaryString_validCompoundFormula_notConnector_noIndexing() throws Exception {
String packageName = "com.test.app";
String ruleBits =
START_BIT
@@ -175,7 +178,7 @@ public class RuleBinaryParserTest {
}
@Test
- public void testBinaryString_validCompoundFormula_andConnector() throws Exception {
+ public void testBinaryString_validCompoundFormula_andConnector_noIndexing() throws Exception {
String packageName = "com.test.app";
String appCertificate = "test_cert";
String ruleBits =
@@ -223,7 +226,7 @@ public class RuleBinaryParserTest {
}
@Test
- public void testBinaryString_validCompoundFormula_orConnector() throws Exception {
+ public void testBinaryString_validCompoundFormula_orConnector_noIndexing() throws Exception {
String packageName = "com.test.app";
String appCertificate = "test_cert";
String ruleBits =
@@ -272,7 +275,7 @@ public class RuleBinaryParserTest {
}
@Test
- public void testBinaryString_validAtomicFormula_stringValue() throws Exception {
+ public void testBinaryString_validAtomicFormula_stringValue_noIndexing() throws Exception {
String packageName = "com.test.app";
String ruleBits =
START_BIT
@@ -304,7 +307,7 @@ public class RuleBinaryParserTest {
}
@Test
- public void testBinaryString_validAtomicFormula_hashedValue() throws Exception {
+ public void testBinaryString_validAtomicFormula_hashedValue_noIndexing() throws Exception {
String appCertificate = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
String ruleBits =
START_BIT
@@ -337,7 +340,7 @@ public class RuleBinaryParserTest {
}
@Test
- public void testBinaryString_validAtomicFormula_integerValue() throws Exception {
+ public void testBinaryString_validAtomicFormula_integerValue_noIndexing() throws Exception {
int versionCode = 1;
String ruleBits =
START_BIT
@@ -365,7 +368,7 @@ public class RuleBinaryParserTest {
}
@Test
- public void testBinaryString_validAtomicFormula_booleanValue() throws Exception {
+ public void testBinaryString_validAtomicFormula_booleanValue_noIndexing() throws Exception {
String isPreInstalled = "1";
String ruleBits =
START_BIT
@@ -392,7 +395,7 @@ public class RuleBinaryParserTest {
}
@Test
- public void testBinaryString_invalidAtomicFormula() throws Exception {
+ public void testBinaryString_invalidAtomicFormula_noIndexing() {
int versionCode = 1;
String ruleBits =
START_BIT
@@ -415,7 +418,7 @@ public class RuleBinaryParserTest {
}
@Test
- public void testBinaryString_withNoRuleList() throws RuleParseException {
+ public void testBinaryString_withNoRuleList_noIndexing() throws RuleParseException {
ByteBuffer rule = ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length);
rule.put(DEFAULT_FORMAT_VERSION_BYTES);
RuleParser binaryParser = new RuleBinaryParser();
@@ -426,7 +429,7 @@ public class RuleBinaryParserTest {
}
@Test
- public void testBinaryString_withEmptyRule() throws RuleParseException {
+ public void testBinaryString_withEmptyRule_noIndexing() {
String ruleBits = START_BIT;
byte[] ruleBytes = getBytes(ruleBits);
ByteBuffer rule =
@@ -442,7 +445,7 @@ public class RuleBinaryParserTest {
}
@Test
- public void testBinaryString_invalidCompoundFormula_invalidNumberOfFormulas() throws Exception {
+ public void testBinaryString_invalidCompoundFormula_invalidNumberOfFormulas_noIndexing() {
String packageName = "com.test.app";
String appCertificate = "test_cert";
String ruleBits =
@@ -478,7 +481,7 @@ public class RuleBinaryParserTest {
}
@Test
- public void testBinaryString_invalidRule_invalidOperator() throws Exception {
+ public void testBinaryString_invalidRule_invalidOperator_noIndexing() {
int versionCode = 1;
String ruleBits =
START_BIT
@@ -506,7 +509,7 @@ public class RuleBinaryParserTest {
}
@Test
- public void testBinaryString_invalidRule_invalidEffect() throws Exception {
+ public void testBinaryString_invalidRule_invalidEffect_noIndexing() {
String packageName = "com.test.app";
String ruleBits =
START_BIT
@@ -536,7 +539,7 @@ public class RuleBinaryParserTest {
}
@Test
- public void testBinaryString_invalidRule_invalidConnector() throws Exception {
+ public void testBinaryString_invalidRule_invalidConnector_noIndexing() {
String packageName = "com.test.app";
String ruleBits =
START_BIT
@@ -566,7 +569,7 @@ public class RuleBinaryParserTest {
}
@Test
- public void testBinaryString_invalidRule_invalidKey() throws Exception {
+ public void testBinaryString_invalidRule_invalidKey_noIndexing() {
String packageName = "com.test.app";
String ruleBits =
START_BIT
@@ -596,7 +599,7 @@ public class RuleBinaryParserTest {
}
@Test
- public void testBinaryString_invalidRule_invalidSeparator() throws Exception {
+ public void testBinaryString_invalidRule_invalidSeparator_noIndexing() {
String packageName = "com.test.app";
String ruleBits =
START_BIT
@@ -626,7 +629,7 @@ public class RuleBinaryParserTest {
}
@Test
- public void testBinaryString_invalidRule_invalidEndMarker() throws Exception {
+ public void testBinaryString_invalidRule_invalidEndMarker_noIndexing() {
String packageName = "com.test.app";
String ruleBits =
START_BIT
@@ -653,4 +656,65 @@ public class RuleBinaryParserTest {
/* expectedExceptionMessageRegex */ "A rule must end with a '1' bit",
() -> binaryParser.parse(rule.array()));
}
+
+ @Test
+ public void testBinaryStream_multipleRules_indexingIdentifiesParsesIndexRangeCorrectly()
+ throws Exception {
+ String packageName2 = "com.test.2";
+
+ byte[] ruleBytes1 = getBytes(getRulesWithPackageName("com.test.1"));
+ byte[] ruleBytes2 = getBytes(getRulesWithPackageName(packageName2));
+ byte[] ruleBytes3 = getBytes(getRulesWithPackageName("com.test.3"));
+
+ ByteBuffer rule =
+ ByteBuffer.allocate(
+ DEFAULT_FORMAT_VERSION_BYTES.length
+ + ruleBytes1.length
+ + ruleBytes2.length
+ + ruleBytes3.length);
+ rule.put(DEFAULT_FORMAT_VERSION_BYTES);
+ rule.put(ruleBytes1);
+ rule.put(ruleBytes2);
+ rule.put(ruleBytes3);
+ InputStream inputStream = new ByteArrayInputStream(rule.array());
+
+ RuleParser binaryParser = new RuleBinaryParser();
+
+ List<RuleIndexRange> indexRanges = new ArrayList<>();
+ indexRanges.add(
+ new RuleIndexRange(
+ DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes1.length,
+ DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes1.length
+ + ruleBytes2.length));
+ List<Rule> rules = binaryParser.parse(inputStream, indexRanges);
+
+ Rule expectedRule =
+ new Rule(
+ new CompoundFormula(
+ CompoundFormula.NOT,
+ Collections.singletonList(
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.PACKAGE_NAME,
+ packageName2,
+ /* isHashedValue= */ false))),
+ Rule.DENY);
+
+ assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
+ }
+
+ private static String getRulesWithPackageName(String packageName) {
+ return START_BIT
+ + COMPOUND_FORMULA_START_BITS
+ + NOT
+ + ATOMIC_FORMULA_START_BITS
+ + PACKAGE_NAME
+ + EQ
+ + IS_NOT_HASHED
+ + getBits(packageName.length(), VALUE_SIZE_BITS)
+ + getValueBits(packageName)
+ + COMPOUND_FORMULA_END_BITS
+ + DENY
+ + END_BIT;
+
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java
index a14197b17529..6944aee7fcb9 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java
@@ -73,7 +73,7 @@ public class RuleXmlParserTest {
/* isHashedValue= */ false))),
Rule.DENY);
- List<Rule> rules = xmlParser.parse(inputStream);
+ List<Rule> rules = xmlParser.parse(inputStream, Collections.emptyList());
assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
}
@@ -623,7 +623,7 @@ public class RuleXmlParserTest {
assertExpectException(
RuleParseException.class,
/* expectedExceptionMessageRegex */ "Rules must start with RuleList <RL> tag",
- () -> xmlParser.parse(inputStream));
+ () -> xmlParser.parse(inputStream, Collections.emptyList()));
}
private String generateTagWithAttribute(
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
index 821d97acc230..670bd8107bed 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
@@ -67,13 +67,25 @@ import javax.crypto.KeyGenerator;
public class PlatformKeyManagerTest {
private static final String DATABASE_FILE_NAME = "recoverablekeystore.db";
- private static final int USER_AUTHENTICATION_VALIDITY_DURATION_SECONDS = 15;
+ private static final int MIN_GENERATION_ID = 1000000;
+ private static final int PRIMARY_USER_ID_FIXTURE = 0;
private static final int USER_ID_FIXTURE = 42;
private static final long USER_SID = 4200L;
private static final String KEY_ALGORITHM = "AES";
private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
private static final String TESTING_KEYSTORE_KEY_ALIAS = "testing-key-store-key-alias";
+ private static final String ENCRYPTION_KEY_ALIAS_1 =
+ "com.android.server.locksettings.recoverablekeystore/platform/42/1000000/encrypt";
+ private static final String DECRYPTION_KEY_ALIAS_1 =
+ "com.android.server.locksettings.recoverablekeystore/platform/42/1000000/decrypt";
+ private static final String DECRYPTION_KEY_FOR_ALIAS_PRIMARY_USER_1 =
+ "com.android.server.locksettings.recoverablekeystore/platform/0/1000000/decrypt";
+ private static final String ENCRYPTION_KEY_ALIAS_2 =
+ "com.android.server.locksettings.recoverablekeystore/platform/42/1000001/encrypt";
+ private static final String DECRYPTION_KEY_ALIAS_2 =
+ "com.android.server.locksettings.recoverablekeystore/platform/42/1000001/decrypt";
+
@Mock private Context mContext;
@Mock private KeyStoreProxy mKeyStoreProxy;
@Mock private KeyguardManager mKeyguardManager;
@@ -114,7 +126,7 @@ public class PlatformKeyManagerTest {
mPlatformKeyManager.init(USER_ID_FIXTURE);
verify(mKeyStoreProxy).setEntry(
- eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt"),
+ eq(ENCRYPTION_KEY_ALIAS_1),
any(),
any());
}
@@ -156,7 +168,7 @@ public class PlatformKeyManagerTest {
mPlatformKeyManager.init(USER_ID_FIXTURE);
verify(mKeyStoreProxy).setEntry(
- eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"),
+ eq(DECRYPTION_KEY_ALIAS_1),
any(),
any());
}
@@ -187,19 +199,33 @@ public class PlatformKeyManagerTest {
}
@Test
- public void init_createsDecryptKeyWithAuthenticationRequired() throws Exception {
+ public void init_primaryUser_createsDecryptKeyWithUnlockedDeviceRequired() throws Exception {
+ mPlatformKeyManager.init(PRIMARY_USER_ID_FIXTURE);
+
+ assertTrue(getDecryptKeyProtectionForPrimaryUser().isUnlockedDeviceRequired());
+ }
+
+ @Test
+ public void init_primaryUser_createsDecryptKeyWithoutAuthenticationRequired() throws Exception {
+ mPlatformKeyManager.init(PRIMARY_USER_ID_FIXTURE);
+
+ assertFalse(getDecryptKeyProtectionForPrimaryUser().isUserAuthenticationRequired());
+ }
+
+ @Test
+ public void init_secondaryUser_createsDecryptKeyWithoutUnlockedDeviceRequired()
+ throws Exception {
mPlatformKeyManager.init(USER_ID_FIXTURE);
- assertTrue(getDecryptKeyProtection().isUserAuthenticationRequired());
+ assertFalse(getDecryptKeyProtection().isUnlockedDeviceRequired());
}
@Test
- public void init_createsDecryptKeyWithAuthenticationValidFor15Seconds() throws Exception {
+ public void init_secondaryUserUser_createsDecryptKeyWithAuthenticationRequired()
+ throws Exception {
mPlatformKeyManager.init(USER_ID_FIXTURE);
- assertEquals(
- USER_AUTHENTICATION_VALIDITY_DURATION_SECONDS,
- getDecryptKeyProtection().getUserAuthenticationValidityDurationSeconds());
+ assertTrue(getDecryptKeyProtection().isUserAuthenticationRequired());
}
@Test
@@ -219,7 +245,7 @@ public class PlatformKeyManagerTest {
mPlatformKeyManager.init(USER_ID_FIXTURE);
verify(mKeyStoreProxy, never()).setEntry(
- eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"),
+ eq(DECRYPTION_KEY_ALIAS_1),
any(),
any());
}
@@ -231,7 +257,7 @@ public class PlatformKeyManagerTest {
expectThrows(RemoteException.class, () -> mPlatformKeyManager.init(USER_ID_FIXTURE));
verify(mKeyStoreProxy, never()).setEntry(
- eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"),
+ eq(DECRYPTION_KEY_ALIAS_1),
any(),
any());
}
@@ -251,15 +277,15 @@ public class PlatformKeyManagerTest {
public void init_savesGenerationIdToDatabase() throws Exception {
mPlatformKeyManager.init(USER_ID_FIXTURE);
- assertEquals(1,
+ assertEquals(MIN_GENERATION_ID,
mRecoverableKeyStoreDb.getPlatformKeyGenerationId(USER_ID_FIXTURE));
}
@Test
- public void init_setsGenerationIdTo1() throws Exception {
+ public void init_setsGenerationId() throws Exception {
mPlatformKeyManager.init(USER_ID_FIXTURE);
- assertEquals(1, mPlatformKeyManager.getGenerationId(USER_ID_FIXTURE));
+ assertEquals(MIN_GENERATION_ID, mPlatformKeyManager.getGenerationId(USER_ID_FIXTURE));
}
@Test
@@ -268,22 +294,20 @@ public class PlatformKeyManagerTest {
mPlatformKeyManager.init(USER_ID_FIXTURE);
- assertEquals(2, mPlatformKeyManager.getGenerationId(USER_ID_FIXTURE));
+ assertEquals(MIN_GENERATION_ID + 1, mPlatformKeyManager.getGenerationId(USER_ID_FIXTURE));
}
@Test
public void init_doesNotIncrementGenerationIdIfKeyAvailable() throws Exception {
mPlatformKeyManager.init(USER_ID_FIXTURE);
when(mKeyStoreProxy
- .containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/1/decrypt")).thenReturn(true);
+ .containsAlias(DECRYPTION_KEY_ALIAS_1)).thenReturn(true);
when(mKeyStoreProxy
- .containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/1/encrypt")).thenReturn(true);
+ .containsAlias(ENCRYPTION_KEY_ALIAS_1)).thenReturn(true);
mPlatformKeyManager.init(USER_ID_FIXTURE);
- assertEquals(1, mPlatformKeyManager.getGenerationId(USER_ID_FIXTURE));
+ assertEquals(MIN_GENERATION_ID, mPlatformKeyManager.getGenerationId(USER_ID_FIXTURE));
}
@Test
@@ -294,226 +318,194 @@ public class PlatformKeyManagerTest {
@Test
public void getDecryptKey_getsDecryptKeyWithCorrectAlias() throws Exception {
when(mKeyStoreProxy
- .containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/1/decrypt")).thenReturn(true);
+ .containsAlias(DECRYPTION_KEY_ALIAS_1)).thenReturn(true);
when(mKeyStoreProxy
- .containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/1/encrypt")).thenReturn(true);
+ .containsAlias(ENCRYPTION_KEY_ALIAS_1)).thenReturn(true);
when(mKeyStoreProxy.getKey(
- eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"),
+ eq(DECRYPTION_KEY_ALIAS_1),
any())).thenReturn(generateAndroidKeyStoreKey());
mPlatformKeyManager.getDecryptKey(USER_ID_FIXTURE);
verify(mKeyStoreProxy).getKey(
- eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"),
+ eq(DECRYPTION_KEY_ALIAS_1),
any());
}
@Test
public void getDecryptKey_generatesNewKeyIfOldDecryptKeyWasRemoved() throws Exception {
when(mKeyStoreProxy
- .containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/1/encrypt")).thenReturn(true);
+ .containsAlias(ENCRYPTION_KEY_ALIAS_1)).thenReturn(true);
when(mKeyStoreProxy
- .containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/1/decrypt")).thenReturn(false); // was removed
+ .containsAlias(DECRYPTION_KEY_ALIAS_1)).thenReturn(false); // was removed
when(mKeyStoreProxy
- .containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/2/encrypt")).thenReturn(true); // new version is available
+ .containsAlias(ENCRYPTION_KEY_ALIAS_2)).thenReturn(true); // new version
when(mKeyStoreProxy
- .containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/2/decrypt")).thenReturn(true);
+ .containsAlias(DECRYPTION_KEY_ALIAS_2)).thenReturn(true);
when(mKeyStoreProxy.getKey(
- eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/decrypt"),
+ eq(DECRYPTION_KEY_ALIAS_2),
any())).thenReturn(generateAndroidKeyStoreKey());
mPlatformKeyManager.getDecryptKey(USER_ID_FIXTURE);
verify(mKeyStoreProxy).containsAlias(
- eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"));
+ eq(DECRYPTION_KEY_ALIAS_1));
// Attempt to get regenerated key.
verify(mKeyStoreProxy).getKey(
- eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/decrypt"),
+ eq(DECRYPTION_KEY_ALIAS_2),
any());
}
@Test
public void getDecryptKey_generatesNewKeyIfOldEncryptKeyWasRemoved() throws Exception {
when(mKeyStoreProxy
- .containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/1/encrypt")).thenReturn(false); // was removed
+ .containsAlias(ENCRYPTION_KEY_ALIAS_1)).thenReturn(false); // was removed
when(mKeyStoreProxy
- .containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/1/decrypt")).thenReturn(true);
+ .containsAlias(DECRYPTION_KEY_ALIAS_1)).thenReturn(true);
when(mKeyStoreProxy
- .containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/2/encrypt")).thenReturn(true);
+ .containsAlias(ENCRYPTION_KEY_ALIAS_2)).thenReturn(true);
when(mKeyStoreProxy
- .containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/2/decrypt")).thenReturn(true);
+ .containsAlias(DECRYPTION_KEY_ALIAS_2)).thenReturn(true);
mPlatformKeyManager.getDecryptKey(USER_ID_FIXTURE);
verify(mKeyStoreProxy).containsAlias(
- eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt"));
+ eq(ENCRYPTION_KEY_ALIAS_1));
// Attempt to get regenerated key.
verify(mKeyStoreProxy).getKey(
- eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/decrypt"),
+ eq(DECRYPTION_KEY_ALIAS_2),
any());
}
@Test
public void getEncryptKey_generatesNewKeyIfOldOneIsInvalid() throws Exception {
doThrow(new UnrecoverableKeyException()).when(mKeyStoreProxy).getKey(
- eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt"),
+ eq(ENCRYPTION_KEY_ALIAS_1),
any());
when(mKeyStoreProxy
- .containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/1/encrypt")).thenReturn(true);
+ .containsAlias(ENCRYPTION_KEY_ALIAS_1)).thenReturn(true);
when(mKeyStoreProxy
- .containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/1/decrypt")).thenReturn(true);
+ .containsAlias(DECRYPTION_KEY_ALIAS_1)).thenReturn(true);
when(mKeyStoreProxy
- .containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/2/encrypt")).thenReturn(true);
+ .containsAlias(ENCRYPTION_KEY_ALIAS_2)).thenReturn(true);
when(mKeyStoreProxy
- .containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/2/decrypt")).thenReturn(true);
+ .containsAlias(DECRYPTION_KEY_ALIAS_2)).thenReturn(true);
mPlatformKeyManager.getEncryptKey(USER_ID_FIXTURE);
verify(mKeyStoreProxy).getKey(
- eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt"),
+ eq(ENCRYPTION_KEY_ALIAS_1),
any());
// Attempt to get regenerated key.
verify(mKeyStoreProxy).getKey(
- eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/encrypt"),
+ eq(ENCRYPTION_KEY_ALIAS_2),
any());
}
@Test
public void getDecryptKey_generatesNewKeyIfOldOneIsInvalid() throws Exception {
doThrow(new UnrecoverableKeyException()).when(mKeyStoreProxy).getKey(
- eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"),
+ eq(DECRYPTION_KEY_ALIAS_1),
any());
when(mKeyStoreProxy.getKey(
- eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/decrypt"),
+ eq(DECRYPTION_KEY_ALIAS_2),
any())).thenReturn(generateAndroidKeyStoreKey());
when(mKeyStoreProxy
- .containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/1/encrypt")).thenReturn(true);
+ .containsAlias(ENCRYPTION_KEY_ALIAS_1)).thenReturn(true);
when(mKeyStoreProxy
- .containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/1/decrypt")).thenReturn(true);
+ .containsAlias(DECRYPTION_KEY_ALIAS_1)).thenReturn(true);
when(mKeyStoreProxy
- .containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/2/encrypt")).thenReturn(true);
+ .containsAlias(ENCRYPTION_KEY_ALIAS_2)).thenReturn(true);
when(mKeyStoreProxy
- .containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/2/decrypt")).thenReturn(true);
+ .containsAlias(DECRYPTION_KEY_ALIAS_2)).thenReturn(true);
mPlatformKeyManager.getDecryptKey(USER_ID_FIXTURE);
verify(mKeyStoreProxy).containsAlias(
- eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"));
+ eq(DECRYPTION_KEY_ALIAS_1));
// Attempt to get regenerated key.
verify(mKeyStoreProxy).getKey(
- eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/decrypt"),
+ eq(DECRYPTION_KEY_ALIAS_2),
any());
}
@Test
public void getEncryptKey_generatesNewKeyIfDecryptKeyIsUnrecoverable() throws Exception {
doThrow(new UnrecoverableKeyException()).when(mKeyStoreProxy).getKey(
- eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"),
+ eq(DECRYPTION_KEY_ALIAS_1),
any());
when(mKeyStoreProxy
- .containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/1/encrypt")).thenReturn(true);
+ .containsAlias(ENCRYPTION_KEY_ALIAS_1)).thenReturn(true);
when(mKeyStoreProxy
- .containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/1/decrypt")).thenReturn(false); // was removed
+ .containsAlias(DECRYPTION_KEY_ALIAS_1)).thenReturn(false); // was removed
when(mKeyStoreProxy
- .containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/2/encrypt")).thenReturn(true); // new version is available
+ .containsAlias(ENCRYPTION_KEY_ALIAS_2)).thenReturn(true); // new version
when(mKeyStoreProxy
- .containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/2/decrypt")).thenReturn(true);
+ .containsAlias(DECRYPTION_KEY_ALIAS_2)).thenReturn(true);
mPlatformKeyManager.getEncryptKey(USER_ID_FIXTURE);
// Attempt to get regenerated key.
verify(mKeyStoreProxy).getKey(
- eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/encrypt"),
+ eq(ENCRYPTION_KEY_ALIAS_2),
any());
}
@Test
public void getEncryptKey_generatesNewKeyIfOldDecryptKeyWasRemoved() throws Exception {
when(mKeyStoreProxy
- .containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/1/encrypt")).thenReturn(true);
+ .containsAlias(ENCRYPTION_KEY_ALIAS_1)).thenReturn(true);
when(mKeyStoreProxy
- .containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/1/decrypt")).thenReturn(false); // was removed
+ .containsAlias(DECRYPTION_KEY_ALIAS_1)).thenReturn(false); // was removed
when(mKeyStoreProxy
- .containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/2/encrypt")).thenReturn(true); // new version is available
+ .containsAlias(ENCRYPTION_KEY_ALIAS_2)).thenReturn(true); // new version
when(mKeyStoreProxy
- .containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/2/decrypt")).thenReturn(true);
+ .containsAlias(DECRYPTION_KEY_ALIAS_2)).thenReturn(true);
mPlatformKeyManager.getEncryptKey(USER_ID_FIXTURE);
verify(mKeyStoreProxy).containsAlias(
- eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt"));
+ eq(ENCRYPTION_KEY_ALIAS_1));
// Attempt to get regenerated key.
verify(mKeyStoreProxy).getKey(
- eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/encrypt"),
+ eq(ENCRYPTION_KEY_ALIAS_2),
any());
}
@Test
public void getEncryptKey_generatesNewKeyIfOldEncryptKeyWasRemoved() throws Exception {
when(mKeyStoreProxy
- .containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/1/encrypt")).thenReturn(false); // was removed
+ .containsAlias(ENCRYPTION_KEY_ALIAS_1)).thenReturn(false); // was removed
when(mKeyStoreProxy
- .containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/1/decrypt")).thenReturn(true);
+ .containsAlias(DECRYPTION_KEY_ALIAS_1)).thenReturn(true);
when(mKeyStoreProxy
- .containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/2/encrypt")).thenReturn(true);
+ .containsAlias(ENCRYPTION_KEY_ALIAS_2)).thenReturn(true);
when(mKeyStoreProxy
- .containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/2/decrypt")).thenReturn(true);
+ .containsAlias(DECRYPTION_KEY_ALIAS_2)).thenReturn(true);
mPlatformKeyManager.getEncryptKey(USER_ID_FIXTURE);
verify(mKeyStoreProxy).containsAlias(
- eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt"));
+ eq(ENCRYPTION_KEY_ALIAS_1));
// Attempt to get regenerated key.
verify(mKeyStoreProxy).getKey(
- eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/encrypt"),
+ eq(ENCRYPTION_KEY_ALIAS_2),
any());
}
@Test
public void getEncryptKey_getsEncryptKeyWithCorrectAlias() throws Exception {
when(mKeyStoreProxy
- .containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/1/encrypt")).thenReturn(true);
+ .containsAlias(ENCRYPTION_KEY_ALIAS_1)).thenReturn(true);
when(mKeyStoreProxy
- .containsAlias("com.android.server.locksettings.recoverablekeystore/"
- + "platform/42/1/decrypt")).thenReturn(true);
+ .containsAlias(DECRYPTION_KEY_ALIAS_1)).thenReturn(true);
mPlatformKeyManager.getEncryptKey(USER_ID_FIXTURE);
verify(mKeyStoreProxy).getKey(
- eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt"),
+ eq(ENCRYPTION_KEY_ALIAS_1),
any());
}
@@ -523,7 +515,7 @@ public class PlatformKeyManagerTest {
mPlatformKeyManager.regenerate(USER_ID_FIXTURE);
- assertEquals(2, mPlatformKeyManager.getGenerationId(USER_ID_FIXTURE));
+ assertEquals(MIN_GENERATION_ID + 1, mPlatformKeyManager.getGenerationId(USER_ID_FIXTURE));
}
@Test
@@ -533,17 +525,17 @@ public class PlatformKeyManagerTest {
mPlatformKeyManager.regenerate(USER_ID_FIXTURE);
verify(mKeyStoreProxy).deleteEntry(
- eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt"));
+ eq(ENCRYPTION_KEY_ALIAS_1));
verify(mKeyStoreProxy).deleteEntry(
- eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"));
+ eq(DECRYPTION_KEY_ALIAS_1));
mPlatformKeyManager.regenerate(USER_ID_FIXTURE);
// Removes second generation keys.
verify(mKeyStoreProxy).deleteEntry(
- eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/encrypt"));
+ eq(ENCRYPTION_KEY_ALIAS_2));
verify(mKeyStoreProxy).deleteEntry(
- eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/decrypt"));
+ eq(DECRYPTION_KEY_ALIAS_2));
}
@Test
@@ -553,7 +545,7 @@ public class PlatformKeyManagerTest {
mPlatformKeyManager.regenerate(USER_ID_FIXTURE);
verify(mKeyStoreProxy).setEntry(
- eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/encrypt"),
+ eq(ENCRYPTION_KEY_ALIAS_2),
any(),
any());
}
@@ -565,14 +557,14 @@ public class PlatformKeyManagerTest {
mPlatformKeyManager.regenerate(USER_ID_FIXTURE);
verify(mKeyStoreProxy).setEntry(
- eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/decrypt"),
+ eq(DECRYPTION_KEY_ALIAS_2),
any(),
any());
}
private KeyProtection getEncryptKeyProtection() throws Exception {
verify(mKeyStoreProxy).setEntry(
- eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt"),
+ eq(ENCRYPTION_KEY_ALIAS_1),
any(),
mProtectionParameterCaptor.capture());
return (KeyProtection) mProtectionParameterCaptor.getValue();
@@ -580,7 +572,15 @@ public class PlatformKeyManagerTest {
private KeyProtection getDecryptKeyProtection() throws Exception {
verify(mKeyStoreProxy).setEntry(
- eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"),
+ eq(DECRYPTION_KEY_ALIAS_1),
+ any(),
+ mProtectionParameterCaptor.capture());
+ return (KeyProtection) mProtectionParameterCaptor.getValue();
+ }
+
+ private KeyProtection getDecryptKeyProtectionForPrimaryUser() throws Exception {
+ verify(mKeyStoreProxy).setEntry(
+ eq(DECRYPTION_KEY_FOR_ALIAS_PRIMARY_USER_1),
any(),
mProtectionParameterCaptor.capture());
return (KeyProtection) mProtectionParameterCaptor.getValue();
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index 68900175cc8f..ac7447006444 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -29,6 +29,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeast;
@@ -84,7 +85,7 @@ import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Map;
import java.util.Random;
-import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ScheduledExecutorService;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
@@ -157,7 +158,7 @@ public class RecoverableKeyStoreManagerTest {
@Mock private PlatformKeyManager mPlatformKeyManager;
@Mock private ApplicationKeyStorage mApplicationKeyStorage;
@Mock private CleanupManager mCleanupManager;
- @Mock private ExecutorService mExecutorService;
+ @Mock private ScheduledExecutorService mExecutorService;
@Spy private TestOnlyInsecureCertificateHelper mTestOnlyInsecureCertificateHelper;
private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
@@ -1253,7 +1254,7 @@ public class RecoverableKeyStoreManagerTest {
mRecoverableKeyStoreManager.lockScreenSecretAvailable(
LockPatternUtils.CREDENTIAL_TYPE_PATTERN, "password".getBytes(), 11);
- verify(mExecutorService).execute(any());
+ verify(mExecutorService).schedule(any(Runnable.class), anyLong(), any());
}
@Test
@@ -1263,7 +1264,7 @@ public class RecoverableKeyStoreManagerTest {
"password".getBytes(),
11);
- verify(mExecutorService).execute(any());
+ verify(mExecutorService).schedule(any(Runnable.class), anyLong(), any());
}
private static byte[] encryptedApplicationKey(
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
index c478ec472e61..15327b6e5463 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
@@ -156,8 +156,7 @@ public class PackageInstallerSessionTest {
if (isMultiPackage) {
params.isMultiPackage = true;
}
- InstallSource installSource = InstallSource.create("testInstaller", null, "testInstaller",
- false);
+ InstallSource installSource = InstallSource.create("testInstaller", null, "testInstaller");
return new PackageInstallerSession(
/* callback */ null,
/* context */null,
diff --git a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
index a83d94001cf8..f871203728c0 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
@@ -98,7 +98,7 @@ public class AppDataRollbackHelperTest {
final int[] installedUsers) {
return new PackageRollbackInfo(
new VersionedPackage(packageName, 2), new VersionedPackage(packageName, 1),
- new IntArray(), new ArrayList<>(), false, IntArray.wrap(installedUsers),
+ new IntArray(), new ArrayList<>(), false, false, IntArray.wrap(installedUsers),
new SparseLongArray());
}
diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
index 757a884f8ded..d0d2edc59861 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
@@ -87,13 +87,15 @@ public class RollbackStoreTest {
+ "[{'versionRolledBackFrom':{'packageName':'blah','longVersionCode':55},"
+ "'versionRolledBackTo':{'packageName':'blah1','longVersionCode':50},'pendingBackups':"
+ "[59,1245,124544],'pendingRestores':[{'userId':498,'appId':32322,'seInfo':'wombles'},"
- + "{'userId':-895,'appId':1,'seInfo':'pingu'}],'isApex':false,'installedUsers':"
+ + "{'userId':-895,'appId':1,'seInfo':'pingu'}],'isApex':false,'isApkInApex':false,"
+ + "'installedUsers':"
+ "[498468432,1111,98464],'ceSnapshotInodes':[{'userId':1,'ceSnapshotInode':-6},"
+ "{'userId':2222,'ceSnapshotInode':81641654445},{'userId':546546,"
+ "'ceSnapshotInode':345689375}]},{'versionRolledBackFrom':{'packageName':'chips',"
+ "'longVersionCode':28},'versionRolledBackTo':{'packageName':'com.chips.test',"
+ "'longVersionCode':48},'pendingBackups':[5],'pendingRestores':[{'userId':18,"
- + "'appId':-12,'seInfo':''}],'isApex':false,'installedUsers':[55,79],"
+ + "'appId':-12,'seInfo':''}],'isApex':false,'isApkInApex':false,"
+ + "'installedUsers':[55,79],"
+ "'ceSnapshotInodes':[]}],'isStaged':false,'causePackages':[{'packageName':'hello',"
+ "'longVersionCode':23},{'packageName':'something','longVersionCode':999}],"
+ "'committedSessionId':45654465},'timestamp':'2019-10-01T12:29:08.855Z',"
@@ -155,7 +157,7 @@ public class RollbackStoreTest {
PackageRollbackInfo pkgInfo1 =
new PackageRollbackInfo(new VersionedPackage("com.made.up", 18),
new VersionedPackage("com.something.else", 5), new IntArray(),
- new ArrayList<>(), false, new IntArray(), new SparseLongArray());
+ new ArrayList<>(), false, false, new IntArray(), new SparseLongArray());
pkgInfo1.getPendingBackups().add(8);
pkgInfo1.getPendingBackups().add(888);
pkgInfo1.getPendingBackups().add(88885);
@@ -175,7 +177,7 @@ public class RollbackStoreTest {
PackageRollbackInfo pkgInfo2 = new PackageRollbackInfo(
new VersionedPackage("another.package", 2),
new VersionedPackage("com.test.ing", 48888), new IntArray(), new ArrayList<>(),
- false, new IntArray(), new SparseLongArray());
+ false, false, new IntArray(), new SparseLongArray());
pkgInfo2.getPendingBackups().add(57);
pkgInfo2.getPendingRestores().add(
@@ -205,7 +207,7 @@ public class RollbackStoreTest {
PackageRollbackInfo pkgInfo1 = new PackageRollbackInfo(new VersionedPackage("blah", 55),
new VersionedPackage("blah1", 50), new IntArray(), new ArrayList<>(),
- false, new IntArray(), new SparseLongArray());
+ false, false, new IntArray(), new SparseLongArray());
pkgInfo1.getPendingBackups().add(59);
pkgInfo1.getPendingBackups().add(1245);
pkgInfo1.getPendingBackups().add(124544);
@@ -224,7 +226,7 @@ public class RollbackStoreTest {
PackageRollbackInfo pkgInfo2 = new PackageRollbackInfo(new VersionedPackage("chips", 28),
new VersionedPackage("com.chips.test", 48), new IntArray(), new ArrayList<>(),
- false, new IntArray(), new SparseLongArray());
+ false, false, new IntArray(), new SparseLongArray());
pkgInfo2.getPendingBackups().add(5);
pkgInfo2.getPendingRestores().add(
diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
index e368d634b968..164c88382828 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
@@ -295,7 +295,8 @@ public class RollbackUnitTest {
String packageName, long fromVersion, long toVersion, boolean isApex) {
return new PackageRollbackInfo(new VersionedPackage(packageName, fromVersion),
new VersionedPackage(packageName, toVersion),
- new IntArray(), new ArrayList<>(), isApex, new IntArray(), new SparseLongArray());
+ new IntArray(), new ArrayList<>(), isApex, false, new IntArray(),
+ new SparseLongArray());
}
private static class PackageRollbackInfoForPackage implements
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
index f8915c06b555..0f75816082fd 100644
--- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
@@ -155,7 +155,16 @@ public class SoundTriggerMiddlewareImplTest {
return properties;
}
- private static void validateDefaultProperties(SoundTriggerModuleProperties properties,
+ private static android.hardware.soundtrigger.V2_3.Properties createDefaultProperties_2_3(
+ boolean supportConcurrentCapture) {
+ android.hardware.soundtrigger.V2_3.Properties properties =
+ new android.hardware.soundtrigger.V2_3.Properties();
+ properties.base = createDefaultProperties(supportConcurrentCapture);
+ properties.supportedModelArch = "supportedModelArch";
+ return properties;
+ }
+
+ private void validateDefaultProperties(SoundTriggerModuleProperties properties,
boolean supportConcurrentCapture) {
assertEquals("implementor", properties.implementor);
assertEquals("description", properties.description);
@@ -173,8 +182,20 @@ public class SoundTriggerMiddlewareImplTest {
assertEquals(supportConcurrentCapture, properties.concurrentCapture);
assertTrue(properties.triggerInEvent);
assertEquals(432, properties.powerConsumptionMw);
+
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
+ assertEquals("supportedModelArch", properties.supportedModelArch);
+ } else {
+ assertEquals("", properties.supportedModelArch);
+ }
}
+ private void verifyNotGetProperties() throws RemoteException {
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
+ verify((android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver,
+ never()).getProperties(any());
+ }
+ }
private static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_0(
int hwHandle,
@@ -290,6 +311,22 @@ public class SoundTriggerMiddlewareImplTest {
properties);
return null;
}).when(mHalDriver).getProperties(any());
+
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
+ android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
+ (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
+ doAnswer(invocation -> {
+ android.hardware.soundtrigger.V2_3.Properties properties =
+ createDefaultProperties_2_3(
+ supportConcurrentCapture);
+ ((android.hardware.soundtrigger.V2_3.ISoundTriggerHw.getProperties_2_3Callback)
+ invocation.getArgument(
+ 0)).onValues(0,
+ properties);
+ return null;
+ }).when(driver).getProperties_2_3(any());
+ }
+
mService = new SoundTriggerMiddlewareImpl(mHalDriver, mAudioSessionProvider);
}
@@ -716,6 +753,7 @@ public class SoundTriggerMiddlewareImplTest {
SoundTriggerModuleProperties properties = allDescriptors[0].properties;
validateDefaultProperties(properties, true);
+ verifyNotGetProperties();
}
@Test
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 12ba219d0365..1dd7e64690c7 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -365,8 +365,9 @@ public class AppStandbyControllerTests {
public void testSetAppStandbyBucket() throws Exception {
// For a known package, standby bucket should be set properly
reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
+ mInjector.mElapsedRealtime = HOUR_MS;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
- REASON_MAIN_TIMEOUT, HOUR_MS);
+ REASON_MAIN_TIMEOUT);
assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
// For an unknown package, standby bucket should not be set, hence NEVER is returned
@@ -374,7 +375,7 @@ public class AppStandbyControllerTests {
mController.clearAppIdleForPackage(PACKAGE_UNKNOWN, USER_ID);
isPackageInstalled = false; // Mock package is not installed
mController.setAppStandbyBucket(PACKAGE_UNKNOWN, USER_ID, STANDBY_BUCKET_ACTIVE,
- REASON_MAIN_TIMEOUT, HOUR_MS);
+ REASON_MAIN_TIMEOUT);
isPackageInstalled = true; // Reset mocked variable for other tests
assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController, PACKAGE_UNKNOWN));
}
@@ -468,12 +469,13 @@ public class AppStandbyControllerTests {
@Test
public void testPredictionTimedout() throws Exception {
// Set it to timeout or usage, so that prediction can override it
+ mInjector.mElapsedRealtime = HOUR_MS;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
- REASON_MAIN_TIMEOUT, HOUR_MS);
+ REASON_MAIN_TIMEOUT);
assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
- REASON_MAIN_PREDICTED, HOUR_MS);
+ REASON_MAIN_PREDICTED);
assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
// Fast forward 12 hours
@@ -497,29 +499,31 @@ public class AppStandbyControllerTests {
@Test
public void testOverrides() throws Exception {
// Can force to NEVER
+ mInjector.mElapsedRealtime = HOUR_MS;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
- REASON_MAIN_FORCED, 1 * HOUR_MS);
+ REASON_MAIN_FORCED);
assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController, PACKAGE_1));
// Prediction can't override FORCED reason
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
- REASON_MAIN_FORCED, 1 * HOUR_MS);
+ REASON_MAIN_FORCED);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
- REASON_MAIN_PREDICTED, 1 * HOUR_MS);
+ REASON_MAIN_PREDICTED);
assertEquals(STANDBY_BUCKET_FREQUENT, getStandbyBucket(mController, PACKAGE_1));
// Prediction can't override NEVER
+ mInjector.mElapsedRealtime = 2 * HOUR_MS;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
- REASON_MAIN_DEFAULT, 2 * HOUR_MS);
+ REASON_MAIN_DEFAULT);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
- REASON_MAIN_PREDICTED, 2 * HOUR_MS);
+ REASON_MAIN_PREDICTED);
assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController, PACKAGE_1));
// Prediction can't set to NEVER
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
- REASON_MAIN_USAGE, 2 * HOUR_MS);
+ REASON_MAIN_USAGE);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
- REASON_MAIN_PREDICTED, 2 * HOUR_MS);
+ REASON_MAIN_PREDICTED);
assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
}
@@ -530,7 +534,7 @@ public class AppStandbyControllerTests {
mInjector.mElapsedRealtime = 2000;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
- REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
+ REASON_MAIN_PREDICTED);
assertBucket(STANDBY_BUCKET_ACTIVE);
// bucketing works after timeout
@@ -554,15 +558,17 @@ public class AppStandbyControllerTests {
assertBucket(STANDBY_BUCKET_ACTIVE);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
- REASON_MAIN_PREDICTED, 1000);
+ REASON_MAIN_PREDICTED);
assertBucket(STANDBY_BUCKET_ACTIVE);
+ mInjector.mElapsedRealtime = 2000 + mController.mStrongUsageTimeoutMillis;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
- REASON_MAIN_PREDICTED, 2000 + mController.mStrongUsageTimeoutMillis);
+ REASON_MAIN_PREDICTED);
assertBucket(STANDBY_BUCKET_WORKING_SET);
+ mInjector.mElapsedRealtime = 2000 + mController.mNotificationSeenTimeoutMillis;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
- REASON_MAIN_PREDICTED, 2000 + mController.mNotificationSeenTimeoutMillis);
+ REASON_MAIN_PREDICTED);
assertBucket(STANDBY_BUCKET_FREQUENT);
}
@@ -582,18 +588,18 @@ public class AppStandbyControllerTests {
// Still in ACTIVE after first USER_INTERACTION times out
mInjector.mElapsedRealtime = mController.mStrongUsageTimeoutMillis + 1000;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
- REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
+ REASON_MAIN_PREDICTED);
assertBucket(STANDBY_BUCKET_ACTIVE);
// Both timed out, so NOTIFICATION_SEEN timeout should be effective
mInjector.mElapsedRealtime = mController.mStrongUsageTimeoutMillis * 2 + 2000;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
- REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
+ REASON_MAIN_PREDICTED);
assertBucket(STANDBY_BUCKET_WORKING_SET);
mInjector.mElapsedRealtime = mController.mNotificationSeenTimeoutMillis + 2000;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
- REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
+ REASON_MAIN_PREDICTED);
assertBucket(STANDBY_BUCKET_RARE);
}
@@ -625,7 +631,7 @@ public class AppStandbyControllerTests {
mInjector.mElapsedRealtime = 1 * RARE_THRESHOLD + 100;
// Make sure app is in NEVER bucket
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
- REASON_MAIN_FORCED, mInjector.mElapsedRealtime);
+ REASON_MAIN_FORCED);
mController.checkIdleStates(USER_ID);
assertBucket(STANDBY_BUCKET_NEVER);
@@ -670,7 +676,7 @@ public class AppStandbyControllerTests {
// Predict to ACTIVE
mInjector.mElapsedRealtime += 1000;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
- REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
+ REASON_MAIN_PREDICTED);
assertBucket(STANDBY_BUCKET_ACTIVE);
// CheckIdleStates should not change the prediction
@@ -687,7 +693,7 @@ public class AppStandbyControllerTests {
// Predict to FREQUENT
mInjector.mElapsedRealtime = RARE_THRESHOLD;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
- REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
+ REASON_MAIN_PREDICTED);
assertBucket(STANDBY_BUCKET_FREQUENT);
// Add a short timeout event
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
index eb45960bc82f..bd7d9ecf84d1 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
@@ -20,7 +20,6 @@ import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
import static org.mockito.Matchers.any;
@@ -65,11 +64,10 @@ public class NotificationChannelExtractorTest extends UiServiceTestCase {
NotificationChannel updatedChannel =
new NotificationChannel("a", "", IMPORTANCE_HIGH);
- when(mConfig.getNotificationChannel(any(), anyInt(), eq("a"), eq(false)))
+ when(mConfig.getNotificationChannel(any(), anyInt(), eq("a"), eq(null), eq(false)))
.thenReturn(updatedChannel);
assertNull(extractor.process(r));
assertEquals(updatedChannel, r.getChannel());
}
-
}
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 172df99921a0..62f52306ea49 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -112,6 +112,7 @@ import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.Parcel;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -133,6 +134,7 @@ import android.testing.TestableLooper.RunWithLooper;
import android.testing.TestablePermissions;
import android.testing.TestableResources;
import android.text.Html;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
@@ -475,7 +477,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
when(mPreferencesHelper.bubblesEnabled()).thenReturn(globalEnabled);
when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(pkgEnabled);
when(mPreferencesHelper.getNotificationChannel(
- anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
+ anyString(), anyInt(), anyString(), eq(null), anyBoolean())).thenReturn(
mTestNotificationChannel);
when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(
mTestNotificationChannel.getImportance());
@@ -1762,7 +1764,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mBinderService.enqueueNotificationWithTag(PKG, PKG, "testTvExtenderChannelOverride_onTv", 0,
generateNotificationRecord(null, tv).getNotification(), 0);
verify(mPreferencesHelper, times(1)).getNotificationChannel(
- anyString(), anyInt(), eq("foo"), anyBoolean());
+ anyString(), anyInt(), eq("foo"), eq(null), anyBoolean());
}
@Test
@@ -1777,7 +1779,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mBinderService.enqueueNotificationWithTag(PKG, PKG, "testTvExtenderChannelOverride_notOnTv",
0, generateNotificationRecord(null, tv).getNotification(), 0);
verify(mPreferencesHelper, times(1)).getNotificationChannel(
- anyString(), anyInt(), eq(mTestNotificationChannel.getId()), anyBoolean());
+ anyString(), anyInt(), eq(mTestNotificationChannel.getId()), eq(null),
+ anyBoolean());
}
@Test
@@ -3210,9 +3213,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
final NotificationVisibility nv = NotificationVisibility.obtain(r.getKey(), 1, 2, true);
mService.mNotificationDelegate.onNotificationVisibilityChanged(
new NotificationVisibility[] {nv}, new NotificationVisibility[]{});
+ verify(mAssistants).notifyAssistantVisibilityChangedLocked(eq(r.sbn), eq(true));
assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasSeen());
mService.mNotificationDelegate.onNotificationVisibilityChanged(
new NotificationVisibility[] {}, new NotificationVisibility[]{nv});
+ verify(mAssistants).notifyAssistantVisibilityChangedLocked(eq(r.sbn), eq(false));
assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasSeen());
}
@@ -4463,6 +4468,16 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void testOnPanelRevealedAndHidden() {
+ int items = 5;
+ mService.mNotificationDelegate.onPanelRevealed(false, items);
+ verify(mAssistants, times(1)).onPanelRevealed(eq(items));
+
+ mService.mNotificationDelegate.onPanelHidden();
+ verify(mAssistants, times(1)).onPanelHidden();
+ }
+
+ @Test
public void testOnNotificationSmartReplySent() {
final int replyIndex = 2;
final String reply = "Hello";
@@ -5044,10 +5059,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
nb.build(), new UserHandle(mUid), null, 0);
// Make sure it has foreground service
sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
- NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
- nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ sbn.getId(), sbn.getNotification(), sbn.getUserId());
waitForIdle();
// yes phone call, yes person, yes foreground service, but not allowed, no bubble
@@ -5802,4 +5816,79 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
verify(mHistoryManager, times(1)).addNotification(any());
}
+
+ @Test
+ public void createConversationNotificationChannel() throws Exception {
+ NotificationChannel original = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
+ original.setAllowBubbles(!original.canBubble());
+ original.setShowBadge(!original.canShowBadge());
+
+ Parcel parcel = Parcel.obtain();
+ original.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ NotificationChannel orig = NotificationChannel.CREATOR.createFromParcel(parcel);
+ assertEquals(original, orig);
+ assertFalse(TextUtils.isEmpty(orig.getName()));
+
+ mBinderService.createNotificationChannels(PKG, new ParceledListSlice(Arrays.asList(
+ orig)));
+
+ mBinderService.createConversationNotificationChannelForPackage(PKG, mUid, orig, "friend");
+
+ NotificationChannel friendChannel = mBinderService.getConversationNotificationChannel(
+ PKG, 0, PKG, original.getId(), "friend");
+
+ assertEquals(original.getName(), friendChannel.getName());
+ assertEquals(original.getId(), friendChannel.getParentChannelId());
+ assertEquals("friend", friendChannel.getConversationId());
+ assertEquals(null, original.getConversationId());
+ assertEquals(original.canShowBadge(), friendChannel.canShowBadge());
+ assertEquals(original.canBubble(), friendChannel.canBubble());
+ assertFalse(original.getId().equals(friendChannel.getId()));
+ assertNotNull(friendChannel.getId());
+ }
+
+ @Test
+ public void deleteConversationNotificationChannels() throws Exception {
+ NotificationChannel messagesParent =
+ new NotificationChannel("messages", "messages", IMPORTANCE_HIGH);
+ Parcel msgParcel = Parcel.obtain();
+ messagesParent.writeToParcel(msgParcel, 0);
+ msgParcel.setDataPosition(0);
+
+ NotificationChannel callsParent =
+ new NotificationChannel("calls", "calls", IMPORTANCE_HIGH);
+ Parcel callParcel = Parcel.obtain();
+ callsParent.writeToParcel(callParcel, 0);
+ callParcel.setDataPosition(0);
+
+ mBinderService.createNotificationChannels(PKG, new ParceledListSlice(Arrays.asList(
+ messagesParent, callsParent)));
+
+ String conversationId = "friend";
+
+ mBinderService.createConversationNotificationChannelForPackage(
+ PKG, mUid, NotificationChannel.CREATOR.createFromParcel(msgParcel), conversationId);
+ mBinderService.createConversationNotificationChannelForPackage(
+ PKG, mUid, NotificationChannel.CREATOR.createFromParcel(callParcel),
+ conversationId);
+
+ NotificationChannel messagesChild = mBinderService.getConversationNotificationChannel(
+ PKG, 0, PKG, messagesParent.getId(), conversationId);
+ NotificationChannel callsChild = mBinderService.getConversationNotificationChannel(
+ PKG, 0, PKG, callsParent.getId(), conversationId);
+
+ assertEquals(messagesParent.getId(), messagesChild.getParentChannelId());
+ assertEquals(conversationId, messagesChild.getConversationId());
+
+ assertEquals(callsParent.getId(), callsChild.getParentChannelId());
+ assertEquals(conversationId, callsChild.getConversationId());
+
+ mBinderService.deleteConversationNotificationChannels(PKG, mUid, conversationId);
+
+ assertNull(mBinderService.getConversationNotificationChannel(
+ PKG, 0, PKG, messagesParent.getId(), conversationId));
+ assertNull(mBinderService.getConversationNotificationChannel(
+ PKG, 0, PKG, callsParent.getId(), conversationId));
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 8961796ed617..7173e0cb625c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -15,6 +15,7 @@
*/
package com.android.server.notification;
+import static android.app.NotificationChannel.CONVERSATION_CHANNEL_ID_FORMAT;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
@@ -224,6 +225,26 @@ public class PreferencesHelperTest extends UiServiceTestCase {
assertEquals(expected.getGroup(), actual.getGroup());
assertEquals(expected.getAudioAttributes(), actual.getAudioAttributes());
assertEquals(expected.getLightColor(), actual.getLightColor());
+ assertEquals(expected.getParentChannelId(), actual.getParentChannelId());
+ assertEquals(expected.getConversationId(), actual.getConversationId());
+ }
+
+ private void compareChannelsParentChild(NotificationChannel parent,
+ NotificationChannel actual, String conversationId) {
+ assertEquals(parent.getName(), actual.getName());
+ assertEquals(parent.getDescription(), actual.getDescription());
+ assertEquals(parent.shouldVibrate(), actual.shouldVibrate());
+ assertEquals(parent.shouldShowLights(), actual.shouldShowLights());
+ assertEquals(parent.getImportance(), actual.getImportance());
+ assertEquals(parent.getLockscreenVisibility(), actual.getLockscreenVisibility());
+ assertEquals(parent.getSound(), actual.getSound());
+ assertEquals(parent.canBypassDnd(), actual.canBypassDnd());
+ assertTrue(Arrays.equals(parent.getVibrationPattern(), actual.getVibrationPattern()));
+ assertEquals(parent.getGroup(), actual.getGroup());
+ assertEquals(parent.getAudioAttributes(), actual.getAudioAttributes());
+ assertEquals(parent.getLightColor(), actual.getLightColor());
+ assertEquals(parent.getId(), actual.getParentChannelId());
+ assertEquals(conversationId, actual.getConversationId());
}
private void compareGroups(NotificationChannelGroup expected, NotificationChannelGroup actual) {
@@ -2786,4 +2807,40 @@ public class PreferencesHelperTest extends UiServiceTestCase {
assertEquals(user10Importance, mHelper.getNotificationChannel(
pkg, uidList10[0], channelId, false).getImportance());
}
+
+ @Test
+ public void testGetConversationNotificationChannel() {
+ String conversationId = "friend";
+
+ NotificationChannel parent =
+ new NotificationChannel("parent", "messages", IMPORTANCE_DEFAULT);
+ mHelper.createNotificationChannel(PKG_O, UID_O, parent, true, false);
+
+ NotificationChannel friend = new NotificationChannel(String.format(
+ CONVERSATION_CHANNEL_ID_FORMAT, parent.getId(), conversationId),
+ "messages", IMPORTANCE_DEFAULT);
+ friend.setConversationId(parent.getId(), conversationId);
+ mHelper.createNotificationChannel(PKG_O, UID_O, friend, true, false);
+
+ compareChannelsParentChild(parent, mHelper.getNotificationChannel(
+ PKG_O, UID_O, parent.getId(), conversationId, false), conversationId);
+ }
+
+ @Test
+ public void testConversationNotificationChannelsRequireParents() {
+ String parentId = "does not exist";
+ String conversationId = "friend";
+
+ NotificationChannel friend = new NotificationChannel(String.format(
+ CONVERSATION_CHANNEL_ID_FORMAT, parentId, conversationId),
+ "messages", IMPORTANCE_DEFAULT);
+ friend.setConversationId(parentId, conversationId);
+
+ try {
+ mHelper.createNotificationChannel(PKG_O, UID_O, friend, true, false);
+ fail("allowed creation of conversation channel without a parent");
+ } catch (IllegalArgumentException e) {
+ // good
+ }
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
index 09ac9ce381c0..d819b1ada659 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -63,6 +63,26 @@ public class InsetsSourceProviderTest extends WindowTestsBase {
assertEquals(Insets.of(0, 100, 0, 0),
mProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500),
false /* ignoreVisibility */));
+ assertEquals(Insets.of(0, 100, 0, 0),
+ mProvider.getSource().calculateVisibleInsets(new Rect(0, 0, 500, 500)));
+ }
+
+ @Test
+ public void testPostLayout_givenInsets() {
+ final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
+ ime.getFrameLw().set(0, 0, 500, 100);
+ ime.getGivenContentInsetsLw().set(0, 0, 0, 60);
+ ime.getGivenVisibleInsetsLw().set(0, 0, 0, 75);
+ ime.mHasSurface = true;
+ mProvider.setWindow(ime, null);
+ mProvider.onPostLayout();
+ assertEquals(new Rect(0, 0, 500, 40), mProvider.getSource().getFrame());
+ assertEquals(new Rect(0, 0, 500, 25), mProvider.getSource().getVisibleFrame());
+ assertEquals(Insets.of(0, 40, 0, 0),
+ mProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500),
+ false /* ignoreVisibility */));
+ assertEquals(Insets.of(0, 25, 0, 0),
+ mProvider.getSource().calculateVisibleInsets(new Rect(0, 0, 500, 500)));
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
new file mode 100644
index 000000000000..8d2da1e6cb5b
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+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.times;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.view.ITaskOrganizer;
+import android.view.SurfaceControl;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test class for {@link TaskOrganizer}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:TaskOrganizerTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class TaskOrganizerTests extends WindowTestsBase {
+ private ITaskOrganizer makeAndRegisterMockOrganizer() {
+ final ITaskOrganizer organizer = mock(ITaskOrganizer.class);
+ when(organizer.asBinder()).thenReturn(new Binder());
+
+ mWm.mAtmService.registerTaskOrganizer(organizer, WINDOWING_MODE_PINNED);
+
+ return organizer;
+ }
+
+ @Test
+ public void testAppearVanish() throws RemoteException {
+ final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
+ final Task task = createTaskInStack(stack, 0 /* userId */);
+ final ITaskOrganizer organizer = makeAndRegisterMockOrganizer();
+
+ task.setTaskOrganizer(organizer);
+ verify(organizer).taskAppeared(any(), any());
+ assertTrue(task.isControlledByTaskOrganizer());
+
+ task.removeImmediately();
+ verify(organizer).taskVanished(any());
+ }
+
+ @Test
+ public void testSwapOrganizer() throws RemoteException {
+ final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
+ final Task task = createTaskInStack(stack, 0 /* userId */);
+ final ITaskOrganizer organizer = makeAndRegisterMockOrganizer();
+ final ITaskOrganizer organizer2 = makeAndRegisterMockOrganizer();
+
+ task.setTaskOrganizer(organizer);
+ verify(organizer).taskAppeared(any(), any());
+ task.setTaskOrganizer(organizer2);
+ verify(organizer).taskVanished(any());
+ verify(organizer2).taskAppeared(any(), any());
+ }
+
+ @Test
+ public void testClearOrganizer() throws RemoteException {
+ final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
+ final Task task = createTaskInStack(stack, 0 /* userId */);
+ final ITaskOrganizer organizer = makeAndRegisterMockOrganizer();
+
+ task.setTaskOrganizer(organizer);
+ verify(organizer).taskAppeared(any(), any());
+ assertTrue(task.isControlledByTaskOrganizer());
+
+ task.setTaskOrganizer(null);
+ verify(organizer).taskVanished(any());
+ assertFalse(task.isControlledByTaskOrganizer());
+ }
+
+ @Test
+ public void testTransferStackToOrganizer() throws RemoteException {
+ final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
+ final Task task = createTaskInStack(stack, 0 /* userId */);
+ final Task task2 = createTaskInStack(stack, 0 /* userId */);
+ final ITaskOrganizer organizer = makeAndRegisterMockOrganizer();
+
+ stack.transferToTaskOrganizer(organizer);
+
+ verify(organizer, times(2)).taskAppeared(any(), any());
+ assertTrue(task.isControlledByTaskOrganizer());
+ assertTrue(task2.isControlledByTaskOrganizer());
+
+ stack.transferToTaskOrganizer(null);
+
+ verify(organizer, times(2)).taskVanished(any());
+ assertFalse(task.isControlledByTaskOrganizer());
+ assertFalse(task2.isControlledByTaskOrganizer());
+ }
+
+ @Test
+ public void testRegisterTaskOrganizerTaskWindowingModeChanges() throws RemoteException {
+ final ITaskOrganizer organizer = makeAndRegisterMockOrganizer();
+
+ final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
+ final Task task = createTaskInStack(stack, 0 /* userId */);
+ task.setWindowingMode(WINDOWING_MODE_PINNED);
+ verify(organizer).taskAppeared(any(), any());
+ assertTrue(task.isControlledByTaskOrganizer());
+
+ task.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ verify(organizer).taskVanished(any());
+ assertFalse(task.isControlledByTaskOrganizer());
+ }
+
+ @Test
+ public void testRegisterTaskOrganizerStackWindowingModeChanges() throws RemoteException {
+ final ITaskOrganizer organizer = makeAndRegisterMockOrganizer();
+
+ final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
+ final Task task = createTaskInStack(stack, 0 /* userId */);
+ final Task task2 = createTaskInStack(stack, 0 /* userId */);
+ stack.setWindowingMode(WINDOWING_MODE_PINNED);
+ verify(organizer, times(2)).taskAppeared(any(), any());
+ assertTrue(task.isControlledByTaskOrganizer());
+ assertTrue(task2.isControlledByTaskOrganizer());
+
+ stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ verify(organizer, times(2)).taskVanished(any());
+ assertFalse(task.isControlledByTaskOrganizer());
+ assertFalse(task2.isControlledByTaskOrganizer());
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/RotationAnimationUtilsTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/RotationAnimationUtilsTest.java
deleted file mode 100644
index 9cda08458640..000000000000
--- a/services/tests/wmtests/src/com/android/server/wm/utils/RotationAnimationUtilsTest.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.utils;
-
-import static android.graphics.Bitmap.Config.ARGB_8888;
-
-import static org.junit.Assert.assertEquals;
-
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.graphics.ColorSpace;
-import android.graphics.GraphicBuffer;
-import android.graphics.Matrix;
-import android.graphics.PointF;
-import android.view.Surface;
-
-import org.junit.Before;
-import org.junit.Test;
-
-public class RotationAnimationUtilsTest {
-
- private static final int BITMAP_HEIGHT = 100;
- private static final int BITMAP_WIDTH = 100;
- private static final int POINT_WIDTH = 1000;
- private static final int POINT_HEIGHT = 2000;
-
- private ColorSpace mColorSpace = ColorSpace.get(ColorSpace.Named.DISPLAY_P3);
- private Matrix mMatrix;
-
- @Before
- public void setup() {
- mMatrix = new Matrix();
- }
-
- @Test
- public void blackLuma() {
- Bitmap swBitmap = createBitmap(0);
- GraphicBuffer gb = swBitmapToGraphicsBuffer(swBitmap);
- float borderLuma = RotationAnimationUtils.getAvgBorderLuma(gb, mColorSpace);
- assertEquals(0, borderLuma, 0);
- }
-
- @Test
- public void whiteLuma() {
- Bitmap swBitmap = createBitmap(1);
- GraphicBuffer gb = swBitmapToGraphicsBuffer(swBitmap);
- float borderLuma = RotationAnimationUtils.getAvgBorderLuma(gb, mColorSpace);
- assertEquals(1, borderLuma, 0);
- }
-
- @Test
- public void whiteImageBlackBorderLuma() {
- Bitmap swBitmap = createBitmap(1);
- setBorderLuma(swBitmap, 0);
- GraphicBuffer gb = swBitmapToGraphicsBuffer(swBitmap);
- float borderLuma = RotationAnimationUtils.getAvgBorderLuma(gb, mColorSpace);
- assertEquals(0, borderLuma, 0);
- }
-
- @Test
- public void blackImageWhiteBorderLuma() {
- Bitmap swBitmap = createBitmap(0);
- setBorderLuma(swBitmap, 1);
- GraphicBuffer gb = swBitmapToGraphicsBuffer(swBitmap);
- float borderLuma = RotationAnimationUtils.getAvgBorderLuma(gb, mColorSpace);
- assertEquals(1, borderLuma, 0);
- }
-
- @Test
- public void rotate_0_bottomRight() {
- RotationAnimationUtils.createRotationMatrix(Surface.ROTATION_0,
- POINT_WIDTH, POINT_HEIGHT, mMatrix);
- PointF newPoints = checkMappedPoints(POINT_WIDTH, POINT_HEIGHT);
- assertEquals(POINT_WIDTH, newPoints.x, 0);
- assertEquals(POINT_HEIGHT, newPoints.y, 0);
- }
-
- @Test
- public void rotate_90_bottomRight() {
- RotationAnimationUtils.createRotationMatrix(Surface.ROTATION_90,
- POINT_WIDTH, POINT_HEIGHT, mMatrix);
- PointF newPoints = checkMappedPoints(POINT_WIDTH, POINT_HEIGHT);
- assertEquals(0, newPoints.x, 0);
- assertEquals(POINT_WIDTH, newPoints.y, 0);
- }
-
- @Test
- public void rotate_180_bottomRight() {
- RotationAnimationUtils.createRotationMatrix(Surface.ROTATION_180,
- POINT_WIDTH, POINT_HEIGHT, mMatrix);
- PointF newPoints = checkMappedPoints(POINT_WIDTH, POINT_HEIGHT);
- assertEquals(0, newPoints.x, 0);
- assertEquals(0, newPoints.y, 0);
- }
-
- @Test
- public void rotate_270_bottomRight() {
- RotationAnimationUtils.createRotationMatrix(Surface.ROTATION_270,
- POINT_WIDTH, POINT_HEIGHT, mMatrix);
- PointF newPoints = checkMappedPoints(POINT_WIDTH, POINT_HEIGHT);
- assertEquals(POINT_HEIGHT, newPoints.x, 0);
- assertEquals(0, newPoints.y, 0);
- }
-
- private PointF checkMappedPoints(int x, int y) {
- final float[] fs = new float[] {x, y};
- mMatrix.mapPoints(fs);
- return new PointF(fs[0], fs[1]);
- }
-
- private Bitmap createBitmap(float luma) {
- Bitmap bitmap = Bitmap.createBitmap(BITMAP_WIDTH, BITMAP_HEIGHT, ARGB_8888);
- for (int i = 0; i < BITMAP_WIDTH; i++) {
- for (int j = 0; j < BITMAP_HEIGHT; j++) {
- bitmap.setPixel(i, j, Color.argb(1, luma, luma, luma));
- }
- }
- return bitmap;
- }
-
- private GraphicBuffer swBitmapToGraphicsBuffer(Bitmap swBitmap) {
- Bitmap hwBitmap = swBitmap.copy(Bitmap.Config.HARDWARE, false);
- return hwBitmap.createGraphicBufferHandle();
- }
-
- private void setBorderLuma(Bitmap swBitmap, float luma) {
- int i;
- int width = swBitmap.getWidth();
- int height = swBitmap.getHeight();
- for (i = 0; i < width; i++) {
- swBitmap.setPixel(i, 0, Color.argb(1, luma, luma, luma));
- swBitmap.setPixel(i, height - 1, Color.argb(1, luma, luma, luma));
- }
- for (i = 0; i < height; i++) {
- swBitmap.setPixel(0, i, Color.argb(1, luma, luma, luma));
- swBitmap.setPixel(width - 1, i, Color.argb(1, luma, luma, luma));
- }
- }
-}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index c900f386b438..8397aa485595 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -1568,44 +1568,16 @@ public class UsageStatsService extends SystemService implements
}
@Override
- public void setAppStandbyBucket(String packageName,
- int bucket, int userId) {
+ public void setAppStandbyBucket(String packageName, int bucket, int userId) {
getContext().enforceCallingPermission(Manifest.permission.CHANGE_APP_IDLE_STATE,
"No permission to change app standby state");
- if (bucket < UsageStatsManager.STANDBY_BUCKET_ACTIVE
- || bucket > UsageStatsManager.STANDBY_BUCKET_NEVER) {
- throw new IllegalArgumentException("Cannot set the standby bucket to " + bucket);
- }
final int callingUid = Binder.getCallingUid();
- try {
- userId = ActivityManager.getService().handleIncomingUser(
- Binder.getCallingPid(), callingUid, userId, false, true,
- "setAppStandbyBucket", null);
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
- final boolean shellCaller = callingUid == 0 || callingUid == Process.SHELL_UID;
- final boolean systemCaller = UserHandle.isCore(callingUid);
- final int reason = systemCaller
- ? UsageStatsManager.REASON_MAIN_FORCED
- : UsageStatsManager.REASON_MAIN_PREDICTED;
+ final int callingPid = Binder.getCallingPid();
final long token = Binder.clearCallingIdentity();
try {
- final int packageUid = mPackageManagerInternal.getPackageUid(packageName,
- PackageManager.MATCH_ANY_USER | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
- | PackageManager.MATCH_DIRECT_BOOT_AWARE, userId);
- // Caller cannot set their own standby state
- if (packageUid == callingUid) {
- throw new IllegalArgumentException("Cannot set your own standby bucket");
- }
- if (packageUid < 0) {
- throw new IllegalArgumentException(
- "Cannot set standby bucket for non existent package (" + packageName
- + ")");
- }
- mAppStandby.setAppStandbyBucket(packageName, userId, bucket, reason,
- SystemClock.elapsedRealtime(), shellCaller);
+ mAppStandby.setAppStandbyBucket(packageName, bucket, userId,
+ callingUid, callingPid);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1643,37 +1615,11 @@ public class UsageStatsService extends SystemService implements
"No permission to change app standby state");
final int callingUid = Binder.getCallingUid();
- try {
- userId = ActivityManager.getService().handleIncomingUser(
- Binder.getCallingPid(), callingUid, userId, false, true,
- "setAppStandbyBucket", null);
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
- final boolean shellCaller = callingUid == 0 || callingUid == Process.SHELL_UID;
- final int reason = shellCaller
- ? UsageStatsManager.REASON_MAIN_FORCED
- : UsageStatsManager.REASON_MAIN_PREDICTED;
+ final int callingPid = Binder.getCallingPid();
final long token = Binder.clearCallingIdentity();
try {
- final long elapsedRealtime = SystemClock.elapsedRealtime();
- List<AppStandbyInfo> bucketList = appBuckets.getList();
- for (AppStandbyInfo bucketInfo : bucketList) {
- final String packageName = bucketInfo.mPackageName;
- final int bucket = bucketInfo.mStandbyBucket;
- if (bucket < UsageStatsManager.STANDBY_BUCKET_ACTIVE
- || bucket > UsageStatsManager.STANDBY_BUCKET_NEVER) {
- throw new IllegalArgumentException(
- "Cannot set the standby bucket to " + bucket);
- }
- // Caller cannot set their own standby state
- if (mPackageManagerInternal.getPackageUid(packageName,
- PackageManager.MATCH_ANY_USER, userId) == callingUid) {
- throw new IllegalArgumentException("Cannot set your own standby bucket");
- }
- mAppStandby.setAppStandbyBucket(packageName, userId, bucket, reason,
- elapsedRealtime, shellCaller);
- }
+ mAppStandby.setAppStandbyBuckets(appBuckets.getList(), userId,
+ callingUid, callingPid);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index 198b4c31249a..bde2cfd52c0f 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -611,63 +611,89 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
int setParameter(UUID modelId, @ModelParams int modelParam, int value) {
synchronized (mLock) {
- MetricsLogger.count(mContext, "sth_set_parameter", 1);
- if (modelId == null || mModule == null) {
- return SoundTrigger.STATUS_ERROR;
- }
- ModelData modelData = mModelDataMap.get(modelId);
- if (modelData == null) {
- Slog.w(TAG, "SetParameter: Invalid model id:" + modelId);
- return SoundTrigger.STATUS_BAD_VALUE;
- }
- if (!modelData.isModelLoaded()) {
- Slog.i(TAG, "SetParameter: Given model is not loaded:" + modelId);
- return SoundTrigger.STATUS_BAD_VALUE;
- }
+ return setParameterLocked(mModelDataMap.get(modelId), modelParam, value);
+ }
+ }
- return mModule.setParameter(modelData.getHandle(), modelParam, value);
+ int setKeyphraseParameter(int keyphraseId, @ModelParams int modelParam, int value) {
+ synchronized (mLock) {
+ return setParameterLocked(getKeyphraseModelDataLocked(keyphraseId), modelParam, value);
}
}
- int getParameter(@NonNull UUID modelId, @ModelParams int modelParam)
- throws UnsupportedOperationException, IllegalArgumentException {
+ private int setParameterLocked(@Nullable ModelData modelData, @ModelParams int modelParam,
+ int value) {
+ MetricsLogger.count(mContext, "sth_set_parameter", 1);
+ if (mModule == null) {
+ return SoundTrigger.STATUS_NO_INIT;
+ }
+ if (modelData == null || !modelData.isModelLoaded()) {
+ Slog.i(TAG, "SetParameter: Given model is not loaded:" + modelData);
+ return SoundTrigger.STATUS_BAD_VALUE;
+ }
+
+ return mModule.setParameter(modelData.getHandle(), modelParam, value);
+ }
+
+ int getParameter(@NonNull UUID modelId, @ModelParams int modelParam) {
synchronized (mLock) {
- MetricsLogger.count(mContext, "sth_get_parameter", 1);
- if (mModule == null) {
- throw new UnsupportedOperationException("SoundTriggerModule not initialized");
- }
+ return getParameterLocked(mModelDataMap.get(modelId), modelParam);
+ }
+ }
- ModelData modelData = mModelDataMap.get(modelId);
- if (modelData == null) {
- throw new IllegalArgumentException("Invalid model id:" + modelId);
- }
- if (!modelData.isModelLoaded()) {
- throw new UnsupportedOperationException("Given model is not loaded:" + modelId);
- }
+ int getKeyphraseParameter(int keyphraseId, @ModelParams int modelParam) {
+ synchronized (mLock) {
+ return getParameterLocked(getKeyphraseModelDataLocked(keyphraseId), modelParam);
+ }
+ }
- return mModule.getParameter(modelData.getHandle(), modelParam);
+ private int getParameterLocked(@Nullable ModelData modelData, @ModelParams int modelParam) {
+ MetricsLogger.count(mContext, "sth_get_parameter", 1);
+ if (mModule == null) {
+ throw new UnsupportedOperationException("SoundTriggerModule not initialized");
}
+
+ if (modelData == null) {
+ throw new IllegalArgumentException("Invalid model id");
+ }
+ if (!modelData.isModelLoaded()) {
+ throw new UnsupportedOperationException("Given model is not loaded:" + modelData);
+ }
+
+ return mModule.getParameter(modelData.getHandle(), modelParam);
}
@Nullable
ModelParamRange queryParameter(@NonNull UUID modelId, @ModelParams int modelParam) {
synchronized (mLock) {
- MetricsLogger.count(mContext, "sth_query_parameter", 1);
- if (mModule == null) {
- return null;
- }
- ModelData modelData = mModelDataMap.get(modelId);
- if (modelData == null) {
- Slog.w(TAG, "queryParameter: Invalid model id:" + modelId);
- return null;
- }
- if (!modelData.isModelLoaded()) {
- Slog.i(TAG, "queryParameter: Given model is not loaded:" + modelId);
- return null;
- }
+ return queryParameterLocked(mModelDataMap.get(modelId), modelParam);
+ }
+ }
- return mModule.queryParameter(modelData.getHandle(), modelParam);
+ @Nullable
+ ModelParamRange queryKeyphraseParameter(int keyphraseId, @ModelParams int modelParam) {
+ synchronized (mLock) {
+ return queryParameterLocked(getKeyphraseModelDataLocked(keyphraseId), modelParam);
+ }
+ }
+
+ @Nullable
+ private ModelParamRange queryParameterLocked(@Nullable ModelData modelData,
+ @ModelParams int modelParam) {
+ MetricsLogger.count(mContext, "sth_query_parameter", 1);
+ if (mModule == null) {
+ return null;
+ }
+ if (modelData == null) {
+ Slog.w(TAG, "queryParameter: Invalid model id");
+ return null;
}
+ if (!modelData.isModelLoaded()) {
+ Slog.i(TAG, "queryParameter: Given model is not loaded:" + modelData);
+ return null;
+ }
+
+ return mModule.queryParameter(modelData.getHandle(), modelParam);
}
//---- SoundTrigger.StatusListener methods
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerInternal.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerInternal.java
index d05e044499ab..54dffdc4c13a 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerInternal.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerInternal.java
@@ -16,17 +16,17 @@
package com.android.server.soundtrigger;
+import android.annotation.Nullable;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
+import android.hardware.soundtrigger.ModelParams;
import android.hardware.soundtrigger.SoundTrigger;
import android.hardware.soundtrigger.SoundTrigger.Keyphrase;
-import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionEvent;
-import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra;
import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
+import android.hardware.soundtrigger.SoundTrigger.ModelParamRange;
import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
-import android.hardware.soundtrigger.SoundTrigger.RecognitionEvent;
-import android.hardware.soundtrigger.SoundTrigger.SoundModelEvent;
-import android.hardware.soundtrigger.SoundTriggerModule;
+
+import com.android.server.voiceinteraction.VoiceInteractionManagerService;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -71,6 +71,58 @@ public abstract class SoundTriggerInternal {
public abstract ModuleProperties getModuleProperties();
/**
+ * Set a model specific {@link ModelParams} with the given value. This
+ * parameter will keep its value for the duration the model is loaded regardless of starting and
+ * stopping recognition. Once the model is unloaded, the value will be lost.
+ * {@link SoundTriggerInternal#queryParameter} should be checked first before calling this
+ * method.
+ *
+ * @param keyphraseId The identifier of the keyphrase for which
+ * to modify model parameters
+ * @param modelParam {@link ModelParams}
+ * @param value Value to set
+ * @return - {@link SoundTrigger#STATUS_OK} in case of success
+ * - {@link SoundTrigger#STATUS_NO_INIT} if the native service cannot be reached
+ * - {@link SoundTrigger#STATUS_BAD_VALUE} invalid input parameter
+ * - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence or
+ * if API is not supported by HAL
+ */
+ public abstract int setParameter(int keyphraseId, @ModelParams int modelParam, int value);
+
+ /**
+ * Get a model specific {@link ModelParams}. This parameter will keep its value
+ * for the duration the model is loaded regardless of starting and stopping recognition.
+ * Once the model is unloaded, the value will be lost. If the value is not set, a default
+ * value is returned. See ModelParams for parameter default values.
+ * {@link SoundTriggerInternal#queryParameter} should be checked first before calling this
+ * method.
+ *
+ * @param keyphraseId The identifier of the keyphrase for which
+ * to modify model parameters
+ * @param modelParam {@link ModelParams}
+ * @return value of parameter
+ * @throws UnsupportedOperationException if hal or model do not support this API.
+ * queryParameter should be checked first.
+ * @throws IllegalArgumentException if invalid model handle or parameter is passed.
+ * queryParameter should be checked first.
+ */
+ public abstract int getParameter(int keyphraseId, @ModelParams int modelParam);
+
+ /**
+ * Determine if parameter control is supported for the given model handle.
+ * This method should be checked prior to calling {@link SoundTriggerInternal#setParameter}
+ * or {@link SoundTriggerInternal#getParameter}.
+ *
+ * @param keyphraseId The identifier of the keyphrase for which
+ * to modify model parameters
+ * @param modelParam {@link ModelParams}
+ * @return supported range of parameter, null if not supported
+ */
+ @Nullable
+ public abstract ModelParamRange queryParameter(int keyphraseId,
+ @ModelParams int modelParam);
+
+ /**
* Unloads (and stops if running) the given keyphraseId
*/
public abstract int unloadKeyphraseModel(int keyphaseId);
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index 68b16f39e149..e37755bddcaa 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -1446,26 +1446,45 @@ public class SoundTriggerService extends SystemService {
@Override
public int startRecognition(int keyphraseId, KeyphraseSoundModel soundModel,
IRecognitionStatusCallback listener, RecognitionConfig recognitionConfig) {
- if (!isInitialized()) return STATUS_ERROR;
+ if (!isInitialized()) throw new UnsupportedOperationException();
return mSoundTriggerHelper.startKeyphraseRecognition(keyphraseId, soundModel, listener,
recognitionConfig);
}
@Override
public synchronized int stopRecognition(int keyphraseId, IRecognitionStatusCallback listener) {
- if (!isInitialized()) return STATUS_ERROR;
+ if (!isInitialized()) throw new UnsupportedOperationException();
return mSoundTriggerHelper.stopKeyphraseRecognition(keyphraseId, listener);
}
@Override
public ModuleProperties getModuleProperties() {
- if (!isInitialized()) return null;
+ if (!isInitialized()) throw new UnsupportedOperationException();
return mSoundTriggerHelper.getModuleProperties();
}
@Override
+ public int setParameter(int keyphraseId, @ModelParams int modelParam, int value) {
+ if (!isInitialized()) throw new UnsupportedOperationException();
+ return mSoundTriggerHelper.setKeyphraseParameter(keyphraseId, modelParam, value);
+ }
+
+ @Override
+ public int getParameter(int keyphraseId, @ModelParams int modelParam) {
+ if (!isInitialized()) throw new UnsupportedOperationException();
+ return mSoundTriggerHelper.getKeyphraseParameter(keyphraseId, modelParam);
+ }
+
+ @Override
+ @Nullable
+ public ModelParamRange queryParameter(int keyphraseId, @ModelParams int modelParam) {
+ if (!isInitialized()) throw new UnsupportedOperationException();
+ return mSoundTriggerHelper.queryKeyphraseParameter(keyphraseId, modelParam);
+ }
+
+ @Override
public int unloadKeyphraseModel(int keyphraseId) {
- if (!isInitialized()) return STATUS_ERROR;
+ if (!isInitialized()) throw new UnsupportedOperationException();
return mSoundTriggerHelper.unloadKeyphraseSoundModel(keyphraseId);
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index e9db31ba3e25..06c807421d1a 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -41,7 +41,9 @@ import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
+import android.hardware.soundtrigger.ModelParams;
import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
+import android.hardware.soundtrigger.SoundTrigger.ModelParamRange;
import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
import android.os.Binder;
@@ -1084,6 +1086,55 @@ public class VoiceInteractionManagerService extends SystemService {
}
}
+ @Override
+ public int setParameter(IVoiceInteractionService service, int keyphraseId,
+ @ModelParams int modelParam, int value) {
+ // Allow the call if this is the current voice interaction service.
+ synchronized (this) {
+ enforceIsCurrentVoiceInteractionService(service);
+ }
+
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ return mSoundTriggerInternal.setParameter(keyphraseId, modelParam, value);
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ }
+ }
+
+ @Override
+ public int getParameter(IVoiceInteractionService service, int keyphraseId,
+ @ModelParams int modelParam) {
+ // Allow the call if this is the current voice interaction service.
+ synchronized (this) {
+ enforceIsCurrentVoiceInteractionService(service);
+ }
+
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ return mSoundTriggerInternal.getParameter(keyphraseId, modelParam);
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ }
+ }
+
+ @Override
+ @Nullable
+ public ModelParamRange queryParameter(IVoiceInteractionService service,
+ int keyphraseId, @ModelParams int modelParam) {
+ // Allow the call if this is the current voice interaction service.
+ synchronized (this) {
+ enforceIsCurrentVoiceInteractionService(service);
+ }
+
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ return mSoundTriggerInternal.queryParameter(keyphraseId, modelParam);
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ }
+ }
+
private synchronized void unloadAllKeyphraseModels() {
for (int i = 0; i < mLoadedKeyphraseIds.size(); i++) {
final long caller = Binder.clearCallingIdentity();
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 9cf4803966c6..c4c1e21e7c41 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -49,6 +49,7 @@ import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.Executor;
/**
@@ -882,7 +883,8 @@ public class TelecomManager {
*/
public TelecomManager(Context context, ITelecomService telecomServiceImpl) {
Context appContext = context.getApplicationContext();
- if (appContext != null) {
+ if (appContext != null && Objects.equals(context.getFeatureId(),
+ appContext.getFeatureId())) {
mContext = appContext;
} else {
mContext = context;
@@ -916,7 +918,7 @@ public class TelecomManager {
try {
if (isServiceConnected()) {
return getTelecomService().getDefaultOutgoingPhoneAccount(uriScheme,
- mContext.getOpPackageName());
+ mContext.getOpPackageName(), mContext.getFeatureId());
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#getDefaultOutgoingPhoneAccount", e);
@@ -1113,7 +1115,8 @@ public class TelecomManager {
public List<PhoneAccountHandle> getSelfManagedPhoneAccounts() {
try {
if (isServiceConnected()) {
- return getTelecomService().getSelfManagedPhoneAccounts(mContext.getOpPackageName());
+ return getTelecomService().getSelfManagedPhoneAccounts(mContext.getOpPackageName(),
+ mContext.getFeatureId());
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#getSelfManagedPhoneAccounts()", e);
@@ -1138,8 +1141,8 @@ public class TelecomManager {
boolean includeDisabledAccounts) {
try {
if (isServiceConnected()) {
- return getTelecomService().getCallCapablePhoneAccounts(
- includeDisabledAccounts, mContext.getOpPackageName());
+ return getTelecomService().getCallCapablePhoneAccounts(includeDisabledAccounts,
+ mContext.getOpPackageName(), mContext.getFeatureId());
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#getCallCapablePhoneAccounts(" +
@@ -1442,7 +1445,7 @@ public class TelecomManager {
try {
if (isServiceConnected()) {
return getTelecomService().isVoiceMailNumber(accountHandle, number,
- mContext.getOpPackageName());
+ mContext.getOpPackageName(), mContext.getFeatureId());
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException calling ITelecomService#isVoiceMailNumber.", e);
@@ -1464,7 +1467,7 @@ public class TelecomManager {
try {
if (isServiceConnected()) {
return getTelecomService().getVoiceMailNumber(accountHandle,
- mContext.getOpPackageName());
+ mContext.getOpPackageName(), mContext.getFeatureId());
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException calling ITelecomService#hasVoiceMailNumber.", e);
@@ -1485,7 +1488,7 @@ public class TelecomManager {
try {
if (isServiceConnected()) {
return getTelecomService().getLine1Number(accountHandle,
- mContext.getOpPackageName());
+ mContext.getOpPackageName(), mContext.getFeatureId());
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException calling ITelecomService#getLine1Number.", e);
@@ -1506,7 +1509,8 @@ public class TelecomManager {
public boolean isInCall() {
try {
if (isServiceConnected()) {
- return getTelecomService().isInCall(mContext.getOpPackageName());
+ return getTelecomService().isInCall(mContext.getOpPackageName(),
+ mContext.getFeatureId());
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException calling isInCall().", e);
@@ -1531,7 +1535,8 @@ public class TelecomManager {
public boolean isInManagedCall() {
try {
if (isServiceConnected()) {
- return getTelecomService().isInManagedCall(mContext.getOpPackageName());
+ return getTelecomService().isInManagedCall(mContext.getOpPackageName(),
+ mContext.getFeatureId());
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException calling isInManagedCall().", e);
@@ -1711,7 +1716,8 @@ public class TelecomManager {
public boolean isTtySupported() {
try {
if (isServiceConnected()) {
- return getTelecomService().isTtySupported(mContext.getOpPackageName());
+ return getTelecomService().isTtySupported(mContext.getOpPackageName(),
+ mContext.getFeatureId());
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException attempting to get TTY supported state.", e);
@@ -1735,7 +1741,8 @@ public class TelecomManager {
public @TtyMode int getCurrentTtyMode() {
try {
if (isServiceConnected()) {
- return getTelecomService().getCurrentTtyMode(mContext.getOpPackageName());
+ return getTelecomService().getCurrentTtyMode(mContext.getOpPackageName(),
+ mContext.getFeatureId());
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException attempting to get the current TTY mode.", e);
@@ -1925,7 +1932,8 @@ public class TelecomManager {
ITelecomService service = getTelecomService();
if (service != null) {
try {
- service.showInCallScreen(showDialpad, mContext.getOpPackageName());
+ service.showInCallScreen(showDialpad, mContext.getOpPackageName(),
+ mContext.getFeatureId());
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#showCallScreen", e);
}
@@ -1988,7 +1996,7 @@ public class TelecomManager {
}
try {
service.placeCall(address, extras == null ? new Bundle() : extras,
- mContext.getOpPackageName());
+ mContext.getOpPackageName(), mContext.getFeatureId());
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#placeCall", e);
}
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 204c37e9aa38..c54da6b4d527 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -35,12 +35,13 @@ interface ITelecomService {
*
* @param showDialpad if true, make the dialpad visible initially.
*/
- void showInCallScreen(boolean showDialpad, String callingPackage);
+ void showInCallScreen(boolean showDialpad, String callingPackage, String callingFeatureId);
/**
* @see TelecomServiceImpl#getDefaultOutgoingPhoneAccount
*/
- PhoneAccountHandle getDefaultOutgoingPhoneAccount(in String uriScheme, String callingPackage);
+ PhoneAccountHandle getDefaultOutgoingPhoneAccount(in String uriScheme, String callingPackage,
+ String callingFeatureId);
/**
* @see TelecomServiceImpl#getUserSelectedOutgoingPhoneAccount
@@ -56,12 +57,13 @@ interface ITelecomService {
* @see TelecomServiceImpl#getCallCapablePhoneAccounts
*/
List<PhoneAccountHandle> getCallCapablePhoneAccounts(
- boolean includeDisabledAccounts, String callingPackage);
+ boolean includeDisabledAccounts, String callingPackage, String callingFeatureId);
/**
* @see TelecomServiceImpl#getSelfManagedPhoneAccounts
*/
- List<PhoneAccountHandle> getSelfManagedPhoneAccounts(String callingPackage);
+ List<PhoneAccountHandle> getSelfManagedPhoneAccounts(String callingPackage,
+ String callingFeatureId);
/**
* @see TelecomManager#getPhoneAccountsSupportingScheme
@@ -123,17 +125,19 @@ interface ITelecomService {
* @see TelecomServiceImpl#isVoiceMailNumber
*/
boolean isVoiceMailNumber(in PhoneAccountHandle accountHandle, String number,
- String callingPackage);
+ String callingPackage, String callingFeatureId);
/**
* @see TelecomServiceImpl#getVoiceMailNumber
*/
- String getVoiceMailNumber(in PhoneAccountHandle accountHandle, String callingPackage);
+ String getVoiceMailNumber(in PhoneAccountHandle accountHandle, String callingPackage,
+ String callingFeatureId);
/**
* @see TelecomServiceImpl#getLine1Number
*/
- String getLine1Number(in PhoneAccountHandle accountHandle, String callingPackage);
+ String getLine1Number(in PhoneAccountHandle accountHandle, String callingPackage,
+ String callingFeatureId);
/**
* @see TelecomServiceImpl#getDefaultPhoneApp
@@ -172,12 +176,12 @@ interface ITelecomService {
/**
* @see TelecomServiceImpl#isInCall
*/
- boolean isInCall(String callingPackage);
+ boolean isInCall(String callingPackage, String callingFeatureId);
/**
* @see TelecomServiceImpl#isInManagedCall
*/
- boolean isInManagedCall(String callingPackage);
+ boolean isInManagedCall(String callingPackage, String callingFeatureId);
/**
* @see TelecomServiceImpl#isRinging
@@ -229,12 +233,12 @@ interface ITelecomService {
/**
* @see TelecomServiceImpl#isTtySupported
*/
- boolean isTtySupported(String callingPackage);
+ boolean isTtySupported(String callingPackage, String callingFeatureId);
/**
* @see TelecomServiceImpl#getCurrentTtyMode
*/
- int getCurrentTtyMode(String callingPackage);
+ int getCurrentTtyMode(String callingPackage, String callingFeatureId);
/**
* @see TelecomServiceImpl#addNewIncomingCall
@@ -249,7 +253,7 @@ interface ITelecomService {
/**
* @see TelecomServiceImpl#placeCall
*/
- void placeCall(in Uri handle, in Bundle extras, String callingPackage);
+ void placeCall(in Uri handle, in Bundle extras, String callingPackage, String callingFeatureId);
/**
* @see TelecomServiceImpl#enablePhoneAccount
diff --git a/telephony/common/android/telephony/LocationAccessPolicy.java b/telephony/common/android/telephony/LocationAccessPolicy.java
index f39981fdf25d..aaafee29e24a 100644
--- a/telephony/common/android/telephony/LocationAccessPolicy.java
+++ b/telephony/common/android/telephony/LocationAccessPolicy.java
@@ -16,6 +16,8 @@
package android.telephony;
+import com.android.telephony.Rlog;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
index eb02ea6f5e40..97bcbc061f8a 100644
--- a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
+++ b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
@@ -18,14 +18,16 @@ package com.android.internal.telephony;
import android.annotation.Nullable;
import android.content.ContentResolver;
+import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.permission.IPermissionManager;
import android.provider.Settings;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -76,7 +78,7 @@ public final class CarrierAppUtils {
*/
public static synchronized void disableCarrierAppsUntilPrivileged(String callingPackage,
IPackageManager packageManager, IPermissionManager permissionManager,
- TelephonyManager telephonyManager, ContentResolver contentResolver, int userId) {
+ TelephonyManager telephonyManager, int userId, Context context) {
if (DEBUG) {
Rlog.d(TAG, "disableCarrierAppsUntilPrivileged");
}
@@ -85,6 +87,7 @@ public final class CarrierAppUtils {
config.getDisabledUntilUsedPreinstalledCarrierApps();
ArrayMap<String, List<String>> systemCarrierAssociatedAppsDisabledUntilUsed =
config.getDisabledUntilUsedPreinstalledCarrierAssociatedApps();
+ ContentResolver contentResolver = getContentResolverForUser(context, userId);
disableCarrierAppsUntilPrivileged(callingPackage, packageManager, permissionManager,
telephonyManager, contentResolver, userId, systemCarrierAppsDisabledUntilUsed,
systemCarrierAssociatedAppsDisabledUntilUsed);
@@ -102,8 +105,8 @@ public final class CarrierAppUtils {
* Manager can kill it, and this can lead to crashes as the app is in an unexpected state.
*/
public static synchronized void disableCarrierAppsUntilPrivileged(String callingPackage,
- IPackageManager packageManager, IPermissionManager permissionManager,
- ContentResolver contentResolver, int userId) {
+ IPackageManager packageManager, IPermissionManager permissionManager, int userId,
+ Context context) {
if (DEBUG) {
Rlog.d(TAG, "disableCarrierAppsUntilPrivileged");
}
@@ -114,15 +117,23 @@ public final class CarrierAppUtils {
ArrayMap<String, List<String>> systemCarrierAssociatedAppsDisabledUntilUsed =
config.getDisabledUntilUsedPreinstalledCarrierAssociatedApps();
+ ContentResolver contentResolver = getContentResolverForUser(context, userId);
disableCarrierAppsUntilPrivileged(callingPackage, packageManager, permissionManager,
null /* telephonyManager */, contentResolver, userId,
systemCarrierAppsDisabledUntilUsed, systemCarrierAssociatedAppsDisabledUntilUsed);
}
+ private static ContentResolver getContentResolverForUser(Context context, int userId) {
+ Context userContext = context.createContextAsUser(UserHandle.getUserHandleForUid(userId),
+ 0);
+ return userContext.getContentResolver();
+ }
+
/**
* Disable carrier apps until they are privileged
* Must be public b/c framework unit tests can't access package-private methods.
*/
+ // Must be public b/c framework unit tests can't access package-private methods.
@VisibleForTesting
public static void disableCarrierAppsUntilPrivileged(String callingPackage,
IPackageManager packageManager, IPermissionManager permissionManager,
@@ -142,9 +153,8 @@ public final class CarrierAppUtils {
systemCarrierAssociatedAppsDisabledUntilUsed);
List<String> enabledCarrierPackages = new ArrayList<>();
-
- boolean hasRunOnce = Settings.Secure.getIntForUser(
- contentResolver, Settings.Secure.CARRIER_APPS_HANDLED, 0, userId) == 1;
+ boolean hasRunOnce = Settings.Secure.getInt(contentResolver,
+ Settings.Secure.CARRIER_APPS_HANDLED, 0) == 1;
try {
for (ApplicationInfo ai : candidates) {
@@ -259,8 +269,7 @@ public final class CarrierAppUtils {
// Mark the execution so we do not disable apps again.
if (!hasRunOnce) {
- Settings.Secure.putIntForUser(
- contentResolver, Settings.Secure.CARRIER_APPS_HANDLED, 1, userId);
+ Settings.Secure.putInt(contentResolver, Settings.Secure.CARRIER_APPS_HANDLED, 1);
}
if (!enabledCarrierPackages.isEmpty()) {
diff --git a/telephony/common/com/android/internal/telephony/GsmAlphabet.java b/telephony/common/com/android/internal/telephony/GsmAlphabet.java
index 22cbdaa0f133..a36ff9341275 100644
--- a/telephony/common/com/android/internal/telephony/GsmAlphabet.java
+++ b/telephony/common/com/android/internal/telephony/GsmAlphabet.java
@@ -19,7 +19,7 @@ package com.android.internal.telephony;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.Resources;
import android.os.Build;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
import android.text.TextUtils;
import android.util.SparseIntArray;
diff --git a/telephony/common/com/android/internal/telephony/HbpcdUtils.java b/telephony/common/com/android/internal/telephony/HbpcdUtils.java
index 2f3194214be6..45a563c09394 100644
--- a/telephony/common/com/android/internal/telephony/HbpcdUtils.java
+++ b/telephony/common/com/android/internal/telephony/HbpcdUtils.java
@@ -19,7 +19,7 @@ package com.android.internal.telephony;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
import com.android.internal.telephony.HbpcdLookup.ArbitraryMccSidMatch;
import com.android.internal.telephony.HbpcdLookup.MccIdd;
diff --git a/telephony/common/com/android/internal/telephony/SmsApplication.java b/telephony/common/com/android/internal/telephony/SmsApplication.java
index b30258906368..afb9b6f52bdb 100644
--- a/telephony/common/com/android/internal/telephony/SmsApplication.java
+++ b/telephony/common/com/android/internal/telephony/SmsApplication.java
@@ -40,7 +40,7 @@ import android.os.UserHandle;
import android.provider.Telephony;
import android.provider.Telephony.Sms.Intents;
import android.telephony.PackageChangeReceiver;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
import android.telephony.TelephonyManager;
import android.util.Log;
diff --git a/telephony/common/com/android/internal/telephony/SmsNumberUtils.java b/telephony/common/com/android/internal/telephony/SmsNumberUtils.java
index 06c08f56aa1f..06c37288a1a6 100644
--- a/telephony/common/com/android/internal/telephony/SmsNumberUtils.java
+++ b/telephony/common/com/android/internal/telephony/SmsNumberUtils.java
@@ -24,7 +24,7 @@ import android.os.PersistableBundle;
import android.os.SystemProperties;
import android.telephony.CarrierConfigManager;
import android.telephony.PhoneNumberUtils;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index 80a55b2a1147..4109ca6bd7d0 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -28,7 +28,7 @@ import android.os.Binder;
import android.os.Build;
import android.os.Process;
import android.os.UserHandle;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.Log;
diff --git a/telephony/java/android/service/carrier/CarrierIdentifier.java b/telephony/java/android/service/carrier/CarrierIdentifier.java
index af5bf7475f95..7957c258b54f 100644
--- a/telephony/java/android/service/carrier/CarrierIdentifier.java
+++ b/telephony/java/android/service/carrier/CarrierIdentifier.java
@@ -20,7 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
import android.telephony.TelephonyManager;
import com.android.internal.telephony.uicc.IccUtils;
diff --git a/telephony/java/android/telephony/AnomalyReporter.java b/telephony/java/android/telephony/AnomalyReporter.java
index 9753d8be4065..097041f672ac 100644
--- a/telephony/java/android/telephony/AnomalyReporter.java
+++ b/telephony/java/android/telephony/AnomalyReporter.java
@@ -16,6 +16,8 @@
package android.telephony;
+import com.android.telephony.Rlog;
+
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.content.Context;
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 4d5713259a0f..6a622378dac7 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -16,6 +16,8 @@
package android.telephony;
+import com.android.telephony.Rlog;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -3123,7 +3125,6 @@ public class CarrierConfigManager {
* EAP-AKA: "0"
* EAP-SIM: "1"
* EAP-AKA_PRIME: "6"
- * @hide
*/
public static final String ENABLE_EAP_METHOD_PREFIX_BOOL = "enable_eap_method_prefix_bool";
@@ -3398,12 +3399,17 @@ public class CarrierConfigManager {
/** Prefix of all Ims.KEY_* constants. */
public static final String KEY_PREFIX = "ims.";
- //TODO: Add configs related to IMS.
+ /**
+ * Delay in milliseconds to turn off wifi when IMS is registered over wifi.
+ */
+ public static final String KEY_WIFI_OFF_DEFERRING_TIME_INT =
+ KEY_PREFIX + "wifi_off_deferring_time_int";
private Ims() {}
private static PersistableBundle getDefaults() {
PersistableBundle defaults = new PersistableBundle();
+ defaults.putInt(KEY_WIFI_OFF_DEFERRING_TIME_INT, 0);
return defaults;
}
}
diff --git a/telephony/java/android/telephony/CbGeoUtils.java b/telephony/java/android/telephony/CbGeoUtils.java
index 84be4e8b9ba4..719ba8d98773 100644
--- a/telephony/java/android/telephony/CbGeoUtils.java
+++ b/telephony/java/android/telephony/CbGeoUtils.java
@@ -16,6 +16,8 @@
package android.telephony;
+import com.android.telephony.Rlog;
+
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.text.TextUtils;
diff --git a/telephony/java/android/telephony/CellIdentity.java b/telephony/java/android/telephony/CellIdentity.java
index 534546921ebf..3f0aeb54f132 100644
--- a/telephony/java/android/telephony/CellIdentity.java
+++ b/telephony/java/android/telephony/CellIdentity.java
@@ -20,10 +20,13 @@ import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.hardware.radio.V1_0.CellInfoType;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import com.android.telephony.Rlog;
+
import java.util.Objects;
import java.util.UUID;
@@ -322,6 +325,86 @@ public abstract class CellIdentity implements Parcelable {
}
/** @hide */
+ public static CellIdentity create(android.hardware.radio.V1_0.CellIdentity cellIdentity) {
+ if (cellIdentity == null) return null;
+ switch(cellIdentity.cellInfoType) {
+ case CellInfoType.GSM: {
+ if (cellIdentity.cellIdentityGsm.size() == 1) {
+ return new CellIdentityGsm(cellIdentity.cellIdentityGsm.get(0));
+ }
+ break;
+ }
+ case CellInfoType.WCDMA: {
+ if (cellIdentity.cellIdentityWcdma.size() == 1) {
+ return new CellIdentityWcdma(cellIdentity.cellIdentityWcdma.get(0));
+ }
+ break;
+ }
+ case CellInfoType.TD_SCDMA: {
+ if (cellIdentity.cellIdentityTdscdma.size() == 1) {
+ return new CellIdentityTdscdma(cellIdentity.cellIdentityTdscdma.get(0));
+ }
+ break;
+ }
+ case CellInfoType.LTE: {
+ if (cellIdentity.cellIdentityLte.size() == 1) {
+ return new CellIdentityLte(cellIdentity.cellIdentityLte.get(0));
+ }
+ break;
+ }
+ case CellInfoType.CDMA: {
+ if (cellIdentity.cellIdentityCdma.size() == 1) {
+ return new CellIdentityCdma(cellIdentity.cellIdentityCdma.get(0));
+ }
+ break;
+ }
+ case CellInfoType.NONE: break;
+ default: break;
+ }
+ return null;
+ }
+
+ /** @hide */
+ public static CellIdentity create(android.hardware.radio.V1_2.CellIdentity cellIdentity) {
+ if (cellIdentity == null) return null;
+ switch(cellIdentity.cellInfoType) {
+ case CellInfoType.GSM: {
+ if (cellIdentity.cellIdentityGsm.size() == 1) {
+ return new CellIdentityGsm(cellIdentity.cellIdentityGsm.get(0));
+ }
+ break;
+ }
+ case CellInfoType.WCDMA: {
+ if (cellIdentity.cellIdentityWcdma.size() == 1) {
+ return new CellIdentityWcdma(cellIdentity.cellIdentityWcdma.get(0));
+ }
+ break;
+ }
+ case CellInfoType.TD_SCDMA: {
+ if (cellIdentity.cellIdentityTdscdma.size() == 1) {
+ return new CellIdentityTdscdma(cellIdentity.cellIdentityTdscdma.get(0));
+ }
+ break;
+ }
+ case CellInfoType.LTE: {
+ if (cellIdentity.cellIdentityLte.size() == 1) {
+ return new CellIdentityLte(cellIdentity.cellIdentityLte.get(0));
+ }
+ break;
+ }
+ case CellInfoType.CDMA: {
+ if (cellIdentity.cellIdentityCdma.size() == 1) {
+ return new CellIdentityCdma(cellIdentity.cellIdentityCdma.get(0));
+ }
+ break;
+ }
+ case CellInfoType.NONE: break;
+ default: break;
+ }
+ return null;
+ }
+
+ /** @hide */
public static CellIdentity create(android.hardware.radio.V1_5.CellIdentity ci) {
if (ci == null) return null;
switch (ci.getDiscriminator()) {
diff --git a/telephony/java/android/telephony/CellInfoCdma.java b/telephony/java/android/telephony/CellInfoCdma.java
index 2b1387c313ae..acb21f450243 100644
--- a/telephony/java/android/telephony/CellInfoCdma.java
+++ b/telephony/java/android/telephony/CellInfoCdma.java
@@ -16,6 +16,8 @@
package android.telephony;
+import com.android.telephony.Rlog;
+
import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
diff --git a/telephony/java/android/telephony/CellInfoGsm.java b/telephony/java/android/telephony/CellInfoGsm.java
index 4f7c7a93d19b..79a9d44f36a2 100644
--- a/telephony/java/android/telephony/CellInfoGsm.java
+++ b/telephony/java/android/telephony/CellInfoGsm.java
@@ -16,6 +16,8 @@
package android.telephony;
+import com.android.telephony.Rlog;
+
import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
diff --git a/telephony/java/android/telephony/CellInfoLte.java b/telephony/java/android/telephony/CellInfoLte.java
index 6d19261c8932..fed3ebf4af03 100644
--- a/telephony/java/android/telephony/CellInfoLte.java
+++ b/telephony/java/android/telephony/CellInfoLte.java
@@ -16,6 +16,8 @@
package android.telephony;
+import com.android.telephony.Rlog;
+
import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
diff --git a/telephony/java/android/telephony/CellInfoTdscdma.java b/telephony/java/android/telephony/CellInfoTdscdma.java
index f1305f5ca768..58ff8c9558d9 100644
--- a/telephony/java/android/telephony/CellInfoTdscdma.java
+++ b/telephony/java/android/telephony/CellInfoTdscdma.java
@@ -16,6 +16,8 @@
package android.telephony;
+import com.android.telephony.Rlog;
+
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
diff --git a/telephony/java/android/telephony/CellInfoWcdma.java b/telephony/java/android/telephony/CellInfoWcdma.java
index ee5fec838d2d..33f6a555414c 100644
--- a/telephony/java/android/telephony/CellInfoWcdma.java
+++ b/telephony/java/android/telephony/CellInfoWcdma.java
@@ -18,7 +18,7 @@ package android.telephony;
import android.os.Parcel;
import android.os.Parcelable;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
import java.util.Objects;
diff --git a/telephony/java/android/telephony/CellLocation.java b/telephony/java/android/telephony/CellLocation.java
index 11931996009c..2d0bd52f84ee 100644
--- a/telephony/java/android/telephony/CellLocation.java
+++ b/telephony/java/android/telephony/CellLocation.java
@@ -53,8 +53,7 @@ public abstract class CellLocation {
/**
* Create a new CellLocation from a intent notifier Bundle
*
- * This method is used by PhoneStateIntentReceiver and maybe by
- * external applications.
+ * This method maybe used by external applications.
*
* @param bundle Bundle from intent notifier
* @return newly created CellLocation
diff --git a/telephony/java/android/telephony/CellSignalStrengthCdma.java b/telephony/java/android/telephony/CellSignalStrengthCdma.java
index 199843905854..cab3b0cd3c47 100644
--- a/telephony/java/android/telephony/CellSignalStrengthCdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthCdma.java
@@ -20,7 +20,7 @@ import android.annotation.IntRange;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
import java.util.Objects;
diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java
index a9f3487a0880..28052aa93486 100644
--- a/telephony/java/android/telephony/CellSignalStrengthGsm.java
+++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java
@@ -16,6 +16,8 @@
package android.telephony;
+import com.android.telephony.Rlog;
+
import android.annotation.IntRange;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index a6ba9c279ae3..2ef2a52977ff 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -16,6 +16,8 @@
package android.telephony;
+import com.android.telephony.Rlog;
+
import android.annotation.IntRange;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
diff --git a/telephony/java/android/telephony/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java
index d28d750c6011..4d67bcf536cf 100644
--- a/telephony/java/android/telephony/CellSignalStrengthNr.java
+++ b/telephony/java/android/telephony/CellSignalStrengthNr.java
@@ -16,6 +16,8 @@
package android.telephony;
+import com.android.telephony.Rlog;
+
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.os.Parcel;
diff --git a/telephony/java/android/telephony/CellSignalStrengthTdscdma.java b/telephony/java/android/telephony/CellSignalStrengthTdscdma.java
index f4a3dbb37988..3bd9d5810136 100644
--- a/telephony/java/android/telephony/CellSignalStrengthTdscdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthTdscdma.java
@@ -16,6 +16,8 @@
package android.telephony;
+import com.android.telephony.Rlog;
+
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.os.Parcel;
diff --git a/telephony/java/android/telephony/CellSignalStrengthWcdma.java b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
index 34b13858f104..535e9520074d 100644
--- a/telephony/java/android/telephony/CellSignalStrengthWcdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
@@ -16,6 +16,8 @@
package android.telephony;
+import com.android.telephony.Rlog;
+
import android.annotation.IntRange;
import android.annotation.StringDef;
import android.compat.annotation.UnsupportedAppUsage;
diff --git a/telephony/java/android/telephony/NetworkScan.java b/telephony/java/android/telephony/NetworkScan.java
index b10649c7208f..a6dedf761636 100644
--- a/telephony/java/android/telephony/NetworkScan.java
+++ b/telephony/java/android/telephony/NetworkScan.java
@@ -16,6 +16,8 @@
package android.telephony;
+import com.android.telephony.Rlog;
+
import android.annotation.IntDef;
import android.os.RemoteException;
diff --git a/telephony/java/android/telephony/NetworkService.java b/telephony/java/android/telephony/NetworkService.java
index 8c5e10788b89..844289ce75d4 100644
--- a/telephony/java/android/telephony/NetworkService.java
+++ b/telephony/java/android/telephony/NetworkService.java
@@ -16,6 +16,8 @@
package android.telephony;
+import com.android.telephony.Rlog;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
diff --git a/telephony/java/android/telephony/NetworkServiceCallback.java b/telephony/java/android/telephony/NetworkServiceCallback.java
index 89b96654451e..214ab41ae4f2 100644
--- a/telephony/java/android/telephony/NetworkServiceCallback.java
+++ b/telephony/java/android/telephony/NetworkServiceCallback.java
@@ -16,6 +16,8 @@
package android.telephony;
+import com.android.telephony.Rlog;
+
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.SystemApi;
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 6e86a4211834..2f9e6ac0f9ff 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -16,6 +16,8 @@
package android.telephony;
+import com.android.telephony.Rlog;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 66feb7bac25d..2c62d0667e19 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -16,6 +16,8 @@
package android.telephony;
+import com.android.telephony.Rlog;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index 3350a3371504..1c58f8faf7cf 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -16,6 +16,8 @@
package android.telephony;
+import com.android.telephony.Rlog;
+
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -85,8 +87,7 @@ public class SignalStrength implements Parcelable {
/**
* Create a new SignalStrength from a intent notifier Bundle
*
- * This method is used by PhoneStateIntentReceiver and maybe by
- * external applications.
+ * This method may be used by external applications.
*
* @param m Bundle from intent notifier
* @return newly created SignalStrength
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index db33be313941..eb328a705e56 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -2448,18 +2448,14 @@ public final class SmsManager {
* @param sentIntent if not NULL this <code>PendingIntent</code> is
* broadcast when the message is successfully sent, or failed
* @throws IllegalArgumentException if contentUri is empty
- * @deprecated use {@link MmsManager#sendMultimediaMessage} instead.
*/
public void sendMultimediaMessage(Context context, Uri contentUri, String locationUrl,
Bundle configOverrides, PendingIntent sentIntent) {
if (contentUri == null) {
throw new IllegalArgumentException("Uri contentUri null");
}
- MmsManager m = (MmsManager) context.getSystemService(Context.MMS_SERVICE);
- if (m != null) {
- m.sendMultimediaMessage(getSubscriptionId(), contentUri, locationUrl, configOverrides,
- sentIntent);
- }
+ MmsManager.getInstance().sendMultimediaMessage(getSubscriptionId(), contentUri,
+ locationUrl, configOverrides, sentIntent);
}
/**
@@ -2483,7 +2479,6 @@ public final class SmsManager {
* @param downloadedIntent if not NULL this <code>PendingIntent</code> is
* broadcast when the message is downloaded, or the download is failed
* @throws IllegalArgumentException if locationUrl or contentUri is empty
- * @deprecated use {@link MmsManager#downloadMultimediaMessage} instead.
*/
public void downloadMultimediaMessage(Context context, String locationUrl, Uri contentUri,
Bundle configOverrides, PendingIntent downloadedIntent) {
@@ -2493,11 +2488,8 @@ public final class SmsManager {
if (contentUri == null) {
throw new IllegalArgumentException("Uri contentUri null");
}
- MmsManager m = (MmsManager) context.getSystemService(Context.MMS_SERVICE);
- if (m != null) {
- m.downloadMultimediaMessage(getSubscriptionId(), locationUrl, contentUri,
- configOverrides, downloadedIntent);
- }
+ MmsManager.getInstance().downloadMultimediaMessage(getSubscriptionId(), locationUrl,
+ contentUri, configOverrides, downloadedIntent);
}
// MMS send/download failure result codes
@@ -2539,9 +2531,9 @@ public final class SmsManager {
* </p>
*
* @return the bundle key/values pairs that contains MMS configuration values
- * or an empty bundle if they cannot be found.
*/
- @NonNull public Bundle getCarrierConfigValues() {
+ @Nullable
+ public Bundle getCarrierConfigValues() {
try {
ISms iSms = getISmsService();
if (iSms != null) {
@@ -2550,7 +2542,7 @@ public final class SmsManager {
} catch (RemoteException ex) {
// ignore it
}
- return new Bundle();
+ return null;
}
/**
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index c0bc29d26c6c..c217b8b83c26 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -16,6 +16,8 @@
package android.telephony;
+import com.android.telephony.Rlog;
+
import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
import android.annotation.Nullable;
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 2c0a1c94dc8d..c24eeb74f6cd 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -16,6 +16,8 @@
package android.telephony;
+import com.android.telephony.Rlog;
+
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 321753bc1776..4510fede4e8e 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -16,6 +16,8 @@
package android.telephony;
+import com.android.telephony.Rlog;
+
import static android.net.NetworkPolicyManager.OVERRIDE_CONGESTED;
import static android.net.NetworkPolicyManager.OVERRIDE_UNMETERED;
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 607450b76917..405b3a583247 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -46,6 +46,7 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.ConnectivityManager;
import android.net.NetworkStats;
@@ -449,12 +450,8 @@ public class TelephonyManager {
case UNKNOWN:
modemCount = MODEM_COUNT_SINGLE_MODEM;
// check for voice and data support, 0 if not supported
- if (!isVoiceCapable() && !isSmsCapable() && mContext != null) {
- ConnectivityManager cm = (ConnectivityManager) mContext
- .getSystemService(Context.CONNECTIVITY_SERVICE);
- if (cm != null && !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)) {
- modemCount = MODEM_COUNT_NO_MODEM;
- }
+ if (!isVoiceCapable() && !isSmsCapable() && !isDataCapable()) {
+ modemCount = MODEM_COUNT_NO_MODEM;
}
break;
case DSDS:
@@ -5868,7 +5865,10 @@ public class TelephonyManager {
* @param AID Application id. See ETSI 102.221 and 101.220.
* @param p2 P2 parameter (described in ISO 7816-4).
* @return an IccOpenLogicalChannelResponse object.
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
*/
+ // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
+ @Deprecated
public IccOpenLogicalChannelResponse iccOpenLogicalChannel(String AID, int p2) {
return iccOpenLogicalChannel(getSubId(), AID, p2);
}
@@ -5899,7 +5899,10 @@ public class TelephonyManager {
* @param p2 P2 parameter (described in ISO 7816-4).
* @return an IccOpenLogicalChannelResponse object.
* @hide
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
*/
+ // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
+ @Deprecated
public IccOpenLogicalChannelResponse iccOpenLogicalChannel(int subId, String AID, int p2) {
try {
ITelephony telephony = getITelephony();
@@ -5927,7 +5930,10 @@ public class TelephonyManager {
* iccOpenLogicalChannel.
* @return true if the channel was closed successfully.
* @hide
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
*/
+ // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
+ @Deprecated
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@SystemApi
public boolean iccCloseLogicalChannelBySlot(int slotIndex, int channel) {
@@ -5954,7 +5960,10 @@ public class TelephonyManager {
* @param channel is the channel id to be closed as returned by a successful
* iccOpenLogicalChannel.
* @return true if the channel was closed successfully.
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
*/
+ // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
+ @Deprecated
public boolean iccCloseLogicalChannel(int channel) {
return iccCloseLogicalChannel(getSubId(), channel);
}
@@ -5973,7 +5982,10 @@ public class TelephonyManager {
* iccOpenLogicalChannel.
* @return true if the channel was closed successfully.
* @hide
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
*/
+ // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
+ @Deprecated
public boolean iccCloseLogicalChannel(int subId, int channel) {
try {
ITelephony telephony = getITelephony();
@@ -6009,7 +6021,10 @@ public class TelephonyManager {
* @return The APDU response from the ICC card with the status appended at the end, or null if
* there is an issue connecting to the Telephony service.
* @hide
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
*/
+ // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
+ @Deprecated
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@SystemApi
@Nullable
@@ -6047,7 +6062,10 @@ public class TelephonyManager {
* @param data Data to be sent with the APDU.
* @return The APDU response from the ICC card with the status appended at
* the end.
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
*/
+ // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
+ @Deprecated
public String iccTransmitApduLogicalChannel(int channel, int cla,
int instruction, int p1, int p2, int p3, String data) {
return iccTransmitApduLogicalChannel(getSubId(), channel, cla,
@@ -6076,7 +6094,10 @@ public class TelephonyManager {
* @return The APDU response from the ICC card with the status appended at
* the end.
* @hide
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
*/
+ // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
+ @Deprecated
public String iccTransmitApduLogicalChannel(int subId, int channel, int cla,
int instruction, int p1, int p2, int p3, String data) {
try {
@@ -6112,7 +6133,10 @@ public class TelephonyManager {
* @return The APDU response from the ICC card with the status appended at
* the end.
* @hide
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
*/
+ // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
+ @Deprecated
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@SystemApi
@NonNull
@@ -6148,7 +6172,10 @@ public class TelephonyManager {
* @param data Data to be sent with the APDU.
* @return The APDU response from the ICC card with the status appended at
* the end.
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
*/
+ // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
+ @Deprecated
public String iccTransmitApduBasicChannel(int cla,
int instruction, int p1, int p2, int p3, String data) {
return iccTransmitApduBasicChannel(getSubId(), cla,
@@ -6175,7 +6202,10 @@ public class TelephonyManager {
* @return The APDU response from the ICC card with the status appended at
* the end.
* @hide
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
*/
+ // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
+ @Deprecated
public String iccTransmitApduBasicChannel(int subId, int cla,
int instruction, int p1, int p2, int p3, String data) {
try {
@@ -6203,7 +6233,10 @@ public class TelephonyManager {
* @param p3 P3 value of the APDU command.
* @param filePath
* @return The APDU response.
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
*/
+ // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
+ @Deprecated
public byte[] iccExchangeSimIO(int fileID, int command, int p1, int p2, int p3,
String filePath) {
return iccExchangeSimIO(getSubId(), fileID, command, p1, p2, p3, filePath);
@@ -6225,7 +6258,10 @@ public class TelephonyManager {
* @param filePath
* @return The APDU response.
* @hide
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
*/
+ // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
+ @Deprecated
public byte[] iccExchangeSimIO(int subId, int fileID, int command, int p1, int p2,
int p3, String filePath) {
try {
@@ -6251,7 +6287,10 @@ public class TelephonyManager {
* @return The APDU response from the ICC card in hexadecimal format
* with the last 4 bytes being the status word. If the command fails,
* returns an empty string.
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
*/
+ // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
+ @Deprecated
public String sendEnvelopeWithStatus(String content) {
return sendEnvelopeWithStatus(getSubId(), content);
}
@@ -6271,7 +6310,10 @@ public class TelephonyManager {
* with the last 4 bytes being the status word. If the command fails,
* returns an empty string.
* @hide
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
*/
+ // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
+ @Deprecated
public String sendEnvelopeWithStatus(int subId, String content) {
try {
ITelephony telephony = getITelephony();
@@ -10624,12 +10666,21 @@ public class TelephonyManager {
}
/**
+ * Checks whether cellular data connection is enabled in the device.
+ *
+ * Whether cellular data connection is enabled, meaning upon request whether will try to setup
+ * metered data connection considering all factors below:
+ * 1) User turned on data setting {@link #isDataEnabled}.
+ * 2) Carrier allows data to be on.
+ * 3) Network policy.
+ * And possibly others.
+ *
+ * @return {@code true} if the overall data connection is capable; {@code false} if not.
* @hide
- * It's similar to isDataEnabled, but unlike isDataEnabled, this API also evaluates
- * carrierDataEnabled, policyDataEnabled etc to give a final decision of whether mobile data is
- * capable of using.
*/
- public boolean isDataCapable() {
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public boolean isDataConnectionEnabled() {
boolean retVal = false;
try {
int subId = getSubId(SubscriptionManager.getDefaultDataSubscriptionId());
@@ -10637,13 +10688,24 @@ public class TelephonyManager {
if (telephony != null)
retVal = telephony.isDataEnabled(subId);
} catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelephony#isDataEnabled", e);
+ Log.e(TAG, "Error isDataConnectionEnabled", e);
} catch (NullPointerException e) {
}
return retVal;
}
/**
+ * Checks if FEATURE_TELEPHONY_DATA is enabled.
+ *
+ * @hide
+ */
+ public boolean isDataCapable() {
+ if (mContext == null) return true;
+ return mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY_DATA);
+ }
+
+ /**
* In this mode, modem will not send specified indications when screen is off.
* @hide
*/
diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java
index 8e6c170f255b..a1d40e85fb10 100644
--- a/telephony/java/android/telephony/TelephonyScanManager.java
+++ b/telephony/java/android/telephony/TelephonyScanManager.java
@@ -16,6 +16,8 @@
package android.telephony;
+import com.android.telephony.Rlog;
+
import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.Nullable;
diff --git a/telephony/java/android/telephony/UiccAccessRule.java b/telephony/java/android/telephony/UiccAccessRule.java
index 93ccba1dd996..81a09c645070 100644
--- a/telephony/java/android/telephony/UiccAccessRule.java
+++ b/telephony/java/android/telephony/UiccAccessRule.java
@@ -15,6 +15,8 @@
*/
package android.telephony;
+import com.android.telephony.Rlog;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
diff --git a/telephony/java/android/telephony/VoLteServiceState.java b/telephony/java/android/telephony/VoLteServiceState.java
index 414b9995fd58..d4a27d925208 100644
--- a/telephony/java/android/telephony/VoLteServiceState.java
+++ b/telephony/java/android/telephony/VoLteServiceState.java
@@ -16,6 +16,8 @@
package android.telephony;
+import com.android.telephony.Rlog;
+
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Bundle;
@@ -51,8 +53,7 @@ public final class VoLteServiceState implements Parcelable {
/**
* Create a new VoLteServiceState from a intent notifier Bundle
*
- * This method is used by PhoneStateIntentReceiver and maybe by
- * external applications.
+ * This method is maybe used by external applications.
*
* @param m Bundle from intent notifier
* @return newly created VoLteServiceState
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index dbfb6a2a0f2e..fab1bf2215af 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -28,7 +28,7 @@ import android.provider.Telephony;
import android.provider.Telephony.Carriers;
import android.telephony.Annotation.ApnType;
import android.telephony.Annotation.NetworkType;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index 372bdf1c0f81..bff12b624ae8 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -31,7 +31,7 @@ import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.telephony.AccessNetworkConstants;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java
index 11dc78a611ff..d33d3f9a5eee 100644
--- a/telephony/java/android/telephony/data/DataServiceCallback.java
+++ b/telephony/java/android/telephony/data/DataServiceCallback.java
@@ -22,7 +22,7 @@ import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.net.LinkProperties;
import android.os.RemoteException;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
import android.telephony.data.DataService.DataServiceProvider;
import java.lang.annotation.Retention;
diff --git a/telephony/java/android/telephony/data/QualifiedNetworksService.java b/telephony/java/android/telephony/data/QualifiedNetworksService.java
index e793979a61c9..8220b16500de 100644
--- a/telephony/java/android/telephony/data/QualifiedNetworksService.java
+++ b/telephony/java/android/telephony/data/QualifiedNetworksService.java
@@ -28,7 +28,7 @@ import android.os.Message;
import android.os.RemoteException;
import android.telephony.AccessNetworkConstants.AccessNetworkType;
import android.telephony.Annotation.ApnType;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java
index 16662652847d..cd3fc953f9d2 100644
--- a/telephony/java/android/telephony/emergency/EmergencyNumber.java
+++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java
@@ -25,7 +25,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.CarrierConfigManager;
import android.telephony.PhoneNumberUtils;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/telephony/java/android/telephony/ims/ImsConferenceState.java b/telephony/java/android/telephony/ims/ImsConferenceState.java
index 8d2049b97138..abfee61930ed 100644
--- a/telephony/java/android/telephony/ims/ImsConferenceState.java
+++ b/telephony/java/android/telephony/ims/ImsConferenceState.java
@@ -24,7 +24,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.telecom.Call;
import android.telecom.Connection;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
import android.util.Log;
import java.util.HashMap;
diff --git a/telephony/java/android/telephony/ims/ImsExternalCallState.java b/telephony/java/android/telephony/ims/ImsExternalCallState.java
index dcb9c9d5ec27..136a83e2eec9 100644
--- a/telephony/java/android/telephony/ims/ImsExternalCallState.java
+++ b/telephony/java/android/telephony/ims/ImsExternalCallState.java
@@ -24,7 +24,7 @@ import android.annotation.TestApi;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index 4f0f089dfa79..c96271432ea2 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -35,6 +35,8 @@ import android.telephony.ims.feature.RcsFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.util.Log;
+import com.android.internal.telephony.IIntegerConsumer;
+
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -158,9 +160,20 @@ public class ImsRcsManager implements RegistrationManager {
if (executor == null) {
throw new IllegalArgumentException("Must include a non-null Executor.");
}
+
+ IImsRcsController imsRcsController = getIImsRcsController();
+ if (imsRcsController == null) {
+ Log.e(TAG, "Register registration callback: IImsRcsController is null");
+ throw new ImsException("Cannot find remote IMS service",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+
c.setExecutor(executor);
- throw new UnsupportedOperationException("registerImsRegistrationCallback is not"
- + "supported.");
+ try {
+ imsRcsController.registerImsRegistrationCallback(mSubId, c.getBinder());
+ } catch (RemoteException | IllegalStateException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
}
/**{@inheritDoc}*/
@@ -171,8 +184,18 @@ public class ImsRcsManager implements RegistrationManager {
if (c == null) {
throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
}
- throw new UnsupportedOperationException("unregisterImsRegistrationCallback is not"
- + "supported.");
+
+ IImsRcsController imsRcsController = getIImsRcsController();
+ if (imsRcsController == null) {
+ Log.e(TAG, "Unregister registration callback: IImsRcsController is null");
+ throw new IllegalStateException("Cannot find remote IMS service");
+ }
+
+ try {
+ imsRcsController.unregisterImsRegistrationCallback(mSubId, c.getBinder());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
}
/**{@inheritDoc}*/
@@ -186,8 +209,23 @@ public class ImsRcsManager implements RegistrationManager {
if (executor == null) {
throw new IllegalArgumentException("Must include a non-null Executor.");
}
- throw new UnsupportedOperationException("getRegistrationState is not"
- + "supported.");
+
+ IImsRcsController imsRcsController = getIImsRcsController();
+ if (imsRcsController == null) {
+ Log.e(TAG, "Get registration state error: IImsRcsController is null");
+ throw new IllegalStateException("Cannot find remote IMS service");
+ }
+
+ try {
+ imsRcsController.getImsRcsRegistrationState(mSubId, new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ executor.execute(() -> stateCallback.accept(result));
+ }
+ });
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
}
/**{@inheritDoc}*/
@@ -202,10 +240,25 @@ public class ImsRcsManager implements RegistrationManager {
if (executor == null) {
throw new IllegalArgumentException("Must include a non-null Executor.");
}
- throw new UnsupportedOperationException("getRegistrationTransportType is not"
- + "supported.");
- }
+ IImsRcsController imsRcsController = getIImsRcsController();
+ if (imsRcsController == null) {
+ Log.e(TAG, "Get registration transport type error: IImsRcsController is null");
+ throw new IllegalStateException("Cannot find remote IMS service");
+ }
+
+ try {
+ imsRcsController.getImsRcsRegistrationTransportType(mSubId,
+ new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ executor.execute(() -> transportTypeCallback.accept(result));
+ }
+ });
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
/**
* Registers an {@link AvailabilityCallback} with the system, which will provide RCS
diff --git a/telephony/java/android/telephony/ims/ImsSsData.java b/telephony/java/android/telephony/ims/ImsSsData.java
index 6b728599c7d3..2d2e63812fad 100644
--- a/telephony/java/android/telephony/ims/ImsSsData.java
+++ b/telephony/java/android/telephony/ims/ImsSsData.java
@@ -22,7 +22,7 @@ import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
index e81bac0f6764..6f6aa44371fa 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
@@ -19,6 +19,9 @@ package android.telephony.ims.aidl;
import android.net.Uri;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IRcsUceControllerCallback;
+import android.telephony.ims.aidl.IImsRegistrationCallback;
+
+import com.android.internal.telephony.IIntegerConsumer;
/**
* Interface used to interact with the Telephony IMS.
@@ -26,6 +29,13 @@ import android.telephony.ims.aidl.IRcsUceControllerCallback;
* {@hide}
*/
interface IImsRcsController {
+ // IMS RCS registration commands
+ void registerImsRegistrationCallback(int subId, IImsRegistrationCallback c);
+ void unregisterImsRegistrationCallback(int subId, IImsRegistrationCallback c);
+ void getImsRcsRegistrationState(int subId, IIntegerConsumer consumer);
+ void getImsRcsRegistrationTransportType(int subId, IIntegerConsumer consumer);
+
+ // IMS RCS capability commands
void registerRcsAvailabilityCallback(int subId, IImsCapabilityCallback c);
void unregisterRcsAvailabilityCallback(int subId, IImsCapabilityCallback c);
boolean isCapable(int subId, int capability, int radioTech);
diff --git a/telephony/java/com/android/ims/ImsConfig.java b/telephony/java/com/android/ims/ImsConfig.java
index cfc803ca3639..9116a3bf3bde 100644
--- a/telephony/java/com/android/ims/ImsConfig.java
+++ b/telephony/java/com/android/ims/ImsConfig.java
@@ -19,7 +19,7 @@ package com.android.ims;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.ProvisioningManager;
import android.telephony.ims.aidl.IImsConfig;
diff --git a/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java b/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java
index 1d6ec2d82eb7..8e86ff788a08 100644
--- a/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java
+++ b/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java
@@ -19,7 +19,7 @@ package com.android.internal.telephony;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
import android.util.SparseIntArray;
import com.android.internal.telephony.cdma.sms.UserData;
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index f78c65ffb3aa..54c07cfd3428 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -123,32 +123,6 @@ public class TelephonyIntents {
public static final String ACTION_EMERGENCY_CALL_STATE_CHANGED
= TelephonyManager.ACTION_EMERGENCY_CALL_STATE_CHANGED;
- /**
- * Broadcast Action: The phone's signal strength has changed. The intent will have the
- * following extra values:</p>
- * <ul>
- * <li><em>phoneName</em> - A string version of the phone name.</li>
- * <li><em>asu</em> - A numeric value for the signal strength.
- * An ASU is 0-31 or -1 if unknown (for GSM, dBm = -113 - 2 * asu).
- * The following special values are defined:
- * <ul><li>0 means "-113 dBm or less".</li><li>31 means "-51 dBm or greater".</li></ul>
- * </li>
- * </ul>
- *
- * <p class="note">
- * You can <em>not</em> receive this through components declared
- * in manifests, only by exlicitly registering for it with
- * {@link android.content.Context#registerReceiver(android.content.BroadcastReceiver,
- * android.content.IntentFilter) Context.registerReceiver()}.
- *
- * <p class="note">
- * Requires the READ_PHONE_STATE permission.
- *
- * <p class="note">This is a protected intent that can only be sent
- * by the system.
- */
- public static final String ACTION_SIGNAL_STRENGTH_CHANGED = "android.intent.action.SIG_STR";
-
/**
* Broadcast Action: The data connection state has changed for any one of the
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index e75c5933f1df..832502cae37d 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -20,7 +20,7 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.Resources;
import android.sysprop.TelephonyProperties;
import android.telephony.PhoneNumberUtils;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
import android.telephony.SmsCbLocation;
import android.telephony.SmsCbMessage;
import android.telephony.cdma.CdmaSmsCbProgramData;
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
index b5af6467a0ad..cbf0f5c297e1 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -18,7 +18,7 @@ package com.android.internal.telephony.cdma.sms;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.Resources;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
import android.telephony.SmsCbCmasInfo;
import android.telephony.cdma.CdmaSmsCbProgramData;
import android.telephony.cdma.CdmaSmsCbProgramResults;
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index 0681dc11066e..417aafd765ea 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -28,7 +28,7 @@ import static com.android.internal.telephony.SmsConstants.MessageClass;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.Resources;
import android.telephony.PhoneNumberUtils;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
import android.text.TextUtils;
import com.android.internal.telephony.EncodeException;
diff --git a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
index eed9a86cf448..0dc740194034 100644
--- a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
+++ b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
@@ -21,7 +21,7 @@ import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
import android.graphics.Bitmap;
import android.graphics.Color;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.GsmAlphabet;
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index b4cafe41662e..656628eb39d5 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -896,39 +896,78 @@ public class PackageWatchdogTest {
assertThat(observer.mHealthCheckFailedPackages).containsExactly(APP_A);
}
- /** Test that observers execute correctly for different failure reasons */
+ /** Test that observers execute correctly for failures reasons that go through thresholding. */
@Test
- public void testFailureReasons() {
+ public void testNonImmediateFailureReasons() {
PackageWatchdog watchdog = createWatchdog();
TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
- TestObserver observer3 = new TestObserver(OBSERVER_NAME_3);
- TestObserver observer4 = new TestObserver(OBSERVER_NAME_4);
watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
watchdog.startObservingHealth(observer2, Arrays.asList(APP_B), SHORT_DURATION);
- watchdog.startObservingHealth(observer3, Arrays.asList(APP_C), SHORT_DURATION);
- watchdog.startObservingHealth(observer4, Arrays.asList(APP_D), SHORT_DURATION);
raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A,
- VERSION_CODE)), PackageWatchdog.FAILURE_REASON_NATIVE_CRASH);
- raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_B,
- VERSION_CODE)), PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK);
- raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_C,
VERSION_CODE)), PackageWatchdog.FAILURE_REASON_APP_CRASH);
- raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_D,
+ raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_B,
VERSION_CODE)), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING);
assertThat(observer1.getLastFailureReason()).isEqualTo(
- PackageWatchdog.FAILURE_REASON_NATIVE_CRASH);
- assertThat(observer2.getLastFailureReason()).isEqualTo(
- PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK);
- assertThat(observer3.getLastFailureReason()).isEqualTo(
PackageWatchdog.FAILURE_REASON_APP_CRASH);
- assertThat(observer4.getLastFailureReason()).isEqualTo(
+ assertThat(observer2.getLastFailureReason()).isEqualTo(
PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING);
}
+ /** Test that observers execute correctly for failures reasons that skip thresholding. */
+ @Test
+ public void testImmediateFailures() {
+ PackageWatchdog watchdog = createWatchdog();
+ TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
+
+ watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+
+ raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A,
+ VERSION_CODE)), PackageWatchdog.FAILURE_REASON_NATIVE_CRASH);
+ raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_B,
+ VERSION_CODE)), PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK);
+
+ assertThat(observer1.mMitigatedPackages).containsExactly(APP_A, APP_B);
+ }
+
+ /**
+ * Test that a persistent observer will mitigate failures if it wishes to observe a package.
+ */
+ @Test
+ public void testPersistentObserverWatchesPackage() {
+ PackageWatchdog watchdog = createWatchdog();
+ TestObserver persistentObserver = new TestObserver(OBSERVER_NAME_1);
+ persistentObserver.setPersistent(true);
+ persistentObserver.setMayObservePackages(true);
+
+ watchdog.startObservingHealth(persistentObserver, Arrays.asList(APP_B), SHORT_DURATION);
+
+ raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A,
+ VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN);
+ assertThat(persistentObserver.mHealthCheckFailedPackages).containsExactly(APP_A);
+ }
+
+ /**
+ * Test that a persistent observer will not mitigate failures if it does not wish to observe
+ * a given package.
+ */
+ @Test
+ public void testPersistentObserverDoesNotWatchPackage() {
+ PackageWatchdog watchdog = createWatchdog();
+ TestObserver persistentObserver = new TestObserver(OBSERVER_NAME_1);
+ persistentObserver.setPersistent(true);
+ persistentObserver.setMayObservePackages(false);
+
+ watchdog.startObservingHealth(persistentObserver, Arrays.asList(APP_B), SHORT_DURATION);
+
+ raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A,
+ VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN);
+ assertThat(persistentObserver.mHealthCheckFailedPackages).isEmpty();
+ }
+
private void adoptShellPermissions(String... permissions) {
InstrumentationRegistry
.getInstrumentation()
@@ -964,7 +1003,12 @@ public class PackageWatchdogTest {
/** Trigger package failures above the threshold. */
private void raiseFatalFailureAndDispatch(PackageWatchdog watchdog,
List<VersionedPackage> packages, int failureReason) {
- for (int i = 0; i < watchdog.getTriggerFailureCount(); i++) {
+ long triggerFailureCount = watchdog.getTriggerFailureCount();
+ if (failureReason == PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK
+ || failureReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) {
+ triggerFailureCount = 1;
+ }
+ for (int i = 0; i < triggerFailureCount; i++) {
watchdog.onPackageFailure(packages, failureReason);
}
mTestLooper.dispatchAll();
@@ -1000,6 +1044,8 @@ public class PackageWatchdogTest {
private final String mName;
private int mImpact;
private int mLastFailureReason;
+ private boolean mIsPersistent = false;
+ private boolean mMayObservePackages = false;
final List<String> mHealthCheckFailedPackages = new ArrayList<>();
final List<String> mMitigatedPackages = new ArrayList<>();
@@ -1028,9 +1074,25 @@ public class PackageWatchdogTest {
return mName;
}
+ public boolean isPersistent() {
+ return mIsPersistent;
+ }
+
+ public boolean mayObservePackage(String packageName) {
+ return mMayObservePackages;
+ }
+
public int getLastFailureReason() {
return mLastFailureReason;
}
+
+ public void setPersistent(boolean persistent) {
+ mIsPersistent = persistent;
+ }
+
+ public void setMayObservePackages(boolean mayObservePackages) {
+ mMayObservePackages = mayObservePackages;
+ }
}
private static class TestController extends ExplicitHealthCheckController {
diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp
index 2bc129ae4840..091edd4dc0d9 100644
--- a/tests/RollbackTest/Android.bp
+++ b/tests/RollbackTest/Android.bp
@@ -19,15 +19,17 @@ android_test {
static_libs: ["androidx.test.rules", "cts-rollback-lib", "cts-install-lib"],
test_suites: ["general-tests"],
test_config: "RollbackTest.xml",
+ java_resources: [":com.android.apex.apkrollback.test_v2"],
}
java_test_host {
name: "StagedRollbackTest",
srcs: ["StagedRollbackTest/src/**/*.java"],
libs: ["tradefed"],
- static_libs: ["testng"],
+ static_libs: ["testng", "compatibility-tradefed"],
test_suites: ["general-tests"],
test_config: "StagedRollbackTest.xml",
+ data: [":com.android.apex.apkrollback.test_v1"],
}
java_test_host {
@@ -37,3 +39,44 @@ java_test_host {
test_suites: ["general-tests"],
test_config: "MultiUserRollbackTest.xml",
}
+
+genrule {
+ name: "com.android.apex.apkrollback.test.pem",
+ out: ["com.android.apex.apkrollback.test.pem"],
+ cmd: "openssl genrsa -out $(out) 4096",
+}
+
+genrule {
+ name: "com.android.apex.apkrollback.test.pubkey",
+ srcs: [":com.android.apex.apkrollback.test.pem"],
+ out: ["com.android.apex.apkrollback.test.pubkey"],
+ tools: ["avbtool"],
+ cmd: "$(location avbtool) extract_public_key --key $(in) --output $(out)",
+}
+
+apex_key {
+ name: "com.android.apex.apkrollback.test.key",
+ private_key: ":com.android.apex.apkrollback.test.pem",
+ public_key: ":com.android.apex.apkrollback.test.pubkey",
+ installable: false,
+}
+
+apex {
+ name: "com.android.apex.apkrollback.test_v1",
+ manifest: "testdata/manifest_v1.json",
+ androidManifest: "testdata/AndroidManifest.xml",
+ file_contexts: ":apex.test-file_contexts",
+ key: "com.android.apex.apkrollback.test.key",
+ apps: ["TestAppAv1"],
+ installable: false,
+}
+
+apex {
+ name: "com.android.apex.apkrollback.test_v2",
+ manifest: "testdata/manifest_v2.json",
+ androidManifest: "testdata/AndroidManifest.xml",
+ file_contexts: ":apex.test-file_contexts",
+ key: "com.android.apex.apkrollback.test.key",
+ apps: ["TestAppAv2"],
+ installable: false,
+} \ No newline at end of file
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
index 9e490f765eab..3877cc139a3e 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -435,11 +435,64 @@ public class StagedRollbackTest {
// testNativeWatchdogTriggersRollback will fail if multiple staged sessions are
// committed on a device which doesn't support checkpoint. Let's clean up all rollbacks
// so there is only one rollback to commit when testing native crashes.
- RollbackManager rm = RollbackUtils.getRollbackManager();
+ RollbackManager rm = RollbackUtils.getRollbackManager();
rm.getAvailableRollbacks().stream().flatMap(info -> info.getPackages().stream())
.map(info -> info.getPackageName()).forEach(rm::expireRollbackForPackage);
}
+ private static final String APK_IN_APEX_TESTAPEX_NAME = "com.android.apex.apkrollback.test";
+ private static final TestApp TEST_APEX_WITH_APK_V1 = new TestApp("TestApexWithApkV1",
+ APK_IN_APEX_TESTAPEX_NAME, 1, /*isApex*/true, APK_IN_APEX_TESTAPEX_NAME + "_v1.apex");
+ private static final TestApp TEST_APEX_WITH_APK_V2 = new TestApp("TestApexWithApkV2",
+ APK_IN_APEX_TESTAPEX_NAME, 2, /*isApex*/true, APK_IN_APEX_TESTAPEX_NAME + "_v2.apex");
+ private static final TestApp TEST_APP_A_V2_UNKNOWN = new TestApp("Av2Unknown", TestApp.A, 0,
+ /*isApex*/false, "TestAppAv2.apk");
+
+ @Test
+ public void testRollbackApexWithApk_Phase1() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+ InstallUtils.processUserData(TestApp.A);
+
+ int sessionId = Install.single(TEST_APEX_WITH_APK_V2).setStaged().setEnableRollback()
+ .commit();
+ InstallUtils.waitForSessionReady(sessionId);
+ }
+
+ @Test
+ public void testRollbackApexWithApk_Phase2() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(APK_IN_APEX_TESTAPEX_NAME)).isEqualTo(2);
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
+ InstallUtils.processUserData(TestApp.A);
+
+ RollbackInfo available = RollbackUtils.getAvailableRollback(APK_IN_APEX_TESTAPEX_NAME);
+ assertThat(available).isStaged();
+ assertThat(available).packagesContainsExactly(
+ Rollback.from(TEST_APEX_WITH_APK_V2).to(TEST_APEX_WITH_APK_V1),
+ Rollback.from(TEST_APP_A_V2_UNKNOWN).to(TestApp.A1));
+
+ RollbackUtils.rollback(available.getRollbackId(), TEST_APEX_WITH_APK_V2);
+ RollbackInfo committed = RollbackUtils.getCommittedRollbackById(available.getRollbackId());
+ assertThat(committed).isNotNull();
+ assertThat(committed).isStaged();
+ assertThat(committed).packagesContainsExactly(
+ Rollback.from(TEST_APEX_WITH_APK_V2).to(TEST_APEX_WITH_APK_V1),
+ Rollback.from(TEST_APP_A_V2_UNKNOWN).to(TestApp.A1));
+ assertThat(committed).causePackagesContainsExactly(TEST_APEX_WITH_APK_V2);
+ assertThat(committed.getCommittedSessionId()).isNotEqualTo(-1);
+
+ // Note: The app is not rolled back until after the rollback is staged
+ // and the device has been rebooted.
+ InstallUtils.waitForSessionReady(committed.getCommittedSessionId());
+ assertThat(InstallUtils.getInstalledVersion(APK_IN_APEX_TESTAPEX_NAME)).isEqualTo(2);
+ }
+
+ @Test
+ public void testRollbackApexWithApk_Phase3() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(APK_IN_APEX_TESTAPEX_NAME)).isEqualTo(1);
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+ InstallUtils.processUserData(TestApp.A);
+ }
+
private static void runShellCommand(String cmd) {
ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation().getUiAutomation()
.executeShellCommand(cmd);
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
index 91577c202df9..6daa6bc723c4 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -19,6 +19,7 @@ package com.android.tests.rollback.host;
import static org.junit.Assert.assertTrue;
import static org.testng.Assert.assertThrows;
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
@@ -27,6 +28,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.File;
import java.util.concurrent.TimeUnit;
/**
@@ -48,14 +50,32 @@ public class StagedRollbackTest extends BaseHostJUnit4Test {
phase));
}
+ private static final String APK_IN_APEX_TESTAPEX_NAME = "com.android.apex.apkrollback.test";
+
@Before
public void setUp() throws Exception {
+ if (!getDevice().isAdbRoot()) {
+ getDevice().enableAdbRoot();
+ }
+ getDevice().remountSystemWritable();
+ getDevice().executeShellCommand(
+ "rm -f /system/apex/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex "
+ + "/data/apex/active/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex");
getDevice().reboot();
}
@After
public void tearDown() throws Exception {
runPhase("testCleanUp");
+
+ if (!getDevice().isAdbRoot()) {
+ getDevice().enableAdbRoot();
+ }
+ getDevice().remountSystemWritable();
+ getDevice().executeShellCommand(
+ "rm -f /system/apex/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex "
+ + "/data/apex/active/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex");
+ getDevice().reboot();
}
/**
@@ -184,6 +204,28 @@ public class StagedRollbackTest extends BaseHostJUnit4Test {
runPhase("testRollbackDataPolicy_Phase3");
}
+ /**
+ * Tests that userdata of apk-in-apex is restored when apex is rolled back.
+ */
+ @Test
+ public void testRollbackApexWithApk() throws Exception {
+ getDevice().uninstallPackage("com.android.cts.install.lib.testapp.A");
+ CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
+ final String fileName = APK_IN_APEX_TESTAPEX_NAME + "_v1.apex";
+ final File apex = buildHelper.getTestFile(fileName);
+ if (!getDevice().isAdbRoot()) {
+ getDevice().enableAdbRoot();
+ }
+ getDevice().remountSystemWritable();
+ assertTrue(getDevice().pushFile(apex, "/system/apex/" + fileName));
+ getDevice().reboot();
+ runPhase("testRollbackApexWithApk_Phase1");
+ getDevice().reboot();
+ runPhase("testRollbackApexWithApk_Phase2");
+ getDevice().reboot();
+ runPhase("testRollbackApexWithApk_Phase3");
+ }
+
private void crashProcess(String processName, int numberOfCrashes) throws Exception {
String pid = "";
String lastPid = "invalid";
diff --git a/tests/RollbackTest/testdata/AndroidManifest.xml b/tests/RollbackTest/testdata/AndroidManifest.xml
new file mode 100644
index 000000000000..f21ec899eb69
--- /dev/null
+++ b/tests/RollbackTest/testdata/AndroidManifest.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.apex.apkrollback.test">
+ <!-- APEX does not have classes.dex -->
+ <application android:hasCode="false" />
+ <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="29"/>
+</manifest>
+
diff --git a/tests/RollbackTest/testdata/manifest_v1.json b/tests/RollbackTest/testdata/manifest_v1.json
new file mode 100644
index 000000000000..1762fc6764cf
--- /dev/null
+++ b/tests/RollbackTest/testdata/manifest_v1.json
@@ -0,0 +1,4 @@
+{
+ "name": "com.android.apex.apkrollback.test",
+ "version": 1
+}
diff --git a/tests/RollbackTest/testdata/manifest_v2.json b/tests/RollbackTest/testdata/manifest_v2.json
new file mode 100644
index 000000000000..c5127b9c3023
--- /dev/null
+++ b/tests/RollbackTest/testdata/manifest_v2.json
@@ -0,0 +1,4 @@
+{
+ "name": "com.android.apex.apkrollback.test",
+ "version": 2
+}
diff --git a/tests/TaskOrganizerTest/Android.bp b/tests/TaskOrganizerTest/Android.bp
new file mode 100644
index 000000000000..8a13dbc52c66
--- /dev/null
+++ b/tests/TaskOrganizerTest/Android.bp
@@ -0,0 +1,22 @@
+//
+// 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.
+//
+
+android_test {
+ name: "TaskOrganizerTest",
+ srcs: ["**/*.java"],
+ platform_apis: true,
+ certificate: "platform",
+}
diff --git a/packages/SettingsProvider/res/values/overlayable.xml b/tests/TaskOrganizerTest/AndroidManifest.xml
index dc41a77d0e2d..0cb6c10a7ff5 100644
--- a/packages/SettingsProvider/res/values/overlayable.xml
+++ b/tests/TaskOrganizerTest/AndroidManifest.xml
@@ -1,25 +1,23 @@
-<?xml version="1.0" encoding="utf-8" ?>
+<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <overlayable name="SettingsToNotRestore">
- <policy type="product|system|vendor">
- <item type="array" name="restore_blocked_device_specific_settings" />
- <item type="array" name="restore_blocked_global_settings" />
- <item type="array" name="restore_blocked_secure_settings" />
- <item type="array" name="restore_blocked_system_settings" />
- </policy>
- </overlayable>
-</resources>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test.taskembed">
+ <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
+ <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+ <application>
+ <service android:name=".TaskOrganizerPipTest"
+ android:exported="true">
+ </service>
+ </application>
+</manifest>
diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java
new file mode 100644
index 000000000000..6ffa19d4ec98
--- /dev/null
+++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java
@@ -0,0 +1,127 @@
+/*
+ * 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.test.taskembed;
+
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.Service;
+import android.app.WindowConfiguration;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.view.ITaskOrganizer;
+import android.view.IWindowContainer;
+import android.view.WindowContainerTransaction;
+import android.view.SurfaceControl;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+
+public class TaskOrganizerPipTest extends Service {
+ static final int PIP_WIDTH = 640;
+ static final int PIP_HEIGHT = 360;
+
+ class PipOrgView extends SurfaceView implements SurfaceHolder.Callback {
+ PipOrgView(Context c) {
+ super(c);
+ getHolder().addCallback(this);
+ setZOrderOnTop(true);
+ }
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ try {
+ ActivityTaskManager.getService().registerTaskOrganizer(mOrganizer,
+ WindowConfiguration.WINDOWING_MODE_PINNED);
+ } catch (Exception e) {
+ }
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ }
+
+ void reparentTask(IWindowContainer wc) {
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ SurfaceControl leash = null;
+ try {
+ leash = wc.getLeash();
+ } catch (Exception e) {
+ // System server died.. oh well
+ }
+ t.reparent(leash, getSurfaceControl())
+ .setPosition(leash, 0, 0)
+ .apply();
+ }
+ }
+
+ PipOrgView mPipView;
+
+ class Organizer extends ITaskOrganizer.Stub {
+ public void taskAppeared(IWindowContainer wc, ActivityManager.RunningTaskInfo ti) {
+ mPipView.reparentTask(wc);
+
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.scheduleFinishEnterPip(wc, new Rect(0, 0, PIP_WIDTH, PIP_HEIGHT));
+ try {
+ ActivityTaskManager.getService().applyContainerTransaction(wct);
+ } catch (Exception e) {
+ }
+ }
+ public void taskVanished(IWindowContainer wc) {
+ }
+ public void transactionReady(int id, SurfaceControl.Transaction t) {
+ }
+ }
+
+ Organizer mOrganizer = new Organizer();
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ final WindowManager.LayoutParams wlp = new WindowManager.LayoutParams();
+ wlp.setTitle("TaskOrganizerPipTest");
+ wlp.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+ wlp.width = wlp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
+
+ FrameLayout layout = new FrameLayout(this);
+ ViewGroup.LayoutParams lp =
+ new ViewGroup.LayoutParams(PIP_WIDTH, PIP_HEIGHT);
+ mPipView = new PipOrgView(this);
+ layout.addView(mPipView, lp);
+
+ WindowManager wm = getSystemService(WindowManager.class);
+ wm.addView(layout, wlp);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ }
+}
diff --git a/tests/utils/testutils/Android.bp b/tests/utils/testutils/Android.bp
index f71be7b0b7d3..a6625ab9c17f 100644
--- a/tests/utils/testutils/Android.bp
+++ b/tests/utils/testutils/Android.bp
@@ -22,6 +22,7 @@ java_library {
static_libs: [
"junit",
"hamcrest-library",
+ "androidx.test.runner",
],
libs: [
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MessageCapturingHandler.java b/tests/utils/testutils/java/com/android/server/accessibility/test/MessageCapturingHandler.java
index e2b517f875db..bce2ab5c5a7f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MessageCapturingHandler.java
+++ b/tests/utils/testutils/java/com/android/server/accessibility/test/MessageCapturingHandler.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.accessibility;
+package com.android.server.accessibility.test;
import android.os.Handler;
import android.os.Looper;
@@ -31,7 +31,7 @@ import java.util.List;
* at their target.
*/
public class MessageCapturingHandler extends Handler {
- List<Pair<Message, Long>> timedMessages = new ArrayList<>();
+ public List<Pair<Message, Long>> timedMessages = new ArrayList<>();
Handler.Callback mCallback;
diff --git a/tools/hiddenapi/generate_hiddenapi_lists.py b/tools/hiddenapi/generate_hiddenapi_lists.py
index 46105f4d66b0..0b2077d9bba0 100755
--- a/tools/hiddenapi/generate_hiddenapi_lists.py
+++ b/tools/hiddenapi/generate_hiddenapi_lists.py
@@ -149,7 +149,12 @@ def extract_package(signature):
The package name of the class containing the field/method.
"""
full_class_name = signature.split(";->")[0]
- package_name = full_class_name[1:full_class_name.rindex("/")]
+ # Example: Landroid/hardware/radio/V1_2/IRadio$Proxy
+ if (full_class_name[0] != "L"):
+ raise ValueError("Expected to start with 'L': %s" % full_class_name)
+ full_class_name = full_class_name[1:]
+ # If full_class_name doesn't contain '/', then package_name will be ''.
+ package_name = full_class_name.rpartition("/")[0]
return package_name.replace('/', '.')
class FlagsDict:
diff --git a/tools/lock_agent/Android.bp b/tools/lock_agent/Android.bp
index 79dce4a8ce09..7b2ca9a65242 100644
--- a/tools/lock_agent/Android.bp
+++ b/tools/lock_agent/Android.bp
@@ -25,6 +25,7 @@ cc_binary_host {
srcs: ["agent.cpp"],
static_libs: [
"libbase",
+ "liblog",
"libz",
"slicer",
],
diff --git a/wifi/Android.bp b/wifi/Android.bp
index 09d5386dcfc5..286be0b82db7 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -32,6 +32,7 @@ filegroup {
// framework-wifi.jar. This is not a good idea, should move WifiNetworkScoreCache
// to a separate package.
"java/android/net/wifi/WifiNetworkScoreCache.java",
+ "java/android/net/wifi/WifiOemConfigStoreMigrationHook.java",
"java/android/net/wifi/wificond/*.java",
":libwificond_ipc_aidl",
],
@@ -67,10 +68,15 @@ java_library {
optimize: {
enabled: false
},
+ hostdex: true, // for hiddenapi check
visibility: [
"//frameworks/base", // TODO(b/140299412) remove once all dependencies are fixed
"//frameworks/opt/net/wifi/service:__subpackages__",
] + test_access_hidden_api_whitelist,
+ apex_available: [
+ "com.android.wifi",
+ "test_com.android.wifi",
+ ],
plugins: ["java_api_finder"],
}
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 71942f0f80f4..f490766559de 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -250,4 +250,6 @@ interface IWifiManager
void unregisterSuggestionConnectionStatusListener(int listenerIdentifier, String packageName);
int calculateSignalLevel(int rssi);
+
+ List<WifiConfiguration> getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(in List<ScanResult> scanResults);
}
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java
index 65e9b7910e5e..2b7f8af728d7 100644
--- a/wifi/java/android/net/wifi/SoftApConfiguration.java
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.java
@@ -182,6 +182,12 @@ public final class SoftApConfiguration implements Parcelable {
private final @SecurityType int mSecurityType;
/**
+ * Delay in milliseconds before shutting down soft AP when
+ * there are no connected devices.
+ */
+ private final int mShutdownTimeoutMillis;
+
+ /**
* Security types we support.
*/
/** @hide */
@@ -213,7 +219,7 @@ public final class SoftApConfiguration implements Parcelable {
/** Private constructor for Builder and Parcelable implementation. */
private SoftApConfiguration(@Nullable String ssid, @Nullable MacAddress bssid,
@Nullable String passphrase, boolean hiddenSsid, @BandType int band, int channel,
- @SecurityType int securityType, int maxNumberOfClients) {
+ @SecurityType int securityType, int maxNumberOfClients, int shutdownTimeoutMillis) {
mSsid = ssid;
mBssid = bssid;
mPassphrase = passphrase;
@@ -222,6 +228,7 @@ public final class SoftApConfiguration implements Parcelable {
mChannel = channel;
mSecurityType = securityType;
mMaxNumberOfClients = maxNumberOfClients;
+ mShutdownTimeoutMillis = shutdownTimeoutMillis;
}
@Override
@@ -240,13 +247,14 @@ public final class SoftApConfiguration implements Parcelable {
&& mBand == other.mBand
&& mChannel == other.mChannel
&& mSecurityType == other.mSecurityType
- && mMaxNumberOfClients == other.mMaxNumberOfClients;
+ && mMaxNumberOfClients == other.mMaxNumberOfClients
+ && mShutdownTimeoutMillis == other.mShutdownTimeoutMillis;
}
@Override
public int hashCode() {
return Objects.hash(mSsid, mBssid, mPassphrase, mHiddenSsid,
- mBand, mChannel, mSecurityType, mMaxNumberOfClients);
+ mBand, mChannel, mSecurityType, mMaxNumberOfClients, mShutdownTimeoutMillis);
}
@Override
@@ -261,6 +269,7 @@ public final class SoftApConfiguration implements Parcelable {
sbuf.append(" \n Channel =").append(mChannel);
sbuf.append(" \n SecurityType=").append(getSecurityType());
sbuf.append(" \n MaxClient=").append(mMaxNumberOfClients);
+ sbuf.append(" \n ShutdownTimeoutMillis=").append(mShutdownTimeoutMillis);
return sbuf.toString();
}
@@ -274,6 +283,7 @@ public final class SoftApConfiguration implements Parcelable {
dest.writeInt(mChannel);
dest.writeInt(mSecurityType);
dest.writeInt(mMaxNumberOfClients);
+ dest.writeInt(mShutdownTimeoutMillis);
}
@Override
@@ -289,7 +299,7 @@ public final class SoftApConfiguration implements Parcelable {
in.readString(),
in.readParcelable(MacAddress.class.getClassLoader()),
in.readString(), in.readBoolean(), in.readInt(), in.readInt(), in.readInt(),
- in.readInt());
+ in.readInt(), in.readInt());
}
@Override
@@ -316,19 +326,6 @@ public final class SoftApConfiguration implements Parcelable {
return mBssid;
}
- // TODO: Remove it after update the caller
- /**
- * Returns String set to be passphrase for the WPA2-PSK AP.
- * {@link #setWpa2Passphrase(String)}.
- */
- @Nullable
- public String getWpa2Passphrase() {
- if (mSecurityType == SECURITY_TYPE_WPA2_PSK) {
- return mPassphrase;
- }
- return null;
- }
-
/**
* Returns String set to be passphrase for current AP.
* {@link #setPassphrase(String, @SecurityType int)}.
@@ -381,6 +378,15 @@ public final class SoftApConfiguration implements Parcelable {
}
/**
+ * Returns the shutdown timeout in milliseconds.
+ * The Soft AP will shutdown when there are no devices associated to it for
+ * the timeout duration. See {@link Builder#setShutdownTimeoutMillis(int)}.
+ */
+ public int getShutdownTimeoutMillis() {
+ return mShutdownTimeoutMillis;
+ }
+
+ /**
* Builds a {@link SoftApConfiguration}, which allows an app to configure various aspects of a
* Soft AP.
*
@@ -396,6 +402,7 @@ public final class SoftApConfiguration implements Parcelable {
private int mChannel;
private int mMaxNumberOfClients;
private int mSecurityType;
+ private int mShutdownTimeoutMillis;
/**
* Constructs a Builder with default values (see {@link Builder}).
@@ -409,6 +416,7 @@ public final class SoftApConfiguration implements Parcelable {
mChannel = 0;
mMaxNumberOfClients = 0;
mSecurityType = SECURITY_TYPE_OPEN;
+ mShutdownTimeoutMillis = 0;
}
/**
@@ -425,6 +433,7 @@ public final class SoftApConfiguration implements Parcelable {
mChannel = other.mChannel;
mMaxNumberOfClients = other.mMaxNumberOfClients;
mSecurityType = other.mSecurityType;
+ mShutdownTimeoutMillis = other.mShutdownTimeoutMillis;
}
/**
@@ -435,7 +444,8 @@ public final class SoftApConfiguration implements Parcelable {
@NonNull
public SoftApConfiguration build() {
return new SoftApConfiguration(mSsid, mBssid, mPassphrase,
- mHiddenSsid, mBand, mChannel, mSecurityType, mMaxNumberOfClients);
+ mHiddenSsid, mBand, mChannel, mSecurityType, mMaxNumberOfClients,
+ mShutdownTimeoutMillis);
}
/**
@@ -482,22 +492,6 @@ public final class SoftApConfiguration implements Parcelable {
return this;
}
- // TODO: Remove it after update the caller
- /**
- * Specifies that this AP should use WPA2-PSK with the given ASCII WPA2 passphrase.
- * When set to null, an open network is created.
- * <p>
- *
- * @param passphrase The passphrase to use, or null to unset a previously-set WPA2-PSK
- * configuration.
- * @return Builder for chaining.
- * @throws IllegalArgumentException when the passphrase is the empty string
- */
- @NonNull
- public Builder setWpa2Passphrase(@Nullable String passphrase) {
- return setPassphrase(passphrase, SECURITY_TYPE_WPA2_PSK);
- }
-
/**
* Specifies that this AP should use specific security type with the given ASCII passphrase.
*
@@ -643,5 +637,30 @@ public final class SoftApConfiguration implements Parcelable {
mMaxNumberOfClients = maxNumberOfClients;
return this;
}
+
+ /**
+ * Specifies the shutdown timeout in milliseconds.
+ * The Soft AP will shut down when there are no devices connected to it for
+ * the timeout duration.
+ *
+ * Specify a value of 0 to have the framework automatically use default timeout
+ * setting which defined in {@link R.integer.config_wifi_framework_soft_ap_timeout_delay}
+ *
+ * <p>
+ * <li>If not set, defaults to 0</li>
+ * <li>The shut down timout will apply when
+ * {@link Settings.Global.SOFT_AP_TIMEOUT_ENABLED} is true</li>
+ *
+ * @param timeoutMillis milliseconds of the timeout delay.
+ * @return Builder for chaining.
+ */
+ @NonNull
+ public Builder setShutdownTimeoutMillis(int timeoutMillis) {
+ if (timeoutMillis < 0) {
+ throw new IllegalArgumentException("Invalid timeout value");
+ }
+ mShutdownTimeoutMillis = timeoutMillis;
+ return this;
+ }
}
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index e870481ccf26..1baab12b8ab5 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1366,6 +1366,36 @@ public class WifiManager {
}
/**
+ * Retrieve a list of {@link WifiConfiguration} for available {@link WifiNetworkSuggestion}
+ * matching the given list of {@link ScanResult}.
+ *
+ * An available {@link WifiNetworkSuggestion} must satisfy:
+ * <ul>
+ * <li> Matching one of the {@link ScanResult} from the given list.
+ * <li> and {@link WifiNetworkSuggestion.Builder#setIsUserAllowedToManuallyConnect(boolean)} set
+ * to true.
+ * </ul>
+ *
+ * @param scanResults a list of scanResult.
+ * @return a list of @link WifiConfiguration} for available {@link WifiNetworkSuggestion}
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD
+ })
+ @NonNull
+ public List<WifiConfiguration> getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(
+ @NonNull List<ScanResult> scanResults) {
+ try {
+ return mService.getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(scanResults);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
* Returns a list of unique Hotspot 2.0 OSU (Online Sign-Up) providers associated with a given
* list of ScanResult.
*
diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
index 9c1475ffc8cd..c0e089090dc9 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
@@ -116,12 +116,12 @@ public final class WifiNetworkSuggestion implements Parcelable {
/**
* Whether this network is shared credential with user to allow user manually connect.
*/
- private boolean mIsUserAllowed;
+ private boolean mIsSharedWithUser;
/**
- * Whether the setIsUserAllowedToManuallyConnect have been called.
+ * Whether the setCredentialSharedWithUser have been called.
*/
- private boolean mIsUserAllowedBeenSet;
+ private boolean mIsSharedWithUserSet;
/**
* Pre-shared key for use with WAPI-PSK networks.
*/
@@ -146,8 +146,8 @@ public final class WifiNetworkSuggestion implements Parcelable {
mIsAppInteractionRequired = false;
mIsUserInteractionRequired = false;
mIsMetered = false;
- mIsUserAllowed = true;
- mIsUserAllowedBeenSet = false;
+ mIsSharedWithUser = true;
+ mIsSharedWithUserSet = false;
mPriority = UNASSIGNED_PRIORITY;
mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
mWapiPskPassphrase = null;
@@ -430,13 +430,13 @@ public final class WifiNetworkSuggestion implements Parcelable {
* <li>If not set, defaults to true (i.e. allow user to manually connect) for secure
* networks and false for open networks.</li>
*
- * @param isAllowed {@code true} to indicate that the credentials may be used by the user to
+ * @param isShared {@code true} to indicate that the credentials may be used by the user to
* manually connect to the network, {@code false} otherwise.
* @return Instance of {@link Builder} to enable chaining of the builder method.
*/
- public @NonNull Builder setIsUserAllowedToManuallyConnect(boolean isAllowed) {
- mIsUserAllowed = isAllowed;
- mIsUserAllowedBeenSet = true;
+ public @NonNull Builder setCredentialSharedWithUser(boolean isShared) {
+ mIsSharedWithUser = isShared;
+ mIsSharedWithUserSet = true;
return this;
}
@@ -602,11 +602,11 @@ public final class WifiNetworkSuggestion implements Parcelable {
}
wifiConfiguration = buildWifiConfiguration();
if (wifiConfiguration.isOpenNetwork()) {
- if (mIsUserAllowedBeenSet && mIsUserAllowed) {
+ if (mIsSharedWithUserSet && mIsSharedWithUser) {
throw new IllegalStateException("Open network should not be "
- + "setIsUserAllowedToManuallyConnect to true");
+ + "setCredentialSharedWithUser to true");
}
- mIsUserAllowed = false;
+ mIsSharedWithUser = false;
}
}
@@ -615,7 +615,7 @@ public final class WifiNetworkSuggestion implements Parcelable {
mPasspointConfiguration,
mIsAppInteractionRequired,
mIsUserInteractionRequired,
- mIsUserAllowed);
+ mIsSharedWithUser);
}
}
diff --git a/wifi/java/android/net/wifi/WifiOemConfigStoreMigrationHook.java b/wifi/java/android/net/wifi/WifiOemConfigStoreMigrationHook.java
new file mode 100755
index 000000000000..642dcb9211ae
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiOemConfigStoreMigrationHook.java
@@ -0,0 +1,180 @@
+/*
+ * 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.wifi;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.List;
+
+/**
+ * Class used to provide one time hooks for existing OEM devices to migrate their config store
+ * data to the wifi mainline module.
+ * <p>
+ * Note:
+ * <li> OEM's need to implement {@link #load()} 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 #load()} 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 and then return null on any subsequent reboots. The first & only relevant invocation
+ * of {@link #load()} occurs when a previously released device upgrades to the wifi
+ * mainline module from an OEM implementation of the wifi stack.
+ * </li>
+ * @hide
+ */
+@SystemApi
+public final class WifiOemConfigStoreMigrationHook {
+ /**
+ * Container for all the wifi config data to migrate.
+ */
+ public static final class MigrationData implements Parcelable {
+ /**
+ * Builder to create instance of {@link MigrationData}.
+ */
+ 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 MigrationData}.
+ *
+ * @return Instance of {@link MigrationData}.
+ */
+ public @NonNull MigrationData build() {
+ return new MigrationData(mUserSavedNetworkConfigurations, mUserSoftApConfiguration);
+ }
+ }
+
+ private final List<WifiConfiguration> mUserSavedNetworkConfigurations;
+ private final SoftApConfiguration mUserSoftApConfiguration;
+
+ private MigrationData(
+ @Nullable List<WifiConfiguration> userSavedNetworkConfigurations,
+ @Nullable SoftApConfiguration userSoftApConfiguration) {
+ mUserSavedNetworkConfigurations = userSavedNetworkConfigurations;
+ mUserSoftApConfiguration = userSoftApConfiguration;
+ }
+
+ public static final @NonNull Parcelable.Creator<MigrationData> CREATOR =
+ new Parcelable.Creator<MigrationData>() {
+ @Override
+ public MigrationData createFromParcel(Parcel in) {
+ List<WifiConfiguration> userSavedNetworkConfigurations =
+ in.readArrayList(null);
+ SoftApConfiguration userSoftApConfiguration = in.readParcelable(null);
+ return new MigrationData(
+ userSavedNetworkConfigurations, userSoftApConfiguration);
+ }
+
+ @Override
+ public MigrationData[] newArray(int size) {
+ return new MigrationData[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;
+ }
+ }
+
+ private WifiOemConfigStoreMigrationHook() { }
+
+ /**
+ * Load data from OEM's config store.
+ *
+ * @return Instance of {@link MigrationData} for migrating data, null if no
+ * migration is necessary.
+ */
+ @Nullable
+ public static MigrationData load() {
+ // Note: OEM's should add code to parse data from their config store format here!
+ return null;
+ }
+}
diff --git a/wifi/java/com/android/server/wifi/BaseWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java
index d91efbccc2de..3c13562d6952 100644
--- a/wifi/java/com/android/server/wifi/BaseWifiService.java
+++ b/wifi/java/com/android/server/wifi/BaseWifiService.java
@@ -589,4 +589,10 @@ public class BaseWifiService extends IWifiManager.Stub {
public int calculateSignalLevel(int rssi) {
throw new UnsupportedOperationException();
}
+
+ @Override
+ public List<WifiConfiguration> getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(
+ List<ScanResult> scanResults) {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
index acd334355806..eeea7e2a6cd8 100644
--- a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
@@ -117,6 +117,7 @@ public class SoftApConfigurationTest {
.setChannel(149, SoftApConfiguration.BAND_5GHZ)
.setHiddenSsid(true)
.setMaxNumberOfClients(10)
+ .setShutdownTimeoutMillis(500000)
.build();
assertThat(original.getPassphrase()).isEqualTo("secretsecret");
assertThat(original.getSecurityType()).isEqualTo(
@@ -125,6 +126,7 @@ public class SoftApConfigurationTest {
assertThat(original.getChannel()).isEqualTo(149);
assertThat(original.isHiddenSsid()).isEqualTo(true);
assertThat(original.getMaxNumberOfClients()).isEqualTo(10);
+ assertThat(original.getShutdownTimeoutMillis()).isEqualTo(500000);
SoftApConfiguration unparceled = parcelUnparcel(original);
assertThat(unparceled).isNotSameAs(original);
@@ -230,4 +232,10 @@ public class SoftApConfigurationTest {
.build();
}
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalieShutdownTimeoutMillis() {
+ SoftApConfiguration original = new SoftApConfiguration.Builder()
+ .setShutdownTimeoutMillis(-1)
+ .build();
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 5ac50a0d1c97..4b837184dc9a 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -2185,4 +2185,18 @@ public class WifiManagerTest {
result = WifiManager.parseDppChannelList(channelList);
assertEquals(result.size(), 0);
}
+
+ /**
+ * Test getWifiConfigsForMatchedNetworkSuggestions for given scanResults.
+ */
+ @Test
+ public void testGetWifiConfigsForMatchedNetworkSuggestions() throws Exception {
+ List<WifiConfiguration> testResults = new ArrayList<>();
+ testResults.add(new WifiConfiguration());
+
+ when(mWifiService.getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(any(List.class)))
+ .thenReturn(testResults);
+ assertEquals(testResults, mWifiManager
+ .getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(new ArrayList<>()));
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
index 4cdc4bc2ad48..ac915447f3c4 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
@@ -76,7 +76,7 @@ public class WifiNetworkSuggestionTest {
.setSsid(TEST_SSID)
.setWpa2Passphrase(TEST_PRESHARED_KEY)
.setIsAppInteractionRequired(true)
- .setIsUserAllowedToManuallyConnect(false)
+ .setCredentialSharedWithUser(false)
.setPriority(0)
.build();
@@ -151,7 +151,7 @@ public class WifiNetworkSuggestionTest {
WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder()
.setSsid(TEST_SSID)
.setWpa3Passphrase(TEST_PRESHARED_KEY)
- .setIsUserAllowedToManuallyConnect(true)
+ .setCredentialSharedWithUser(true)
.build();
assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID);
@@ -709,14 +709,14 @@ public class WifiNetworkSuggestionTest {
/**
* Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception
- * when {@link WifiNetworkSuggestion.Builder#setIsUserAllowedToManuallyConnect(boolean)} to
+ * when {@link WifiNetworkSuggestion.Builder#setCredentialSharedWithUser(boolean)} to
* true on a open network suggestion.
*/
@Test(expected = IllegalStateException.class)
public void testSetIsUserAllowedToManuallyConnectToWithOpenNetwork() {
WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder()
.setSsid(TEST_SSID)
- .setIsUserAllowedToManuallyConnect(true)
+ .setCredentialSharedWithUser(true)
.build();
}
}