summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp11
-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/sdkextensions/TEST_MAPPING2
-rw-r--r--api/current.txt30
-rwxr-xr-xapi/system-current.txt16
-rw-r--r--cmds/statsd/Android.bp3
-rw-r--r--cmds/statsd/src/external/StatsPullerManager.cpp5
-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--core/java/android/app/admin/DevicePolicyManagerInternal.java22
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java7
-rw-r--r--core/java/android/content/pm/CrossProfileApps.java57
-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/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.java7
-rw-r--r--core/java/android/os/UpdateEngine.java10
-rw-r--r--core/java/android/provider/Settings.java41
-rw-r--r--core/java/android/text/SpannableStringInternal.java13
-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.java113
-rw-r--r--core/java/android/view/InsetsSourceConsumer.java28
-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.java1
-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/com/android/internal/app/AccessibilityButtonChooserActivity.java260
-rw-r--r--core/res/AndroidManifest.xml5
-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/accessibility/AccessibilityManagerTest.java (renamed from services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerTest.java)44
-rw-r--r--data/etc/privapp-permissions-platform.xml1
-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/tv/tuner/Descrambler.java103
-rw-r--r--media/java/android/media/tv/tuner/Dvr.java152
-rw-r--r--media/java/android/media/tv/tuner/Filter.java142
-rw-r--r--media/java/android/media/tv/tuner/FrontendCapabilities.java291
-rw-r--r--media/java/android/media/tv/tuner/TimeFilter.java127
-rw-r--r--media/java/android/media/tv/tuner/Tuner.java437
-rw-r--r--media/java/android/media/tv/tuner/TunerConstants.java15
-rw-r--r--media/java/android/media/tv/tuner/TunerUtils.java13
-rw-r--r--media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java4
-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/Atsc3FrontendCapabilities.java65
-rw-r--r--media/java/android/media/tv/tuner/frontend/AtscFrontendCapabilities.java33
-rw-r--r--media/java/android/media/tv/tuner/frontend/DvbcFrontendCapabilities.java46
-rw-r--r--media/java/android/media/tv/tuner/frontend/DvbsFrontendCapabilities.java46
-rw-r--r--media/java/android/media/tv/tuner/frontend/DvbtFrontendCapabilities.java78
-rw-r--r--media/java/android/media/tv/tuner/frontend/FrontendCapabilities.java24
-rw-r--r--media/java/android/media/tv/tuner/frontend/FrontendInfo.java1
-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/IsdbsFrontendCapabilities.java40
-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--packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java3
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java3
-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/SystemUI/res/layout/media_carousel.xml35
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java12
-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/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/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.java46
-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/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/NotificationGroupAlertTransferHelper.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java3
-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.java15
-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/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.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java3
-rw-r--r--packages/Tethering/AndroidManifest.xml1
-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--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/core/Android.bp6
-rw-r--r--services/core/java/com/android/server/GnssManagerService.java9
-rw-r--r--services/core/java/com/android/server/LocationManagerService.java852
-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/am/AppErrors.java25
-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/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
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java2
-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.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java4
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java38
-rw-r--r--services/core/java/com/android/server/pm/Settings.java19
-rw-r--r--services/core/java/com/android/server/policy/PermissionPolicyService.java27
-rw-r--r--services/core/java/com/android/server/wm/ActivityStackSupervisor.java3
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java7
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java2
-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/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java129
-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/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/pm/PackageInstallerSessionTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java48
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java68
-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.java2
-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.java2
-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/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.java2
-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.java45
-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.java2
-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/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/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
-rw-r--r--wifi/java/android/net/wifi/SoftApConfiguration.java58
-rw-r--r--wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java8
270 files changed, 6256 insertions, 4407 deletions
diff --git a/Android.bp b/Android.bp
index efe530798041..0d9cbd8daf5c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -442,8 +442,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
@@ -619,6 +617,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/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/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/api/current.txt b/api/current.txt
index b673df0b5c90..0c93555fc483 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11356,6 +11356,8 @@ package android.content.pm {
}
public class CrossProfileApps {
+ method public boolean canInteractAcrossProfiles();
+ method public boolean canRequestInteractAcrossProfiles();
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 +11930,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 +29158,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;
@@ -39524,7 +39528,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";
@@ -45010,6 +45016,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";
@@ -45207,6 +45214,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 {
@@ -46134,12 +46142,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();
@@ -46157,7 +46165,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);
@@ -51599,9 +51607,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);
@@ -53281,9 +53290,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..3d5a2a8d4a1c 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4311,7 +4311,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 {
@@ -4855,6 +4855,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,6 +5836,7 @@ 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();
@@ -5860,6 +5862,7 @@ 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);
}
@@ -7185,7 +7188,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();
@@ -7860,6 +7863,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";
@@ -10399,10 +10403,10 @@ 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);
@@ -11833,6 +11837,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/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/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 50896f84da43..1d31873209b2 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"
@@ -273,10 +272,6 @@ std::map<PullerKey, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = {
// 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},
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/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/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
index e897b917fcc2..abf32c5e0840 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -30,6 +30,7 @@ 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 +170,62 @@ 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();
+ }
+ }
+
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/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..5fe647c8b6bf 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -758,11 +758,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/provider/Settings.java b/core/java/android/provider/Settings.java
index 9ee4cc30949d..06805238e6f5 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -14404,6 +14404,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 +14465,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/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/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..0207abdda355 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -37,6 +37,7 @@ 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;
@@ -47,6 +48,8 @@ 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 +70,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 +143,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 +161,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
onAnimationFinish();
}
});
+ mStartingAnimation = true;
mAnimator.start();
+ mStartingAnimation = false;
}
@Override
@@ -185,6 +217,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
private int mPendingTypesToShow;
private int mLastLegacySoftInputMode;
+ private boolean mStartingAnimation;
private SyncRtSurfaceTransactionApplier mApplier;
@@ -312,7 +345,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 +371,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 +396,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 +433,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 +448,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 +490,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 +656,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 +690,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/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/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..ab89ef46e09e 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.
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/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/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index a808ed8fcc81..5aa0a2dda17e 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -933,11 +933,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 -->
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/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/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/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/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..0bfba8f9d4f3
--- /dev/null
+++ b/media/java/android/media/tv/tuner/Dvr.java
@@ -0,0 +1,152 @@
+/*
+ * 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.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/Filter.java b/media/java/android/media/tv/tuner/Filter.java
new file mode 100644
index 000000000000..db3b97afb1da
--- /dev/null
+++ b/media/java/android/media/tv/tuner/Filter.java
@@ -0,0 +1,142 @@
+/*
+ * 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.annotation.Nullable;
+import android.media.tv.tuner.Tuner.FilterCallback;
+import android.media.tv.tuner.filter.FilterConfiguration;
+import android.media.tv.tuner.filter.Settings;
+
+/**
+ * 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;
+ int mId;
+
+ private native int nativeConfigureFilter(
+ int type, int subType, FilterConfiguration settings);
+ private native int nativeGetId();
+ private native int nativeSetDataSource(Tuner.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) {
+ }
+
+ /**
+ * 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 Tuner.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.
+ *
+ * @hide
+ */
+ @Override
+ public void close() {
+ nativeClose();
+ }
+}
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/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..962a7f6d58f6 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -21,18 +21,15 @@ 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 +37,6 @@ import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import java.io.FileDescriptor;
import java.util.List;
/**
@@ -458,122 +454,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,
@@ -590,115 +475,6 @@ public final class Tuner implements AutoCloseable {
}
/**
- * 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.
@@ -822,81 +598,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);
- }
-
- /**
- * Release the descrambler instance.
- *
- * @return result status of the operation.
- *
- * @hide
- */
- public int close() {
- return nativeClose();
+ private Descrambler() {
}
-
}
/**
@@ -911,137 +617,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..bbaa5180aece 100644
--- a/media/java/android/media/tv/tuner/TunerConstants.java
+++ b/media/java/android/media/tv/tuner/TunerConstants.java
@@ -128,21 +128,6 @@ public final class TunerConstants {
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,
FILTER_SUBTYPE_AUDIO, FILTER_SUBTYPE_VIDEO, FILTER_SUBTYPE_DOWNLOAD,
FILTER_SUBTYPE_RECORD, FILTER_SUBTYPE_TS, FILTER_SUBTYPE_PCR, FILTER_SUBTYPE_TEMI,
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/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/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/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/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/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/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/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/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..5d03570eea80 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendInfo.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendInfo.java
@@ -16,7 +16,6 @@
package android.media.tv.tuner.frontend;
-import android.media.tv.tuner.FrontendCapabilities;
import android.media.tv.tuner.TunerConstants.FrontendType;
/**
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/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/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/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 1ee85188eac3..3c3ebe2fe228 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -110,7 +110,6 @@ import com.android.systemui.statusbar.notification.VisualStabilityManager;
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;
@@ -277,7 +276,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
NotificationGutsManager notificationGutsManager,
NotificationLogger notificationLogger,
NotificationEntryManager notificationEntryManager,
- NotificationRowContentBinder notificationRowContentBinder,
NotificationInterruptionStateProvider notificationInterruptionStateProvider,
NotificationViewHierarchyManager notificationViewHierarchyManager,
KeyguardViewMediator keyguardViewMediator,
@@ -365,7 +363,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
notificationGutsManager,
notificationLogger,
notificationEntryManager,
- notificationRowContentBinder,
notificationInterruptionStateProvider,
notificationViewHierarchyManager,
keyguardViewMediator,
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..a1eccceea771 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
@@ -70,7 +70,6 @@ import com.android.systemui.statusbar.notification.VisualStabilityManager;
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;
@@ -147,7 +146,6 @@ public class CarStatusBarModule {
NotificationGutsManager notificationGutsManager,
NotificationLogger notificationLogger,
NotificationEntryManager notificationEntryManager,
- NotificationRowContentBinder notificationRowContentBinder,
NotificationInterruptionStateProvider notificationInterruptionStateProvider,
NotificationViewHierarchyManager notificationViewHierarchyManager,
KeyguardViewMediator keyguardViewMediator,
@@ -234,7 +232,6 @@ public class CarStatusBarModule {
notificationGutsManager,
notificationLogger,
notificationEntryManager,
- notificationRowContentBinder,
notificationInterruptionStateProvider,
notificationViewHierarchyManager,
keyguardViewMediator,
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/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/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/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/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/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..6dc647d33046 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
@@ -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;
@@ -65,7 +65,6 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
Dependency.get(NotificationInterruptionStateProvider.class);
private final Context mContext;
- private final NotificationRowContentBinder mRowContentBinder;
private final NotificationMessagingUtil mMessagingUtil;
private final ExpandableNotificationRow.ExpansionLogger mExpansionLogger =
this::logNotificationExpansion;
@@ -77,7 +76,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
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;
@@ -85,13 +84,11 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
public NotificationRowBinderImpl(
Context context,
- NotificationRowContentBinder rowContentBinder,
boolean allowLongPress,
KeyguardBypassController keyguardBypassController,
StatusBarStateController statusBarStateController,
NotificationLogger logger) {
mContext = context;
- mRowContentBinder = rowContentBinder;
mMessagingUtil = new NotificationMessagingUtil(context);
mAllowLongPress = allowLongPress;
mKeyguardBypassController = keyguardBypassController;
@@ -120,7 +117,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 +156,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);
+ getRemoteInputManager().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 +186,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);
}
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/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/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/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 30825ed65eb3..ccc86b1f8c5f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -205,7 +205,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;
@@ -414,7 +413,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;
@@ -636,7 +634,6 @@ public class StatusBar extends SystemUI implements DemoMode,
NotificationGutsManager notificationGutsManager,
NotificationLogger notificationLogger,
NotificationEntryManager notificationEntryManager,
- NotificationRowContentBinder notificationRowContentBinder,
NotificationInterruptionStateProvider notificationInterruptionStateProvider,
NotificationViewHierarchyManager notificationViewHierarchyManager,
KeyguardViewMediator keyguardViewMediator,
@@ -718,7 +715,6 @@ public class StatusBar extends SystemUI implements DemoMode,
mGutsManager = notificationGutsManager;
mNotificationLogger = notificationLogger;
mEntryManager = notificationEntryManager;
- mRowContentBinder = notificationRowContentBinder;
mNotificationInterruptionStateProvider = notificationInterruptionStateProvider;
mViewHierarchyManager = notificationViewHierarchyManager;
mKeyguardViewMediator = keyguardViewMediator;
@@ -1244,7 +1240,6 @@ public class StatusBar extends SystemUI implements DemoMode,
final NotificationRowBinderImpl rowBinder =
new NotificationRowBinderImpl(
mContext,
- mRowContentBinder,
mAllowNotificationLongPress,
mKeyguardBypassController,
mStatusBarStateController,
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..153ca22933a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
@@ -69,7 +69,6 @@ import com.android.systemui.statusbar.notification.VisualStabilityManager;
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;
@@ -126,7 +125,6 @@ public class StatusBarModule {
NotificationGutsManager notificationGutsManager,
NotificationLogger notificationLogger,
NotificationEntryManager notificationEntryManager,
- NotificationRowContentBinder notificationRowContentBinder,
NotificationInterruptionStateProvider notificationInterruptionStateProvider,
NotificationViewHierarchyManager notificationViewHierarchyManager,
KeyguardViewMediator keyguardViewMediator,
@@ -209,7 +207,6 @@ public class StatusBarModule {
notificationGutsManager,
notificationLogger,
notificationEntryManager,
- notificationRowContentBinder,
notificationInterruptionStateProvider,
notificationViewHierarchyManager,
keyguardViewMediator,
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..cd33cf922482 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
@@ -83,14 +83,12 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB
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;
@@ -236,7 +234,8 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
mock(NotificationFilter.class),
mNotifLog,
mock(NotificationSectionsFeatureManager.class),
- mock(PeopleNotificationIdentifier.class)),
+ mock(PeopleNotificationIdentifier.class),
+ mock(HighPriorityProvider.class)),
mEnvironment,
mFeatureFlags
);
@@ -244,14 +243,8 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
mEntryManager.addNotificationEntryListener(mEntryListener);
mEntryManager.setNotificationRemoveInterceptor(mRemoveInterceptor);
- NotificationRowContentBinder contentBinder = new NotificationContentInflater(
- mock(NotifRemoteViewCache.class),
- mRemoteInputManager);
-
NotificationRowBinderImpl notificationRowBinder =
- new NotificationRowBinderImpl(mContext,
- contentBinder,
- true, /* allowLongPress */
+ new NotificationRowBinderImpl(mContext, true, /* allowLongPress */
mock(KeyguardBypassController.class),
mock(StatusBarStateController.class),
mock(NotificationLogger.class));
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/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..ea8d4ee20aab 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,7 @@ 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.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;
@@ -165,7 +166,8 @@ 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));
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..7e485f45e5e6 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
@@ -122,7 +122,6 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
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 +247,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;
@@ -358,7 +356,6 @@ public class StatusBarTest extends SysuiTestCase {
mNotificationGutsManager,
notificationLogger,
mEntryManager,
- mNotificationContentInflater,
mNotificationInterruptionStateProvider,
mNotificationViewHierarchyManager,
mKeyguardViewMediator,
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/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/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/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/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/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 0fc5340846f2..32128d5f26f8 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;
@@ -74,7 +72,6 @@ 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;
@@ -92,6 +89,7 @@ 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,6 +103,7 @@ 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.pm.permission.PermissionManagerServiceInternal;
@@ -121,6 +120,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;
/**
@@ -196,6 +197,8 @@ public class LocationManagerService extends ILocationManager.Stub {
private final LocationSettingsStore mSettingsStore;
private final LocationUsageLogger mLocationUsageLogger;
+ private final PassiveLocationProviderManager mPassiveManager;
+
private AppOpsManager mAppOps;
private PackageManager mPackageManager;
private PowerManager mPowerManager;
@@ -205,21 +208,17 @@ public class LocationManagerService extends ILocationManager.Stub {
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,9 +237,9 @@ 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};
+ // current active user on the device
+ private int mCurrentUserId;
+ private int[] mCurrentUserProfiles;
@GuardedBy("mLock")
@PowerManager.LocationPowerSaveMode
@@ -252,6 +251,17 @@ public class LocationManagerService extends ILocationManager.Stub {
mSettingsStore = new LocationSettingsStore(mContext, mHandler);
mLocationUsageLogger = new LocationUsageLogger();
+ mCurrentUserId = UserHandle.USER_NULL;
+ mCurrentUserProfiles = new int[]{UserHandle.USER_NULL};
+
+ // 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(
@@ -415,15 +425,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 +452,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 +476,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,22 +531,22 @@ 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);
}
}
@@ -623,22 +633,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 +661,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,10 +747,7 @@ 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());
}
}
@@ -771,231 +761,202 @@ public class LocationManagerService extends ILocationManager.Stub {
Log.d(TAG, "foreground user is changing to " + userId);
}
- int oldUserId = userId;
+ int oldUserId = mCurrentUserId;
mCurrentUserId = userId;
onUserProfilesChangedLocked();
// let providers know the current user has changed
- for (LocationProviderManager p : mProviders) {
- p.onUseableChangedLocked(oldUserId);
- p.onUseableChangedLocked(mCurrentUserId);
+ for (LocationProviderManager manager : mProviderManagers) {
+ // update LOCATION_PROVIDERS_ALLOWED for best effort backwards compatibility
+ mSettingsStore.setLocationProviderAllowed(manager.getName(),
+ manager.isUseable(mCurrentUserId), mCurrentUserId);
+
+ manager.onUseableChangedLocked(oldUserId);
+ manager.onUseableChangedLocked(mCurrentUserId);
}
}
/**
* 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;
@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; // combined state for each user id
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);
- }
-
- @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);
+ // initialize last since this lets our reference escape
+ mProvider = new MockableLocationProvider(mContext, mLock, this);
}
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);
+ public void setRequest(ProviderRequest request) {
+ mProvider.setRequest(request);
+ }
- 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 sendExtraCommand(int uid, int pid, String command, Bundle extras) {
+ mProvider.sendExtraCommand(uid, pid, command, extras);
+ }
+
+ 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();
+
+ pw.println("useable=" + isUseable(mCurrentUserId));
+ if (!isUseable(mCurrentUserId)) {
+ pw.println("enabled=" + mProvider.getState().enabled);
}
+
+ pw.println("properties=" + mProvider.getState().properties);
}
+ mProvider.dump(fd, pw, args);
+
pw.decreaseIndent();
}
+ @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(mCurrentUserId);
}
}
@GuardedBy("mLock")
- public boolean isUseableLocked() {
- return isUseableLocked(mCurrentUserId);
+ public boolean isUseable() {
+ return isUseable(mCurrentUserId);
}
@GuardedBy("mLock")
- public boolean isUseableLocked(int userId) {
- return mUseable.get(userId, Boolean.FALSE);
+ public boolean isUseable(int userId) {
+ synchronized (mLock) {
+ 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;
+ }
+
// 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 = isCurrentProfileLocked(userId)
+ && mSettingsStore.isLocationEnabled(userId) && mProvider.getState().enabled;
- if (useable == isUseableLocked(userId)) {
+ if (useable == isUseable(userId)) {
return;
}
mUseable.put(userId, useable);
@@ -1007,11 +968,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);
@@ -1031,53 +988,38 @@ public class LocationManagerService extends ILocationManager.Stub {
}
}
- private class MockLocationProvider extends LocationProviderManager {
+ class PassiveLocationProviderManager extends LocationProviderManager {
- private ProviderRequest mCurrentRequest;
-
- private MockLocationProvider(String name) {
- super(name);
+ private PassiveLocationProviderManager() {
+ super(PASSIVE_PROVIDER);
}
@Override
- public void attachLocked(AbstractLocationProvider provider) {
- checkState(provider instanceof MockProvider);
- super.attachLocked(provider);
- }
-
- public boolean isMock() {
- return true;
+ 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 +1123,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 +1374,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 +1385,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 +1396,14 @@ public class LocationManagerService extends ILocationManager.Stub {
@Override
public boolean stopGnssBatch() {
- return mGnssManagerService == null ? false : mGnssManagerService.stopGnssBatch();
+ return mGnssManagerService != null && 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);
- }
-
- @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 +1452,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 +1508,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 +1548,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 +1567,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,12 +1618,12 @@ 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(
@@ -1721,7 +1637,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 +1652,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 +1680,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,7 +1689,7 @@ 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(
UserHandle.getUserId(record.mReceiver.mCallerIdentity.mUid))) {
@@ -1787,10 +1704,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 +1718,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,23 +1730,25 @@ 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(
UserHandle.getUserId(record.mReceiver.mCallerIdentity.mUid))) {
@@ -1837,18 +1756,18 @@ public class LocationManagerService extends ILocationManager.Stub {
// 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 +1777,7 @@ public class LocationManagerService extends ILocationManager.Stub {
}
}
- provider.setRequest(providerRequest, worksource);
+ manager.setRequest(providerRequest.build());
}
/**
@@ -2198,8 +2117,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 +2136,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,8 +2239,8 @@ 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(
@@ -2329,7 +2248,7 @@ public class LocationManagerService extends ILocationManager.Stub {
return null;
}
- if (!provider.isUseableLocked()) {
+ if (!manager.isUseable()) {
return null;
}
@@ -2450,19 +2369,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 +2430,7 @@ public class LocationManagerService extends ILocationManager.Stub {
packageName,
request,
/* hasListener= */ false,
- intent != null,
+ true,
geofence,
mActivityManager.getPackageImportance(packageName));
}
@@ -2542,7 +2461,7 @@ public class LocationManagerService extends ILocationManager.Stub {
packageName,
/* LocationRequest= */ null,
/* hasListener= */ false,
- intent != null,
+ true,
geofence,
mActivityManager.getPackageImportance(packageName));
}
@@ -2555,7 +2474,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 +2488,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 +2504,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 +2520,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 +2551,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 +2568,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 +2665,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 +2704,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 +2751,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,7 +2768,7 @@ public class LocationManagerService extends ILocationManager.Stub {
Receiver receiver = r.mReceiver;
boolean receiverDead = false;
- if (!provider.isUseableLocked() && !isSettingsExemptLocked(r)) {
+ if (!manager.isUseable() && !isSettingsExemptLocked(r)) {
continue;
}
@@ -2949,7 +2863,7 @@ public class LocationManagerService extends ILocationManager.Stub {
for (UpdateRecord r : deadUpdateRecords) {
r.disposeLocked(true);
}
- applyRequirementsLocked(provider);
+ applyRequirementsLocked(manager);
}
}
@@ -3006,143 +2920,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, " ");
@@ -3226,25 +3096,27 @@ public class LocationManagerService extends ILocationManager.Stub {
mLocationFudger.dump(fd, ipw, args);
ipw.decreaseIndent();
}
+ }
- ipw.println("Location Settings:");
- ipw.increaseIndent();
- mSettingsStore.dump(fd, ipw, args);
- 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);
- }
- ipw.decreaseIndent();
+ ipw.println("Location Providers:");
+ ipw.increaseIndent();
+ 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();
+ synchronized (mLock) {
+ 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/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/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/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/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index a4f4d079df27..728b297b4d8e 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2481,7 +2481,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()))
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..c43f23454ca6 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -635,7 +635,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..97a2d43e6183 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,
@@ -3138,7 +3138,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..a9571d97200c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -15158,10 +15158,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 +15207,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);
@@ -19976,19 +19984,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);
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/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/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index fd871bdc1ba2..d63165adb2f8 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);
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/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index cefef37a1363..8ac212f7ef03 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -777,7 +777,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
* otherwise.
*/
boolean isWaitingForTransitionStart() {
- return getActivity(app -> app.isWaitingForTransitionStart()) != null;
+ return false;
}
/**
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/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/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/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/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/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/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/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..368f8f1dab2e 100644
--- a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
+++ b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
@@ -25,7 +25,7 @@ import android.content.res.Resources;
import android.os.RemoteException;
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;
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..8e703fee3126 100644
--- a/telephony/java/android/telephony/CellIdentity.java
+++ b/telephony/java/android/telephony/CellIdentity.java
@@ -16,6 +16,8 @@
package android.telephony;
+import com.android.telephony.Rlog;
+
import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
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/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..1f7d55f2758a 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;
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..843c0656efc3 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -5868,7 +5868,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 +5902,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 +5933,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 +5963,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 +5985,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 +6024,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 +6065,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 +6097,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 +6136,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 +6175,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 +6205,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 +6236,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 +6261,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 +6290,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 +6313,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();
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..121401277ce9 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;
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/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/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/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java
index 65e9b7910e5e..945a23605b74 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
@@ -381,6 +391,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 +415,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 +429,7 @@ public final class SoftApConfiguration implements Parcelable {
mChannel = 0;
mMaxNumberOfClients = 0;
mSecurityType = SECURITY_TYPE_OPEN;
+ mShutdownTimeoutMillis = 0;
}
/**
@@ -425,6 +446,7 @@ public final class SoftApConfiguration implements Parcelable {
mChannel = other.mChannel;
mMaxNumberOfClients = other.mMaxNumberOfClients;
mSecurityType = other.mSecurityType;
+ mShutdownTimeoutMillis = other.mShutdownTimeoutMillis;
}
/**
@@ -435,7 +457,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);
}
/**
@@ -643,5 +666,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/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();
+ }
}