summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp5
-rw-r--r--apct-tests/perftests/multiuser/OWNERS1
-rw-r--r--apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl3
-rw-r--r--apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java95
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java10
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java183
-rw-r--r--core/api/current.txt108
-rw-r--r--core/api/module-lib-current.txt4
-rw-r--r--core/api/system-current.txt121
-rw-r--r--core/api/test-current.txt3
-rw-r--r--core/java/android/app/ActivityManagerInternal.java28
-rw-r--r--core/java/android/app/ActivityThread.java2
-rw-r--r--core/java/android/app/EventLogTags.logtags5
-rw-r--r--core/java/android/app/OWNERS3
-rw-r--r--core/java/android/app/SystemServiceRegistry.java16
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java36
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl1
-rw-r--r--core/java/android/bluetooth/BluetoothA2dp.java6
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java2
-rw-r--r--core/java/android/bluetooth/BluetoothHeadset.java6
-rw-r--r--core/java/android/companion/AssociationRequest.java55
-rw-r--r--core/java/android/content/Context.java11
-rw-r--r--core/java/android/content/om/IOverlayManager.aidl15
-rw-r--r--core/java/android/content/om/OverlayManager.java23
-rw-r--r--core/java/android/content/om/OverlayManagerTransaction.java216
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl2
-rw-r--r--core/java/android/content/pm/OWNERS1
-rw-r--r--core/java/android/content/pm/PackageManager.java8
-rw-r--r--core/java/android/hardware/biometrics/BiometricManager.java21
-rw-r--r--core/java/android/hardware/biometrics/IAuthService.aidl6
-rw-r--r--core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl4
-rw-r--r--core/java/android/hardware/biometrics/IBiometricService.aidl6
-rw-r--r--core/java/android/hardware/biometrics/IInvalidationCallback.aidl27
-rw-r--r--core/java/android/hardware/face/IFaceService.aidl4
-rw-r--r--core/java/android/hardware/fingerprint/IFingerprintService.aidl4
-rw-r--r--core/java/android/net/CaptivePortalData.java38
-rw-r--r--core/java/android/net/IIpConnectivityMetrics.aidl8
-rw-r--r--core/java/android/net/INetworkManagementEventObserver.aidl4
-rw-r--r--core/java/android/net/NetworkAgent.java7
-rw-r--r--core/java/android/net/NetworkCapabilities.java98
-rw-r--r--core/java/android/net/NetworkPolicyManager.java4
-rw-r--r--core/java/android/net/TransportInfo.java38
-rw-r--r--core/java/android/net/metrics/IpConnectivityLog.java59
-rw-r--r--core/java/android/os/DropBoxManager.java83
-rw-r--r--core/java/android/os/OWNERS4
-rw-r--r--core/java/android/os/Process.java37
-rw-r--r--core/java/android/os/image/DynamicSystemClient.java48
-rw-r--r--core/java/android/os/incremental/IIncrementalService.aidl6
-rw-r--r--core/java/android/os/incremental/IncrementalFileStorages.java14
-rw-r--r--core/java/android/os/incremental/IncrementalManager.java9
-rw-r--r--core/java/android/os/incremental/IncrementalStorage.java4
-rw-r--r--core/java/android/os/incremental/PerUidReadTimeouts.aidl45
-rw-r--r--core/java/android/permission/PermissionControllerManager.java28
-rw-r--r--core/java/android/permission/PermissionControllerService.java9
-rw-r--r--core/java/android/provider/CallLog.java229
-rw-r--r--core/java/android/service/translation/ITranslationCallback.aidl29
-rw-r--r--core/java/android/service/translation/ITranslationService.aidl38
-rw-r--r--core/java/android/service/translation/OWNERS8
-rw-r--r--core/java/android/service/translation/OnTranslationResultCallbackWrapper.java92
-rw-r--r--core/java/android/service/translation/TranslationRequest.aidl19
-rw-r--r--core/java/android/service/translation/TranslationRequest.java281
-rw-r--r--core/java/android/service/translation/TranslationService.java261
-rw-r--r--core/java/android/service/translation/TranslationServiceInfo.java173
-rw-r--r--core/java/android/util/SparseArray.java8
-rw-r--r--core/java/android/view/View.java2
-rw-r--r--core/java/android/view/translation/ITranslationDirectManager.aidl33
-rw-r--r--core/java/android/view/translation/ITranslationManager.aidl32
-rw-r--r--core/java/android/view/translation/OWNERS8
-rw-r--r--core/java/android/view/translation/TranslationData.aidl (renamed from core/java/android/content/om/OverlayManagerTransaction.aidl)6
-rw-r--r--core/java/android/view/translation/TranslationManager.java201
-rw-r--r--core/java/android/view/translation/TranslationRequest.aidl19
-rw-r--r--core/java/android/view/translation/TranslationRequest.java215
-rw-r--r--core/java/android/view/translation/TranslationResponse.aidl19
-rw-r--r--core/java/android/view/translation/TranslationResponse.java311
-rw-r--r--core/java/android/view/translation/TranslationSpec.aidl19
-rw-r--r--core/java/android/view/translation/TranslationSpec.java189
-rw-r--r--core/java/android/view/translation/Translator.java298
-rw-r--r--core/java/android/window/ITaskOrganizerController.aidl6
-rw-r--r--core/java/android/window/TaskOrganizer.java13
-rw-r--r--core/java/android/window/WindowContainerTransaction.java141
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java25
-rw-r--r--core/java/com/android/internal/compat/CompatibilityChangeInfo.java18
-rw-r--r--core/java/com/android/internal/os/IDropBoxManagerService.aidl9
-rw-r--r--core/java/com/android/server/net/BaseNetworkObserver.java2
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp9
-rw-r--r--core/proto/android/server/biometrics.proto13
-rw-r--r--core/res/AndroidManifest.xml12
-rw-r--r--core/res/res/values/attrs.xml17
-rw-r--r--core/res/res/values/config.xml12
-rw-r--r--core/res/res/values/strings.xml11
-rw-r--r--core/res/res/values/symbols.xml3
-rw-r--r--core/tests/overlaytests/device/Android.bp6
-rw-r--r--core/tests/overlaytests/device/AndroidManifest.xml2
-rw-r--r--core/tests/overlaytests/device/AndroidTest.xml13
-rw-r--r--core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java78
-rw-r--r--core/tests/overlaytests/device/src/com/android/overlaytest/TransactionTest.java133
-rw-r--r--core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java9
-rw-r--r--core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java11
-rw-r--r--core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java9
-rw-r--r--core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp2
-rw-r--r--core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp2
-rw-r--r--data/etc/OWNERS2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java307
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java217
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedThread.java62
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandler.java95
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTouchHandler.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java71
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java18
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt12
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterLegacySplitScreenTest.kt86
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottomTest.kt29
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenTest.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt88
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt88
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreenTest.kt149
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/SplitScreenTestBase.kt44
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedAnimationControllerTest.java7
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java16
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java90
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedGestureHandlerTest.java10
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedSettingsUtilTest.java1
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java68
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTouchHandlerTest.java14
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java19
-rw-r--r--libs/services/include/android/os/DropBoxManager.h2
-rw-r--r--libs/services/src/os/DropBoxManager.cpp31
-rw-r--r--media/java/android/media/MediaCas.java9
-rw-r--r--media/java/android/media/session/MediaSessionManager.java135
-rw-r--r--media/java/android/media/tv/tuner/Lnb.java5
-rw-r--r--media/java/android/media/tv/tuner/Tuner.java98
-rw-r--r--media/java/android/media/tv/tuner/dvr/DvrRecorder.java2
-rw-r--r--media/java/android/media/tv/tunerresourcemanager/Android.bp41
-rw-r--r--media/java/android/media/tv/tunerresourcemanager/CasSessionRequest.java114
-rw-r--r--media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java131
-rw-r--r--media/java/android/media/tv/tunerresourcemanager/TunerDemuxRequest.java96
-rw-r--r--media/java/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.java96
-rw-r--r--media/java/android/media/tv/tunerresourcemanager/TunerFrontendInfo.java142
-rw-r--r--media/java/android/media/tv/tunerresourcemanager/TunerFrontendRequest.java114
-rw-r--r--media/java/android/media/tv/tunerresourcemanager/TunerLnbRequest.java96
-rw-r--r--media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java55
-rw-r--r--media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/CasSessionRequest.aidl (renamed from media/java/android/media/tv/tunerresourcemanager/CasSessionRequest.aidl)6
-rw-r--r--media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/IResourcesReclaimListener.aidl (renamed from media/java/android/media/tv/tunerresourcemanager/IResourcesReclaimListener.aidl)0
-rw-r--r--media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl (renamed from media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl)39
-rw-r--r--media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ResourceClientProfile.aidl (renamed from media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.aidl)6
-rw-r--r--media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerCiCamRequest.aidl28
-rw-r--r--media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerDemuxRequest.aidl (renamed from media/java/android/media/tv/tunerresourcemanager/TunerDemuxRequest.aidl)4
-rw-r--r--media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.aidl (renamed from media/java/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.aidl)4
-rw-r--r--media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl (renamed from media/java/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl)8
-rw-r--r--media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendRequest.aidl (renamed from media/java/android/media/tv/tunerresourcemanager/TunerFrontendRequest.aidl)6
-rw-r--r--media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerLnbRequest.aidl (renamed from media/java/android/media/tv/tunerresourcemanager/TunerLnbRequest.aidl)4
-rw-r--r--media/jni/Android.bp5
-rw-r--r--media/jni/android_media_tv_Tuner.cpp646
-rw-r--r--media/jni/android_media_tv_Tuner.h78
-rw-r--r--media/jni/tuner/DemuxClient.cpp37
-rw-r--r--media/jni/tuner/DemuxClient.h13
-rw-r--r--media/jni/tuner/DescramblerClient.cpp98
-rw-r--r--media/jni/tuner/DescramblerClient.h89
-rw-r--r--media/jni/tuner/FrontendClient.cpp167
-rw-r--r--media/jni/tuner/FrontendClient.h58
-rw-r--r--media/jni/tuner/LnbClient.cpp130
-rw-r--r--media/jni/tuner/LnbClient.h134
-rw-r--r--media/jni/tuner/LnbClientCallback.h33
-rw-r--r--media/jni/tuner/TimeFilterClient.cpp115
-rw-r--r--media/jni/tuner/TimeFilterClient.h88
-rw-r--r--media/jni/tuner/TunerClient.cpp231
-rw-r--r--media/jni/tuner/TunerClient.h68
-rw-r--r--packages/CompanionDeviceManager/res/values/strings.xml5
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java34
-rw-r--r--packages/SystemUI/AndroidManifest.xml1
-rw-r--r--packages/SystemUI/res/layout/qs_paged_page_side_labels.xml23
-rw-r--r--packages/SystemUI/res/layout/qs_paged_tile_layout_side_labels.xml27
-rw-r--r--packages/SystemUI/res/layout/qs_tile_label.xml3
-rw-r--r--packages/SystemUI/res/layout/qs_tile_label_divider.xml27
-rw-r--r--packages/SystemUI/res/values/attrs.xml4
-rw-r--r--packages/SystemUI/res/values/dimens.xml1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java42
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanel.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/TileLayout.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt116
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java241
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java69
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/AccessPointControllerImplTest.kt229
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java13
-rw-r--r--packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_apps.xml18
-rw-r--r--packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_devices_other.xml6
-rw-r--r--packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_help.xml6
-rw-r--r--packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_phone_info.xml6
-rw-r--r--packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_accessibility.xml10
-rw-r--r--packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_accounts.xml4
-rw-r--r--packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_battery_white.xml2
-rw-r--r--packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_display_white.xml4
-rw-r--r--packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_location.xml4
-rw-r--r--packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_privacy.xml6
-rw-r--r--packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_security_white.xml4
-rw-r--r--packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml6
-rw-r--r--packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_wireless.xml8
-rw-r--r--packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_storage_white.xml12
-rw-r--r--packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_volume_up_24dp.xml6
-rw-r--r--packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_apps.xml18
-rw-r--r--packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_devices_other.xml6
-rw-r--r--packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_help.xml2
-rw-r--r--packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_phone_info.xml6
-rw-r--r--packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_accessibility.xml10
-rw-r--r--packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_accounts.xml2
-rw-r--r--packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_battery_white.xml2
-rw-r--r--packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_display_white.xml2
-rw-r--r--packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_location.xml2
-rw-r--r--packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_privacy.xml6
-rw-r--r--packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_security_white.xml2
-rw-r--r--packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml2
-rw-r--r--packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_wireless.xml6
-rw-r--r--packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_storage_white.xml6
-rw-r--r--packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_volume_up_24dp.xml6
-rw-r--r--packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_apps.xml18
-rw-r--r--packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_devices_other.xml6
-rw-r--r--packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_help.xml6
-rw-r--r--packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_phone_info.xml6
-rw-r--r--packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_accessibility.xml10
-rw-r--r--packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_accounts.xml4
-rw-r--r--packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_battery_white.xml2
-rw-r--r--packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_display_white.xml4
-rw-r--r--packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_location.xml4
-rw-r--r--packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_privacy.xml6
-rw-r--r--packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_security_white.xml4
-rw-r--r--packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml6
-rw-r--r--packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_wireless.xml6
-rw-r--r--packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_storage_white.xml6
-rw-r--r--packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_volume_up_24dp.xml6
-rw-r--r--packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_apps.xml18
-rw-r--r--packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_devices_other.xml6
-rw-r--r--packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_help.xml6
-rw-r--r--packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_phone_info.xml6
-rw-r--r--packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_accessibility.xml10
-rw-r--r--packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_accounts.xml4
-rw-r--r--packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_battery_white.xml2
-rw-r--r--packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_display_white.xml4
-rw-r--r--packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_location.xml4
-rw-r--r--packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_privacy.xml6
-rw-r--r--packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_security_white.xml4
-rw-r--r--packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml6
-rw-r--r--packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_wireless.xml8
-rw-r--r--packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_storage_white.xml12
-rw-r--r--packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_volume_up_24dp.xml6
-rw-r--r--packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_apps.xml18
-rw-r--r--packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_devices_other.xml2
-rw-r--r--packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_help.xml2
-rw-r--r--packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_phone_info.xml6
-rw-r--r--packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_accessibility.xml10
-rw-r--r--packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_accounts.xml4
-rw-r--r--packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_battery_white.xml2
-rw-r--r--packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_display_white.xml4
-rw-r--r--packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_location.xml2
-rw-r--r--packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_privacy.xml6
-rw-r--r--packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_security_white.xml2
-rw-r--r--packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml2
-rw-r--r--packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_wireless.xml6
-rw-r--r--packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_storage_white.xml6
-rw-r--r--packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_volume_up_24dp.xml6
-rw-r--r--packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_apps.xml18
-rw-r--r--packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_devices_other.xml6
-rw-r--r--packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_help.xml6
-rw-r--r--packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_phone_info.xml6
-rw-r--r--packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_accessibility.xml4
-rw-r--r--packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_accounts.xml4
-rw-r--r--packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_battery_white.xml2
-rw-r--r--packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_display_white.xml4
-rw-r--r--packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_location.xml4
-rw-r--r--packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_privacy.xml6
-rw-r--r--packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_security_white.xml4
-rw-r--r--packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml6
-rw-r--r--packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_wireless.xml6
-rw-r--r--packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_storage_white.xml6
-rw-r--r--packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_volume_up_24dp.xml6
-rw-r--r--services/Android.bp2
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java94
-rw-r--r--services/core/Android.bp4
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java270
-rw-r--r--services/core/java/com/android/server/DropBoxManagerInternal.java40
-rw-r--r--services/core/java/com/android/server/DropBoxManagerService.java174
-rw-r--r--services/core/java/com/android/server/TestNetworkService.java1
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java26
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java107
-rw-r--r--services/core/java/com/android/server/am/CachedAppOptimizer.java82
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java18
-rw-r--r--services/core/java/com/android/server/biometrics/AuthService.java14
-rw-r--r--services/core/java/com/android/server/biometrics/BiometricService.java77
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java24
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceService.java16
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java11
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java1
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java14
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java11
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java1
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java6
-rw-r--r--services/core/java/com/android/server/compat/CompatChange.java12
-rw-r--r--services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java47
-rw-r--r--services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java21
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkAgentInfo.java9
-rw-r--r--services/core/java/com/android/server/display/DisplayModeDirector.java35
-rw-r--r--services/core/java/com/android/server/location/LocationManagerService.java2
-rw-r--r--services/core/java/com/android/server/location/LocationShellCommand.java195
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java65
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java9
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java30
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerService.java462
-rw-r--r--services/core/java/com/android/server/pm/DumpState.java1
-rw-r--r--services/core/java/com/android/server/pm/MULTIUSER_AND_ENTERPRISE_OWNERS2
-rw-r--r--services/core/java/com/android/server/pm/OWNERS13
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java10
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java141
-rw-r--r--services/core/java/com/android/server/pm/PerPackageReadTimeouts.java187
-rwxr-xr-xservices/core/java/com/android/server/tv/TvInputHardwareManager.java5
-rw-r--r--services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java8
-rw-r--r--services/core/java/com/android/server/tv/tunerresourcemanager/CiCamResource.java70
-rw-r--r--services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java42
-rw-r--r--services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java281
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java4
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java17
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java104
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java39
-rw-r--r--services/core/java/com/android/server/wm/EventLogTags.logtags20
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java8
-rw-r--r--services/core/java/com/android/server/wm/Task.java49
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java95
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java306
-rw-r--r--services/core/java/com/android/server/wm/WindowAnimator.java1
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java5
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java146
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java17
-rw-r--r--services/core/xsd/platform-compat-config.xsd5
-rw-r--r--services/core/xsd/platform-compat-schema/current.txt2
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java6
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java15
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java104
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/Owners.java47
-rw-r--r--services/incremental/BinderIncrementalService.cpp9
-rw-r--r--services/incremental/BinderIncrementalService.h3
-rw-r--r--services/incremental/IncrementalService.cpp169
-rw-r--r--services/incremental/IncrementalService.h49
-rw-r--r--services/incremental/IncrementalServiceValidation.cpp7
-rw-r--r--services/incremental/ServiceWrappers.cpp5
-rw-r--r--services/incremental/ServiceWrappers.h5
-rw-r--r--services/incremental/test/IncrementalServiceTest.cpp224
-rw-r--r--services/java/com/android/server/SystemServer.java12
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java477
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java130
-rw-r--r--services/tests/servicestests/src/com/android/server/DropBoxTest.java68
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/InvalidationTrackerTest.java111
-rw-r--r--services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java35
-rw-r--r--services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java31
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java29
-rw-r--r--services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java42
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java218
-rw-r--r--services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java418
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java17
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java7
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java197
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java17
-rw-r--r--services/translation/Android.bp13
-rw-r--r--services/translation/OWNERS8
-rw-r--r--services/translation/java/com/android/server/translation/RemoteTranslationService.java87
-rw-r--r--services/translation/java/com/android/server/translation/TranslationManagerService.java92
-rw-r--r--services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java125
-rwxr-xr-xtelecomm/java/android/telecom/Call.java8
-rw-r--r--telecomm/java/android/telecom/Connection.java10
-rw-r--r--telephony/java/android/telephony/CarrierBandwidth.java28
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java20
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java369
-rw-r--r--telephony/java/android/telephony/ims/ImsCallProfile.java9
-rw-r--r--telephony/java/android/telephony/ims/ImsMmTelManager.java101
-rw-r--r--telephony/java/android/telephony/ims/ImsRcsManager.java1
-rw-r--r--telephony/java/android/telephony/ims/RcsContactPresenceTuple.java26
-rw-r--r--telephony/java/android/telephony/ims/RcsContactUceCapability.java17
-rw-r--r--telephony/java/android/telephony/ims/RcsUceAdapter.java224
-rw-r--r--telephony/java/android/telephony/ims/RegistrationManager.java97
-rw-r--r--telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java115
-rw-r--r--telephony/java/android/telephony/ims/aidl/ICapabilityExchangeEventListener.aidl49
-rw-r--r--telephony/java/android/telephony/ims/aidl/IOptionsRequestCallback.aidl1
-rw-r--r--telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java6
-rw-r--r--telephony/java/android/telephony/ims/feature/ImsFeature.java7
-rw-r--r--telephony/java/android/telephony/ims/feature/RcsFeature.java114
-rw-r--r--telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java108
-rw-r--r--telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java20
-rw-r--r--telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java73
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl17
-rw-r--r--tests/net/common/Android.bp1
-rw-r--r--tests/net/common/java/android/net/CaptivePortalDataTest.kt10
-rw-r--r--tests/net/common/java/android/net/NetworkCapabilitiesTest.java94
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java348
-rw-r--r--tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java22
419 files changed, 14166 insertions, 5147 deletions
diff --git a/Android.bp b/Android.bp
index 1a73e9d323ff..35f97ac57281 100644
--- a/Android.bp
+++ b/Android.bp
@@ -223,6 +223,9 @@ filegroup {
"media/java/**/*.java",
"media/java/**/*.aidl",
],
+ exclude_srcs: [
+ ":framework-media-tv-tunerresourcemanager-sources-aidl",
+ ],
path: "media/java",
}
@@ -630,6 +633,7 @@ java_defaults {
// in favor of an API stubs dependency in java_library "framework" below.
"mimemap",
"av-types-aidl-java",
+ "tv_tuner_resource_manager_aidl_interface-java",
"soundtrigger_middleware-aidl-java",
"modules-utils-os",
],
@@ -1115,6 +1119,7 @@ filegroup {
"core/java/android/os/incremental/IStorageLoadingProgressListener.aidl",
"core/java/android/os/incremental/IncrementalNewFileParams.aidl",
"core/java/android/os/incremental/IStorageHealthListener.aidl",
+ "core/java/android/os/incremental/PerUidReadTimeouts.aidl",
"core/java/android/os/incremental/StorageHealthCheckParams.aidl",
],
path: "core/java",
diff --git a/apct-tests/perftests/multiuser/OWNERS b/apct-tests/perftests/multiuser/OWNERS
new file mode 100644
index 000000000000..1a206cb27c3b
--- /dev/null
+++ b/apct-tests/perftests/multiuser/OWNERS
@@ -0,0 +1 @@
+include /MULTIUSER_OWNERS \ No newline at end of file
diff --git a/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl b/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl
index 7d02d2d6cd29..5693abe4d4e1 100644
--- a/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl
+++ b/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl
@@ -41,7 +41,8 @@ interface IDeviceIdleController {
int[] getAppIdTempWhitelist();
boolean isPowerSaveWhitelistExceptIdleApp(String name);
boolean isPowerSaveWhitelistApp(String name);
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 30,
+ publicAlternatives = "Use SystemApi {@code PowerWhitelistManager#whitelistAppTemporarily(String, int, String)}.")
void addPowerSaveTempWhitelistApp(String name, long duration, int userId, String reason);
long addPowerSaveTempWhitelistAppForMms(String name, int userId, String reason);
long addPowerSaveTempWhitelistAppForSms(String name, int userId, String reason);
diff --git a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
index 4dc9cf850893..cc3e9c33fe42 100644
--- a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
+++ b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
@@ -274,11 +274,8 @@ public class AppStateTrackerImpl implements AppStateTracker {
int uid, @NonNull String packageName) {
updateJobsForUidPackage(uid, packageName, sender.isUidActive(uid));
- if (!sender.areAlarmsRestricted(uid, packageName, /*allowWhileIdle=*/ false)) {
+ if (!sender.areAlarmsRestricted(uid, packageName)) {
unblockAlarmsForUidPackage(uid, packageName);
- } else if (!sender.areAlarmsRestricted(uid, packageName, /*allowWhileIdle=*/ true)) {
- // we need to deliver the allow-while-idle alarms for this uid, package
- unblockAllUnrestrictedAlarms();
}
if (!sender.isRunAnyInBackgroundAppOpsAllowed(uid, packageName)) {
@@ -302,6 +299,7 @@ public class AppStateTrackerImpl implements AppStateTracker {
final boolean isActive = sender.isUidActive(uid);
updateJobsForUid(uid, isActive);
+ updateAlarmsForUid(uid);
if (isActive) {
unblockAlarmsForUid(uid);
@@ -313,7 +311,7 @@ public class AppStateTrackerImpl implements AppStateTracker {
*/
private void onPowerSaveUnexempted(AppStateTrackerImpl sender) {
updateAllJobs();
- unblockAllUnrestrictedAlarms();
+ updateAllAlarms();
}
/**
@@ -322,6 +320,8 @@ public class AppStateTrackerImpl implements AppStateTracker {
*/
private void onPowerSaveExemptionListChanged(AppStateTrackerImpl sender) {
updateAllJobs();
+ updateAllAlarms();
+ unblockAllUnrestrictedAlarms();
}
/**
@@ -344,7 +344,7 @@ public class AppStateTrackerImpl implements AppStateTracker {
private void onExemptedBucketChanged(AppStateTrackerImpl sender) {
// This doesn't happen very often, so just re-evaluate all jobs / alarms.
updateAllJobs();
- unblockAllUnrestrictedAlarms();
+ updateAllAlarms();
}
/**
@@ -352,10 +352,7 @@ public class AppStateTrackerImpl implements AppStateTracker {
*/
private void onForceAllAppsStandbyChanged(AppStateTrackerImpl sender) {
updateAllJobs();
-
- if (!sender.isForceAllAppsStandbyEnabled()) {
- unblockAllUnrestrictedAlarms();
- }
+ updateAllAlarms();
}
/**
@@ -387,6 +384,19 @@ public class AppStateTrackerImpl implements AppStateTracker {
}
/**
+ * Called when all alarms need to be re-evaluated for eligibility based on
+ * {@link #areAlarmsRestrictedByBatterySaver}.
+ */
+ public void updateAllAlarms() {
+ }
+
+ /**
+ * Called when the given uid state changes to active / idle.
+ */
+ public void updateAlarmsForUid(int uid) {
+ }
+
+ /**
* Called when the job restrictions for multiple UIDs might have changed, so the alarm
* manager should re-evaluate all restrictions for all blocked jobs.
*/
@@ -918,7 +928,7 @@ public class AppStateTrackerImpl implements AppStateTracker {
// Feature flag for forced app standby changed.
final boolean unblockAlarms;
synchronized (mLock) {
- unblockAlarms = !mForcedAppStandbyEnabled && !mForceAllAppsStandby;
+ unblockAlarms = !mForcedAppStandbyEnabled;
}
for (Listener l : cloneListeners()) {
l.updateAllJobs();
@@ -1109,53 +1119,64 @@ public class AppStateTrackerImpl implements AppStateTracker {
}
/**
- * @return whether alarms should be restricted for a UID package-name.
- */
- public boolean areAlarmsRestricted(int uid, @NonNull String packageName,
- boolean isExemptOnBatterySaver) {
- return isRestricted(uid, packageName, /*useTempExemptionListToo=*/ false,
- isExemptOnBatterySaver);
- }
-
- /**
- * @return whether jobs should be restricted for a UID package-name.
+ * @return whether alarms should be restricted for a UID package-name, due to explicit
+ * user-forced app standby. Use {{@link #areAlarmsRestrictedByBatterySaver} to check for
+ * restrictions induced by battery saver.
*/
- public boolean areJobsRestricted(int uid, @NonNull String packageName,
- boolean hasForegroundExemption) {
- return isRestricted(uid, packageName, /*useTempExemptionListToo=*/ true,
- hasForegroundExemption);
+ public boolean areAlarmsRestricted(int uid, @NonNull String packageName) {
+ if (isUidActive(uid)) {
+ return false;
+ }
+ synchronized (mLock) {
+ final int appId = UserHandle.getAppId(uid);
+ if (ArrayUtils.contains(mPowerExemptAllAppIds, appId)) {
+ return false;
+ }
+ return (mForcedAppStandbyEnabled && isRunAnyRestrictedLocked(uid, packageName));
+ }
}
/**
- * @return whether foreground services should be suppressed in the background
- * due to forced app standby for the given app
+ * @return whether alarms should be restricted when due to battery saver.
*/
- public boolean areForegroundServicesRestricted(int uid, @NonNull String packageName) {
+ public boolean areAlarmsRestrictedByBatterySaver(int uid, @NonNull String packageName) {
+ if (isUidActive(uid)) {
+ return false;
+ }
synchronized (mLock) {
- return isRunAnyRestrictedLocked(uid, packageName);
+ final int appId = UserHandle.getAppId(uid);
+ if (ArrayUtils.contains(mPowerExemptAllAppIds, appId)) {
+ return false;
+ }
+ final int userId = UserHandle.getUserId(uid);
+ if (mAppStandbyInternal.isAppIdleEnabled() && !mAppStandbyInternal.isInParole()
+ && mExemptedBucketPackages.contains(userId, packageName)) {
+ return false;
+ }
+ return mForceAllAppsStandby;
}
}
+
/**
- * @return whether force-app-standby is effective for a UID package-name.
+ * @return whether jobs should be restricted for a UID package-name. This could be due to
+ * battery saver or user-forced app standby
*/
- private boolean isRestricted(int uid, @NonNull String packageName,
- boolean useTempExemptionListToo, boolean exemptOnBatterySaver) {
+ public boolean areJobsRestricted(int uid, @NonNull String packageName,
+ boolean hasForegroundExemption) {
if (isUidActive(uid)) {
return false;
}
synchronized (mLock) {
final int appId = UserHandle.getAppId(uid);
- if (ArrayUtils.contains(mPowerExemptAllAppIds, appId)) {
- return false;
- }
- if (useTempExemptionListToo && ArrayUtils.contains(mTempExemptAppIds, appId)) {
+ if (ArrayUtils.contains(mPowerExemptAllAppIds, appId)
+ || ArrayUtils.contains(mTempExemptAppIds, appId)) {
return false;
}
if (mForcedAppStandbyEnabled && isRunAnyRestrictedLocked(uid, packageName)) {
return true;
}
- if (exemptOnBatterySaver) {
+ if (hasForegroundExemption) {
return false;
}
final int userId = UserHandle.getUserId(uid);
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
index a8c0f0ec3e00..657c368d0aee 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
@@ -42,7 +42,7 @@ import java.util.Date;
*/
class Alarm {
@VisibleForTesting
- public static final int NUM_POLICIES = 3;
+ public static final int NUM_POLICIES = 4;
/**
* Index used to store the time the alarm was requested to expire. To be used with
* {@link #setPolicyElapsed(int, long)}.
@@ -59,6 +59,12 @@ class Alarm {
*/
public static final int DEVICE_IDLE_POLICY_INDEX = 2;
+ /**
+ * Index used to store the earliest time the alarm can expire based on battery saver policy.
+ * To be used with {@link #setPolicyElapsed(int, long)}.
+ */
+ public static final int BATTERY_SAVER_POLICY_INDEX = 3;
+
public final int type;
/**
* The original trigger time supplied by the caller. This can be in the elapsed or rtc time base
@@ -223,6 +229,8 @@ class Alarm {
return "app_standby";
case DEVICE_IDLE_POLICY_INDEX:
return "device_idle";
+ case BATTERY_SAVER_POLICY_INDEX:
+ return "battery_saver";
default:
return "--unknown--";
}
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 7842d487dfb2..aa46cfdc5c8a 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -28,6 +28,7 @@ import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.os.UserHandle.USER_SYSTEM;
import static com.android.server.alarm.Alarm.APP_STANDBY_POLICY_INDEX;
+import static com.android.server.alarm.Alarm.BATTERY_SAVER_POLICY_INDEX;
import static com.android.server.alarm.Alarm.DEVICE_IDLE_POLICY_INDEX;
import static com.android.server.alarm.Alarm.REQUESTER_POLICY_INDEX;
@@ -156,6 +157,7 @@ public class AlarmManagerService extends SystemService {
static final int TICK_HISTORY_DEPTH = 10;
static final long MILLIS_IN_DAY = 24 * 60 * 60 * 1000;
+ static final long INDEFINITE_DELAY = 365 * MILLIS_IN_DAY;
// Indices into the KEYS_APP_STANDBY_QUOTAS array.
static final int ACTIVE_INDEX = 0;
@@ -964,8 +966,7 @@ public class AlarmManagerService extends SystemService {
* Check all alarms in {@link #mPendingBackgroundAlarms} and send the ones that are not
* restricted.
*
- * This is only called when the global "force all apps-standby" flag changes or when the
- * power save whitelist changes, so it's okay to be slow.
+ * This is only called when the power save whitelist changes, so it's okay to be slow.
*/
void sendAllUnrestrictedPendingBackgroundAlarmsLocked() {
final ArrayList<Alarm> alarmsToDeliver = new ArrayList<>();
@@ -984,7 +985,6 @@ public class AlarmManagerService extends SystemService {
Predicate<Alarm> isBackgroundRestricted) {
for (int uidIndex = pendingAlarms.size() - 1; uidIndex >= 0; uidIndex--) {
- final int uid = pendingAlarms.keyAt(uidIndex);
final ArrayList<Alarm> alarmsForUid = pendingAlarms.valueAt(uidIndex);
for (int alarmIndex = alarmsForUid.size() - 1; alarmIndex >= 0; alarmIndex--) {
@@ -1620,6 +1620,44 @@ public class AlarmManagerService extends SystemService {
}
/**
+ * Adjusts the delivery time of the alarm based on battery saver rules.
+ *
+ * @param alarm The alarm to adjust
+ * @return {@code true} if the alarm delivery time was updated.
+ */
+ private boolean adjustDeliveryTimeBasedOnBatterySaver(Alarm alarm) {
+ final long nowElapsed = mInjector.getElapsedRealtime();
+ if (isExemptFromBatterySaver(alarm)) {
+ return false;
+ }
+
+ if (!(mAppStateTracker != null && mAppStateTracker.areAlarmsRestrictedByBatterySaver(
+ alarm.creatorUid, alarm.sourcePackage))) {
+ return alarm.setPolicyElapsed(BATTERY_SAVER_POLICY_INDEX, nowElapsed);
+ }
+
+ final long batterSaverPolicyElapsed;
+ if ((alarm.flags & (AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED)) != 0) {
+ // Unrestricted.
+ batterSaverPolicyElapsed = nowElapsed;
+ } else if ((alarm.flags & AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) {
+ // Allowed but limited.
+ final long minDelay;
+ if (mUseAllowWhileIdleShortTime.get(alarm.creatorUid)) {
+ minDelay = mConstants.ALLOW_WHILE_IDLE_SHORT_TIME;
+ } else {
+ minDelay = mConstants.ALLOW_WHILE_IDLE_LONG_TIME;
+ }
+ final long lastDispatch = mLastAllowWhileIdleDispatch.get(alarm.creatorUid, 0);
+ batterSaverPolicyElapsed = (lastDispatch == 0) ? nowElapsed : lastDispatch + minDelay;
+ } else {
+ // Not allowed.
+ batterSaverPolicyElapsed = nowElapsed + INDEFINITE_DELAY;
+ }
+ return alarm.setPolicyElapsed(BATTERY_SAVER_POLICY_INDEX, batterSaverPolicyElapsed);
+ }
+
+ /**
* Adjusts the delivery time of the alarm based on device_idle (doze) rules.
*
* @param alarm The alarm to adjust
@@ -1756,6 +1794,7 @@ public class AlarmManagerService extends SystemService {
if (a.alarmClock != null) {
mNextAlarmClockMayChange = true;
}
+ adjustDeliveryTimeBasedOnBatterySaver(a);
adjustDeliveryTimeBasedOnBucketLocked(a);
mAlarmStore.add(a);
rescheduleKernelAlarmsLocked();
@@ -2230,14 +2269,6 @@ public class AlarmManagerService extends SystemService {
pw.print(": ");
final long lastTime = mLastAllowWhileIdleDispatch.valueAt(i);
TimeUtils.formatDuration(lastTime, nowELAPSED, pw);
-
- final long minInterval = getWhileIdleMinIntervalLocked(uid);
- pw.print(" Next allowed:");
- TimeUtils.formatDuration(lastTime + minInterval, nowELAPSED, pw);
- pw.print(" (");
- TimeUtils.formatDuration(minInterval, 0, pw);
- pw.print(")");
-
pw.println();
}
pw.decreaseIndent();
@@ -2511,8 +2542,6 @@ public class AlarmManagerService extends SystemService {
proto.write(AlarmManagerServiceDumpProto.LastAllowWhileIdleDispatch.UID, uid);
proto.write(AlarmManagerServiceDumpProto.LastAllowWhileIdleDispatch.TIME_MS,
lastTime);
- proto.write(AlarmManagerServiceDumpProto.LastAllowWhileIdleDispatch.NEXT_ALLOWED_MS,
- lastTime + getWhileIdleMinIntervalLocked(uid));
proto.end(token);
}
@@ -3119,30 +3148,36 @@ public class AlarmManagerService extends SystemService {
}
}
+ private boolean isExemptFromBatterySaver(Alarm alarm) {
+ if (alarm.alarmClock != null) {
+ return true;
+ }
+ if ((alarm.operation != null)
+ && (alarm.operation.isActivity() || alarm.operation.isForegroundService())) {
+ return true;
+ }
+ if (UserHandle.isCore(alarm.creatorUid)) {
+ return true;
+ }
+ return false;
+ }
+
private boolean isBackgroundRestricted(Alarm alarm) {
- boolean exemptOnBatterySaver = (alarm.flags & FLAG_ALLOW_WHILE_IDLE) != 0;
if (alarm.alarmClock != null) {
// Don't defer alarm clocks
return false;
}
- if (alarm.operation != null) {
- if (alarm.operation.isActivity()) {
- // Don't defer starting actual UI
- return false;
- }
- if (alarm.operation.isForegroundService()) {
- // FG service alarms are nearly as important; consult AST policy
- exemptOnBatterySaver = true;
- }
+ if (alarm.operation != null && alarm.operation.isActivity()) {
+ // Don't defer starting actual UI
+ return false;
}
final String sourcePackage = alarm.sourcePackage;
final int sourceUid = alarm.creatorUid;
if (UserHandle.isCore(sourceUid)) {
return false;
}
- return (mAppStateTracker != null) &&
- mAppStateTracker.areAlarmsRestricted(sourceUid, sourcePackage,
- exemptOnBatterySaver);
+ return (mAppStateTracker != null) && mAppStateTracker.areAlarmsRestricted(sourceUid,
+ sourcePackage);
}
private static native long init();
@@ -3153,46 +3188,10 @@ public class AlarmManagerService extends SystemService {
private static native int setKernelTimezone(long nativeData, int minuteswest);
private static native long getNextAlarm(long nativeData, int type);
- private long getWhileIdleMinIntervalLocked(int uid) {
- final boolean ebs = (mAppStateTracker != null)
- && mAppStateTracker.isForceAllAppsStandbyEnabled();
-
- if (!ebs || mUseAllowWhileIdleShortTime.get(uid)) {
- // if the last allow-while-idle went off while uid was fg, or the uid
- // recently came into fg, don't block the alarm for long.
- return mConstants.ALLOW_WHILE_IDLE_SHORT_TIME;
- }
- return mConstants.ALLOW_WHILE_IDLE_LONG_TIME;
- }
-
boolean triggerAlarmsLocked(ArrayList<Alarm> triggerList, final long nowELAPSED) {
boolean hasWakeup = false;
final ArrayList<Alarm> pendingAlarms = mAlarmStore.removePendingAlarms(nowELAPSED);
for (final Alarm alarm : pendingAlarms) {
- if ((alarm.flags & AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) {
- // If this is an ALLOW_WHILE_IDLE alarm, we constrain how frequently the app can
- // schedule such alarms. The first such alarm from an app is always delivered.
- final long lastTime = mLastAllowWhileIdleDispatch.get(alarm.creatorUid, -1);
- final long minTime = lastTime + getWhileIdleMinIntervalLocked(alarm.creatorUid);
- if (lastTime >= 0 && nowELAPSED < minTime) {
- // Whoops, it hasn't been long enough since the last ALLOW_WHILE_IDLE
- // alarm went off for this app. Reschedule the alarm to be in the
- // correct time period.
- alarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, minTime);
- if (RECORD_DEVICE_IDLE_ALARMS) {
- IdleDispatchEntry ent = new IdleDispatchEntry();
- ent.uid = alarm.uid;
- ent.pkg = alarm.operation.getCreatorPackage();
- ent.tag = alarm.operation.getTag("");
- ent.op = "RESCHEDULE";
- ent.elapsedRealtime = nowELAPSED;
- ent.argRealtime = lastTime;
- mAllowWhileIdleDispatches.add(ent);
- }
- setImplLocked(alarm);
- continue;
- }
- }
if (isBackgroundRestricted(alarm)) {
// Alarms with FLAG_WAKE_FROM_IDLE or mPendingIdleUntil alarm are not deferred
if (DEBUG_BG_LIMIT) {
@@ -3924,8 +3923,41 @@ public class AlarmManagerService extends SystemService {
}
private final Listener mForceAppStandbyListener = new Listener() {
+
+ @Override
+ public void updateAllAlarms() {
+ // Called when:
+ // 1. Power exemption list changes,
+ // 2. Battery saver state is toggled,
+ // 3. Any package is moved into or out of the EXEMPTED bucket.
+ synchronized (mLock) {
+ if (mAlarmStore.updateAlarmDeliveries(
+ a -> adjustDeliveryTimeBasedOnBatterySaver(a))) {
+ rescheduleKernelAlarmsLocked();
+ }
+ }
+ }
+
+ @Override
+ public void updateAlarmsForUid(int uid) {
+ // Called when the given uid's state switches b/w active and idle.
+ synchronized (mLock) {
+ if (mAlarmStore.updateAlarmDeliveries(a -> {
+ if (a.creatorUid != uid) {
+ return false;
+ }
+ return adjustDeliveryTimeBasedOnBatterySaver(a);
+ })) {
+ rescheduleKernelAlarmsLocked();
+ }
+ }
+ }
+
@Override
public void unblockAllUnrestrictedAlarms() {
+ // Called when:
+ // 1. Power exemption list changes,
+ // 2. User FAS feature is disabled.
synchronized (mLock) {
sendAllUnrestrictedPendingBackgroundAlarmsLocked();
}
@@ -3934,12 +3966,14 @@ public class AlarmManagerService extends SystemService {
@Override
public void unblockAlarmsForUid(int uid) {
synchronized (mLock) {
+ // Called when the given uid becomes active.
sendPendingBackgroundAlarmsLocked(uid, null);
}
}
@Override
public void unblockAlarmsForUidPackage(int uid, String packageName) {
+ // Called when user turns off FAS for this (uid, package).
synchronized (mLock) {
sendPendingBackgroundAlarmsLocked(uid, packageName);
}
@@ -3950,9 +3984,14 @@ public class AlarmManagerService extends SystemService {
synchronized (mLock) {
if (foreground) {
mUseAllowWhileIdleShortTime.put(uid, true);
-
- // Note we don't have to drain the pending while-idle alarms here, because
- // this event should coincide with unblockAlarmsForUid().
+ if (mAlarmStore.updateAlarmDeliveries(a -> {
+ if (a.creatorUid != uid || (a.flags & FLAG_ALLOW_WHILE_IDLE) == 0) {
+ return false;
+ }
+ return adjustDeliveryTimeBasedOnBatterySaver(a);
+ })) {
+ rescheduleKernelAlarmsLocked();
+ }
}
}
}
@@ -4236,18 +4275,20 @@ public class AlarmManagerService extends SystemService {
if (allowWhileIdle) {
// Record the last time this uid handled an ALLOW_WHILE_IDLE alarm.
mLastAllowWhileIdleDispatch.put(alarm.creatorUid, nowELAPSED);
- mAlarmStore.updateAlarmDeliveries(a -> {
- if (a.creatorUid != alarm.creatorUid) {
- return false;
- }
- return adjustDeliveryTimeBasedOnDeviceIdle(a);
- });
if ((mAppStateTracker == null)
|| mAppStateTracker.isUidInForeground(alarm.creatorUid)) {
mUseAllowWhileIdleShortTime.put(alarm.creatorUid, true);
} else {
mUseAllowWhileIdleShortTime.put(alarm.creatorUid, false);
}
+ mAlarmStore.updateAlarmDeliveries(a -> {
+ if (a.creatorUid != alarm.creatorUid
+ || (a.flags & FLAG_ALLOW_WHILE_IDLE) == 0) {
+ return false;
+ }
+ return adjustDeliveryTimeBasedOnDeviceIdle(a)
+ | adjustDeliveryTimeBasedOnBatterySaver(a);
+ });
if (RECORD_DEVICE_IDLE_ALARMS) {
IdleDispatchEntry ent = new IdleDispatchEntry();
ent.uid = alarm.uid;
diff --git a/core/api/current.txt b/core/api/current.txt
index 4c3b1537727c..5a020d4382dd 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -12377,6 +12377,7 @@ package android.content.pm {
field public static final String FEATURE_TOUCHSCREEN_MULTITOUCH = "android.hardware.touchscreen.multitouch";
field public static final String FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT = "android.hardware.touchscreen.multitouch.distinct";
field public static final String FEATURE_TOUCHSCREEN_MULTITOUCH_JAZZHAND = "android.hardware.touchscreen.multitouch.jazzhand";
+ field public static final String FEATURE_TRANSLATION = "android.software.translation";
field public static final String FEATURE_USB_ACCESSORY = "android.hardware.usb.accessory";
field public static final String FEATURE_USB_HOST = "android.hardware.usb.host";
field public static final String FEATURE_VERIFIED_BOOT = "android.software.verified_boot";
@@ -30377,9 +30378,9 @@ package android.os {
public class DropBoxManager {
ctor protected DropBoxManager();
- method public void addData(String, byte[], int);
- method public void addFile(String, java.io.File, int) throws java.io.IOException;
- method public void addText(String, String);
+ method public void addData(@NonNull String, @Nullable byte[], int);
+ method public void addFile(@NonNull String, @NonNull java.io.File, int) throws java.io.IOException;
+ method public void addText(@NonNull String, @NonNull String);
method @Nullable @RequiresPermission(allOf={android.Manifest.permission.READ_LOGS, android.Manifest.permission.PACKAGE_USAGE_STATS}) public android.os.DropBoxManager.Entry getNextEntry(String, long);
method public boolean isTagEnabled(String);
field public static final String ACTION_DROPBOX_ENTRY_ADDED = "android.intent.action.DROPBOX_ENTRY_ADDED";
@@ -30392,17 +30393,17 @@ package android.os {
}
public static class DropBoxManager.Entry implements java.io.Closeable android.os.Parcelable {
- ctor public DropBoxManager.Entry(String, long);
- ctor public DropBoxManager.Entry(String, long, String);
- ctor public DropBoxManager.Entry(String, long, byte[], int);
- ctor public DropBoxManager.Entry(String, long, android.os.ParcelFileDescriptor, int);
- ctor public DropBoxManager.Entry(String, long, java.io.File, int) throws java.io.IOException;
+ ctor public DropBoxManager.Entry(@NonNull String, long);
+ ctor public DropBoxManager.Entry(@NonNull String, long, @NonNull String);
+ ctor public DropBoxManager.Entry(@NonNull String, long, @Nullable byte[], int);
+ ctor public DropBoxManager.Entry(@NonNull String, long, @Nullable android.os.ParcelFileDescriptor, int);
+ ctor public DropBoxManager.Entry(@NonNull String, long, @NonNull java.io.File, int) throws java.io.IOException;
method public void close();
method public int describeContents();
method public int getFlags();
- method public java.io.InputStream getInputStream() throws java.io.IOException;
- method public String getTag();
- method public String getText(int);
+ method @Nullable public java.io.InputStream getInputStream() throws java.io.IOException;
+ method @NonNull public String getTag();
+ method @Nullable public String getText(int);
method public long getTimeMillis();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.os.DropBoxManager.Entry> CREATOR;
@@ -38767,6 +38768,7 @@ package android.telecom {
field public static final int DIRECTION_UNKNOWN = -1; // 0xffffffff
field public static final int PROPERTY_ASSISTED_DIALING = 512; // 0x200
field public static final int PROPERTY_CONFERENCE = 1; // 0x1
+ field public static final int PROPERTY_CROSS_SIM = 16384; // 0x4000
field public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 4; // 0x4
field public static final int PROPERTY_ENTERPRISE_CALL = 32; // 0x20
field public static final int PROPERTY_GENERIC_CONFERENCE = 2; // 0x2
@@ -39060,6 +39062,7 @@ package android.telecom {
field public static final String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
field public static final String EXTRA_SIP_INVITE = "android.telecom.extra.SIP_INVITE";
field public static final int PROPERTY_ASSISTED_DIALING = 512; // 0x200
+ field public static final int PROPERTY_CROSS_SIM = 8192; // 0x2000
field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20
field public static final int PROPERTY_HIGH_DEF_AUDIO = 4; // 0x4
field public static final int PROPERTY_IS_ADHOC_CONFERENCE = 4096; // 0x1000
@@ -41817,6 +41820,8 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void switchMultiSimConfig(int);
method public void unregisterPhoneStateListener(@NonNull android.telephony.PhoneStateListener);
method public void updateAvailableNetworks(@NonNull java.util.List<android.telephony.AvailableNetworkInfo>, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>);
+ method public void uploadCallComposerPicture(@NonNull java.nio.file.Path, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.os.ParcelUuid,android.telephony.TelephonyManager.CallComposerException>);
+ method public void uploadCallComposerPicture(@NonNull java.io.InputStream, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.os.ParcelUuid,android.telephony.TelephonyManager.CallComposerException>);
field public static final String ACTION_CARRIER_MESSAGING_CLIENT_SERVICE = "android.telephony.action.CARRIER_MESSAGING_CLIENT_SERVICE";
field public static final String ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE = "android.telephony.action.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE";
field public static final String ACTION_CARRIER_SIGNAL_PCO_VALUE = "android.telephony.action.CARRIER_SIGNAL_PCO_VALUE";
@@ -41959,6 +41964,18 @@ package android.telephony {
field public static final String VVM_TYPE_OMTP = "vvm_type_omtp";
}
+ public static class TelephonyManager.CallComposerException extends java.lang.Exception {
+ ctor public TelephonyManager.CallComposerException(int, @Nullable java.io.IOException);
+ method public int getErrorCode();
+ method @Nullable public java.io.IOException getIOException();
+ field public static final int ERROR_AUTHENTICATION_FAILED = 3; // 0x3
+ field public static final int ERROR_FILE_TOO_LARGE = 2; // 0x2
+ field public static final int ERROR_INPUT_CLOSED = 4; // 0x4
+ field public static final int ERROR_IO_EXCEPTION = 5; // 0x5
+ field public static final int ERROR_REMOTE_END_CLOSED = 1; // 0x1
+ field public static final int ERROR_UNKNOWN = 0; // 0x0
+ }
+
public abstract static class TelephonyManager.CellInfoCallback {
ctor public TelephonyManager.CellInfoCallback();
method public abstract void onCellInfo(@NonNull java.util.List<android.telephony.CellInfo>);
@@ -42363,6 +42380,7 @@ package android.telephony.ims {
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public int getVoWiFiModeSetting();
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isAdvancedCallingSettingEnabled();
+ method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isCrossSimCallingEnabledByUser() throws android.telephony.ims.ImsException;
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isTtyOverVolteEnabled();
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isVoWiFiRoamingSettingEnabled();
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isVoWiFiSettingEnabled();
@@ -42579,6 +42597,7 @@ package android.telephony.ims {
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback);
+ field public static final int ATTR_EPDG_OVER_CELL_INTERNET = 1; // 0x1
field public static final int REGISTRATION_STATE_NOT_REGISTERED = 0; // 0x0
field public static final int REGISTRATION_STATE_REGISTERED = 2; // 0x2
field public static final int REGISTRATION_STATE_REGISTERING = 1; // 0x1
@@ -42586,8 +42605,10 @@ package android.telephony.ims {
public static class RegistrationManager.RegistrationCallback {
ctor public RegistrationManager.RegistrationCallback();
- method public void onRegistered(int);
- method public void onRegistering(int);
+ method @Deprecated public void onRegistered(int);
+ method public void onRegistered(int, int);
+ method @Deprecated public void onRegistering(int);
+ method public void onRegistering(int, int);
method public void onTechnologyChangeFailed(int, @NonNull android.telephony.ims.ImsReasonInfo);
method public void onUnregistered(@NonNull android.telephony.ims.ImsReasonInfo);
}
@@ -45461,6 +45482,7 @@ package android.util {
method public void remove(int);
method public void removeAt(int);
method public void removeAtRange(int, int);
+ method public void set(int, E);
method public void setValueAt(int, E);
method public int size();
method public E valueAt(int);
@@ -51611,6 +51633,66 @@ package android.view.textservice {
}
+package android.view.translation {
+
+ public final class TranslationManager {
+ method @Nullable @WorkerThread public android.view.translation.Translator createTranslator(@NonNull android.view.translation.TranslationSpec, @NonNull android.view.translation.TranslationSpec);
+ method @NonNull @WorkerThread public java.util.List<java.lang.String> getSupportedLocales();
+ }
+
+ public final class TranslationRequest implements android.os.Parcelable {
+ ctor public TranslationRequest(@Nullable CharSequence);
+ method public int describeContents();
+ method @Nullable public android.view.autofill.AutofillId getAutofillId();
+ method @Nullable public CharSequence getTranslationText();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.view.translation.TranslationRequest> CREATOR;
+ }
+
+ public static final class TranslationRequest.Builder {
+ ctor public TranslationRequest.Builder();
+ method @NonNull public android.view.translation.TranslationRequest build();
+ method @NonNull public android.view.translation.TranslationRequest.Builder setAutofillId(@NonNull android.view.autofill.AutofillId);
+ method @NonNull public android.view.translation.TranslationRequest.Builder setTranslationText(@NonNull CharSequence);
+ }
+
+ public final class TranslationResponse implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getTranslationStatus();
+ method @NonNull public java.util.List<android.view.translation.TranslationRequest> getTranslations();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.view.translation.TranslationResponse> CREATOR;
+ field public static final int TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE = 2; // 0x2
+ field public static final int TRANSLATION_STATUS_SUCCESS = 0; // 0x0
+ field public static final int TRANSLATION_STATUS_UNKNOWN_ERROR = 1; // 0x1
+ }
+
+ public static final class TranslationResponse.Builder {
+ ctor public TranslationResponse.Builder(int);
+ method @NonNull public android.view.translation.TranslationResponse.Builder addTranslations(@NonNull android.view.translation.TranslationRequest);
+ method @NonNull public android.view.translation.TranslationResponse build();
+ method @NonNull public android.view.translation.TranslationResponse.Builder setTranslationStatus(int);
+ method @NonNull public android.view.translation.TranslationResponse.Builder setTranslations(@NonNull java.util.List<android.view.translation.TranslationRequest>);
+ }
+
+ public final class TranslationSpec implements android.os.Parcelable {
+ ctor public TranslationSpec(@NonNull String, int);
+ method public int describeContents();
+ method public int getDataFormat();
+ method @NonNull public String getLanguage();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.view.translation.TranslationSpec> CREATOR;
+ field public static final int DATA_FORMAT_TEXT = 1; // 0x1
+ }
+
+ public class Translator {
+ method public void destroy();
+ method public boolean isDestroyed();
+ method @Nullable @WorkerThread public android.view.translation.TranslationResponse translate(@NonNull android.view.translation.TranslationRequest);
+ }
+
+}
+
package android.webkit {
public abstract class ClientCertRequest {
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index ef6c8b713c2c..bf92b34a5657 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -108,7 +108,7 @@ package android.media.session {
}
public final class MediaSessionManager {
- method public void addOnActiveSessionsChangedListener(@NonNull android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, @Nullable android.content.ComponentName, int, @Nullable android.os.Handler);
+ method public void addOnActiveSessionsChangedListener(@NonNull android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, @Nullable android.content.ComponentName, @NonNull android.os.UserHandle, @Nullable android.os.Handler);
method public void dispatchMediaKeyEvent(@NonNull android.view.KeyEvent);
method public void dispatchMediaKeyEvent(@NonNull android.view.KeyEvent, boolean);
method public void dispatchMediaKeyEventAsSystemService(@NonNull android.view.KeyEvent);
@@ -116,7 +116,7 @@ package android.media.session {
method public void dispatchVolumeKeyEvent(@NonNull android.view.KeyEvent, int, boolean);
method public void dispatchVolumeKeyEventAsSystemService(@NonNull android.view.KeyEvent, int);
method public void dispatchVolumeKeyEventToSessionAsSystemService(@NonNull android.view.KeyEvent, @NonNull android.media.session.MediaSession.Token);
- method @NonNull public java.util.List<android.media.session.MediaController> getActiveSessionsForUser(@Nullable android.content.ComponentName, int);
+ method @NonNull public java.util.List<android.media.session.MediaController> getActiveSessionsForUser(@Nullable android.content.ComponentName, @NonNull android.os.UserHandle);
method public void registerRemoteSessionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.session.MediaSessionManager.RemoteSessionCallback);
method public void unregisterRemoteSessionCallback(@NonNull android.media.session.MediaSessionManager.RemoteSessionCallback);
field public static final int RESULT_MEDIA_KEY_HANDLED = 1; // 0x1
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 473a2808cd00..9451d69c59f8 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -52,6 +52,7 @@ package android {
field public static final String BIND_TELEPHONY_NETWORK_SERVICE = "android.permission.BIND_TELEPHONY_NETWORK_SERVICE";
field public static final String BIND_TEXTCLASSIFIER_SERVICE = "android.permission.BIND_TEXTCLASSIFIER_SERVICE";
field public static final String BIND_TIME_ZONE_PROVIDER_SERVICE = "android.permission.BIND_TIME_ZONE_PROVIDER_SERVICE";
+ field public static final String BIND_TRANSLATION_SERVICE = "android.permission.BIND_TRANSLATION_SERVICE";
field public static final String BIND_TRUST_AGENT = "android.permission.BIND_TRUST_AGENT";
field public static final String BIND_TV_REMOTE_SERVICE = "android.permission.BIND_TV_REMOTE_SERVICE";
field public static final String BRICK = "android.permission.BRICK";
@@ -1941,6 +1942,7 @@ package android.content {
field public static final String SYSTEM_CONFIG_SERVICE = "system_config";
field public static final String SYSTEM_UPDATE_SERVICE = "system_update";
field public static final String TETHERING_SERVICE = "tethering";
+ field public static final String TRANSLATION_MANAGER_SERVICE = "transformer";
field public static final String VR_SERVICE = "vrmanager";
field public static final String WIFI_NL80211_SERVICE = "wifinl80211";
field @Deprecated public static final String WIFI_RTT_SERVICE = "rttmanager";
@@ -6639,6 +6641,7 @@ package android.net {
method public long getExpiryTimeMillis();
method public long getRefreshTimeMillis();
method @Nullable public android.net.Uri getUserPortalUrl();
+ method @Nullable public String getVenueFriendlyName();
method @Nullable public android.net.Uri getVenueInfoUrl();
method public boolean isCaptive();
method public boolean isSessionExtendable();
@@ -6656,6 +6659,7 @@ package android.net {
method @NonNull public android.net.CaptivePortalData.Builder setRefreshTime(long);
method @NonNull public android.net.CaptivePortalData.Builder setSessionExtendable(boolean);
method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri);
+ method @NonNull public android.net.CaptivePortalData.Builder setVenueFriendlyName(@Nullable String);
method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri);
}
@@ -6907,13 +6911,16 @@ package android.net {
}
public final class NetworkCapabilities implements android.os.Parcelable {
+ ctor public NetworkCapabilities(@Nullable android.net.NetworkCapabilities, boolean);
method @NonNull public int[] getAdministratorUids();
method @Nullable public String getSsid();
method @NonNull public int[] getTransportTypes();
method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities);
+ field public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28; // 0x1c
field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16
field public static final int NET_CAPABILITY_OEM_PRIVATE = 26; // 0x1a
field public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24; // 0x18
+ field public static final int NET_CAPABILITY_VEHICLE_INTERNAL = 27; // 0x1b
}
public static final class NetworkCapabilities.Builder {
@@ -7128,6 +7135,11 @@ package android.net {
field public static final int TAG_SYSTEM_IMPERSONATION_RANGE_START = -256; // 0xffffff00
}
+ public interface TransportInfo {
+ method public default boolean hasLocationSensitiveFields();
+ method @NonNull public default android.net.TransportInfo makeCopy(boolean);
+ }
+
public abstract class Uri implements java.lang.Comparable<android.net.Uri> android.os.Parcelable {
method @NonNull public String toSafeString();
}
@@ -8555,10 +8567,11 @@ package android.printservice.recommendation {
package android.provider {
public class CallLog {
- method @RequiresPermission(android.Manifest.permission.WRITE_CALL_LOG) public static void storeCallComposerPictureAsUser(@NonNull android.content.Context, @Nullable android.os.UserHandle, @NonNull java.io.InputStream, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.net.Uri,android.provider.CallLog.CallComposerLoggingException>);
+ method @RequiresPermission(allOf={android.Manifest.permission.WRITE_CALL_LOG, android.Manifest.permission.INTERACT_ACROSS_USERS}) public static void storeCallComposerPictureAsUser(@NonNull android.content.Context, @Nullable android.os.UserHandle, @NonNull java.io.InputStream, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.net.Uri,android.provider.CallLog.CallComposerLoggingException>);
}
public static class CallLog.CallComposerLoggingException extends java.lang.Throwable {
+ ctor public CallLog.CallComposerLoggingException(int);
method public int getErrorCode();
field public static final int ERROR_INPUT_CLOSED = 3; // 0x3
field public static final int ERROR_REMOTE_END_CLOSED = 1; // 0x1
@@ -9854,6 +9867,48 @@ package android.service.timezone {
}
+package android.service.translation {
+
+ public final class TranslationRequest implements android.os.Parcelable {
+ ctor public TranslationRequest(int, @NonNull android.view.translation.TranslationSpec, @NonNull android.view.translation.TranslationSpec, @NonNull java.util.List<android.view.translation.TranslationRequest>);
+ method public int describeContents();
+ method @NonNull public android.view.translation.TranslationSpec getDestSpec();
+ method public int getRequestId();
+ method @NonNull public android.view.translation.TranslationSpec getSourceSpec();
+ method @NonNull public java.util.List<android.view.translation.TranslationRequest> getTranslationRequests();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.service.translation.TranslationRequest> CREATOR;
+ }
+
+ public static final class TranslationRequest.Builder {
+ ctor public TranslationRequest.Builder(int, @NonNull android.view.translation.TranslationSpec, @NonNull android.view.translation.TranslationSpec, @NonNull java.util.List<android.view.translation.TranslationRequest>);
+ method @NonNull public android.service.translation.TranslationRequest.Builder addTranslationRequests(@NonNull android.view.translation.TranslationRequest);
+ method @NonNull public android.service.translation.TranslationRequest build();
+ method @NonNull public android.service.translation.TranslationRequest.Builder setDestSpec(@NonNull android.view.translation.TranslationSpec);
+ method @NonNull public android.service.translation.TranslationRequest.Builder setRequestId(int);
+ method @NonNull public android.service.translation.TranslationRequest.Builder setSourceSpec(@NonNull android.view.translation.TranslationSpec);
+ method @NonNull public android.service.translation.TranslationRequest.Builder setTranslationRequests(@NonNull java.util.List<android.view.translation.TranslationRequest>);
+ }
+
+ public abstract class TranslationService extends android.app.Service {
+ ctor public TranslationService();
+ method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
+ method public void onConnected();
+ method public abstract void onCreateTranslationSession(@NonNull android.view.translation.TranslationSpec, @NonNull android.view.translation.TranslationSpec, int);
+ method public void onDisconnected();
+ method public abstract void onFinishTranslationSession(int);
+ method public abstract void onTranslationRequest(@NonNull android.service.translation.TranslationRequest, int, @NonNull android.os.CancellationSignal, @NonNull android.service.translation.TranslationService.OnTranslationResultCallback);
+ field public static final String SERVICE_INTERFACE = "android.service.translation.TranslationService";
+ field public static final String SERVICE_META_DATA = "android.translation_service";
+ }
+
+ public static interface TranslationService.OnTranslationResultCallback {
+ method public void onError();
+ method public void onTranslationSuccess(@NonNull android.view.translation.TranslationResponse);
+ }
+
+}
+
package android.service.trust {
public class TrustAgentService extends android.app.Service {
@@ -11089,6 +11144,7 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUiccApplicationsEnabled(int, boolean);
field @RequiresPermission(android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS) public static final String ACTION_SUBSCRIPTION_PLANS_CHANGED = "android.telephony.action.SUBSCRIPTION_PLANS_CHANGED";
field @NonNull public static final android.net.Uri ADVANCED_CALLING_ENABLED_CONTENT_URI;
+ field @NonNull public static final android.net.Uri CROSS_SIM_ENABLED_CONTENT_URI;
field @Deprecated public static final int PROFILE_CLASS_DEFAULT = -1; // 0xffffffff
field public static final int PROFILE_CLASS_OPERATIONAL = 2; // 0x2
field public static final int PROFILE_CLASS_PROVISIONING = 1; // 0x1
@@ -11208,6 +11264,7 @@ package android.telephony {
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isOpportunisticNetworkEnabled();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isPotentialEmergencyNumber(@NonNull String);
+ method public boolean isRadioInterfaceCapabilitySupported(@NonNull String);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRadioOn();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isTetheringApnRequired();
@@ -11282,6 +11339,7 @@ package android.telephony {
field public static final int CALL_WAITING_STATUS_ENABLED = 1; // 0x1
field public static final int CALL_WAITING_STATUS_NOT_SUPPORTED = 4; // 0x4
field public static final int CALL_WAITING_STATUS_UNKNOWN_ERROR = 3; // 0x3
+ field public static final String CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE = "CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE";
field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
@@ -12149,6 +12207,7 @@ package android.telephony.ims {
field public static final String EXTRA_EXTENDING_TO_CONFERENCE_SUPPORTED = "android.telephony.ims.extra.EXTENDING_TO_CONFERENCE_SUPPORTED";
field public static final String EXTRA_FORWARDED_NUMBER = "android.telephony.ims.extra.FORWARDED_NUMBER";
field public static final String EXTRA_IS_CALL_PULL = "CallPull";
+ field public static final String EXTRA_IS_CROSS_SIM_CALL = "android.telephony.ims.extra.IS_CROSS_SIM_CALL";
field public static final String EXTRA_LOCATION = "android.telephony.ims.extra.LOCATION";
field public static final String EXTRA_OI = "oi";
field public static final String EXTRA_OIR = "oir";
@@ -12278,6 +12337,7 @@ package android.telephony.ims {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void isSupported(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>) throws android.telephony.ims.ImsException;
method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setAdvancedCallingSettingEnabled(boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCrossSimCallingEnabled(boolean) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setRttCapabilitySetting(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiModeSetting(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiNonPersistent(boolean, int);
@@ -12290,6 +12350,8 @@ package android.telephony.ims {
@Deprecated public static class ImsMmTelManager.RegistrationCallback extends android.telephony.ims.RegistrationManager.RegistrationCallback {
ctor @Deprecated public ImsMmTelManager.RegistrationCallback();
+ method @Deprecated public void onRegistered(int);
+ method @Deprecated public void onRegistering(int);
}
public final class ImsReasonInfo implements android.os.Parcelable {
@@ -12586,7 +12648,32 @@ package android.telephony.ims {
}
public class RcsUceAdapter {
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void addOnPublishStateChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.OnPublishStateChangedListener) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getUcePublishState() throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void removeOnPublishStateChangedListener(@NonNull android.telephony.ims.RcsUceAdapter.OnPublishStateChangedListener) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
+ field public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 1; // 0x1
+ field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 7; // 0x7
+ field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 6; // 0x6
+ field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD = 4; // 0x4
+ field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS = 5; // 0x5
+ field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN = 9; // 0x9
+ field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED = 2; // 0x2
+ field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED = 3; // 0x3
+ field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED = 10; // 0xa
+ field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED = 11; // 0xb
+ field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN = 8; // 0x8
+ field public static final int CAPABILITY_UPDATE_TRIGGER_UNKNOWN = 0; // 0x0
+ field public static final int PUBLISH_STATE_NOT_PUBLISHED = 2; // 0x2
+ field public static final int PUBLISH_STATE_OK = 1; // 0x1
+ field public static final int PUBLISH_STATE_OTHER_ERROR = 6; // 0x6
+ field public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4; // 0x4
+ field public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5; // 0x5
+ field public static final int PUBLISH_STATE_VOICE_PROVISION_ERROR = 3; // 0x3
+ }
+
+ public static interface RcsUceAdapter.OnPublishStateChangedListener {
+ method public void onPublishStateChange(int);
}
public final class RtpHeaderExtension implements android.os.Parcelable {
@@ -12793,16 +12880,24 @@ package android.telephony.ims.feature {
}
public class RcsFeature extends android.telephony.ims.feature.ImsFeature {
- ctor public RcsFeature();
+ ctor @Deprecated public RcsFeature();
+ ctor public RcsFeature(@NonNull java.util.concurrent.Executor);
method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
+ method @NonNull public android.telephony.ims.stub.RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.stub.CapabilityExchangeEventListener);
method public void onFeatureReady();
method public void onFeatureRemoved();
+ method public void removeCapabilityExchangeImpl(@NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase);
}
}
package android.telephony.ims.stub {
+ public interface CapabilityExchangeEventListener {
+ method public void onRequestPublishCapabilities(int) throws android.telephony.ims.ImsException;
+ method public void onUnpublish() throws android.telephony.ims.ImsException;
+ }
+
public interface DelegateConnectionMessageCallback {
method public void onMessageReceived(@NonNull android.telephony.ims.SipMessage);
method public void onMessageSendFailure(@NonNull String, int);
@@ -12929,6 +13024,7 @@ package android.telephony.ims.stub {
method public void triggerFullNetworkRegistration(@IntRange(from=100, to=699) int, @Nullable String);
method public void triggerSipDelegateDeregistration();
method public void updateSipDelegateRegistration();
+ field public static final int REGISTRATION_TECH_CROSS_SIM = 2; // 0x2
field public static final int REGISTRATION_TECH_IWLAN = 1; // 0x1
field public static final int REGISTRATION_TECH_LTE = 0; // 0x0
field public static final int REGISTRATION_TECH_NONE = -1; // 0xffffffff
@@ -12983,6 +13079,27 @@ package android.telephony.ims.stub {
method public int updateColr(int);
}
+ public class RcsCapabilityExchangeImplBase {
+ ctor public RcsCapabilityExchangeImplBase(@NonNull java.util.concurrent.Executor);
+ method public void publishCapabilities(@NonNull String, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.PublishResponseCallback);
+ field public static final int COMMAND_CODE_FETCH_ERROR = 3; // 0x3
+ field public static final int COMMAND_CODE_GENERIC_FAILURE = 1; // 0x1
+ field public static final int COMMAND_CODE_INSUFFICIENT_MEMORY = 5; // 0x5
+ field public static final int COMMAND_CODE_INVALID_PARAM = 2; // 0x2
+ field public static final int COMMAND_CODE_LOST_NETWORK_CONNECTION = 6; // 0x6
+ field public static final int COMMAND_CODE_NOT_FOUND = 8; // 0x8
+ field public static final int COMMAND_CODE_NOT_SUPPORTED = 7; // 0x7
+ field public static final int COMMAND_CODE_NO_CHANGE = 10; // 0xa
+ field public static final int COMMAND_CODE_REQUEST_TIMEOUT = 4; // 0x4
+ field public static final int COMMAND_CODE_SERVICE_UNAVAILABLE = 9; // 0x9
+ field public static final int COMMAND_CODE_SERVICE_UNKNOWN = 0; // 0x0
+ }
+
+ public static interface RcsCapabilityExchangeImplBase.PublishResponseCallback {
+ method public void onCommandError(int) throws android.telephony.ims.ImsException;
+ method public void onNetworkResponse(@IntRange(from=100, to=699) int, @NonNull String) throws android.telephony.ims.ImsException;
+ }
+
public interface SipDelegate {
method public void closeDialog(@NonNull String);
method public void notifyMessageReceiveError(@NonNull String, int);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index c03461aa7742..1422561e3347 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2512,7 +2512,6 @@ package android.window {
method @CallSuper @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public java.util.List<android.window.TaskAppearedInfo> registerOrganizer();
method @BinderThread public void removeStartingWindow(int);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void setInterceptBackPressedOnTaskRoot(@NonNull android.window.WindowContainerToken, boolean);
- method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void setLaunchRoot(int, @NonNull android.window.WindowContainerToken);
method @CallSuper @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void unregisterOrganizer();
}
@@ -2527,6 +2526,7 @@ package android.window {
method public int describeContents();
method @NonNull public android.window.WindowContainerTransaction reorder(@NonNull android.window.WindowContainerToken, boolean);
method @NonNull public android.window.WindowContainerTransaction reparent(@NonNull android.window.WindowContainerToken, @Nullable android.window.WindowContainerToken, boolean);
+ method @NonNull public android.window.WindowContainerTransaction reparentTasks(@Nullable android.window.WindowContainerToken, @Nullable android.window.WindowContainerToken, @Nullable int[], @Nullable int[], boolean);
method @NonNull public android.window.WindowContainerTransaction scheduleFinishEnterPip(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect);
method @NonNull public android.window.WindowContainerTransaction setActivityWindowingMode(@NonNull android.window.WindowContainerToken, int);
method @NonNull public android.window.WindowContainerTransaction setAppBounds(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect);
@@ -2534,6 +2534,7 @@ package android.window {
method @NonNull public android.window.WindowContainerTransaction setBoundsChangeTransaction(@NonNull android.window.WindowContainerToken, @NonNull android.view.SurfaceControl.Transaction);
method @NonNull public android.window.WindowContainerTransaction setFocusable(@NonNull android.window.WindowContainerToken, boolean);
method @NonNull public android.window.WindowContainerTransaction setHidden(@NonNull android.window.WindowContainerToken, boolean);
+ method @NonNull public android.window.WindowContainerTransaction setLaunchRoot(@NonNull android.window.WindowContainerToken, @Nullable int[], @Nullable int[]);
method @NonNull public android.window.WindowContainerTransaction setScreenSizeDp(@NonNull android.window.WindowContainerToken, int, int);
method @NonNull public android.window.WindowContainerTransaction setSmallestScreenWidthDp(@NonNull android.window.WindowContainerToken, int);
method @NonNull public android.window.WindowContainerTransaction setWindowingMode(@NonNull android.window.WindowContainerToken, int);
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 6e7bb83b2fcb..b1890b075c59 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -32,10 +32,12 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.TransactionTooLargeException;
import android.os.WorkSource;
+import android.util.ArraySet;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* Activity manager local system service interface.
@@ -446,6 +448,32 @@ public abstract class ActivityManagerInternal {
*/
public abstract void setDeviceOwnerUid(int uid);
+ /** Is this a profile owner app? */
+ public abstract boolean isProfileOwner(int uid);
+
+ /**
+ * Called by DevicePolicyManagerService to set the uid of the profile owner.
+ * @param profileOwnerUids The profile owner UIDs. The ownership of the array is
+ * passed to callee.
+ */
+ public abstract void setProfileOwnerUid(ArraySet<Integer> profileOwnerUids);
+
+ /**
+ * Set all associated companion app that belongs to a userId.
+ * @param userId
+ * @param companionAppUids ActivityManager will take ownership of this Set, the caller
+ * shouldn't touch this Set after calling this interface.
+ */
+ public abstract void setCompanionAppUids(int userId, Set<Integer> companionAppUids);
+
+ /**
+ * is the uid an associated companion app of a userId?
+ * @param userId
+ * @param uid
+ * @return
+ */
+ public abstract boolean isAssociatedCompanionApp(int userId, int uid);
+
/**
* Sends a broadcast, assuming the caller to be the system and allowing the inclusion of an
* approved whitelist of app Ids >= {@link android.os.Process#FIRST_APPLICATION_UID} that the
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 2fe1711bdbd4..bd437f4ce192 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2328,7 +2328,7 @@ public final class ActivityThread extends ClientTransactionHandler {
return null;
}
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @UnsupportedAppUsage(trackingBug = 171933273)
public final LoadedApk getPackageInfo(ApplicationInfo ai, CompatibilityInfo compatInfo,
int flags) {
boolean includeCode = (flags&Context.CONTEXT_INCLUDE_CODE) != 0;
diff --git a/core/java/android/app/EventLogTags.logtags b/core/java/android/app/EventLogTags.logtags
index 6296a689b6dd..d0856f44d9a9 100644
--- a/core/java/android/app/EventLogTags.logtags
+++ b/core/java/android/app/EventLogTags.logtags
@@ -10,8 +10,6 @@ option java_package android.app
# The activity's onResume has been called.
30022 wm_on_resume_called (Token|1|5),(Component Name|3),(Reason|3)
-# Attempting to stop an activity
-30048 wm_stop_activity (User|1|5),(Token|1|5),(Component Name|3)
# The activity's onStop has been called.
30049 wm_on_stop_called (Token|1|5),(Component Name|3),(Reason|3)
@@ -31,6 +29,3 @@ option java_package android.app
# The activity's onTopResumedActivityChanged(false) has been called.
30065 wm_on_top_resumed_lost_called (Token|1|5),(Component Name|3),(Reason|3)
-# An activity been add into stopping list
-30066 wm_add_to_stopping (User|1|5),(Token|1|5),(Component Name|3),(Reason|3)
-
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index c804e6427dce..6d79e2d3c166 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -39,6 +39,9 @@ per-file *Alarm* = file:/apex/jobscheduler/OWNERS
# AppOps
per-file *AppOp* = file:/core/java/android/permission/OWNERS
+# Multiuser
+per-file *User* = file:/MULTIUSER_OWNERS
+
# Notification
per-file *Notification* = file:/packages/SystemUI/OWNERS
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 050d194c4094..dd016a2cf78e 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -209,6 +209,8 @@ import android.view.contentcapture.IContentCaptureManager;
import android.view.inputmethod.InputMethodManager;
import android.view.textclassifier.TextClassificationManager;
import android.view.textservice.TextServicesManager;
+import android.view.translation.ITranslationManager;
+import android.view.translation.TranslationManager;
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IBatteryStats;
@@ -1172,6 +1174,20 @@ public final class SystemServiceRegistry {
return null;
}});
+ registerService(Context.TRANSLATION_MANAGER_SERVICE, TranslationManager.class,
+ new CachedServiceFetcher<TranslationManager>() {
+ @Override
+ public TranslationManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ IBinder b = ServiceManager.getService(Context.TRANSLATION_MANAGER_SERVICE);
+ ITranslationManager service = ITranslationManager.Stub.asInterface(b);
+ // Service is null when not provided by OEM.
+ if (service != null) {
+ return new TranslationManager(ctx.getOuterContext(), service);
+ }
+ return null;
+ }});
+
registerService(Context.SEARCH_UI_SERVICE, SearchUiManager.class,
new CachedServiceFetcher<SearchUiManager>() {
@Override
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 97879b80ea06..7bc967fe251d 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2729,6 +2729,17 @@ public class DevicePolicyManager {
return DebugUtils.constantToString(DevicePolicyManager.class, PREFIX_OPERATION, operation);
}
+ /** @hide */
+ public void resetNewUserDisclaimer() {
+ if (mService != null) {
+ try {
+ mService.resetNewUserDisclaimer();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
/**
* Return true if the given administrator component is currently active (enabled) in the system.
*
@@ -3020,6 +3031,10 @@ public class DevicePolicyManager {
*
* <p><strong>Note:</strong> Specifying password requirements using this method clears the
* password complexity requirements set using {@link #setRequiredPasswordComplexity(int)}.
+ * If this method is called on the {@link DevicePolicyManager} instance returned by
+ * {@link #getParentProfileInstance(ComponentName)}, then password complexity requirements
+ * set on the primary {@link DevicePolicyManager} must be cleared first by calling
+ * {@link #setRequiredPasswordComplexity} with {@link #PASSWORD_COMPLEXITY_NONE) first.
*
* @deprecated Prefer using {@link #setRequiredPasswordComplexity(int)}, to require a password
* that satisfies a complexity level defined by the platform, rather than specifying custom
@@ -3039,6 +3054,9 @@ public class DevicePolicyManager {
* calling app is targeting {@link android.os.Build.VERSION_CODES#S} and above,
* and is calling the method the {@link DevicePolicyManager} instance returned by
* {@link #getParentProfileInstance(ComponentName)}.
+ * @throws IllegalStateException if the caller is trying to set password quality on the parent
+ * {@link DevicePolicyManager} instance while password complexity was set on the
+ * primary {@link DevicePolicyManager} instance.
*/
@Deprecated
public void setPasswordQuality(@NonNull ComponentName admin, int quality) {
@@ -4055,10 +4073,18 @@ public class DevicePolicyManager {
* <p><strong>Note:</strong> Specifying password requirements using this method clears any
* password requirements set using the obsolete {@link #setPasswordQuality(ComponentName, int)}
* and any of its associated methods.
+ * Additionally, if there are password requirements set using the obsolete
+ * {@link #setPasswordQuality(ComponentName, int)} on the parent {@code DevicePolicyManager}
+ * instance, they must be cleared by calling {@link #setPasswordQuality(ComponentName, int)}
+ * with {@link #PASSWORD_QUALITY_UNSPECIFIED} on that instance prior to setting complexity
+ * requirement for the managed profile.
*
* @throws SecurityException if the calling application is not a device owner or a profile
* owner.
* @throws IllegalArgumentException if the complexity level is not one of the four above.
+ * @throws IllegalStateException if the caller is trying to set password complexity while there
+ * are password requirements specified using {@link #setPasswordQuality(ComponentName, int)}
+ * on the parent {@code DevicePolicyManager} instance.
*/
public void setRequiredPasswordComplexity(@PasswordComplexity int passwordComplexity) {
if (mService == null) {
@@ -5191,6 +5217,16 @@ public class DevicePolicyManager {
"android.app.action.MANAGED_USER_CREATED";
/**
+ * Broadcast action: notify system that a new (Android) user was added when the device is
+ * managed by a device owner, so receivers can show the proper disclaimer to the (human) user.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_SHOW_NEW_USER_DISCLAIMER =
+ "android.app.action.ACTION_SHOW_NEW_USER_DISCLAIMER";
+
+ /**
* Widgets are enabled in keyguard
*/
public static final int KEYGUARD_DISABLE_FEATURES_NONE = 0;
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index e81abfe5a409..aaa5f7ce4cb0 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -252,6 +252,7 @@ interface IDevicePolicyManager {
int stopUser(in ComponentName who, in UserHandle userHandle);
int logoutUser(in ComponentName who);
List<UserHandle> getSecondaryUsers(in ComponentName who);
+ void resetNewUserDisclaimer();
void enableSystemApp(in ComponentName admin, in String callerPackage, in String packageName);
int enableSystemAppWithIntent(in ComponentName admin, in String callerPackage, in Intent intent);
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index c0cb32346821..15daf1c59d1a 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -118,7 +118,7 @@ public final class BluetoothA2dp implements BluetoothProfile {
* @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @UnsupportedAppUsage(trackingBug = 171933273)
public static final String ACTION_ACTIVE_DEVICE_CHANGED =
"android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED";
@@ -409,7 +409,7 @@ public final class BluetoothA2dp implements BluetoothProfile {
* @hide
*/
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @UnsupportedAppUsage(trackingBug = 171933273)
public boolean setActiveDevice(@Nullable BluetoothDevice device) {
if (DBG) log("setActiveDevice(" + device + ")");
try {
@@ -433,7 +433,7 @@ public final class BluetoothA2dp implements BluetoothProfile {
* is active
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @UnsupportedAppUsage(trackingBug = 171933273)
@Nullable
@RequiresPermission(Manifest.permission.BLUETOOTH)
public BluetoothDevice getActiveDevice() {
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 406fe8d5023c..7eda50e5c9cb 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1174,7 +1174,7 @@ public final class BluetoothAdapter {
* @return true to indicate adapter shutdown has begun, or false on immediate error
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @UnsupportedAppUsage(trackingBug = 171933273)
public boolean disable(boolean persist) {
try {
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 41610963c047..d6b38fd32e8f 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -113,7 +113,7 @@ public final class BluetoothHeadset implements BluetoothProfile {
* @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @UnsupportedAppUsage(trackingBug = 171933273)
public static final String ACTION_ACTIVE_DEVICE_CHANGED =
"android.bluetooth.headset.profile.action.ACTIVE_DEVICE_CHANGED";
@@ -1172,7 +1172,7 @@ public final class BluetoothHeadset implements BluetoothProfile {
* @hide
*/
@RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @UnsupportedAppUsage(trackingBug = 171933273)
public boolean setActiveDevice(@Nullable BluetoothDevice device) {
if (DBG) {
Log.d(TAG, "setActiveDevice: " + device);
@@ -1198,7 +1198,7 @@ public final class BluetoothHeadset implements BluetoothProfile {
* is active.
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @UnsupportedAppUsage(trackingBug = 171933273)
@Nullable
@RequiresPermission(Manifest.permission.BLUETOOTH)
public BluetoothDevice getActiveDevice() {
diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java
index 57b0828b334c..083ce968d66f 100644
--- a/core/java/android/companion/AssociationRequest.java
+++ b/core/java/android/companion/AssociationRequest.java
@@ -96,11 +96,25 @@ public final class AssociationRequest implements Parcelable {
*/
private @Nullable String mCallingPackage = null;
+ /**
+ * The user-readable description of the device profile's privileges.
+ *
+ * Populated by the system.
+ *
+ * @hide
+ */
+ private @Nullable String mDeviceProfilePrivilegesDescription = null;
+
/** @hide */
public void setCallingPackage(@NonNull String pkg) {
mCallingPackage = pkg;
}
+ /** @hide */
+ public void setDeviceProfilePrivilegesDescription(@NonNull String desc) {
+ mDeviceProfilePrivilegesDescription = desc;
+ }
+
private void onConstructed() {
if (mDeviceProfile != null
&& !Objects.equals(mDeviceProfile, DEVICE_PROFILE_WATCH)) {
@@ -178,14 +192,14 @@ public final class AssociationRequest implements Parcelable {
markUsed();
return new AssociationRequest(
mSingleDevice, emptyIfNull(mDeviceFilters),
- mDeviceProfile, null);
+ mDeviceProfile, null, null);
}
}
- // Code below generated by codegen v1.0.20.
+ // Code below generated by codegen v1.0.22.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -215,6 +229,10 @@ public final class AssociationRequest implements Parcelable {
* The app package making the request.
*
* Populated by the system.
+ * @param deviceProfilePrivilegesDescription
+ * The user-readable description of the device profile's privileges.
+ *
+ * Populated by the system.
* @hide
*/
@DataClass.Generated.Member
@@ -222,7 +240,8 @@ public final class AssociationRequest implements Parcelable {
boolean singleDevice,
@NonNull List<DeviceFilter<?>> deviceFilters,
@Nullable @DeviceProfile String deviceProfile,
- @Nullable String callingPackage) {
+ @Nullable String callingPackage,
+ @Nullable String deviceProfilePrivilegesDescription) {
this.mSingleDevice = singleDevice;
this.mDeviceFilters = deviceFilters;
com.android.internal.util.AnnotationValidations.validate(
@@ -231,6 +250,7 @@ public final class AssociationRequest implements Parcelable {
com.android.internal.util.AnnotationValidations.validate(
DeviceProfile.class, null, mDeviceProfile);
this.mCallingPackage = callingPackage;
+ this.mDeviceProfilePrivilegesDescription = deviceProfilePrivilegesDescription;
onConstructed();
}
@@ -257,6 +277,18 @@ public final class AssociationRequest implements Parcelable {
return mCallingPackage;
}
+ /**
+ * The user-readable description of the device profile's privileges.
+ *
+ * Populated by the system.
+ *
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public @Nullable String getDeviceProfilePrivilegesDescription() {
+ return mDeviceProfilePrivilegesDescription;
+ }
+
@Override
@DataClass.Generated.Member
public String toString() {
@@ -267,7 +299,8 @@ public final class AssociationRequest implements Parcelable {
"singleDevice = " + mSingleDevice + ", " +
"deviceFilters = " + mDeviceFilters + ", " +
"deviceProfile = " + mDeviceProfile + ", " +
- "callingPackage = " + mCallingPackage +
+ "callingPackage = " + mCallingPackage + ", " +
+ "deviceProfilePrivilegesDescription = " + mDeviceProfilePrivilegesDescription +
" }";
}
@@ -287,7 +320,8 @@ public final class AssociationRequest implements Parcelable {
&& mSingleDevice == that.mSingleDevice
&& Objects.equals(mDeviceFilters, that.mDeviceFilters)
&& Objects.equals(mDeviceProfile, that.mDeviceProfile)
- && Objects.equals(mCallingPackage, that.mCallingPackage);
+ && Objects.equals(mCallingPackage, that.mCallingPackage)
+ && Objects.equals(mDeviceProfilePrivilegesDescription, that.mDeviceProfilePrivilegesDescription);
}
@Override
@@ -301,6 +335,7 @@ public final class AssociationRequest implements Parcelable {
_hash = 31 * _hash + Objects.hashCode(mDeviceFilters);
_hash = 31 * _hash + Objects.hashCode(mDeviceProfile);
_hash = 31 * _hash + Objects.hashCode(mCallingPackage);
+ _hash = 31 * _hash + Objects.hashCode(mDeviceProfilePrivilegesDescription);
return _hash;
}
@@ -314,10 +349,12 @@ public final class AssociationRequest implements Parcelable {
if (mSingleDevice) flg |= 0x1;
if (mDeviceProfile != null) flg |= 0x4;
if (mCallingPackage != null) flg |= 0x8;
+ if (mDeviceProfilePrivilegesDescription != null) flg |= 0x10;
dest.writeByte(flg);
dest.writeParcelableList(mDeviceFilters, flags);
if (mDeviceProfile != null) dest.writeString(mDeviceProfile);
if (mCallingPackage != null) dest.writeString(mCallingPackage);
+ if (mDeviceProfilePrivilegesDescription != null) dest.writeString(mDeviceProfilePrivilegesDescription);
}
@Override
@@ -337,6 +374,7 @@ public final class AssociationRequest implements Parcelable {
in.readParcelableList(deviceFilters, DeviceFilter.class.getClassLoader());
String deviceProfile = (flg & 0x4) == 0 ? null : in.readString();
String callingPackage = (flg & 0x8) == 0 ? null : in.readString();
+ String deviceProfilePrivilegesDescription = (flg & 0x10) == 0 ? null : in.readString();
this.mSingleDevice = singleDevice;
this.mDeviceFilters = deviceFilters;
@@ -346,6 +384,7 @@ public final class AssociationRequest implements Parcelable {
com.android.internal.util.AnnotationValidations.validate(
DeviceProfile.class, null, mDeviceProfile);
this.mCallingPackage = callingPackage;
+ this.mDeviceProfilePrivilegesDescription = deviceProfilePrivilegesDescription;
onConstructed();
}
@@ -365,10 +404,10 @@ public final class AssociationRequest implements Parcelable {
};
@DataClass.Generated(
- time = 1604534468409L,
- codegenVersion = "1.0.20",
+ time = 1610132130920L,
+ codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/companion/AssociationRequest.java",
- inputSignatures = "public static final java.lang.String DEVICE_PROFILE_WATCH\nprivate boolean mSingleDevice\nprivate @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.String mCallingPackage\npublic void setCallingPackage(java.lang.String)\nprivate void onConstructed()\npublic @android.compat.annotation.UnsupportedAppUsage boolean isSingleDevice()\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genHiddenConstructor=true, genBuilder=false)")
+ inputSignatures = "public static final java.lang.String DEVICE_PROFILE_WATCH\nprivate boolean mSingleDevice\nprivate @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.String mCallingPackage\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\npublic void setCallingPackage(java.lang.String)\npublic void setDeviceProfilePrivilegesDescription(java.lang.String)\nprivate void onConstructed()\npublic @android.compat.annotation.UnsupportedAppUsage boolean isSingleDevice()\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genHiddenConstructor=true, genBuilder=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 43011fce2125..5ccceca9a69d 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4509,6 +4509,17 @@ public abstract class Context {
public static final String CONTENT_CAPTURE_MANAGER_SERVICE = "content_capture";
/**
+ * Official published name of the translation service.
+ *
+ * @hide
+ * @see #getSystemService(String)
+ */
+ // TODO(b/176208267): change it back to translation before S release.
+ @SystemApi
+ @SuppressLint("ServiceName")
+ public static final String TRANSLATION_MANAGER_SERVICE = "transformer";
+
+ /**
* Used for getting content selections and classifications for task snapshots.
*
* @hide
diff --git a/core/java/android/content/om/IOverlayManager.aidl b/core/java/android/content/om/IOverlayManager.aidl
index 0b950b461285..44b5c4482599 100644
--- a/core/java/android/content/om/IOverlayManager.aidl
+++ b/core/java/android/content/om/IOverlayManager.aidl
@@ -17,7 +17,6 @@
package android.content.om;
import android.content.om.OverlayInfo;
-import android.content.om.OverlayManagerTransaction;
/**
* Api for getting information about overlay packages.
@@ -164,18 +163,4 @@ interface IOverlayManager {
* @param packageName The name of the overlay package whose idmap should be deleted.
*/
void invalidateCachesForOverlay(in String packageName, in int userIs);
-
- /**
- * Perform a series of requests related to overlay packages. This is an
- * atomic operation: either all requests were performed successfully and
- * the changes were propagated to the rest of the system, or at least one
- * request could not be performed successfully and nothing is changed and
- * nothing is propagated to the rest of the system.
- *
- * @see OverlayManagerTransaction
- *
- * @param transaction the series of overlay related requests to perform
- * @throws SecurityException if the transaction failed
- */
- void commit(in OverlayManagerTransaction transaction);
}
diff --git a/core/java/android/content/om/OverlayManager.java b/core/java/android/content/om/OverlayManager.java
index 7c14c2891d01..217f637cf9e3 100644
--- a/core/java/android/content/om/OverlayManager.java
+++ b/core/java/android/content/om/OverlayManager.java
@@ -254,29 +254,6 @@ public class OverlayManager {
}
/**
- * Perform a series of requests related to overlay packages. This is an
- * atomic operation: either all requests were performed successfully and
- * the changes were propagated to the rest of the system, or at least one
- * request could not be performed successfully and nothing is changed and
- * nothing is propagated to the rest of the system.
- *
- * @see OverlayManagerTransaction
- *
- * @param transaction the series of overlay related requests to perform
- * @throws Exception if not all the requests could be successfully and
- * atomically executed
- *
- * @hide
- */
- public void commit(@NonNull final OverlayManagerTransaction transaction) {
- try {
- mService.commit(transaction);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Starting on R, actor enforcement and app visibility changes introduce additional failure
* cases, but the SecurityException thrown with these checks is unexpected for existing
* consumers of the API.
diff --git a/core/java/android/content/om/OverlayManagerTransaction.java b/core/java/android/content/om/OverlayManagerTransaction.java
deleted file mode 100644
index 1fa8973c35b5..000000000000
--- a/core/java/android/content/om/OverlayManagerTransaction.java
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.om;
-
-import static com.android.internal.util.Preconditions.checkNotNull;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.UserHandle;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-/**
- * Container for a batch of requests to the OverlayManagerService.
- *
- * Transactions are created using a builder interface. Example usage:
- *
- * final OverlayManager om = ctx.getSystemService(OverlayManager.class);
- * final OverlayManagerTransaction t = new OverlayManagerTransaction.Builder()
- * .setEnabled(...)
- * .setEnabled(...)
- * .build();
- * om.commit(t);
- *
- * @hide
- */
-public class OverlayManagerTransaction
- implements Iterable<OverlayManagerTransaction.Request>, Parcelable {
- // TODO: remove @hide from this class when OverlayManager is added to the
- // SDK, but keep OverlayManagerTransaction.Request @hidden
- private final List<Request> mRequests;
-
- OverlayManagerTransaction(@NonNull final List<Request> requests) {
- checkNotNull(requests);
- if (requests.contains(null)) {
- throw new IllegalArgumentException("null request");
- }
- mRequests = requests;
- }
-
- private OverlayManagerTransaction(@NonNull final Parcel source) {
- final int size = source.readInt();
- mRequests = new ArrayList<Request>(size);
- for (int i = 0; i < size; i++) {
- final int request = source.readInt();
- final String packageName = source.readString();
- final int userId = source.readInt();
- mRequests.add(new Request(request, packageName, userId));
- }
- }
-
- @Override
- public Iterator<Request> iterator() {
- return mRequests.iterator();
- }
-
- @Override
- public String toString() {
- return String.format("OverlayManagerTransaction { mRequests = %s }", mRequests);
- }
-
- /**
- * A single unit of the transaction, such as a request to enable an
- * overlay, or to disable an overlay.
- *
- * @hide
- */
- public static class Request {
- @IntDef(prefix = "TYPE_", value = {
- TYPE_SET_ENABLED,
- TYPE_SET_DISABLED,
- })
- @Retention(RetentionPolicy.SOURCE)
- @interface RequestType {}
-
- public static final int TYPE_SET_ENABLED = 0;
- public static final int TYPE_SET_DISABLED = 1;
-
- @RequestType public final int type;
- public final String packageName;
- public final int userId;
-
- public Request(@RequestType final int type, @NonNull final String packageName,
- final int userId) {
- this.type = type;
- this.packageName = packageName;
- this.userId = userId;
- }
-
- @Override
- public String toString() {
- return String.format("Request{type=0x%02x (%s), packageName=%s, userId=%d}",
- type, typeToString(), packageName, userId);
- }
-
- /**
- * Translate the request type into a human readable string. Only
- * intended for debugging.
- *
- * @hide
- */
- public String typeToString() {
- switch (type) {
- case TYPE_SET_ENABLED: return "TYPE_SET_ENABLED";
- case TYPE_SET_DISABLED: return "TYPE_SET_DISABLED";
- default: return String.format("TYPE_UNKNOWN (0x%02x)", type);
- }
- }
- }
-
- /**
- * Builder class for OverlayManagerTransaction objects.
- *
- * @hide
- */
- public static class Builder {
- private final List<Request> mRequests = new ArrayList<>();
-
- /**
- * Request that an overlay package be enabled and change its loading
- * order to the last package to be loaded, or disabled
- *
- * If the caller has the correct permissions, it is always possible to
- * disable an overlay. Due to technical and security reasons it may not
- * always be possible to enable an overlay, for instance if the overlay
- * does not successfully overlay any target resources due to
- * overlayable policy restrictions.
- *
- * An enabled overlay is a part of target package's resources, i.e. it will
- * be part of any lookups performed via {@link android.content.res.Resources}
- * and {@link android.content.res.AssetManager}. A disabled overlay will no
- * longer affect the resources of the target package. If the target is
- * currently running, its outdated resources will be replaced by new ones.
- *
- * @param packageName The name of the overlay package.
- * @param enable true to enable the overlay, false to disable it.
- * @return this Builder object, so you can chain additional requests
- */
- public Builder setEnabled(@NonNull String packageName, boolean enable) {
- return setEnabled(packageName, enable, UserHandle.myUserId());
- }
-
- /**
- * @hide
- */
- public Builder setEnabled(@NonNull String packageName, boolean enable, int userId) {
- checkNotNull(packageName);
- @Request.RequestType final int type =
- enable ? Request.TYPE_SET_ENABLED : Request.TYPE_SET_DISABLED;
- mRequests.add(new Request(type, packageName, userId));
- return this;
- }
-
- /**
- * Create a new transaction out of the requests added so far. Execute
- * the transaction by calling OverlayManager#commit.
- *
- * @see OverlayManager#commit
- * @return a new transaction
- */
- public OverlayManagerTransaction build() {
- return new OverlayManagerTransaction(mRequests);
- }
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- final int size = mRequests.size();
- dest.writeInt(size);
- for (int i = 0; i < size; i++) {
- final Request req = mRequests.get(i);
- dest.writeInt(req.type);
- dest.writeString(req.packageName);
- dest.writeInt(req.userId);
- }
- }
-
- public static final Parcelable.Creator<OverlayManagerTransaction> CREATOR =
- new Parcelable.Creator<OverlayManagerTransaction>() {
-
- @Override
- public OverlayManagerTransaction createFromParcel(Parcel source) {
- return new OverlayManagerTransaction(source);
- }
-
- @Override
- public OverlayManagerTransaction[] newArray(int size) {
- return new OverlayManagerTransaction[size];
- }
- };
-}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index f634b8a54a0f..7c0b82161988 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -65,7 +65,7 @@ import android.content.IntentSender;
*/
interface IPackageManager {
void checkPackageStartable(String packageName, int userId);
- @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
+ @UnsupportedAppUsage(trackingBug = 171933273)
boolean isPackageAvailable(String packageName, int userId);
@UnsupportedAppUsage
PackageInfo getPackageInfo(String packageName, int flags, int userId);
diff --git a/core/java/android/content/pm/OWNERS b/core/java/android/content/pm/OWNERS
index f88df958d827..fd32efccbcec 100644
--- a/core/java/android/content/pm/OWNERS
+++ b/core/java/android/content/pm/OWNERS
@@ -7,3 +7,4 @@ patb@google.com
per-file PackageParser.java = chiuwinson@google.com
per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS
per-file *Launcher* = file:/core/java/android/content/pm/LAUNCHER_OWNERS
+per-file UserInfo* = file:/MULTIUSER_OWNERS
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index e074eab99a7c..17c4d25d82d7 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3406,6 +3406,14 @@ public abstract class PackageManager {
/**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+ * The device supports translation of text-to-text in multiple languages via integration with
+ * the system {@link android.service.translation.TranslationService translation provider}.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_TRANSLATION = "android.software.translation";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
* The device implements headtracking suitable for a VR device.
*/
@SdkConstant(SdkConstantType.FEATURE)
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 987d790601b4..4145a7273ed2 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -358,6 +358,27 @@ public class BiometricManager {
}
/**
+ * Requests all {@link Authenticators.Types#BIOMETRIC_STRONG} sensors to have their
+ * authenticatorId invalidated for the specified user. This happens when enrollments have been
+ * added on devices with multiple biometric sensors.
+ *
+ * @param userId userId that the authenticatorId should be invalidated for
+ * @param fromSensorId sensor that triggered the invalidation request
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ public void invalidateAuthenticatorIds(int userId, int fromSensorId,
+ @NonNull IInvalidationCallback callback) {
+ if (mService != null) {
+ try {
+ mService.invalidateAuthenticatorIds(userId, fromSensorId, callback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
* Get a list of AuthenticatorIDs for biometric authenticators which have 1) enrolled templates,
* and 2) meet the requirements for integrating with Keystore. The AuthenticatorIDs are known
* in Keystore land as SIDs, and are used during key generation.
diff --git a/core/java/android/hardware/biometrics/IAuthService.aidl b/core/java/android/hardware/biometrics/IAuthService.aidl
index 8e7f5ce8a85d..0dfd5dbf300e 100644
--- a/core/java/android/hardware/biometrics/IAuthService.aidl
+++ b/core/java/android/hardware/biometrics/IAuthService.aidl
@@ -18,6 +18,7 @@ package android.hardware.biometrics;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricServiceReceiver;
+import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.PromptInfo;
import android.hardware.biometrics.SensorPropertiesInternal;
@@ -57,6 +58,11 @@ interface IAuthService {
// Register callback for when keyguard biometric eligibility changes.
void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback);
+ // Requests all BIOMETRIC_STRONG sensors to have their authenticatorId invalidated for the
+ // specified user. This happens when enrollments have been added on devices with multiple
+ // biometric sensors.
+ void invalidateAuthenticatorIds(int userId, int fromSensorId, IInvalidationCallback callback);
+
// Get a list of AuthenticatorIDs for authenticators which have enrolled templates and meet
// the requirements for integrating with Keystore. The AuthenticatorID are known in Keystore
// land as SIDs, and are used during key generation.
diff --git a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
index cc12125c13f0..fcdf61e99471 100644
--- a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
@@ -18,6 +18,7 @@ package android.hardware.biometrics;
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.SensorPropertiesInternal;
import android.hardware.face.IFaceServiceReceiver;
@@ -63,6 +64,9 @@ interface IBiometricAuthenticator {
// Return the LockoutTracker status for the specified user
int getLockoutModeForUser(int userId);
+ // Request the authenticatorId to be invalidated for the specified user
+ void invalidateAuthenticatorId(int userId, IInvalidationCallback callback);
+
// Gets the authenticator ID representing the current set of enrolled templates
long getAuthenticatorId(int callingUserId);
}
diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl
index 6f7bcb68cc8d..a14a910a9e50 100644
--- a/core/java/android/hardware/biometrics/IBiometricService.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricService.aidl
@@ -19,6 +19,7 @@ package android.hardware.biometrics;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.IBiometricAuthenticator;
+import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.PromptInfo;
import android.hardware.biometrics.SensorPropertiesInternal;
@@ -62,6 +63,11 @@ interface IBiometricService {
// Client lifecycle is still managed in <Biometric>Service.
void onReadyForAuthentication(int cookie);
+ // Requests all BIOMETRIC_STRONG sensors to have their authenticatorId invalidated for the
+ // specified user. This happens when enrollments have been added on devices with multiple
+ // biometric sensors.
+ void invalidateAuthenticatorIds(int userId, int fromSensorId, IInvalidationCallback callback);
+
// Get a list of AuthenticatorIDs for authenticators which have enrolled templates and meet
// the requirements for integrating with Keystore. The AuthenticatorID are known in Keystore
// land as SIDs, and are used during key generation.
diff --git a/core/java/android/hardware/biometrics/IInvalidationCallback.aidl b/core/java/android/hardware/biometrics/IInvalidationCallback.aidl
new file mode 100644
index 000000000000..24f7d9d7d42e
--- /dev/null
+++ b/core/java/android/hardware/biometrics/IInvalidationCallback.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics;
+
+/**
+ * Notifies the caller for BiometricManager#invalidateAuthenticatorIds status updates. See
+ * InvalidationRequesterClient for more info.
+ * @hide
+ */
+interface IInvalidationCallback {
+ // Notifies the caller when all authenticatorId(s) have been invalidated.
+ void onCompleted();
+} \ No newline at end of file
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index 468157a19971..3b19f12a41ba 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -17,6 +17,7 @@ package android.hardware.face;
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.face.IFaceServiceReceiver;
import android.hardware.face.Face;
@@ -104,6 +105,9 @@ interface IFaceService {
// Return the LockoutTracker status for the specified user
int getLockoutModeForUser(int sensorId, int userId);
+ // Requests for the specified sensor+userId's authenticatorId to be invalidated
+ void invalidateAuthenticatorId(int sensorId, int userId, IInvalidationCallback callback);
+
// Gets the authenticator ID for face
long getAuthenticatorId(int sensorId, int callingUserId);
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index ac026c796b51..74c5b5864e87 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -17,6 +17,7 @@ package android.hardware.fingerprint;
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.fingerprint.IFingerprintClientActiveCallback;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
@@ -116,6 +117,9 @@ interface IFingerprintService {
// Return the LockoutTracker status for the specified user
int getLockoutModeForUser(int sensorId, int userId);
+ // Requests for the specified sensor+userId's authenticatorId to be invalidated
+ void invalidateAuthenticatorId(int sensorId, int userId, IInvalidationCallback callback);
+
// Gets the authenticator ID for fingerprint
long getAuthenticatorId(int sensorId, int callingUserId);
diff --git a/core/java/android/net/CaptivePortalData.java b/core/java/android/net/CaptivePortalData.java
index 59e62a675abc..9b56b23cc879 100644
--- a/core/java/android/net/CaptivePortalData.java
+++ b/core/java/android/net/CaptivePortalData.java
@@ -39,9 +39,11 @@ public final class CaptivePortalData implements Parcelable {
private final long mByteLimit;
private final long mExpiryTimeMillis;
private final boolean mCaptive;
+ private final String mVenueFriendlyName;
private CaptivePortalData(long refreshTimeMillis, Uri userPortalUrl, Uri venueInfoUrl,
- boolean isSessionExtendable, long byteLimit, long expiryTimeMillis, boolean captive) {
+ boolean isSessionExtendable, long byteLimit, long expiryTimeMillis, boolean captive,
+ String venueFriendlyName) {
mRefreshTimeMillis = refreshTimeMillis;
mUserPortalUrl = userPortalUrl;
mVenueInfoUrl = venueInfoUrl;
@@ -49,11 +51,12 @@ public final class CaptivePortalData implements Parcelable {
mByteLimit = byteLimit;
mExpiryTimeMillis = expiryTimeMillis;
mCaptive = captive;
+ mVenueFriendlyName = venueFriendlyName;
}
private CaptivePortalData(Parcel p) {
this(p.readLong(), p.readParcelable(null), p.readParcelable(null), p.readBoolean(),
- p.readLong(), p.readLong(), p.readBoolean());
+ p.readLong(), p.readLong(), p.readBoolean(), p.readString());
}
@Override
@@ -70,6 +73,7 @@ public final class CaptivePortalData implements Parcelable {
dest.writeLong(mByteLimit);
dest.writeLong(mExpiryTimeMillis);
dest.writeBoolean(mCaptive);
+ dest.writeString(mVenueFriendlyName);
}
/**
@@ -83,6 +87,7 @@ public final class CaptivePortalData implements Parcelable {
private long mBytesRemaining = -1;
private long mExpiryTime = -1;
private boolean mCaptive;
+ private String mVenueFriendlyName;
/**
* Create an empty builder.
@@ -100,7 +105,8 @@ public final class CaptivePortalData implements Parcelable {
.setSessionExtendable(data.mIsSessionExtendable)
.setBytesRemaining(data.mByteLimit)
.setExpiryTime(data.mExpiryTimeMillis)
- .setCaptive(data.mCaptive);
+ .setCaptive(data.mCaptive)
+ .setVenueFriendlyName(data.mVenueFriendlyName);
}
/**
@@ -167,12 +173,22 @@ public final class CaptivePortalData implements Parcelable {
}
/**
+ * Set the venue friendly name.
+ */
+ @NonNull
+ public Builder setVenueFriendlyName(@Nullable String venueFriendlyName) {
+ mVenueFriendlyName = venueFriendlyName;
+ return this;
+ }
+
+ /**
* Create a new {@link CaptivePortalData}.
*/
@NonNull
public CaptivePortalData build() {
return new CaptivePortalData(mRefreshTime, mUserPortalUrl, mVenueInfoUrl,
- mIsSessionExtendable, mBytesRemaining, mExpiryTime, mCaptive);
+ mIsSessionExtendable, mBytesRemaining, mExpiryTime, mCaptive,
+ mVenueFriendlyName);
}
}
@@ -232,6 +248,14 @@ public final class CaptivePortalData implements Parcelable {
return mCaptive;
}
+ /**
+ * Get the venue friendly name
+ */
+ @Nullable
+ public String getVenueFriendlyName() {
+ return mVenueFriendlyName;
+ }
+
@NonNull
public static final Creator<CaptivePortalData> CREATOR = new Creator<CaptivePortalData>() {
@Override
@@ -248,7 +272,7 @@ public final class CaptivePortalData implements Parcelable {
@Override
public int hashCode() {
return Objects.hash(mRefreshTimeMillis, mUserPortalUrl, mVenueInfoUrl,
- mIsSessionExtendable, mByteLimit, mExpiryTimeMillis, mCaptive);
+ mIsSessionExtendable, mByteLimit, mExpiryTimeMillis, mCaptive, mVenueFriendlyName);
}
@Override
@@ -261,7 +285,8 @@ public final class CaptivePortalData implements Parcelable {
&& mIsSessionExtendable == other.mIsSessionExtendable
&& mByteLimit == other.mByteLimit
&& mExpiryTimeMillis == other.mExpiryTimeMillis
- && mCaptive == other.mCaptive;
+ && mCaptive == other.mCaptive
+ && Objects.equals(mVenueFriendlyName, other.mVenueFriendlyName);
}
@Override
@@ -274,6 +299,7 @@ public final class CaptivePortalData implements Parcelable {
+ ", byteLimit: " + mByteLimit
+ ", expiryTime: " + mExpiryTimeMillis
+ ", captive: " + mCaptive
+ + ", venueFriendlyName: " + mVenueFriendlyName
+ "}";
}
}
diff --git a/core/java/android/net/IIpConnectivityMetrics.aidl b/core/java/android/net/IIpConnectivityMetrics.aidl
index aeaf09d8fafe..aa3682d92105 100644
--- a/core/java/android/net/IIpConnectivityMetrics.aidl
+++ b/core/java/android/net/IIpConnectivityMetrics.aidl
@@ -19,6 +19,9 @@ package android.net;
import android.os.Parcelable;
import android.net.ConnectivityMetricsEvent;
import android.net.INetdEventCallback;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
/** {@hide} */
interface IIpConnectivityMetrics {
@@ -29,6 +32,11 @@ interface IIpConnectivityMetrics {
*/
int logEvent(in ConnectivityMetricsEvent event);
+ void logDefaultNetworkValidity(boolean valid);
+ void logDefaultNetworkEvent(in Network defaultNetwork, int score, boolean validated,
+ in LinkProperties lp, in NetworkCapabilities nc, in Network previousDefaultNetwork,
+ int previousScore, in LinkProperties previousLp, in NetworkCapabilities previousNc);
+
/**
* Callback can be registered by DevicePolicyManager or NetworkWatchlistService only.
* @return status {@code true} if registering/unregistering of the callback was successful,
diff --git a/core/java/android/net/INetworkManagementEventObserver.aidl b/core/java/android/net/INetworkManagementEventObserver.aidl
index 37813ce11a5f..0a6be20226b8 100644
--- a/core/java/android/net/INetworkManagementEventObserver.aidl
+++ b/core/java/android/net/INetworkManagementEventObserver.aidl
@@ -85,14 +85,14 @@ oneway interface INetworkManagementEventObserver {
/**
* Interface data activity status is changed.
*
- * @param networkType The legacy network type of the data activity change.
+ * @param transportType The transport type of the data activity change.
* @param active True if the interface is actively transmitting data, false if it is idle.
* @param tsNanos Elapsed realtime in nanos when the state of the network interface changed.
* @param uid Uid of this event. It represents the uid that was responsible for waking the
* radio. For those events that are reported by system itself, not from specific uid,
* use -1 for the events which means no uid.
*/
- void interfaceClassDataActivityChanged(int networkType, boolean active, long tsNanos, int uid);
+ void interfaceClassDataActivityChanged(int transportType, boolean active, long tsNanos, int uid);
/**
* Information about available DNS servers has been received.
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 4166c2c4f244..4f46736c087d 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -408,7 +408,8 @@ public abstract class NetworkAgent {
throw new IllegalArgumentException();
}
- mInitialConfiguration = new InitialConfiguration(context, new NetworkCapabilities(nc),
+ mInitialConfiguration = new InitialConfiguration(context,
+ new NetworkCapabilities(nc, /* parcelLocationSensitiveFields */ true),
new LinkProperties(lp), score, config, ni);
}
@@ -818,7 +819,9 @@ public abstract class NetworkAgent {
Objects.requireNonNull(networkCapabilities);
mBandwidthUpdatePending.set(false);
mLastBwRefreshTime = System.currentTimeMillis();
- final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
+ final NetworkCapabilities nc =
+ new NetworkCapabilities(networkCapabilities,
+ /* parcelLocationSensitiveFields */ true);
queueOrSendMessage(reg -> reg.sendNetworkCapabilities(nc));
}
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 1a37fb9fb690..455d7426a170 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -76,12 +76,33 @@ public final class NetworkCapabilities implements Parcelable {
*/
private String mRequestorPackageName;
+ /**
+ * Indicates whether parceling should preserve fields that are set based on permissions of
+ * the process receiving the {@link NetworkCapabilities}.
+ */
+ private final boolean mParcelLocationSensitiveFields;
+
public NetworkCapabilities() {
+ mParcelLocationSensitiveFields = false;
clearAll();
mNetworkCapabilities = DEFAULT_CAPABILITIES;
}
public NetworkCapabilities(NetworkCapabilities nc) {
+ this(nc, false /* parcelLocationSensitiveFields */);
+ }
+
+ /**
+ * Make a copy of NetworkCapabilities.
+ *
+ * @param nc Original NetworkCapabilities
+ * @param parcelLocationSensitiveFields Whether to parcel location sensitive data or not.
+ * @hide
+ */
+ @SystemApi
+ public NetworkCapabilities(
+ @Nullable NetworkCapabilities nc, boolean parcelLocationSensitiveFields) {
+ mParcelLocationSensitiveFields = parcelLocationSensitiveFields;
if (nc != null) {
set(nc);
}
@@ -93,6 +114,12 @@ public final class NetworkCapabilities implements Parcelable {
* @hide
*/
public void clearAll() {
+ // Ensures that the internal copies maintained by the connectivity stack does not set
+ // this bit.
+ if (mParcelLocationSensitiveFields) {
+ throw new UnsupportedOperationException(
+ "Cannot clear NetworkCapabilities when parcelLocationSensitiveFields is set");
+ }
mNetworkCapabilities = mTransportTypes = mUnwantedNetworkCapabilities = 0;
mLinkUpBandwidthKbps = mLinkDownBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED;
mNetworkSpecifier = null;
@@ -109,6 +136,8 @@ public final class NetworkCapabilities implements Parcelable {
/**
* Set all contents of this object to the contents of a NetworkCapabilities.
+ *
+ * @param nc Original NetworkCapabilities
* @hide
*/
public void set(@NonNull NetworkCapabilities nc) {
@@ -117,7 +146,11 @@ public final class NetworkCapabilities implements Parcelable {
mLinkUpBandwidthKbps = nc.mLinkUpBandwidthKbps;
mLinkDownBandwidthKbps = nc.mLinkDownBandwidthKbps;
mNetworkSpecifier = nc.mNetworkSpecifier;
- mTransportInfo = nc.mTransportInfo;
+ if (nc.getTransportInfo() != null) {
+ setTransportInfo(nc.getTransportInfo().makeCopy(mParcelLocationSensitiveFields));
+ } else {
+ setTransportInfo(null);
+ }
mSignalStrength = nc.mSignalStrength;
setUids(nc.mUids); // Will make the defensive copy
setAdministratorUids(nc.getAdministratorUids());
@@ -171,6 +204,8 @@ public final class NetworkCapabilities implements Parcelable {
NET_CAPABILITY_PARTIAL_CONNECTIVITY,
NET_CAPABILITY_TEMPORARILY_NOT_METERED,
NET_CAPABILITY_OEM_PRIVATE,
+ NET_CAPABILITY_VEHICLE_INTERNAL,
+ NET_CAPABILITY_NOT_VCN_MANAGED,
})
public @interface NetCapability { }
@@ -357,8 +392,26 @@ public final class NetworkCapabilities implements Parcelable {
@SystemApi
public static final int NET_CAPABILITY_OEM_PRIVATE = 26;
+ /**
+ * Indicates this is an internal vehicle network, meant to communicate with other
+ * automotive systems.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int NET_CAPABILITY_VEHICLE_INTERNAL = 27;
+
+ /**
+ * Indicates that this network is not managed by a Virtual Carrier Network (VCN).
+ *
+ * TODO(b/177299683): Add additional clarifying javadoc.
+ * @hide
+ */
+ @SystemApi
+ public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28;
+
private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
- private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_OEM_PRIVATE;
+ private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_VCN_MANAGED;
/**
* Network capabilities that are expected to be mutable, i.e., can change while a particular
@@ -375,7 +428,8 @@ public final class NetworkCapabilities implements Parcelable {
| (1 << NET_CAPABILITY_NOT_CONGESTED)
| (1 << NET_CAPABILITY_NOT_SUSPENDED)
| (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY)
- | (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED);
+ | (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED)
+ | (1 << NET_CAPABILITY_NOT_VCN_MANAGED);
/**
* Network capabilities that are not allowed in NetworkRequests. This exists because the
@@ -384,16 +438,22 @@ public final class NetworkCapabilities implements Parcelable {
* can get into a cycle where the NetworkFactory endlessly churns out NetworkAgents that then
* get immediately torn down because they do not have the requested capability.
*/
+ // Note that as a historical exception, the TRUSTED and NOT_VCN_MANAGED capabilities
+ // are mutable but requestable. Factories are responsible for not getting
+ // in an infinite loop about these.
private static final long NON_REQUESTABLE_CAPABILITIES =
- MUTABLE_CAPABILITIES & ~(1 << NET_CAPABILITY_TRUSTED);
+ MUTABLE_CAPABILITIES
+ & ~(1 << NET_CAPABILITY_TRUSTED)
+ & ~(1 << NET_CAPABILITY_NOT_VCN_MANAGED);
/**
* Capabilities that are set by default when the object is constructed.
*/
private static final long DEFAULT_CAPABILITIES =
- (1 << NET_CAPABILITY_NOT_RESTRICTED) |
- (1 << NET_CAPABILITY_TRUSTED) |
- (1 << NET_CAPABILITY_NOT_VPN);
+ (1 << NET_CAPABILITY_NOT_RESTRICTED)
+ | (1 << NET_CAPABILITY_TRUSTED)
+ | (1 << NET_CAPABILITY_NOT_VPN)
+ | (1 << NET_CAPABILITY_NOT_VCN_MANAGED);
/**
* Capabilities that suggest that a network is restricted.
@@ -401,15 +461,16 @@ public final class NetworkCapabilities implements Parcelable {
*/
@VisibleForTesting
/* package */ static final long RESTRICTED_CAPABILITIES =
- (1 << NET_CAPABILITY_CBS) |
- (1 << NET_CAPABILITY_DUN) |
- (1 << NET_CAPABILITY_EIMS) |
- (1 << NET_CAPABILITY_FOTA) |
- (1 << NET_CAPABILITY_IA) |
- (1 << NET_CAPABILITY_IMS) |
- (1 << NET_CAPABILITY_RCS) |
- (1 << NET_CAPABILITY_XCAP) |
- (1 << NET_CAPABILITY_MCX);
+ (1 << NET_CAPABILITY_CBS)
+ | (1 << NET_CAPABILITY_DUN)
+ | (1 << NET_CAPABILITY_EIMS)
+ | (1 << NET_CAPABILITY_FOTA)
+ | (1 << NET_CAPABILITY_IA)
+ | (1 << NET_CAPABILITY_IMS)
+ | (1 << NET_CAPABILITY_MCX)
+ | (1 << NET_CAPABILITY_RCS)
+ | (1 << NET_CAPABILITY_VEHICLE_INTERNAL)
+ | (1 << NET_CAPABILITY_XCAP);
/**
* Capabilities that force network to be restricted.
@@ -452,7 +513,8 @@ public final class NetworkCapabilities implements Parcelable {
| (1 << NET_CAPABILITY_NOT_VPN)
| (1 << NET_CAPABILITY_NOT_ROAMING)
| (1 << NET_CAPABILITY_NOT_CONGESTED)
- | (1 << NET_CAPABILITY_NOT_SUSPENDED);
+ | (1 << NET_CAPABILITY_NOT_SUSPENDED)
+ | (1 << NET_CAPABILITY_NOT_VCN_MANAGED);
/**
* Adds the given capability to this {@code NetworkCapability} instance.
@@ -1939,6 +2001,8 @@ public final class NetworkCapabilities implements Parcelable {
case NET_CAPABILITY_PARTIAL_CONNECTIVITY: return "PARTIAL_CONNECTIVITY";
case NET_CAPABILITY_TEMPORARILY_NOT_METERED: return "TEMPORARILY_NOT_METERED";
case NET_CAPABILITY_OEM_PRIVATE: return "OEM_PRIVATE";
+ case NET_CAPABILITY_VEHICLE_INTERNAL: return "NET_CAPABILITY_VEHICLE_INTERNAL";
+ case NET_CAPABILITY_NOT_VCN_MANAGED: return "NOT_VCN_MANAGED";
default: return Integer.toString(capability);
}
}
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 8bfbad605420..c029deae09df 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -32,8 +32,8 @@ import android.content.pm.Signature;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.os.Build;
+import android.os.Process;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.telephony.SubscriptionPlan;
import android.util.DebugUtils;
import android.util.Pair;
@@ -500,7 +500,7 @@ public class NetworkPolicyManager {
@Deprecated
public static boolean isUidValidForPolicy(Context context, int uid) {
// first, quick-reject non-applications
- if (!UserHandle.isApp(uid)) {
+ if (!Process.isApplicationUid(uid)) {
return false;
}
diff --git a/core/java/android/net/TransportInfo.java b/core/java/android/net/TransportInfo.java
index b78d3feccfa0..aa4bbb051179 100644
--- a/core/java/android/net/TransportInfo.java
+++ b/core/java/android/net/TransportInfo.java
@@ -16,10 +16,48 @@
package android.net;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
/**
* A container for transport-specific capabilities which is returned by
* {@link NetworkCapabilities#getTransportInfo()}. Specific networks
* may provide concrete implementations of this interface.
+ * @see android.net.wifi.aware.WifiAwareNetworkInfo
+ * @see android.net.wifi.WifiInfo
*/
public interface TransportInfo {
+
+ /**
+ * Create a copy of a {@link TransportInfo} that will preserve location sensitive fields that
+ * were set based on the permissions of the process that originally received it.
+ *
+ * <p>By default {@link TransportInfo} does not preserve such fields during parceling, as
+ * they should not be shared outside of the process that receives them without appropriate
+ * checks.
+ *
+ * @param parcelLocationSensitiveFields Whether the location sensitive fields should be kept
+ * when parceling
+ * @return Copy of this instance.
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ default TransportInfo makeCopy(boolean parcelLocationSensitiveFields) {
+ return this;
+ }
+
+ /**
+ * Returns whether this TransportInfo type has location sensitive fields or not (helps
+ * to determine whether to perform a location permission check or not before sending to
+ * apps).
+ *
+ * @return {@code true} if this instance contains location sensitive info, {@code false}
+ * otherwise.
+ * @hide
+ */
+ @SystemApi
+ default boolean hasLocationSensitiveFields() {
+ return false;
+ }
}
diff --git a/core/java/android/net/metrics/IpConnectivityLog.java b/core/java/android/net/metrics/IpConnectivityLog.java
index a008d855025a..58ea91573775 100644
--- a/core/java/android/net/metrics/IpConnectivityLog.java
+++ b/core/java/android/net/metrics/IpConnectivityLog.java
@@ -17,10 +17,13 @@
package android.net.metrics;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.net.ConnectivityMetricsEvent;
import android.net.IIpConnectivityMetrics;
+import android.net.LinkProperties;
import android.net.Network;
+import android.net.NetworkCapabilities;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -66,6 +69,9 @@ public class IpConnectivityLog {
final IIpConnectivityMetrics service =
IIpConnectivityMetrics.Stub.asInterface(ServiceManager.getService(SERVICE_NAME));
if (service == null) {
+ if (DBG) {
+ Log.d(TAG, SERVICE_NAME + " service was not ready");
+ }
return false;
}
// Two threads racing here will write the same pointer because getService
@@ -83,9 +89,6 @@ public class IpConnectivityLog {
*/
public boolean log(@NonNull ConnectivityMetricsEvent ev) {
if (!checkLoggerService()) {
- if (DBG) {
- Log.d(TAG, SERVICE_NAME + " service was not ready");
- }
return false;
}
if (ev.timestamp == 0) {
@@ -161,6 +164,56 @@ public class IpConnectivityLog {
return log(makeEv(data));
}
+ /**
+ * Logs the validation status of the default network.
+ * @param valid whether the current default network was validated (i.e., whether it had
+ * {@link NetworkCapabilities.NET_CAPABILITY_VALIDATED}
+ * @return true if the event was successfully logged.
+ * @hide
+ */
+ public boolean logDefaultNetworkValidity(boolean valid) {
+ if (!checkLoggerService()) {
+ return false;
+ }
+ try {
+ mService.logDefaultNetworkValidity(valid);
+ } catch (RemoteException ignored) {
+ // Only called within the system server.
+ }
+ return true;
+ }
+
+ /**
+ * Logs a change in the default network.
+ *
+ * @param defaultNetwork the current default network
+ * @param score the current score of {@code defaultNetwork}
+ * @param lp the {@link LinkProperties} of {@code defaultNetwork}
+ * @param nc the {@link NetworkCapabilities} of the {@code defaultNetwork}
+ * @param validated whether {@code defaultNetwork} network is validated
+ * @param previousDefaultNetwork the previous default network
+ * @param previousScore the score of {@code previousDefaultNetwork}
+ * @param previousLp the {@link LinkProperties} of {@code previousDefaultNetwork}
+ * @param previousNc the {@link NetworkCapabilities} of {@code previousDefaultNetwork}
+ * @return true if the event was successfully logged.
+ * @hide
+ */
+ public boolean logDefaultNetworkEvent(@Nullable Network defaultNetwork, int score,
+ boolean validated, @Nullable LinkProperties lp, @Nullable NetworkCapabilities nc,
+ @Nullable Network previousDefaultNetwork, int previousScore,
+ @Nullable LinkProperties previousLp, @Nullable NetworkCapabilities previousNc) {
+ if (!checkLoggerService()) {
+ return false;
+ }
+ try {
+ mService.logDefaultNetworkEvent(defaultNetwork, score, validated, lp, nc,
+ previousDefaultNetwork, previousScore, previousLp, previousNc);
+ } catch (RemoteException ignored) {
+ // Only called within the system server.
+ }
+ return true;
+ }
+
private static ConnectivityMetricsEvent makeEv(Event data) {
ConnectivityMetricsEvent ev = new ConnectivityMetricsEvent();
ev.data = data;
diff --git a/core/java/android/os/DropBoxManager.java b/core/java/android/os/DropBoxManager.java
index 3dce130f5d61..575fc4c8931f 100644
--- a/core/java/android/os/DropBoxManager.java
+++ b/core/java/android/os/DropBoxManager.java
@@ -19,6 +19,10 @@ package android.os;
import static android.Manifest.permission.PACKAGE_USAGE_STATS;
import static android.Manifest.permission.READ_LOGS;
+import android.annotation.BytesLong;
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
@@ -35,6 +39,9 @@ import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.nio.charset.StandardCharsets;
import java.util.zip.GZIPInputStream;
/**
@@ -54,6 +61,11 @@ public class DropBoxManager {
@UnsupportedAppUsage
private final IDropBoxManagerService mService;
+ /** @hide */
+ @IntDef(flag = true, prefix = { "IS_" }, value = { IS_EMPTY, IS_TEXT, IS_GZIPPED })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Flags {}
+
/** Flag value: Entry's content was deleted to save space. */
public static final int IS_EMPTY = 1;
@@ -105,15 +117,15 @@ public class DropBoxManager {
* {@link #close()} when you are done using it.
*/
public static class Entry implements Parcelable, Closeable {
- private final String mTag;
- private final long mTimeMillis;
+ private final @NonNull String mTag;
+ private final @CurrentTimeMillisLong long mTimeMillis;
- private final byte[] mData;
- private final ParcelFileDescriptor mFileDescriptor;
- private final int mFlags;
+ private final @Nullable byte[] mData;
+ private final @Nullable ParcelFileDescriptor mFileDescriptor;
+ private final @Flags int mFlags;
/** Create a new empty Entry with no contents. */
- public Entry(String tag, long millis) {
+ public Entry(@NonNull String tag, @CurrentTimeMillisLong long millis) {
if (tag == null) throw new NullPointerException("tag == null");
mTag = tag;
@@ -124,13 +136,14 @@ public class DropBoxManager {
}
/** Create a new Entry with plain text contents. */
- public Entry(String tag, long millis, String text) {
+ public Entry(@NonNull String tag, @CurrentTimeMillisLong long millis,
+ @NonNull String text) {
if (tag == null) throw new NullPointerException("tag == null");
if (text == null) throw new NullPointerException("text == null");
mTag = tag;
mTimeMillis = millis;
- mData = text.getBytes();
+ mData = text.getBytes(StandardCharsets.UTF_8);
mFileDescriptor = null;
mFlags = IS_TEXT;
}
@@ -139,7 +152,8 @@ public class DropBoxManager {
* Create a new Entry with byte array contents.
* The data array must not be modified after creating this entry.
*/
- public Entry(String tag, long millis, byte[] data, int flags) {
+ public Entry(@NonNull String tag, @CurrentTimeMillisLong long millis,
+ @Nullable byte[] data, @Flags int flags) {
if (tag == null) throw new NullPointerException("tag == null");
if (((flags & IS_EMPTY) != 0) != (data == null)) {
throw new IllegalArgumentException("Bad flags: " + flags);
@@ -156,7 +170,8 @@ public class DropBoxManager {
* Create a new Entry with streaming data contents.
* Takes ownership of the ParcelFileDescriptor.
*/
- public Entry(String tag, long millis, ParcelFileDescriptor data, int flags) {
+ public Entry(@NonNull String tag, @CurrentTimeMillisLong long millis,
+ @Nullable ParcelFileDescriptor data, @Flags int flags) {
if (tag == null) throw new NullPointerException("tag == null");
if (((flags & IS_EMPTY) != 0) != (data == null)) {
throw new IllegalArgumentException("Bad flags: " + flags);
@@ -173,7 +188,8 @@ public class DropBoxManager {
* Create a new Entry with the contents read from a file.
* The file will be read when the entry's contents are requested.
*/
- public Entry(String tag, long millis, File data, int flags) throws IOException {
+ public Entry(@NonNull String tag, @CurrentTimeMillisLong long millis,
+ @NonNull File data, @Flags int flags) throws IOException {
if (tag == null) throw new NullPointerException("tag == null");
if ((flags & IS_EMPTY) != 0) throw new IllegalArgumentException("Bad flags: " + flags);
@@ -190,19 +206,26 @@ public class DropBoxManager {
}
/** @return the tag originally attached to the entry. */
- public String getTag() { return mTag; }
+ public @NonNull String getTag() {
+ return mTag;
+ }
/** @return time when the entry was originally created. */
- public long getTimeMillis() { return mTimeMillis; }
+ public @CurrentTimeMillisLong long getTimeMillis() {
+ return mTimeMillis;
+ }
/** @return flags describing the content returned by {@link #getInputStream()}. */
- public int getFlags() { return mFlags & ~IS_GZIPPED; } // getInputStream() decompresses.
+ public @Flags int getFlags() {
+ // getInputStream() decompresses.
+ return mFlags & ~IS_GZIPPED;
+ }
/**
* @param maxBytes of string to return (will truncate at this length).
* @return the uncompressed text contents of the entry, null if the entry is not text.
*/
- public String getText(int maxBytes) {
+ public @Nullable String getText(@BytesLong int maxBytes) {
if ((mFlags & IS_TEXT) == 0) return null;
if (mData != null) return new String(mData, 0, Math.min(maxBytes, mData.length));
@@ -225,7 +248,7 @@ public class DropBoxManager {
}
/** @return the uncompressed contents of the entry, or null if the contents were lost */
- public InputStream getInputStream() throws IOException {
+ public @Nullable InputStream getInputStream() throws IOException {
InputStream is;
if (mData != null) {
is = new ByteArrayInputStream(mData);
@@ -293,17 +316,8 @@ public class DropBoxManager {
* @param tag describing the type of entry being stored
* @param data value to store
*/
- public void addText(String tag, String data) {
- try {
- mService.add(new Entry(tag, 0, data));
- } catch (RemoteException e) {
- if (e instanceof TransactionTooLargeException
- && mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
- Log.e(TAG, "App sent too much data, so it was ignored", e);
- return;
- }
- throw e.rethrowFromSystemServer();
- }
+ public void addText(@NonNull String tag, @NonNull String data) {
+ addData(tag, data.getBytes(StandardCharsets.UTF_8), IS_TEXT);
}
/**
@@ -313,10 +327,10 @@ public class DropBoxManager {
* @param data value to store
* @param flags describing the data
*/
- public void addData(String tag, byte[] data, int flags) {
+ public void addData(@NonNull String tag, @Nullable byte[] data, @Flags int flags) {
if (data == null) throw new NullPointerException("data == null");
try {
- mService.add(new Entry(tag, 0, data, flags));
+ mService.addData(tag, data, flags);
} catch (RemoteException e) {
if (e instanceof TransactionTooLargeException
&& mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
@@ -336,15 +350,14 @@ public class DropBoxManager {
* @param flags describing the data
* @throws IOException if the file can't be opened
*/
- public void addFile(String tag, File file, int flags) throws IOException {
+ public void addFile(@NonNull String tag, @NonNull File file, @Flags int flags)
+ throws IOException {
if (file == null) throw new NullPointerException("file == null");
- Entry entry = new Entry(tag, 0, file, flags);
- try {
- mService.add(entry);
+ try (ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file,
+ ParcelFileDescriptor.MODE_READ_ONLY)) {
+ mService.addFile(tag, pfd, flags);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
- } finally {
- entry.close();
}
}
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index e3ad4e665a67..2559a33c1ab2 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -23,6 +23,10 @@ per-file BatteryStats* = file:/BATTERY_STATS_OWNERS
per-file BatteryUsageStats* = file:/BATTERY_STATS_OWNERS
per-file PowerComponents.java = file:/BATTERY_STATS_OWNERS
+# Multiuser
+per-file IUser* = file:/MULTIUSER_OWNERS
+per-file User* = file:/MULTIUSER_OWNERS
+
# Binder
per-file BadParcelableException.java = file:platform/frameworks/native:/libs/binder/OWNERS
per-file Binder.java = file:platform/frameworks/native:/libs/binder/OWNERS
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 8048b9df6097..39e3e146f45b 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -31,9 +31,12 @@ import dalvik.system.VMRuntime;
import libcore.io.IoUtils;
+import java.io.BufferedReader;
import java.io.FileDescriptor;
+import java.io.FileReader;
import java.io.IOException;
import java.util.Map;
+import java.util.StringTokenizer;
import java.util.concurrent.TimeoutException;
/**
@@ -1430,4 +1433,38 @@ public class Process {
}
private static native int nativePidFdOpen(int pid, int flags) throws ErrnoException;
+
+ /**
+ * Checks if a process corresponding to a specific pid owns any file locks.
+ * @param pid The process ID for which we want to know the existence of file locks.
+ * @return true If the process holds any file locks, false otherwise.
+ * @throws IOException if /proc/locks can't be accessed.
+ *
+ * @hide
+ */
+ public static boolean hasFileLocks(int pid) throws IOException {
+ BufferedReader br = null;
+
+ try {
+ br = new BufferedReader(new FileReader("/proc/locks"));
+ String line;
+
+ while ((line = br.readLine()) != null) {
+ StringTokenizer st = new StringTokenizer(line);
+
+ for (int i = 0; i < 5 && st.hasMoreTokens(); i++) {
+ String str = st.nextToken();
+ if (i == 4 && Integer.parseInt(str) == pid) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ } finally {
+ if (br != null) {
+ br.close();
+ }
+ }
+ }
}
diff --git a/core/java/android/os/image/DynamicSystemClient.java b/core/java/android/os/image/DynamicSystemClient.java
index 0fe889410685..5aa4e27fc2f3 100644
--- a/core/java/android/os/image/DynamicSystemClient.java
+++ b/core/java/android/os/image/DynamicSystemClient.java
@@ -35,8 +35,6 @@ import android.os.Message;
import android.os.Messenger;
import android.os.ParcelableException;
import android.os.RemoteException;
-import android.os.SystemProperties;
-import android.util.FeatureFlagUtils;
import android.util.Slog;
import java.lang.annotation.Retention;
@@ -251,13 +249,7 @@ public class DynamicSystemClient {
mService.send(msg);
} catch (RemoteException e) {
Slog.e(TAG, "Unable to get status from installation service");
- if (mExecutor != null) {
- mExecutor.execute(() -> {
- mListener.onStatusChanged(STATUS_UNKNOWN, CAUSE_ERROR_IPC, 0, e);
- });
- } else {
- mListener.onStatusChanged(STATUS_UNKNOWN, CAUSE_ERROR_IPC, 0, e);
- }
+ notifyOnStatusChangedListener(STATUS_UNKNOWN, CAUSE_ERROR_IPC, 0, e);
}
}
@@ -311,6 +303,20 @@ public class DynamicSystemClient {
mExecutor = null;
}
+ private void notifyOnStatusChangedListener(
+ int status, int cause, long progress, Throwable detail) {
+ if (mListener != null) {
+ if (mExecutor != null) {
+ mExecutor.execute(
+ () -> {
+ mListener.onStatusChanged(status, cause, progress, detail);
+ });
+ } else {
+ mListener.onStatusChanged(status, cause, progress, detail);
+ }
+ }
+ }
+
/**
* Bind to {@code DynamicSystem} installation service. Binding to the installation service
* allows it to send status updates to {@link #OnStatusChangedListener}. It is recommanded
@@ -320,11 +326,6 @@ public class DynamicSystemClient {
@RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM)
@SystemApi
public void bind() {
- if (!featureFlagEnabled()) {
- Slog.w(TAG, FeatureFlagUtils.DYNAMIC_SYSTEM + " not enabled; bind() aborted.");
- return;
- }
-
Intent intent = new Intent();
intent.setClassName("com.android.dynsystem",
"com.android.dynsystem.DynamicSystemInstallationService");
@@ -395,11 +396,6 @@ public class DynamicSystemClient {
@RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM)
public void start(@NonNull Uri systemUrl, @BytesLong long systemSize,
@BytesLong long userdataSize) {
- if (!featureFlagEnabled()) {
- Slog.w(TAG, FeatureFlagUtils.DYNAMIC_SYSTEM + " not enabled; start() aborted.");
- return;
- }
-
Intent intent = new Intent();
intent.setClassName("com.android.dynsystem",
@@ -407,6 +403,7 @@ public class DynamicSystemClient {
intent.setData(systemUrl);
intent.setAction(ACTION_START_INSTALL);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(KEY_SYSTEM_SIZE, systemSize);
intent.putExtra(KEY_USERDATA_SIZE, userdataSize);
@@ -414,11 +411,6 @@ public class DynamicSystemClient {
mContext.startActivity(intent);
}
- private boolean featureFlagEnabled() {
- return SystemProperties.getBoolean(
- FeatureFlagUtils.PERSIST_PREFIX + FeatureFlagUtils.DYNAMIC_SYSTEM, false);
- }
-
private void handleMessage(Message msg) {
switch (msg.what) {
case MSG_POST_STATUS:
@@ -432,13 +424,7 @@ public class DynamicSystemClient {
Throwable detail = t == null ? null : t.getCause();
- if (mExecutor != null) {
- mExecutor.execute(() -> {
- mListener.onStatusChanged(status, cause, progress, detail);
- });
- } else {
- mListener.onStatusChanged(status, cause, progress, detail);
- }
+ notifyOnStatusChangedListener(status, cause, progress, detail);
break;
default:
// do nothing
diff --git a/core/java/android/os/incremental/IIncrementalService.aidl b/core/java/android/os/incremental/IIncrementalService.aidl
index ca92ad5deae6..7db5a80b6cf9 100644
--- a/core/java/android/os/incremental/IIncrementalService.aidl
+++ b/core/java/android/os/incremental/IIncrementalService.aidl
@@ -21,6 +21,7 @@ import android.content.pm.IDataLoaderStatusListener;
import android.os.incremental.IncrementalNewFileParams;
import android.os.incremental.IStorageLoadingProgressListener;
import android.os.incremental.IStorageHealthListener;
+import android.os.incremental.PerUidReadTimeouts;
import android.os.incremental.StorageHealthCheckParams;
/** @hide */
@@ -40,7 +41,8 @@ interface IIncrementalService {
int createStorage(in @utf8InCpp String path, in DataLoaderParamsParcel params, int createMode,
in IDataLoaderStatusListener statusListener,
in StorageHealthCheckParams healthCheckParams,
- in IStorageHealthListener healthListener);
+ in IStorageHealthListener healthListener,
+ in PerUidReadTimeouts[] perUidReadTimeouts);
int createLinkedStorage(in @utf8InCpp String path, int otherStorageId, int createMode);
/**
@@ -123,7 +125,7 @@ interface IIncrementalService {
/**
* Permanently disable readlogs reporting for a storage given its ID.
*/
- void disableReadLogs(int storageId);
+ void disallowReadLogs(int storageId);
/**
* Setting up native library directories and extract native libs onto a storage if needed.
diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java
index 284c2df2ee7b..59292baa110c 100644
--- a/core/java/android/os/incremental/IncrementalFileStorages.java
+++ b/core/java/android/os/incremental/IncrementalFileStorages.java
@@ -69,7 +69,8 @@ public final class IncrementalFileStorages {
@Nullable IDataLoaderStatusListener statusListener,
@Nullable StorageHealthCheckParams healthCheckParams,
@Nullable IStorageHealthListener healthListener,
- List<InstallationFileParcel> addedFiles) throws IOException {
+ @NonNull List<InstallationFileParcel> addedFiles,
+ @NonNull PerUidReadTimeouts[] perUidReadTimeouts) throws IOException {
// TODO(b/136132412): validity check if session should not be incremental
IncrementalManager incrementalManager = (IncrementalManager) context.getSystemService(
Context.INCREMENTAL_SERVICE);
@@ -80,7 +81,7 @@ public final class IncrementalFileStorages {
final IncrementalFileStorages result = new IncrementalFileStorages(stageDir,
incrementalManager, dataLoaderParams, statusListener, healthCheckParams,
- healthListener);
+ healthListener, perUidReadTimeouts);
for (InstallationFileParcel file : addedFiles) {
if (file.location == LOCATION_DATA_APP) {
try {
@@ -105,7 +106,8 @@ public final class IncrementalFileStorages {
@NonNull DataLoaderParams dataLoaderParams,
@Nullable IDataLoaderStatusListener statusListener,
@Nullable StorageHealthCheckParams healthCheckParams,
- @Nullable IStorageHealthListener healthListener) throws IOException {
+ @Nullable IStorageHealthListener healthListener,
+ @NonNull PerUidReadTimeouts[] perUidReadTimeouts) throws IOException {
try {
mStageDir = stageDir;
mIncrementalManager = incrementalManager;
@@ -124,7 +126,7 @@ public final class IncrementalFileStorages {
mDefaultStorage = mIncrementalManager.createStorage(stageDir.getAbsolutePath(),
dataLoaderParams, IncrementalManager.CREATE_MODE_CREATE
| IncrementalManager.CREATE_MODE_TEMPORARY_BIND, false,
- statusListener, healthCheckParams, healthListener);
+ statusListener, healthCheckParams, healthListener, perUidReadTimeouts);
if (mDefaultStorage == null) {
throw new IOException(
"Couldn't create incremental storage at " + stageDir);
@@ -163,8 +165,8 @@ public final class IncrementalFileStorages {
/**
* Permanently disables readlogs.
*/
- public void disableReadLogs() {
- mDefaultStorage.disableReadLogs();
+ public void disallowReadLogs() {
+ mDefaultStorage.disallowReadLogs();
}
/**
diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java
index fb47ef04b231..4b9327021cb0 100644
--- a/core/java/android/os/incremental/IncrementalManager.java
+++ b/core/java/android/os/incremental/IncrementalManager.java
@@ -40,6 +40,7 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Objects;
/**
* Provides operations to open or create an IncrementalStorage, using IIncrementalService
@@ -104,10 +105,14 @@ public final class IncrementalManager {
boolean autoStartDataLoader,
@Nullable IDataLoaderStatusListener statusListener,
@Nullable StorageHealthCheckParams healthCheckParams,
- @Nullable IStorageHealthListener healthListener) {
+ @Nullable IStorageHealthListener healthListener,
+ @NonNull PerUidReadTimeouts[] perUidReadTimeouts) {
+ Objects.requireNonNull(path);
+ Objects.requireNonNull(params);
+ Objects.requireNonNull(perUidReadTimeouts);
try {
final int id = mService.createStorage(path, params.getData(), createMode,
- statusListener, healthCheckParams, healthListener);
+ statusListener, healthCheckParams, healthListener, perUidReadTimeouts);
if (id < 0) {
return null;
}
diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java
index b913faf9cc83..5b688bbd0655 100644
--- a/core/java/android/os/incremental/IncrementalStorage.java
+++ b/core/java/android/os/incremental/IncrementalStorage.java
@@ -432,9 +432,9 @@ public final class IncrementalStorage {
/**
* Permanently disable readlogs collection.
*/
- public void disableReadLogs() {
+ public void disallowReadLogs() {
try {
- mService.disableReadLogs(mId);
+ mService.disallowReadLogs(mId);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
diff --git a/core/java/android/os/incremental/PerUidReadTimeouts.aidl b/core/java/android/os/incremental/PerUidReadTimeouts.aidl
new file mode 100644
index 000000000000..84f30a6c3aaf
--- /dev/null
+++ b/core/java/android/os/incremental/PerUidReadTimeouts.aidl
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.incremental;
+
+/**
+ * Max value is ~1hr = 3600s = 3600000ms = 3600000000us
+ * @hide
+ */
+parcelable PerUidReadTimeouts {
+ /** UID to apply these timeouts to */
+ int uid;
+
+ /**
+ * Min time to read any block. Note that this doesn't apply to reads
+ * which are satisfied from the page cache.
+ */
+ long minTimeUs;
+
+ /**
+ * Min time to satisfy a pending read. Must be >= min_time_us. Any
+ * pending read which is filled before this time will be delayed so
+ * that the total read time >= this value.
+ */
+ long minPendingTimeUs;
+
+ /**
+ * Max time to satisfy a pending read before the read times out.
+ * Must be >= min_pending_time_us
+ */
+ long maxPendingTimeUs;
+}
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index 0ba09fdab808..f306805ac3a4 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -663,6 +663,34 @@ public final class PermissionControllerManager {
}
/**
+ * Gets the description of the privileges associated with the given device profiles
+ *
+ * @param profileName Name of the device profile
+ * @param executor Executor on which to invoke the callback
+ * @param callback Callback to receive the result
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_COMPANION_DEVICES)
+ public void getPrivilegesDescriptionStringForProfile(
+ @NonNull String profileName,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<String> callback) {
+ mRemoteService.postAsync(service -> {
+ AndroidFuture<String> future = new AndroidFuture<>();
+ service.getPrivilegesDescriptionStringForProfile(profileName, future);
+ return future;
+ }).whenCompleteAsync((description, err) -> {
+ if (err != null) {
+ Log.e(TAG, "Error from getPrivilegesDescriptionStringForProfile", err);
+ callback.accept(null);
+ } else {
+ callback.accept(description);
+ }
+ }, executor);
+ }
+
+ /**
* @see PermissionControllerManager#updateUserSensitiveForApp
* @hide
*/
diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java
index 8441fea1095a..8105b6517015 100644
--- a/core/java/android/permission/PermissionControllerService.java
+++ b/core/java/android/permission/PermissionControllerService.java
@@ -540,12 +540,13 @@ public abstract class PermissionControllerService extends Service {
public void getPrivilegesDescriptionStringForProfile(
@NonNull String deviceProfileName,
@NonNull AndroidFuture<String> callback) {
- checkStringNotEmpty(deviceProfileName);
- Objects.requireNonNull(callback);
+ try {
+ checkStringNotEmpty(deviceProfileName);
+ Objects.requireNonNull(callback);
- enforceSomePermissionsGrantedToCaller(Manifest.permission.MANAGE_COMPANION_DEVICES);
+ enforceSomePermissionsGrantedToCaller(
+ Manifest.permission.MANAGE_COMPANION_DEVICES);
- try {
callback.complete(PermissionControllerService
.this
.getPrivilegesDescriptionStringForProfile(deviceProfileName));
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index d1aa48914e70..0bdd574d77dd 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -54,6 +54,7 @@ import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
import android.util.Log;
+import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -96,6 +97,10 @@ public class CallLog {
*/
public static final String SHADOW_AUTHORITY = "call_log_shadow";
+ /** @hide */
+ public static final Uri SHADOW_CALL_COMPOSER_PICTURE_URI = CALL_COMPOSER_PICTURE_URI.buildUpon()
+ .authority(SHADOW_AUTHORITY).build();
+
/**
* Describes an error encountered while storing a call composer picture in the call log.
* @hide
@@ -143,7 +148,6 @@ public class CallLog {
private final int mErrorCode;
- /** @hide */
public CallComposerLoggingException(@CallComposerLoggingError int errorCode) {
mErrorCode = errorCode;
}
@@ -154,6 +158,29 @@ public class CallLog {
public @CallComposerLoggingError int getErrorCode() {
return mErrorCode;
}
+
+ @Override
+ public String toString() {
+ String errorString;
+ switch (mErrorCode) {
+ case ERROR_UNKNOWN:
+ errorString = "UNKNOWN";
+ break;
+ case ERROR_REMOTE_END_CLOSED:
+ errorString = "REMOTE_END_CLOSED";
+ break;
+ case ERROR_STORAGE_FULL:
+ errorString = "STORAGE_FULL";
+ break;
+ case ERROR_INPUT_CLOSED:
+ errorString = "INPUT_CLOSED";
+ break;
+ default:
+ errorString = "[[" + mErrorCode + "]]";
+ break;
+ }
+ return "CallComposerLoggingException: " + errorString;
+ }
}
/**
@@ -179,7 +206,10 @@ public class CallLog {
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.WRITE_CALL_LOG)
+ @RequiresPermission(allOf = {
+ Manifest.permission.WRITE_CALL_LOG,
+ Manifest.permission.INTERACT_ACROSS_USERS
+ })
public static void storeCallComposerPictureAsUser(@NonNull Context context,
@Nullable UserHandle user,
@NonNull InputStream input,
@@ -191,69 +221,164 @@ public class CallLog {
Objects.requireNonNull(callback);
executor.execute(() -> {
- Uri pictureFileUri;
- Uri pictureInsertionUri = context.getSystemService(UserManager.class)
- .isUserUnlocked() ? CALL_COMPOSER_PICTURE_URI
- : CALL_COMPOSER_PICTURE_URI.buildUpon().authority(SHADOW_AUTHORITY).build();
- try {
- // ContentResolver#insert says that the second argument is nullable. It is in fact
- // not nullable.
- ContentValues cv = new ContentValues();
- pictureFileUri = context.getContentResolver().insert(pictureInsertionUri, cv);
- } catch (ParcelableException e) {
- // Most likely an IOException. We don't have a good way of distinguishing them so
- // just return an unknown error.
- sendCallComposerError(callback, CallComposerLoggingException.ERROR_UNKNOWN);
+ ByteArrayOutputStream tmpOut = new ByteArrayOutputStream();
+
+ // Read the entire input into memory first in case we have to write multiple times and
+ // the input isn't resettable.
+ byte[] buffer = new byte[1024];
+ int bytesRead;
+ while (true) {
+ try {
+ bytesRead = input.read(buffer);
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "IOException while reading call composer pic from input: "
+ + e);
+ callback.onError(new CallComposerLoggingException(
+ CallComposerLoggingException.ERROR_INPUT_CLOSED));
+ return;
+ }
+ if (bytesRead < 0) {
+ break;
+ }
+ tmpOut.write(buffer, 0, bytesRead);
+ }
+ byte[] picData = tmpOut.toByteArray();
+
+ UserManager userManager = context.getSystemService(UserManager.class);
+ // Nasty casework for the shadow calllog begins...
+ // First see if we're just inserting for one user. If so, insert into the shadow
+ // based on whether that user is unlocked.
+ if (user != null) {
+ Uri baseUri = userManager.isUserUnlocked(user) ? CALL_COMPOSER_PICTURE_URI
+ : SHADOW_CALL_COMPOSER_PICTURE_URI;
+ Uri pictureInsertionUri = ContentProvider.maybeAddUserId(baseUri,
+ user.getIdentifier());
+ Log.i(LOG_TAG, "Inserting call composer for single user at "
+ + pictureInsertionUri);
+
+ try {
+ Uri result = storeCallComposerPictureAtUri(
+ context, pictureInsertionUri, false, picData);
+ callback.onResult(result);
+ } catch (CallComposerLoggingException e) {
+ callback.onError(e);
+ }
return;
}
- if (pictureFileUri == null) {
- // If the call log provider returns null, it means that there's not enough space
- // left to store the maximum-sized call composer image.
- sendCallComposerError(callback, CallComposerLoggingException.ERROR_STORAGE_FULL);
+
+ // Next, see if the system user is locked. If so, only insert to the system shadow
+ if (!userManager.isUserUnlocked(UserHandle.SYSTEM)) {
+ Uri pictureInsertionUri = ContentProvider.maybeAddUserId(
+ SHADOW_CALL_COMPOSER_PICTURE_URI,
+ UserHandle.SYSTEM.getIdentifier());
+ Log.i(LOG_TAG, "Inserting call composer for all users, but system locked at "
+ + pictureInsertionUri);
+ try {
+ Uri result =
+ storeCallComposerPictureAtUri(context, pictureInsertionUri,
+ true, picData);
+ callback.onResult(result);
+ } catch (CallComposerLoggingException e) {
+ callback.onError(e);
+ }
return;
}
- boolean wroteSuccessfully = false;
- try (ParcelFileDescriptor pfd =
- context.getContentResolver().openFileDescriptor(pictureFileUri, "w")) {
- FileOutputStream output = new FileOutputStream(pfd.getFileDescriptor());
- byte[] buffer = new byte[1024];
- int bytesRead;
- while (true) {
- try {
- bytesRead = input.read(buffer);
- } catch (IOException e) {
- sendCallComposerError(callback,
- CallComposerLoggingException.ERROR_INPUT_CLOSED);
- throw e;
- }
- if (bytesRead < 0) {
- break;
- }
+ // If we're inserting to all users and the system user is unlocked, then insert to all
+ // running users. Non running/still locked users will copy from the system when they
+ // start.
+ // First, insert to the system calllog to get the basename to use for the rest of the
+ // users.
+ Uri systemPictureInsertionUri = ContentProvider.maybeAddUserId(
+ CALL_COMPOSER_PICTURE_URI,
+ UserHandle.SYSTEM.getIdentifier());
+ Uri systemInsertedPicture;
+ try {
+ systemInsertedPicture =
+ storeCallComposerPictureAtUri(context, systemPictureInsertionUri,
+ true, picData);
+ Log.i(LOG_TAG, "Inserting call composer for all users, succeeded with system,"
+ + " result is " + systemInsertedPicture);
+ } catch (CallComposerLoggingException e) {
+ callback.onError(e);
+ return;
+ }
+
+ // Next, insert into all users that have call log access AND are running AND are
+ // decrypted.
+ Uri strippedInsertionUri = ContentProvider.getUriWithoutUserId(systemInsertedPicture);
+ for (UserInfo u : userManager.getAliveUsers()) {
+ UserHandle userHandle = u.getUserHandle();
+ if (userHandle.isSystem()) {
+ // Already written.
+ continue;
+ }
+
+ if (!Calls.shouldHaveSharedCallLogEntries(
+ context, userManager, userHandle.getIdentifier())) {
+ // Shouldn't have calllog entries.
+ continue;
+ }
+
+ if (userManager.isUserRunning(userHandle)
+ && userManager.isUserUnlocked(userHandle)) {
+ Uri insertionUri = ContentProvider.maybeAddUserId(strippedInsertionUri,
+ userHandle.getIdentifier());
+ Log.i(LOG_TAG, "Inserting call composer for all users, now on user "
+ + userHandle + " inserting at " + insertionUri);
try {
- output.write(buffer, 0, bytesRead);
- } catch (IOException e) {
- sendCallComposerError(callback,
- CallComposerLoggingException.ERROR_REMOTE_END_CLOSED);
- throw e;
+ storeCallComposerPictureAtUri(context, insertionUri, false, picData);
+ } catch (CallComposerLoggingException e) {
+ Log.e(LOG_TAG, "Error writing for user " + userHandle.getIdentifier()
+ + ": " + e);
+ // If one or more users failed but the system user succeeded, don't return
+ // an error -- the image is still around somewhere, and we'll be able to
+ // find it in the system user's call log if needed.
}
}
- wroteSuccessfully = true;
- } catch (FileNotFoundException e) {
- callback.onError(new CallComposerLoggingException(
- CallComposerLoggingException.ERROR_UNKNOWN));
- } catch (IOException e) {
- Log.e(LOG_TAG, "IOException while writing call composer pic to call log: "
- + e);
}
+ callback.onResult(strippedInsertionUri);
+ });
+ }
- if (wroteSuccessfully) {
- callback.onResult(pictureFileUri);
- } else {
+ private static Uri storeCallComposerPictureAtUri(
+ Context context, Uri insertionUri,
+ boolean forAllUsers, byte[] picData) throws CallComposerLoggingException {
+ Uri pictureFileUri;
+ try {
+ ContentValues cv = new ContentValues();
+ cv.put(Calls.ADD_FOR_ALL_USERS, forAllUsers ? 1 : 0);
+ pictureFileUri = context.getContentResolver().insert(insertionUri, cv);
+ } catch (ParcelableException e) {
+ // Most likely an IOException. We don't have a good way of distinguishing them so
+ // just return an unknown error.
+ throw new CallComposerLoggingException(CallComposerLoggingException.ERROR_UNKNOWN);
+ }
+ if (pictureFileUri == null) {
+ // If the call log provider returns null, it means that there's not enough space
+ // left to store the maximum-sized call composer image.
+ throw new CallComposerLoggingException(CallComposerLoggingException.ERROR_STORAGE_FULL);
+ }
+
+ try (ParcelFileDescriptor pfd =
+ context.getContentResolver().openFileDescriptor(pictureFileUri, "w")) {
+ FileOutputStream output = new FileOutputStream(pfd.getFileDescriptor());
+ try {
+ output.write(picData);
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "Got IOException writing to remote end: " + e);
// Clean up our mess if we didn't successfully write the file.
context.getContentResolver().delete(pictureFileUri, null);
+ throw new CallComposerLoggingException(
+ CallComposerLoggingException.ERROR_REMOTE_END_CLOSED);
}
- });
+ } catch (FileNotFoundException e) {
+ throw new CallComposerLoggingException(CallComposerLoggingException.ERROR_UNKNOWN);
+ } catch (IOException e) {
+ // Ignore, this is only thrown upon closing.
+ Log.e(LOG_TAG, "Got IOException closing remote descriptor: " + e);
+ }
+ return pictureFileUri;
}
// Only call on the correct executor.
diff --git a/core/java/android/service/translation/ITranslationCallback.aidl b/core/java/android/service/translation/ITranslationCallback.aidl
new file mode 100644
index 000000000000..333cb577f790
--- /dev/null
+++ b/core/java/android/service/translation/ITranslationCallback.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.translation;
+
+import android.view.translation.TranslationResponse;
+
+/**
+ * Interface to receive the result of a {@code TranslationRequest}.
+ *
+ * @hide
+ */
+oneway interface ITranslationCallback {
+ void onTranslationComplete(in TranslationResponse translationResponse);
+ void onError();
+}
diff --git a/core/java/android/service/translation/ITranslationService.aidl b/core/java/android/service/translation/ITranslationService.aidl
new file mode 100644
index 000000000000..6d6f2782ef4b
--- /dev/null
+++ b/core/java/android/service/translation/ITranslationService.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.translation;
+
+import android.service.translation.TranslationRequest;
+import android.service.translation.ITranslationCallback;
+import android.view.translation.TranslationSpec;
+import com.android.internal.os.IResultReceiver;
+
+/**
+ * System-wide on-device translation service.
+ *
+ * <p>Services requests to translate text between different languages. The primary use case for this
+ * service is automatic translation of text and web views, when the auto Translate feature is
+ * enabled.
+ *
+ * @hide
+ */
+oneway interface ITranslationService {
+ void onConnected();
+ void onDisconnected();
+ void onCreateTranslationSession(in TranslationSpec sourceSpec, in TranslationSpec destSpec,
+ int sessionId, in IResultReceiver receiver);
+}
diff --git a/core/java/android/service/translation/OWNERS b/core/java/android/service/translation/OWNERS
new file mode 100644
index 000000000000..a1e663aa8ff7
--- /dev/null
+++ b/core/java/android/service/translation/OWNERS
@@ -0,0 +1,8 @@
+# Bug component: 994311
+
+adamhe@google.com
+augale@google.com
+joannechung@google.com
+lpeter@google.com
+svetoslavganov@google.com
+tymtsai@google.com
diff --git a/core/java/android/service/translation/OnTranslationResultCallbackWrapper.java b/core/java/android/service/translation/OnTranslationResultCallbackWrapper.java
new file mode 100644
index 000000000000..345c69c0935d
--- /dev/null
+++ b/core/java/android/service/translation/OnTranslationResultCallbackWrapper.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.translation;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.DeadObjectException;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.translation.TranslationResponse;
+
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Callback to receive the {@link TranslationResponse} on successful translation.
+ *
+ * @hide
+ */
+final class OnTranslationResultCallbackWrapper implements
+ TranslationService.OnTranslationResultCallback {
+
+ private static final String TAG = "OnTranslationResultCallback";
+
+ private final @NonNull ITranslationCallback mCallback;
+
+ private AtomicBoolean mCalled;
+
+ /**
+ * @hide
+ */
+ public OnTranslationResultCallbackWrapper(@NonNull ITranslationCallback callback) {
+ mCallback = Objects.requireNonNull(callback);
+ mCalled = new AtomicBoolean();
+ }
+
+ @Override
+ public void onTranslationSuccess(@Nullable TranslationResponse response) {
+ assertNotCalled();
+ if (mCalled.getAndSet(true)) {
+ throw new IllegalStateException("Already called");
+ }
+
+ try {
+ mCallback.onTranslationComplete(response);
+ } catch (RemoteException e) {
+ if (e instanceof DeadObjectException) {
+ Log.w(TAG, "Process is dead, ignore.");
+ return;
+ }
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void onError() {
+ assertNotCalled();
+ if (mCalled.getAndSet(true)) {
+ throw new IllegalStateException("Already called");
+ }
+
+ try {
+ mCallback.onError();
+ } catch (RemoteException e) {
+ if (e instanceof DeadObjectException) {
+ Log.w(TAG, "Process is dead, ignore.");
+ return;
+ }
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ private void assertNotCalled() {
+ if (mCalled.get()) {
+ throw new IllegalStateException("Already called");
+ }
+ }
+}
diff --git a/core/java/android/service/translation/TranslationRequest.aidl b/core/java/android/service/translation/TranslationRequest.aidl
new file mode 100644
index 000000000000..9a2d4157696e
--- /dev/null
+++ b/core/java/android/service/translation/TranslationRequest.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.translation;
+
+parcelable TranslationRequest;
diff --git a/core/java/android/service/translation/TranslationRequest.java b/core/java/android/service/translation/TranslationRequest.java
new file mode 100644
index 000000000000..b8afd7049a82
--- /dev/null
+++ b/core/java/android/service/translation/TranslationRequest.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.translation;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.translation.TranslationSpec;
+
+import com.android.internal.util.DataClass;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Internal translation request sent to the {@link android.service.translation.TranslationService}
+ * which contains the text to be translated.
+ *
+ * @hide
+ */
+@SystemApi
+@DataClass(genConstructor = true, genBuilder = true, genToString = true)
+public final class TranslationRequest implements Parcelable {
+
+ private final int mRequestId;
+ @NonNull
+ private final TranslationSpec mSourceSpec;
+ @NonNull
+ private final TranslationSpec mDestSpec;
+ @NonNull
+ private final List<android.view.translation.TranslationRequest> mTranslationRequests;
+
+
+
+ // Code below generated by codegen v1.0.22.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/translation/TranslationRequest.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @DataClass.Generated.Member
+ public TranslationRequest(
+ int requestId,
+ @NonNull TranslationSpec sourceSpec,
+ @NonNull TranslationSpec destSpec,
+ @NonNull List<android.view.translation.TranslationRequest> translationRequests) {
+ this.mRequestId = requestId;
+ this.mSourceSpec = sourceSpec;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mSourceSpec);
+ this.mDestSpec = destSpec;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mDestSpec);
+ this.mTranslationRequests = translationRequests;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mTranslationRequests);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public int getRequestId() {
+ return mRequestId;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull TranslationSpec getSourceSpec() {
+ return mSourceSpec;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull TranslationSpec getDestSpec() {
+ return mDestSpec;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull List<android.view.translation.TranslationRequest> getTranslationRequests() {
+ return mTranslationRequests;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "TranslationRequest { " +
+ "requestId = " + mRequestId + ", " +
+ "sourceSpec = " + mSourceSpec + ", " +
+ "destSpec = " + mDestSpec + ", " +
+ "translationRequests = " + mTranslationRequests +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeInt(mRequestId);
+ dest.writeTypedObject(mSourceSpec, flags);
+ dest.writeTypedObject(mDestSpec, flags);
+ dest.writeParcelableList(mTranslationRequests, flags);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ TranslationRequest(@NonNull Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ int requestId = in.readInt();
+ TranslationSpec sourceSpec = (TranslationSpec) in.readTypedObject(TranslationSpec.CREATOR);
+ TranslationSpec destSpec = (TranslationSpec) in.readTypedObject(TranslationSpec.CREATOR);
+ List<android.view.translation.TranslationRequest> translationRequests = new ArrayList<>();
+ in.readParcelableList(translationRequests, android.view.translation.TranslationRequest.class.getClassLoader());
+
+ this.mRequestId = requestId;
+ this.mSourceSpec = sourceSpec;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mSourceSpec);
+ this.mDestSpec = destSpec;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mDestSpec);
+ this.mTranslationRequests = translationRequests;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mTranslationRequests);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<TranslationRequest> CREATOR
+ = new Parcelable.Creator<TranslationRequest>() {
+ @Override
+ public TranslationRequest[] newArray(int size) {
+ return new TranslationRequest[size];
+ }
+
+ @Override
+ public TranslationRequest createFromParcel(@NonNull Parcel in) {
+ return new TranslationRequest(in);
+ }
+ };
+
+ /**
+ * A builder for {@link TranslationRequest}
+ */
+ @SuppressWarnings("WeakerAccess")
+ @DataClass.Generated.Member
+ public static final class Builder {
+
+ private int mRequestId;
+ private @NonNull TranslationSpec mSourceSpec;
+ private @NonNull TranslationSpec mDestSpec;
+ private @NonNull List<android.view.translation.TranslationRequest> mTranslationRequests;
+
+ private long mBuilderFieldsSet = 0L;
+
+ public Builder(
+ int requestId,
+ @NonNull TranslationSpec sourceSpec,
+ @NonNull TranslationSpec destSpec,
+ @NonNull List<android.view.translation.TranslationRequest> translationRequests) {
+ mRequestId = requestId;
+ mSourceSpec = sourceSpec;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mSourceSpec);
+ mDestSpec = destSpec;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mDestSpec);
+ mTranslationRequests = translationRequests;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mTranslationRequests);
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull Builder setRequestId(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x1;
+ mRequestId = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull Builder setSourceSpec(@NonNull TranslationSpec value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2;
+ mSourceSpec = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull Builder setDestSpec(@NonNull TranslationSpec value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4;
+ mDestSpec = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull Builder setTranslationRequests(@NonNull List<android.view.translation.TranslationRequest> value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x8;
+ mTranslationRequests = value;
+ return this;
+ }
+
+ /** @see #setTranslationRequests */
+ @DataClass.Generated.Member
+ public @NonNull Builder addTranslationRequests(@NonNull android.view.translation.TranslationRequest value) {
+ // You can refine this method's name by providing item's singular name, e.g.:
+ // @DataClass.PluralOf("item")) mItems = ...
+
+ if (mTranslationRequests == null) setTranslationRequests(new ArrayList<>());
+ mTranslationRequests.add(value);
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull TranslationRequest build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x10; // Mark builder used
+
+ TranslationRequest o = new TranslationRequest(
+ mRequestId,
+ mSourceSpec,
+ mDestSpec,
+ mTranslationRequests);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x10) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+
+ @DataClass.Generated(
+ time = 1609966181888L,
+ codegenVersion = "1.0.22",
+ sourceFile = "frameworks/base/core/java/android/service/translation/TranslationRequest.java",
+ inputSignatures = "private final int mRequestId\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mSourceSpec\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mDestSpec\nprivate final @android.annotation.NonNull java.util.List<android.view.translation.TranslationRequest> mTranslationRequests\nclass TranslationRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=true, genBuilder=true, genToString=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/service/translation/TranslationService.java b/core/java/android/service/translation/TranslationService.java
new file mode 100644
index 000000000000..b0288076376c
--- /dev/null
+++ b/core/java/android/service/translation/TranslationService.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.translation;
+
+import static android.view.translation.Translator.EXTRA_SERVICE_BINDER;
+import static android.view.translation.Translator.EXTRA_SESSION_ID;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.BaseBundle;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.translation.ITranslationDirectManager;
+import android.view.translation.TranslationManager;
+import android.view.translation.TranslationResponse;
+import android.view.translation.TranslationSpec;
+
+import com.android.internal.os.IResultReceiver;
+import com.android.internal.util.SyncResultReceiver;
+
+/**
+ * Service for translating text.
+ * @hide
+ */
+@SystemApi
+public abstract class TranslationService extends Service {
+ private static final String TAG = "TranslationService";
+
+ /**
+ * The {@link Intent} that must be declared as handled by the service.
+ *
+ * <p>To be supported, the service must also require the
+ * {@link android.Manifest.permission#BIND_TRANSLATION_SERVICE} permission so
+ * that other applications can not abuse it.
+ */
+ public static final String SERVICE_INTERFACE =
+ "android.service.translation.TranslationService";
+
+ /**
+ * Name under which a TranslationService component publishes information about itself.
+ *
+ * <p>This meta-data should reference an XML resource containing a
+ * <code>&lt;{@link
+ * android.R.styleable#TranslationService translation-service}&gt;</code> tag.
+ *
+ * <p>Here's an example of how to use it on {@code AndroidManifest.xml}:
+ * TODO: fill in doc example (check CCService/AFService).
+ */
+ public static final String SERVICE_META_DATA = "android.translation_service";
+
+ private Handler mHandler;
+
+ /**
+ * Binder to receive calls from system server.
+ */
+ private final ITranslationService mInterface = new ITranslationService.Stub() {
+ @Override
+ public void onConnected() {
+ mHandler.sendMessage(obtainMessage(TranslationService::onConnected,
+ TranslationService.this));
+ }
+
+ @Override
+ public void onDisconnected() {
+ mHandler.sendMessage(obtainMessage(TranslationService::onDisconnected,
+ TranslationService.this));
+ }
+
+ @Override
+ public void onCreateTranslationSession(TranslationSpec sourceSpec, TranslationSpec destSpec,
+ int sessionId, IResultReceiver receiver) throws RemoteException {
+ mHandler.sendMessage(obtainMessage(TranslationService::handleOnCreateTranslationSession,
+ TranslationService.this, sourceSpec, destSpec, sessionId, receiver));
+ }
+ };
+
+ /**
+ * Interface definition for a callback to be invoked when the translation is compleled.
+ */
+ public interface OnTranslationResultCallback {
+ /**
+ * Notifies the Android System that a translation request
+ * {@link TranslationService#onTranslationRequest(TranslationRequest, int,
+ * CancellationSignal, OnTranslationResultCallback)} was successfully fulfilled by the
+ * service.
+ *
+ * <p>This method should always be called, even if the service cannot fulfill the request
+ * (in which case it should be called with a TranslationResponse with
+ * {@link android.view.translation.TranslationResponse#TRANSLATION_STATUS_UNKNOWN_ERROR},
+ * or {@link android.view.translation.TranslationResponse
+ * #TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE}).
+ *
+ * @param response translation response for the provided request infos.
+ *
+ * @throws IllegalStateException if this method was already called.
+ */
+ void onTranslationSuccess(@NonNull TranslationResponse response);
+
+ /**
+ * TODO: implement javadoc
+ */
+ void onError();
+ }
+
+ /**
+ * Binder that receives calls from the app.
+ */
+ private final ITranslationDirectManager mClientInterface =
+ new ITranslationDirectManager.Stub() {
+ // TODO: Implement cancellation signal
+ @NonNull
+ private final CancellationSignal mCancellationSignal = new CancellationSignal();
+
+ @Override
+ public void onTranslationRequest(TranslationRequest request, int sessionId,
+ ITranslationCallback callback, IResultReceiver receiver)
+ throws RemoteException {
+ // TODO(b/176464808): Currently, the API is used for both sync and async case.
+ // It may work now, but maybe two methods is more cleaner. To think how to
+ // define the APIs for these two cases.
+ final ITranslationCallback cb = callback != null
+ ? callback
+ : new ITranslationCallback.Stub() {
+ @Override
+ public void onTranslationComplete(
+ TranslationResponse translationResponse)
+ throws RemoteException {
+ receiver.send(0,
+ SyncResultReceiver.bundleFor(translationResponse));
+ }
+
+ @Override
+ public void onError() throws RemoteException {
+ //TODO: implement default error callback
+ }
+ };
+ // TODO(b/176464808): make it a private member of client
+ final OnTranslationResultCallback translationResultCallback =
+ new OnTranslationResultCallbackWrapper(cb);
+ mHandler.sendMessage(obtainMessage(TranslationService::onTranslationRequest,
+ TranslationService.this, request, sessionId, mCancellationSignal,
+ translationResultCallback));
+ }
+
+ @Override
+ public void onFinishTranslationSession(int sessionId) throws RemoteException {
+ mHandler.sendMessage(obtainMessage(
+ TranslationService::onFinishTranslationSession,
+ TranslationService.this, sessionId));
+ }
+ };
+
+ @CallSuper
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mHandler = new Handler(Looper.getMainLooper(), null, true);
+ BaseBundle.setShouldDefuse(true);
+ }
+
+ @Override
+ @Nullable
+ public final IBinder onBind(@NonNull Intent intent) {
+ if (SERVICE_INTERFACE.equals(intent.getAction())) {
+ return mInterface.asBinder();
+ }
+ Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent);
+ return null;
+ }
+
+ /**
+ * Called when the Android system connects to service.
+ *
+ * <p>You should generally do initialization here rather than in {@link #onCreate}.
+ */
+ public void onConnected() {
+ }
+
+ /**
+ * Called when the Android system disconnects from the service.
+ *
+ * <p> At this point this service may no longer be an active {@link TranslationService}.
+ * It should not make calls on {@link TranslationManager} that requires the caller to be
+ * the current service.
+ */
+ public void onDisconnected() {
+ }
+
+ /**
+ * TODO: fill in javadoc.
+ *
+ * @param sourceSpec
+ * @param destSpec
+ * @param sessionId
+ */
+ // TODO(b/176464808): the session id won't be unique cross client/server process. Need to find
+ // solution to make it's safe.
+ public abstract void onCreateTranslationSession(@NonNull TranslationSpec sourceSpec,
+ @NonNull TranslationSpec destSpec, int sessionId);
+
+ /**
+ * TODO: fill in javadoc.
+ *
+ * @param sessionId
+ */
+ public abstract void onFinishTranslationSession(int sessionId);
+
+ /**
+ * TODO: fill in javadoc.
+ *
+ * @param request
+ * @param sessionId
+ * @param callback
+ * @param cancellationSignal
+ */
+ public abstract void onTranslationRequest(@NonNull TranslationRequest request, int sessionId,
+ @NonNull CancellationSignal cancellationSignal,
+ @NonNull OnTranslationResultCallback callback);
+
+ // TODO(b/176464808): Need to handle client dying case
+
+ // TODO(b/176464808): Need to handle the failure case. e.g. if the specs does not support
+
+ private void handleOnCreateTranslationSession(@NonNull TranslationSpec sourceSpec,
+ @NonNull TranslationSpec destSpec, int sessionId, IResultReceiver resultReceiver) {
+ try {
+ final Bundle extras = new Bundle();
+ extras.putBinder(EXTRA_SERVICE_BINDER, mClientInterface.asBinder());
+ extras.putInt(EXTRA_SESSION_ID, sessionId);
+ resultReceiver.send(0, extras);
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException sending client interface: " + e);
+ }
+ onCreateTranslationSession(sourceSpec, destSpec, sessionId);
+ }
+}
diff --git a/core/java/android/service/translation/TranslationServiceInfo.java b/core/java/android/service/translation/TranslationServiceInfo.java
new file mode 100644
index 000000000000..18cc29d12b5f
--- /dev/null
+++ b/core/java/android/service/translation/TranslationServiceInfo.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.translation;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.AppGlobals;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.RemoteException;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * {@link ServiceInfo} and meta-data about an {@link TranslationService}.
+ *
+ * @hide
+ */
+public final class TranslationServiceInfo {
+
+ private static final String TAG = "TranslationServiceInfo";
+ private static final String XML_TAG_SERVICE = "translation-service";
+
+ @NonNull
+ private final ServiceInfo mServiceInfo;
+
+ @Nullable
+ private final String mSettingsActivity;
+
+ private static ServiceInfo getServiceInfoOrThrow(ComponentName comp, boolean isTemp,
+ @UserIdInt int userId) throws PackageManager.NameNotFoundException {
+ int flags = PackageManager.GET_META_DATA;
+ if (!isTemp) {
+ flags |= PackageManager.MATCH_SYSTEM_ONLY;
+ }
+
+ ServiceInfo si = null;
+ try {
+ si = AppGlobals.getPackageManager().getServiceInfo(comp, flags, userId);
+ } catch (RemoteException e) {
+ }
+ if (si == null) {
+ throw new NameNotFoundException("Could not get serviceInfo for "
+ + (isTemp ? " (temp)" : "(default system)")
+ + " " + comp.flattenToShortString());
+ }
+ return si;
+ }
+
+ @NonNull
+ public ServiceInfo getServiceInfo() {
+ return mServiceInfo;
+ }
+
+ @Nullable
+ public String getSettingsActivity() {
+ return mSettingsActivity;
+ }
+
+ public TranslationServiceInfo(@NonNull Context context, @NonNull ComponentName comp,
+ boolean isTemporaryService, @UserIdInt int userId)
+ throws PackageManager.NameNotFoundException {
+ this(context, getServiceInfoOrThrow(comp, isTemporaryService, userId));
+ }
+
+ private TranslationServiceInfo(@NonNull Context context, @NonNull ServiceInfo si) {
+ // Check for permission.
+ if (!Manifest.permission.BIND_TRANSLATION_SERVICE.equals(si.permission)) {
+ Slog.w(TAG, "TranslationServiceInfo from '" + si.packageName
+ + "' does not require permission "
+ + Manifest.permission.BIND_CONTENT_CAPTURE_SERVICE);
+ throw new SecurityException("Service does not require permission "
+ + Manifest.permission.BIND_CONTENT_CAPTURE_SERVICE);
+ }
+
+ mServiceInfo = si;
+
+ // Get the metadata, if declared.
+ // TODO: Try to find more easier way to do this.
+ final XmlResourceParser parser = si.loadXmlMetaData(context.getPackageManager(),
+ TranslationService.SERVICE_META_DATA);
+ if (parser == null) {
+ mSettingsActivity = null;
+ return;
+ }
+
+ String settingsActivity = null;
+
+ try {
+ final Resources resources = context.getPackageManager().getResourcesForApplication(
+ si.applicationInfo);
+
+ int type = 0;
+ while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) {
+ type = parser.next();
+ }
+
+ if (XML_TAG_SERVICE.equals(parser.getName())) {
+ final AttributeSet allAttributes = Xml.asAttributeSet(parser);
+ TypedArray afsAttributes = null;
+ try {
+ afsAttributes = resources.obtainAttributes(allAttributes,
+ com.android.internal.R.styleable.TranslationService);
+ settingsActivity = afsAttributes.getString(
+ R.styleable.ContentCaptureService_settingsActivity);
+ } finally {
+ if (afsAttributes != null) {
+ afsAttributes.recycle();
+ }
+ }
+ } else {
+ Log.e(TAG, "Meta-data does not start with translation-service tag");
+ }
+ } catch (PackageManager.NameNotFoundException | IOException | XmlPullParserException e) {
+ Log.e(TAG, "Error parsing auto fill service meta-data", e);
+ }
+
+ mSettingsActivity = settingsActivity;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append(getClass().getSimpleName());
+ builder.append("[").append(mServiceInfo);
+ builder.append(", settings:").append(mSettingsActivity);
+ return builder.toString();
+ }
+
+ /**
+ * Dumps the service information.
+ */
+ public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+ pw.print(prefix);
+ pw.print("Component: ");
+ pw.println(getServiceInfo().getComponentName());
+ pw.print(prefix);
+ pw.print("Settings: ");
+ pw.println(mSettingsActivity);
+ }
+}
diff --git a/core/java/android/util/SparseArray.java b/core/java/android/util/SparseArray.java
index dae760f989f6..86120d1e650c 100644
--- a/core/java/android/util/SparseArray.java
+++ b/core/java/android/util/SparseArray.java
@@ -241,6 +241,14 @@ public class SparseArray<E> implements Cloneable {
}
/**
+ * Alias for {@link #put(int, Object)} to support Kotlin [index]= operator.
+ * @see #put(int, Object)
+ */
+ public void set(int key, E value) {
+ put(key, value);
+ }
+
+ /**
* Adds a mapping from the specified key to the specified value,
* replacing the previous mapping from the specified key if there
* was one.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index e8584dac90c8..4842aae1af73 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -10448,7 +10448,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @UnsupportedAppUsage(trackingBug = 171933273)
protected boolean isVisibleToUser(Rect boundInView) {
if (mAttachInfo != null) {
// Attached to invisible window means this view is not visible.
diff --git a/core/java/android/view/translation/ITranslationDirectManager.aidl b/core/java/android/view/translation/ITranslationDirectManager.aidl
new file mode 100644
index 000000000000..358f99a5104b
--- /dev/null
+++ b/core/java/android/view/translation/ITranslationDirectManager.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.translation;
+
+import android.service.translation.TranslationRequest;
+import android.service.translation.ITranslationCallback;
+import com.android.internal.os.IResultReceiver;
+
+/**
+ * Interface between an app (TranslationManager / Translator) and the remote TranslationService
+ * providing the TranslationService implementation.
+ *
+ * @hide
+ */
+oneway interface ITranslationDirectManager {
+ void onTranslationRequest(in TranslationRequest request, int sessionId,
+ in ITranslationCallback callback, in IResultReceiver receiver);
+ void onFinishTranslationSession(int sessionId);
+}
diff --git a/core/java/android/view/translation/ITranslationManager.aidl b/core/java/android/view/translation/ITranslationManager.aidl
new file mode 100644
index 000000000000..73addf4d1894
--- /dev/null
+++ b/core/java/android/view/translation/ITranslationManager.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.translation;
+
+import android.service.translation.TranslationRequest;
+import android.view.translation.TranslationSpec;
+import com.android.internal.os.IResultReceiver;
+
+/**
+ * Mediator between apps being translated and translation service implementation.
+ *
+ * {@hide}
+ */
+oneway interface ITranslationManager {
+ void getSupportedLocales(in IResultReceiver receiver, int userId);
+ void onSessionCreated(in TranslationSpec sourceSpec, in TranslationSpec destSpec,
+ int sessionId, in IResultReceiver receiver, int userId);
+}
diff --git a/core/java/android/view/translation/OWNERS b/core/java/android/view/translation/OWNERS
new file mode 100644
index 000000000000..a1e663aa8ff7
--- /dev/null
+++ b/core/java/android/view/translation/OWNERS
@@ -0,0 +1,8 @@
+# Bug component: 994311
+
+adamhe@google.com
+augale@google.com
+joannechung@google.com
+lpeter@google.com
+svetoslavganov@google.com
+tymtsai@google.com
diff --git a/core/java/android/content/om/OverlayManagerTransaction.aidl b/core/java/android/view/translation/TranslationData.aidl
index 6715c82d4e6f..40f21a6b3d4e 100644
--- a/core/java/android/content/om/OverlayManagerTransaction.aidl
+++ b/core/java/android/view/translation/TranslationData.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.content.om;
+package android.view.translation;
-parcelable OverlayManagerTransaction;
+parcelable TranslationData;
diff --git a/core/java/android/view/translation/TranslationManager.java b/core/java/android/view/translation/TranslationManager.java
new file mode 100644
index 000000000000..6554e1a1db54
--- /dev/null
+++ b/core/java/android/view/translation/TranslationManager.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.translation;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
+import android.annotation.SystemService;
+import android.annotation.WorkerThread;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.service.translation.TranslationService;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Pair;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.SyncResultReceiver;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * The {@link TranslationManager} class provides ways for apps to integrate and use the
+ * translation framework.
+ *
+ * <p>The TranslationManager manages {@link Translator}s and help bridge client calls to
+ * the server {@link android.service.translation.TranslationService} </p>
+ */
+@SystemService(Context.TRANSLATION_MANAGER_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_TRANSLATION)
+public final class TranslationManager {
+
+ private static final String TAG = "TranslationManager";
+
+ /**
+ * Timeout for calls to system_server.
+ */
+ static final int SYNC_CALLS_TIMEOUT_MS = 5000;
+ /**
+ * The result code from result receiver success.
+ * @hide
+ */
+ public static final int STATUS_SYNC_CALL_SUCCESS = 1;
+ /**
+ * The result code from result receiver fail.
+ * @hide
+ */
+ public static final int STATUS_SYNC_CALL_FAIL = 2;
+
+ private static final Random ID_GENERATOR = new Random();
+ private final Object mLock = new Object();
+
+ @NonNull
+ private final Context mContext;
+
+ private final ITranslationManager mService;
+
+ @Nullable
+ @GuardedBy("mLock")
+ private ITranslationDirectManager mDirectServiceBinder;
+
+ @NonNull
+ @GuardedBy("mLock")
+ private final SparseArray<Translator> mTranslators = new SparseArray<>();
+
+ @NonNull
+ @GuardedBy("mLock")
+ private final ArrayMap<Pair<TranslationSpec, TranslationSpec>, Integer> mTranslatorIds =
+ new ArrayMap<>();
+
+ @NonNull
+ private final Handler mHandler;
+
+ private static final AtomicInteger sAvailableRequestId = new AtomicInteger(1);
+
+ /**
+ * @hide
+ */
+ public TranslationManager(@NonNull Context context, ITranslationManager service) {
+ mContext = Objects.requireNonNull(context, "context cannot be null");
+ mService = service;
+
+ mHandler = Handler.createAsync(Looper.getMainLooper());
+ }
+
+ /**
+ * Create a Translator for translation.
+ *
+ * <p><strong>NOTE: </strong>Call on a worker thread.
+ *
+ * @param sourceSpec {@link TranslationSpec} for the data to be translated.
+ * @param destSpec {@link TranslationSpec} for the translated data.
+ * @return a {@link Translator} to be used for calling translation APIs.
+ */
+ @Nullable
+ @WorkerThread
+ public Translator createTranslator(@NonNull TranslationSpec sourceSpec,
+ @NonNull TranslationSpec destSpec) {
+ Objects.requireNonNull(sourceSpec, "sourceSpec cannot be null");
+ Objects.requireNonNull(sourceSpec, "destSpec cannot be null");
+
+ synchronized (mLock) {
+ // TODO(b/176464808): Disallow multiple Translator now, it will throw
+ // IllegalStateException. Need to discuss if we can allow multiple Translators.
+ final Pair<TranslationSpec, TranslationSpec> specs = new Pair<>(sourceSpec, destSpec);
+ if (mTranslatorIds.containsKey(specs)) {
+ return mTranslators.get(mTranslatorIds.get(specs));
+ }
+
+ int translatorId;
+ do {
+ translatorId = Math.abs(ID_GENERATOR.nextInt());
+ } while (translatorId == 0 || mTranslators.indexOfKey(translatorId) >= 0);
+
+ final Translator newTranslator = new Translator(mContext, sourceSpec, destSpec,
+ translatorId, this, mHandler, mService);
+ // Start the Translator session and wait for the result
+ newTranslator.start();
+ try {
+ if (!newTranslator.isSessionCreated()) {
+ return null;
+ }
+ mTranslators.put(translatorId, newTranslator);
+ mTranslatorIds.put(specs, translatorId);
+ return newTranslator;
+ } catch (Translator.ServiceBinderReceiver.TimeoutException e) {
+ // TODO(b/176464808): maybe make SyncResultReceiver.TimeoutException constructor
+ // public and use it.
+ Log.e(TAG, "Timed out getting create session: " + e);
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Returns a list of locales supported by the {@link TranslationService}.
+ *
+ * <p><strong>NOTE: </strong>Call on a worker thread.
+ *
+ * TODO: Change to correct language/locale format
+ */
+ @NonNull
+ @WorkerThread
+ public List<String> getSupportedLocales() {
+ try {
+ // TODO: implement it
+ final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
+ mService.getSupportedLocales(receiver, mContext.getUserId());
+ int resutCode = receiver.getIntResult();
+ if (resutCode != STATUS_SYNC_CALL_SUCCESS) {
+ return Collections.emptyList();
+ }
+ return receiver.getParcelableResult();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (SyncResultReceiver.TimeoutException e) {
+ Log.e(TAG, "Timed out getting supported locales: " + e);
+ return Collections.emptyList();
+ }
+ }
+
+ void removeTranslator(int id) {
+ synchronized (mLock) {
+ mTranslators.remove(id);
+ for (int i = 0; i < mTranslatorIds.size(); i++) {
+ if (mTranslatorIds.valueAt(i) == id) {
+ mTranslatorIds.removeAt(i);
+ break;
+ }
+ }
+ }
+ }
+
+ AtomicInteger getAvailableRequestId() {
+ synchronized (mLock) {
+ return sAvailableRequestId;
+ }
+ }
+}
diff --git a/core/java/android/view/translation/TranslationRequest.aidl b/core/java/android/view/translation/TranslationRequest.aidl
new file mode 100644
index 000000000000..c34bf3011462
--- /dev/null
+++ b/core/java/android/view/translation/TranslationRequest.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.translation;
+
+parcelable TranslationRequest;
diff --git a/core/java/android/view/translation/TranslationRequest.java b/core/java/android/view/translation/TranslationRequest.java
new file mode 100644
index 000000000000..a5e3f758ba9f
--- /dev/null
+++ b/core/java/android/view/translation/TranslationRequest.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.translation;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcelable;
+import android.view.autofill.AutofillId;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * Wrapper class for data to be translated by {@link android.service.translation.TranslationService}
+ */
+@DataClass(genToString = true, genBuilder = true)
+public final class TranslationRequest implements Parcelable {
+
+ @Nullable
+ private final AutofillId mAutofillId;
+
+ @Nullable
+ private final CharSequence mTranslationText;
+
+ public TranslationRequest(@Nullable CharSequence text) {
+ mAutofillId = null;
+ mTranslationText = text;
+ }
+
+ private static CharSequence defaultTranslationText() {
+ return null;
+ }
+
+ private static AutofillId defaultAutofillId() {
+ return null;
+ }
+
+
+
+ // Code below generated by codegen v1.0.22.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/translation/TranslationRequest.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @DataClass.Generated.Member
+ /* package-private */ TranslationRequest(
+ @Nullable AutofillId autofillId,
+ @Nullable CharSequence translationText) {
+ this.mAutofillId = autofillId;
+ this.mTranslationText = translationText;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable AutofillId getAutofillId() {
+ return mAutofillId;
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable CharSequence getTranslationText() {
+ return mTranslationText;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "TranslationRequest { " +
+ "autofillId = " + mAutofillId + ", " +
+ "translationText = " + mTranslationText +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ byte flg = 0;
+ if (mAutofillId != null) flg |= 0x1;
+ if (mTranslationText != null) flg |= 0x2;
+ dest.writeByte(flg);
+ if (mAutofillId != null) dest.writeTypedObject(mAutofillId, flags);
+ if (mTranslationText != null) dest.writeCharSequence(mTranslationText);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ TranslationRequest(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ byte flg = in.readByte();
+ AutofillId autofillId = (flg & 0x1) == 0 ? null : (AutofillId) in.readTypedObject(AutofillId.CREATOR);
+ CharSequence translationText = (flg & 0x2) == 0 ? null : (CharSequence) in.readCharSequence();
+
+ this.mAutofillId = autofillId;
+ this.mTranslationText = translationText;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<TranslationRequest> CREATOR
+ = new Parcelable.Creator<TranslationRequest>() {
+ @Override
+ public TranslationRequest[] newArray(int size) {
+ return new TranslationRequest[size];
+ }
+
+ @Override
+ public TranslationRequest createFromParcel(@NonNull android.os.Parcel in) {
+ return new TranslationRequest(in);
+ }
+ };
+
+ /**
+ * A builder for {@link TranslationRequest}
+ */
+ @SuppressWarnings("WeakerAccess")
+ @DataClass.Generated.Member
+ public static final class Builder {
+
+ private @Nullable AutofillId mAutofillId;
+ private @Nullable CharSequence mTranslationText;
+
+ private long mBuilderFieldsSet = 0L;
+
+ public Builder() {
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull Builder setAutofillId(@NonNull AutofillId value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x1;
+ mAutofillId = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull Builder setTranslationText(@NonNull CharSequence value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2;
+ mTranslationText = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull TranslationRequest build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4; // Mark builder used
+
+ if ((mBuilderFieldsSet & 0x1) == 0) {
+ mAutofillId = defaultAutofillId();
+ }
+ if ((mBuilderFieldsSet & 0x2) == 0) {
+ mTranslationText = defaultTranslationText();
+ }
+ TranslationRequest o = new TranslationRequest(
+ mAutofillId,
+ mTranslationText);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x4) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+
+ @DataClass.Generated(
+ time = 1610060189421L,
+ codegenVersion = "1.0.22",
+ sourceFile = "frameworks/base/core/java/android/view/translation/TranslationRequest.java",
+ inputSignatures = "private final @android.annotation.Nullable android.view.autofill.AutofillId mAutofillId\nprivate final @android.annotation.Nullable java.lang.CharSequence mTranslationText\nprivate static java.lang.CharSequence defaultTranslationText()\nprivate static android.view.autofill.AutofillId defaultAutofillId()\nclass TranslationRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genBuilder=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/view/translation/TranslationResponse.aidl b/core/java/android/view/translation/TranslationResponse.aidl
new file mode 100644
index 000000000000..e5350bb54dc2
--- /dev/null
+++ b/core/java/android/view/translation/TranslationResponse.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.translation;
+
+parcelable TranslationResponse;
diff --git a/core/java/android/view/translation/TranslationResponse.java b/core/java/android/view/translation/TranslationResponse.java
new file mode 100644
index 000000000000..d29063fbd914
--- /dev/null
+++ b/core/java/android/view/translation/TranslationResponse.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.translation;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.service.translation.TranslationService;
+
+import com.android.internal.util.DataClass;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Response from the {@link TranslationService}, which contains the translated result.
+ */
+@DataClass(genBuilder = true, genToString = true, genHiddenConstDefs = true)
+public final class TranslationResponse implements Parcelable {
+
+ /**
+ * The {@link TranslationService} was successful in translating.
+ */
+ public static final int TRANSLATION_STATUS_SUCCESS = 0;
+ /**
+ * The {@link TranslationService} returned unknown translation result.
+ */
+ public static final int TRANSLATION_STATUS_UNKNOWN_ERROR = 1;
+ /**
+ * The language of the request is not available to be translated.
+ */
+ public static final int TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE = 2;
+
+ /**
+ * The translation result status code.
+ */
+ private final @TranslationStatus int mTranslationStatus;
+ /**
+ * The translation results. If there is no translation result, set it with an empty list.
+ */
+ @NonNull
+ private List<TranslationRequest> mTranslations = new ArrayList();
+
+
+
+
+ // Code below generated by codegen v1.0.22.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/translation/TranslationResponse.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /** @hide */
+ @IntDef(prefix = "TRANSLATION_STATUS_", value = {
+ TRANSLATION_STATUS_SUCCESS,
+ TRANSLATION_STATUS_UNKNOWN_ERROR,
+ TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @DataClass.Generated.Member
+ public @interface TranslationStatus {}
+
+ /** @hide */
+ @DataClass.Generated.Member
+ public static String translationStatusToString(@TranslationStatus int value) {
+ switch (value) {
+ case TRANSLATION_STATUS_SUCCESS:
+ return "TRANSLATION_STATUS_SUCCESS";
+ case TRANSLATION_STATUS_UNKNOWN_ERROR:
+ return "TRANSLATION_STATUS_UNKNOWN_ERROR";
+ case TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE:
+ return "TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE";
+ default: return Integer.toHexString(value);
+ }
+ }
+
+ @DataClass.Generated.Member
+ /* package-private */ TranslationResponse(
+ @TranslationStatus int translationStatus,
+ @NonNull List<TranslationRequest> translations) {
+ this.mTranslationStatus = translationStatus;
+
+ if (!(mTranslationStatus == TRANSLATION_STATUS_SUCCESS)
+ && !(mTranslationStatus == TRANSLATION_STATUS_UNKNOWN_ERROR)
+ && !(mTranslationStatus == TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE)) {
+ throw new java.lang.IllegalArgumentException(
+ "translationStatus was " + mTranslationStatus + " but must be one of: "
+ + "TRANSLATION_STATUS_SUCCESS(" + TRANSLATION_STATUS_SUCCESS + "), "
+ + "TRANSLATION_STATUS_UNKNOWN_ERROR(" + TRANSLATION_STATUS_UNKNOWN_ERROR + "), "
+ + "TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE(" + TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE + ")");
+ }
+
+ this.mTranslations = translations;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mTranslations);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * The translation result status code.
+ */
+ @DataClass.Generated.Member
+ public @TranslationStatus int getTranslationStatus() {
+ return mTranslationStatus;
+ }
+
+ /**
+ * The translation results. If there is no translation result, set it with an empty list.
+ */
+ @DataClass.Generated.Member
+ public @NonNull List<TranslationRequest> getTranslations() {
+ return mTranslations;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "TranslationResponse { " +
+ "translationStatus = " + translationStatusToString(mTranslationStatus) + ", " +
+ "translations = " + mTranslations +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeInt(mTranslationStatus);
+ dest.writeParcelableList(mTranslations, flags);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ TranslationResponse(@NonNull Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ int translationStatus = in.readInt();
+ List<TranslationRequest> translations = new ArrayList<>();
+ in.readParcelableList(translations, TranslationRequest.class.getClassLoader());
+
+ this.mTranslationStatus = translationStatus;
+
+ if (!(mTranslationStatus == TRANSLATION_STATUS_SUCCESS)
+ && !(mTranslationStatus == TRANSLATION_STATUS_UNKNOWN_ERROR)
+ && !(mTranslationStatus == TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE)) {
+ throw new java.lang.IllegalArgumentException(
+ "translationStatus was " + mTranslationStatus + " but must be one of: "
+ + "TRANSLATION_STATUS_SUCCESS(" + TRANSLATION_STATUS_SUCCESS + "), "
+ + "TRANSLATION_STATUS_UNKNOWN_ERROR(" + TRANSLATION_STATUS_UNKNOWN_ERROR + "), "
+ + "TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE(" + TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE + ")");
+ }
+
+ this.mTranslations = translations;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mTranslations);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<TranslationResponse> CREATOR
+ = new Parcelable.Creator<TranslationResponse>() {
+ @Override
+ public TranslationResponse[] newArray(int size) {
+ return new TranslationResponse[size];
+ }
+
+ @Override
+ public TranslationResponse createFromParcel(@NonNull Parcel in) {
+ return new TranslationResponse(in);
+ }
+ };
+
+ /**
+ * A builder for {@link TranslationResponse}
+ */
+ @SuppressWarnings("WeakerAccess")
+ @DataClass.Generated.Member
+ public static final class Builder {
+
+ private @TranslationStatus int mTranslationStatus;
+ private @NonNull List<TranslationRequest> mTranslations;
+
+ private long mBuilderFieldsSet = 0L;
+
+ /**
+ * Creates a new Builder.
+ *
+ * @param translationStatus
+ * The translation result status code.
+ */
+ public Builder(
+ @TranslationStatus int translationStatus) {
+ mTranslationStatus = translationStatus;
+
+ if (!(mTranslationStatus == TRANSLATION_STATUS_SUCCESS)
+ && !(mTranslationStatus == TRANSLATION_STATUS_UNKNOWN_ERROR)
+ && !(mTranslationStatus == TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE)) {
+ throw new java.lang.IllegalArgumentException(
+ "translationStatus was " + mTranslationStatus + " but must be one of: "
+ + "TRANSLATION_STATUS_SUCCESS(" + TRANSLATION_STATUS_SUCCESS + "), "
+ + "TRANSLATION_STATUS_UNKNOWN_ERROR(" + TRANSLATION_STATUS_UNKNOWN_ERROR + "), "
+ + "TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE(" + TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE + ")");
+ }
+
+ }
+
+ /**
+ * The translation result status code.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setTranslationStatus(@TranslationStatus int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x1;
+ mTranslationStatus = value;
+ return this;
+ }
+
+ /**
+ * The translation results. If there is no translation result, set it with an empty list.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setTranslations(@NonNull List<TranslationRequest> value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2;
+ mTranslations = value;
+ return this;
+ }
+
+ /** @see #setTranslations */
+ @DataClass.Generated.Member
+ public @NonNull Builder addTranslations(@NonNull TranslationRequest value) {
+ // You can refine this method's name by providing item's singular name, e.g.:
+ // @DataClass.PluralOf("item")) mItems = ...
+
+ if (mTranslations == null) setTranslations(new ArrayList<>());
+ mTranslations.add(value);
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull TranslationResponse build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4; // Mark builder used
+
+ if ((mBuilderFieldsSet & 0x2) == 0) {
+ mTranslations = new ArrayList();
+ }
+ TranslationResponse o = new TranslationResponse(
+ mTranslationStatus,
+ mTranslations);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x4) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+
+ @DataClass.Generated(
+ time = 1609973911361L,
+ codegenVersion = "1.0.22",
+ sourceFile = "frameworks/base/core/java/android/view/translation/TranslationResponse.java",
+ inputSignatures = "public static final int TRANSLATION_STATUS_SUCCESS\npublic static final int TRANSLATION_STATUS_UNKNOWN_ERROR\npublic static final int TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE\nprivate final @android.view.translation.TranslationResponse.TranslationStatus int mTranslationStatus\nprivate @android.annotation.NonNull java.util.List<android.view.translation.TranslationRequest> mTranslations\nclass TranslationResponse extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genToString=true, genHiddenConstDefs=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/view/translation/TranslationSpec.aidl b/core/java/android/view/translation/TranslationSpec.aidl
new file mode 100644
index 000000000000..875d798370d4
--- /dev/null
+++ b/core/java/android/view/translation/TranslationSpec.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.translation;
+
+parcelable TranslationSpec;
diff --git a/core/java/android/view/translation/TranslationSpec.java b/core/java/android/view/translation/TranslationSpec.java
new file mode 100644
index 000000000000..ab1bc477e0fd
--- /dev/null
+++ b/core/java/android/view/translation/TranslationSpec.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.translation;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * Specs and additional info for the translation data.
+ *
+ * <p>This spec help specify information such as the language/locale for the translation, as well
+ * as the data format for the translation (text, audio, etc.)</p>
+ */
+@DataClass(genEqualsHashCode = true, genHiddenConstDefs = true)
+public final class TranslationSpec implements Parcelable {
+
+ /** Data format for translation is text. */
+ public static final int DATA_FORMAT_TEXT = 1;
+
+ /** @hide */
+ @android.annotation.IntDef(prefix = "DATA_FORMAT_", value = {
+ DATA_FORMAT_TEXT
+ })
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE)
+ @DataClass.Generated.Member
+ public @interface DataFormat {}
+
+ /**
+ * String representation of language codes e.g. "en", "es", etc.
+ */
+ private final @NonNull String mLanguage;
+
+ private final @DataFormat int mDataFormat;
+
+
+
+ // Code below generated by codegen v1.0.22.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/translation/TranslationSpec.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * Creates a new TranslationSpec.
+ *
+ * @param language
+ * String representation of language codes e.g. "en", "es", etc.
+ */
+ @DataClass.Generated.Member
+ public TranslationSpec(
+ @NonNull String language,
+ @DataFormat int dataFormat) {
+ this.mLanguage = language;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mLanguage);
+ this.mDataFormat = dataFormat;
+ com.android.internal.util.AnnotationValidations.validate(
+ DataFormat.class, null, mDataFormat);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * String representation of language codes e.g. "en", "es", etc.
+ */
+ @DataClass.Generated.Member
+ public @NonNull String getLanguage() {
+ return mLanguage;
+ }
+
+ @DataClass.Generated.Member
+ public @DataFormat int getDataFormat() {
+ return mDataFormat;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@android.annotation.Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(TranslationSpec other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ TranslationSpec that = (TranslationSpec) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && java.util.Objects.equals(mLanguage, that.mLanguage)
+ && mDataFormat == that.mDataFormat;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + java.util.Objects.hashCode(mLanguage);
+ _hash = 31 * _hash + mDataFormat;
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeString(mLanguage);
+ dest.writeInt(mDataFormat);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ TranslationSpec(@NonNull Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ String language = in.readString();
+ int dataFormat = in.readInt();
+
+ this.mLanguage = language;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mLanguage);
+ this.mDataFormat = dataFormat;
+ com.android.internal.util.AnnotationValidations.validate(
+ DataFormat.class, null, mDataFormat);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<TranslationSpec> CREATOR
+ = new Parcelable.Creator<TranslationSpec>() {
+ @Override
+ public TranslationSpec[] newArray(int size) {
+ return new TranslationSpec[size];
+ }
+
+ @Override
+ public TranslationSpec createFromParcel(@NonNull Parcel in) {
+ return new TranslationSpec(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1609964630624L,
+ codegenVersion = "1.0.22",
+ sourceFile = "frameworks/base/core/java/android/view/translation/TranslationSpec.java",
+ inputSignatures = "public static final int DATA_FORMAT_TEXT\nprivate final @android.annotation.NonNull java.lang.String mLanguage\nprivate final @android.view.translation.TranslationSpec.DataFormat int mDataFormat\nclass TranslationSpec extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genHiddenConstDefs=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/view/translation/Translator.java b/core/java/android/view/translation/Translator.java
new file mode 100644
index 000000000000..675f32b19d17
--- /dev/null
+++ b/core/java/android/view/translation/Translator.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.translation;
+
+import static android.view.translation.TranslationManager.STATUS_SYNC_CALL_FAIL;
+import static android.view.translation.TranslationManager.SYNC_CALLS_TIMEOUT_MS;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.WorkerThread;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.IResultReceiver;
+import com.android.internal.util.SyncResultReceiver;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * The {@link Translator} for translation, defined by a source and a dest {@link TranslationSpec}.
+ */
+@SuppressLint("NotCloseable")
+public class Translator {
+
+ private static final String TAG = "Translator";
+
+ // TODO: make this configurable and cross the Translation component
+ private static boolean sDEBUG = false;
+
+ private final Object mLock = new Object();
+
+ private int mId;
+
+ @NonNull
+ private final Context mContext;
+
+ @NonNull
+ private final TranslationSpec mSourceSpec;
+
+ @NonNull
+ private final TranslationSpec mDestSpec;
+
+ @NonNull
+ private final TranslationManager mManager;
+
+ @NonNull
+ private final Handler mHandler;
+
+ /**
+ * Interface to the system_server binder object.
+ */
+ private ITranslationManager mSystemServerBinder;
+
+ /**
+ * Direct interface to the TranslationService binder object.
+ */
+ @Nullable
+ private ITranslationDirectManager mDirectServiceBinder;
+
+ @NonNull
+ private final ServiceBinderReceiver mServiceBinderReceiver;
+
+ @GuardedBy("mLock")
+ private boolean mDestroyed;
+
+ /**
+ * Name of the {@link IResultReceiver} extra used to pass the binder interface to Translator.
+ * @hide
+ */
+ public static final String EXTRA_SERVICE_BINDER = "binder";
+ /**
+ * Name of the extra used to pass the session id to Translator.
+ * @hide
+ */
+ public static final String EXTRA_SESSION_ID = "sessionId";
+
+ static class ServiceBinderReceiver extends IResultReceiver.Stub {
+ private final WeakReference<Translator> mTranslator;
+ private final CountDownLatch mLatch = new CountDownLatch(1);
+ private int mSessionId;
+
+ ServiceBinderReceiver(Translator translator) {
+ mTranslator = new WeakReference<>(translator);
+ }
+
+ int getSessionStateResult() throws TimeoutException {
+ try {
+ if (!mLatch.await(SYNC_CALLS_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ throw new TimeoutException(
+ "Session not created in " + SYNC_CALLS_TIMEOUT_MS + "ms");
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new TimeoutException("Session not created because interrupted");
+ }
+ return mSessionId;
+ }
+
+ @Override
+ public void send(int resultCode, Bundle resultData) {
+ if (resultCode == STATUS_SYNC_CALL_FAIL) {
+ mLatch.countDown();
+ return;
+ }
+ mSessionId = resultData.getInt(EXTRA_SESSION_ID);
+ final Translator translator = mTranslator.get();
+ if (translator == null) {
+ Log.w(TAG, "received result after session is finished");
+ return;
+ }
+ final IBinder binder;
+ if (resultData != null) {
+ binder = resultData.getBinder(EXTRA_SERVICE_BINDER);
+ if (binder == null) {
+ Log.wtf(TAG, "No " + EXTRA_SERVICE_BINDER + " extra result");
+ return;
+ }
+ } else {
+ binder = null;
+ }
+ translator.setServiceBinder(binder);
+ mLatch.countDown();
+ }
+
+ // TODO(b/176464808): maybe make SyncResultReceiver.TimeoutException constructor public
+ // and use it.
+ static final class TimeoutException extends Exception {
+ private TimeoutException(String msg) {
+ super(msg);
+ }
+ }
+ }
+
+ /**
+ * Create the Translator.
+ *
+ * @hide
+ */
+ public Translator(@NonNull Context context,
+ @NonNull TranslationSpec sourceSpec,
+ @NonNull TranslationSpec destSpec, int sessionId,
+ @NonNull TranslationManager translationManager, @NonNull Handler handler,
+ @Nullable ITranslationManager systemServerBinder) {
+ mContext = context;
+ mSourceSpec = sourceSpec;
+ mDestSpec = destSpec;
+ mId = sessionId;
+ mManager = translationManager;
+ mHandler = handler;
+ mSystemServerBinder = systemServerBinder;
+ mServiceBinderReceiver = new ServiceBinderReceiver(this);
+ }
+
+ /**
+ * Starts this Translator session.
+ */
+ void start() {
+ try {
+ mSystemServerBinder.onSessionCreated(mSourceSpec, mDestSpec, mId,
+ mServiceBinderReceiver, mContext.getUserId());
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException calling startSession(): " + e);
+ }
+ }
+
+ /**
+ * Wait this Translator session created.
+ *
+ * @return {@code true} if the session is created successfully.
+ */
+ boolean isSessionCreated() throws ServiceBinderReceiver.TimeoutException {
+ int receivedId = mServiceBinderReceiver.getSessionStateResult();
+ return receivedId > 0;
+ }
+
+ private int getNextRequestId() {
+ // Get from manager to keep the request id unique to different Translators
+ return mManager.getAvailableRequestId().getAndIncrement();
+ }
+
+ private void setServiceBinder(@Nullable IBinder binder) {
+ synchronized (mLock) {
+ if (mDirectServiceBinder != null) {
+ return;
+ }
+ if (binder != null) {
+ mDirectServiceBinder = ITranslationDirectManager.Stub.asInterface(binder);
+ }
+ }
+ }
+
+ /** @hide */
+ public int getTranslatorId() {
+ return mId;
+ }
+
+ /**
+ * Requests a translation for the provided {@link TranslationRequest} using the Translator's
+ * source spec and destination spec.
+ *
+ * <p><strong>NOTE: </strong>Call on a worker thread.
+ *
+ * @param request {@link TranslationRequest} request to be translated.
+ *
+ * @return {@link TranslationRequest} containing translated request,
+ * or null if translation could not be done.
+ * @throws IllegalStateException if this TextClassification session was destroyed when calls
+ */
+ @Nullable
+ @WorkerThread
+ public TranslationResponse translate(@NonNull TranslationRequest request) {
+ Objects.requireNonNull(request, "Translation request cannot be null");
+ if (isDestroyed()) {
+ // TODO(b/176464808): Disallow multiple Translator now, it will throw
+ // IllegalStateException. Need to discuss if we can allow multiple Translators.
+ throw new IllegalStateException(
+ "This translator has been destroyed");
+ }
+ final ArrayList<TranslationRequest> requests = new ArrayList<>();
+ requests.add(request);
+ final android.service.translation.TranslationRequest internalRequest =
+ new android.service.translation.TranslationRequest
+ .Builder(getNextRequestId(), mSourceSpec, mDestSpec, requests)
+ .build();
+
+ TranslationResponse response = null;
+ try {
+ final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
+ mDirectServiceBinder.onTranslationRequest(internalRequest, mId, null, receiver);
+
+ response = receiver.getParcelableResult();
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException calling requestTranslate(): " + e);
+ } catch (SyncResultReceiver.TimeoutException e) {
+ Log.e(TAG, "Timed out calling requestTranslate: " + e);
+ }
+ if (sDEBUG) {
+ Log.v(TAG, "Receive translation response: " + response);
+ }
+ return response;
+ }
+
+ /**
+ * Destroy this Translator.
+ */
+ public void destroy() {
+ synchronized (mLock) {
+ if (mDestroyed) {
+ return;
+ }
+ mDestroyed = true;
+ try {
+ mDirectServiceBinder.onFinishTranslationSession(mId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException calling onSessionFinished");
+ }
+ mDirectServiceBinder = null;
+ mManager.removeTranslator(mId);
+ }
+ }
+
+ /**
+ * Returns whether or not this Translator has been destroyed.
+ *
+ * @see #destroy()
+ */
+ public boolean isDestroyed() {
+ synchronized (mLock) {
+ return mDestroyed;
+ }
+ }
+
+ // TODO: add methods for UI-toolkit case.
+}
diff --git a/core/java/android/window/ITaskOrganizerController.aidl b/core/java/android/window/ITaskOrganizerController.aidl
index 4a43a438b69d..2d0211e129bf 100644
--- a/core/java/android/window/ITaskOrganizerController.aidl
+++ b/core/java/android/window/ITaskOrganizerController.aidl
@@ -56,12 +56,6 @@ interface ITaskOrganizerController {
WindowContainerToken getImeTarget(int display);
/**
- * Set's the root task to launch new tasks into on a display. {@code null} means no launch root
- * and thus new tasks just end up directly on the display.
- */
- void setLaunchRoot(int displayId, in WindowContainerToken root);
-
- /**
* Requests that the given task organizer is notified when back is pressed on the root activity
* of one of its controlled tasks.
*/
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index 73b2fe1ff4d5..f29eb39045ae 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -184,19 +184,6 @@ public class TaskOrganizer extends WindowOrganizer {
}
/**
- * Set's the root task to launch new tasks into on a display. {@code null} means no launch
- * root and thus new tasks just end up directly on the display.
- */
- @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
- public void setLaunchRoot(int displayId, @NonNull WindowContainerToken root) {
- try {
- mTaskOrganizerController.setLaunchRoot(displayId, root);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Requests that the given task organizer is notified when back is pressed on the root activity
* of one of its controlled tasks.
*/
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index eba4fd21166d..6bc31107bddf 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -30,6 +30,7 @@ import android.util.ArrayMap;
import android.view.SurfaceControl;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
@@ -263,8 +264,9 @@ public final class WindowContainerTransaction implements Parcelable {
@NonNull
public WindowContainerTransaction reparent(@NonNull WindowContainerToken child,
@Nullable WindowContainerToken parent, boolean onTop) {
- mHierarchyOps.add(new HierarchyOp(child.asBinder(),
- parent == null ? null : parent.asBinder(), onTop));
+ mHierarchyOps.add(HierarchyOp.createForReparent(child.asBinder(),
+ parent == null ? null : parent.asBinder(),
+ onTop));
return this;
}
@@ -276,7 +278,47 @@ public final class WindowContainerTransaction implements Parcelable {
*/
@NonNull
public WindowContainerTransaction reorder(@NonNull WindowContainerToken child, boolean onTop) {
- mHierarchyOps.add(new HierarchyOp(child.asBinder(), onTop));
+ mHierarchyOps.add(HierarchyOp.createForReorder(child.asBinder(), onTop));
+ return this;
+ }
+
+ /**
+ * Reparent's all children tasks of {@param currentParent} in the specified
+ * {@param windowingMode} and {@param activityType} to {@param newParent} in their current
+ * z-order.
+ *
+ * @param currentParent of the tasks to perform the operation no.
+ * {@code null} will perform the operation on the display.
+ * @param newParent for the tasks. {@code null} will perform the operation on the display.
+ * @param windowingModes of the tasks to reparent.
+ * @param activityTypes of the tasks to reparent.
+ * @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to
+ * the bottom.
+ */
+ @NonNull
+ public WindowContainerTransaction reparentTasks(@Nullable WindowContainerToken currentParent,
+ @Nullable WindowContainerToken newParent, @Nullable int[] windowingModes,
+ @Nullable int[] activityTypes, boolean onTop) {
+ mHierarchyOps.add(HierarchyOp.createForChildrenTasksReparent(
+ currentParent != null ? currentParent.asBinder() : null,
+ newParent != null ? newParent.asBinder() : null,
+ windowingModes,
+ activityTypes,
+ onTop));
+ return this;
+ }
+
+ /**
+ * Sets whether a container should be the launch root for the specified windowing mode and
+ * activity type. This currently only applies to Task containers created by organizer.
+ */
+ @NonNull
+ public WindowContainerTransaction setLaunchRoot(@NonNull WindowContainerToken container,
+ @Nullable int[] windowingModes, @Nullable int[] activityTypes) {
+ mHierarchyOps.add(HierarchyOp.createForSetLaunchRoot(
+ container.asBinder(),
+ windowingModes,
+ activityTypes));
return this;
}
@@ -363,6 +405,7 @@ public final class WindowContainerTransaction implements Parcelable {
private boolean mFocusable = true;
private boolean mHidden = false;
private boolean mIgnoreOrientationRequest = false;
+
private int mChangeMask = 0;
private @ActivityInfo.Config int mConfigSetMask = 0;
private @WindowConfiguration.WindowConfig int mWindowSetMask = 0;
@@ -595,6 +638,14 @@ public final class WindowContainerTransaction implements Parcelable {
* @hide
*/
public static class HierarchyOp implements Parcelable {
+ public static final int HIERARCHY_OP_TYPE_REPARENT = 0;
+ public static final int HIERARCHY_OP_TYPE_REORDER = 1;
+ public static final int HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT = 2;
+ public static final int HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT = 3;
+
+ private final int mType;
+
+ // Container we are performing the operation on.
private final IBinder mContainer;
// If this is same as mContainer, then only change position, don't reparent.
@@ -603,32 +654,68 @@ public final class WindowContainerTransaction implements Parcelable {
// Moves/reparents to top of parent when {@code true}, otherwise moves/reparents to bottom.
private final boolean mToTop;
- public HierarchyOp(@NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) {
- mContainer = container;
- mReparent = reparent;
- mToTop = toTop;
+ final private int[] mWindowingModes;
+ final private int[] mActivityTypes;
+
+ public static HierarchyOp createForReparent(
+ @NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) {
+ return new HierarchyOp(HIERARCHY_OP_TYPE_REPARENT,
+ container, reparent, null, null, toTop);
}
- public HierarchyOp(@NonNull IBinder container, boolean toTop) {
+ public static HierarchyOp createForReorder(@NonNull IBinder container, boolean toTop) {
+ return new HierarchyOp(HIERARCHY_OP_TYPE_REORDER,
+ container, container, null, null, toTop);
+ }
+
+ public static HierarchyOp createForChildrenTasksReparent(IBinder currentParent,
+ IBinder newParent, int[] windowingModes, int[] activityTypes, boolean onTop) {
+ return new HierarchyOp(HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT,
+ currentParent, newParent, windowingModes, activityTypes, onTop);
+ }
+
+ public static HierarchyOp createForSetLaunchRoot(IBinder container,
+ int[] windowingModes, int[] activityTypes) {
+ return new HierarchyOp(HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT,
+ container, null, windowingModes, activityTypes, false);
+ }
+
+ private HierarchyOp(int type, @NonNull IBinder container, @Nullable IBinder reparent,
+ int[] windowingModes, int[] activityTypes, boolean toTop) {
+ mType = type;
mContainer = container;
- mReparent = container;
+ mReparent = reparent;
+ mWindowingModes = windowingModes != null ?
+ Arrays.copyOf(windowingModes, windowingModes.length) : null;
+ mActivityTypes = activityTypes != null ?
+ Arrays.copyOf(activityTypes, activityTypes.length) : null;
mToTop = toTop;
}
public HierarchyOp(@NonNull HierarchyOp copy) {
+ mType = copy.mType;
mContainer = copy.mContainer;
mReparent = copy.mReparent;
mToTop = copy.mToTop;
+ mWindowingModes = copy.mWindowingModes;
+ mActivityTypes = copy.mActivityTypes;
}
protected HierarchyOp(Parcel in) {
+ mType = in.readInt();
mContainer = in.readStrongBinder();
mReparent = in.readStrongBinder();
mToTop = in.readBoolean();
+ mWindowingModes = in.createIntArray();
+ mActivityTypes = in.createIntArray();
+ }
+
+ public int getType() {
+ return mType;
}
public boolean isReparent() {
- return mContainer != mReparent;
+ return mType == HIERARCHY_OP_TYPE_REPARENT;
}
@Nullable
@@ -645,21 +732,45 @@ public final class WindowContainerTransaction implements Parcelable {
return mToTop;
}
+ public int[] getWindowingModes() {
+ return mWindowingModes;
+ }
+
+ public int[] getActivityTypes() {
+ return mActivityTypes;
+ }
+
@Override
public String toString() {
- if (isReparent()) {
- return "{reparent: " + mContainer + " to " + (mToTop ? "top of " : "bottom of ")
- + mReparent + "}";
- } else {
- return "{reorder: " + mContainer + " to " + (mToTop ? "top" : "bottom") + "}";
+ switch (mType) {
+ case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT:
+ return "{ChildrenTasksReparent: from=" + mContainer + " to=" + mReparent
+ + " mToTop=" + mToTop + " mWindowingMode=" + mWindowingModes
+ + " mActivityType=" + mActivityTypes + "}";
+ case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT:
+ return "{SetLaunchRoot: container=" + mContainer
+ + " mWindowingMode=" + mWindowingModes
+ + " mActivityType=" + mActivityTypes + "}";
+ case HIERARCHY_OP_TYPE_REPARENT:
+ return "{reparent: " + mContainer + " to " + (mToTop ? "top of " : "bottom of ")
+ + mReparent + "}";
+ case HIERARCHY_OP_TYPE_REORDER:
+ return "{reorder: " + mContainer + " to " + (mToTop ? "top" : "bottom") + "}";
+ default:
+ return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent
+ + " mToTop=" + mToTop + " mWindowingMode=" + mWindowingModes
+ + " mActivityType=" + mActivityTypes + "}";
}
}
@Override
public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mType);
dest.writeStrongBinder(mContainer);
dest.writeStrongBinder(mReparent);
dest.writeBoolean(mToTop);
+ dest.writeIntArray(mWindowingModes);
+ dest.writeIntArray(mActivityTypes);
}
@Override
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index e06413783fe4..34e03af4c666 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -431,19 +431,22 @@ public class ChooserActivity extends ResolverActivity implements
}
private void maybeHideContentPreview() {
- if (!mAtLeastOneLoaded && mHideParentOnFail) {
- Log.i(TAG, "Hiding image preview area. Timed out waiting for preview to load"
- + " within " + mImageLoadTimeoutMillis + "ms.");
- collapseParentView();
- if (shouldShowTabs()) {
- hideStickyContentPreview();
- } else if (mChooserMultiProfilePagerAdapter.getCurrentRootAdapter() != null) {
- mChooserMultiProfilePagerAdapter.getCurrentRootAdapter().hideContentPreview();
+ if (!mAtLeastOneLoaded) {
+ if (mHideParentOnFail) {
+ Log.i(TAG, "Hiding image preview area. Timed out waiting for preview to load"
+ + " within " + mImageLoadTimeoutMillis + "ms.");
+ collapseParentView();
+ if (shouldShowTabs()) {
+ hideStickyContentPreview();
+ } else if (mChooserMultiProfilePagerAdapter.getCurrentRootAdapter() != null) {
+ mChooserMultiProfilePagerAdapter.getCurrentRootAdapter()
+ .hideContentPreview();
+ }
+ mHideParentOnFail = false;
}
- mHideParentOnFail = false;
+ mRemoveSharedElements = true;
+ startPostponedEnterTransition();
}
- mRemoveSharedElements = true;
- startPostponedEnterTransition();
}
private void collapseParentView() {
diff --git a/core/java/com/android/internal/compat/CompatibilityChangeInfo.java b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
index 670ca9f6091e..03fe4551c249 100644
--- a/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
+++ b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
@@ -32,6 +32,7 @@ public class CompatibilityChangeInfo implements Parcelable {
private final boolean mDisabled;
private final boolean mLoggingOnly;
private final @Nullable String mDescription;
+ private final boolean mOverridable;
public long getId() {
return mChangeId;
@@ -58,9 +59,13 @@ public class CompatibilityChangeInfo implements Parcelable {
return mDescription;
}
+ public boolean getOverridable() {
+ return mOverridable;
+ }
+
public CompatibilityChangeInfo(
Long changeId, String name, int enableAfterTargetSdk, int enableSinceTargetSdk,
- boolean disabled, boolean loggingOnly, String description) {
+ boolean disabled, boolean loggingOnly, String description, boolean overridable) {
this.mChangeId = changeId;
this.mName = name;
if (enableAfterTargetSdk > 0) {
@@ -75,6 +80,7 @@ public class CompatibilityChangeInfo implements Parcelable {
this.mDisabled = disabled;
this.mLoggingOnly = loggingOnly;
this.mDescription = description;
+ this.mOverridable = overridable;
}
public CompatibilityChangeInfo(CompatibilityChangeInfo other) {
@@ -84,6 +90,7 @@ public class CompatibilityChangeInfo implements Parcelable {
this.mDisabled = other.mDisabled;
this.mLoggingOnly = other.mLoggingOnly;
this.mDescription = other.mDescription;
+ this.mOverridable = other.mOverridable;
}
private CompatibilityChangeInfo(Parcel in) {
@@ -93,6 +100,7 @@ public class CompatibilityChangeInfo implements Parcelable {
mDisabled = in.readBoolean();
mLoggingOnly = in.readBoolean();
mDescription = in.readString();
+ mOverridable = in.readBoolean();
}
@Override
@@ -108,6 +116,7 @@ public class CompatibilityChangeInfo implements Parcelable {
dest.writeBoolean(mDisabled);
dest.writeBoolean(mLoggingOnly);
dest.writeString(mDescription);
+ dest.writeBoolean(mOverridable);
}
@Override
@@ -126,6 +135,9 @@ public class CompatibilityChangeInfo implements Parcelable {
if (getLoggingOnly()) {
sb.append("; loggingOnly");
}
+ if (getOverridable()) {
+ sb.append("; overridable");
+ }
return sb.append(")").toString();
}
@@ -143,8 +155,8 @@ public class CompatibilityChangeInfo implements Parcelable {
&& this.mEnableSinceTargetSdk == that.mEnableSinceTargetSdk
&& this.mDisabled == that.mDisabled
&& this.mLoggingOnly == that.mLoggingOnly
- && this.mDescription.equals(that.mDescription);
-
+ && this.mDescription.equals(that.mDescription)
+ && this.mOverridable == that.mOverridable;
}
public static final Parcelable.Creator<CompatibilityChangeInfo> CREATOR =
diff --git a/core/java/com/android/internal/os/IDropBoxManagerService.aidl b/core/java/com/android/internal/os/IDropBoxManagerService.aidl
index 9141719d7a35..38a7203ead4a 100644
--- a/core/java/com/android/internal/os/IDropBoxManagerService.aidl
+++ b/core/java/com/android/internal/os/IDropBoxManagerService.aidl
@@ -17,6 +17,7 @@
package com.android.internal.os;
import android.os.DropBoxManager;
+import android.os.ParcelFileDescriptor;
/**
* "Backend" interface used by {@link android.os.DropBoxManager} to talk to the
@@ -26,12 +27,8 @@ import android.os.DropBoxManager;
* @hide
*/
interface IDropBoxManagerService {
- /**
- * @see DropBoxManager#addText
- * @see DropBoxManager#addData
- * @see DropBoxManager#addFile
- */
- void add(in DropBoxManager.Entry entry);
+ void addData(String tag, in byte[] data, int flags);
+ void addFile(String tag, in ParcelFileDescriptor fd, int flags);
/** @see DropBoxManager#getNextEntry */
boolean isTagEnabled(String tag);
diff --git a/core/java/com/android/server/net/BaseNetworkObserver.java b/core/java/com/android/server/net/BaseNetworkObserver.java
index 93f89b5db820..139b88b108c5 100644
--- a/core/java/com/android/server/net/BaseNetworkObserver.java
+++ b/core/java/com/android/server/net/BaseNetworkObserver.java
@@ -64,7 +64,7 @@ public class BaseNetworkObserver extends INetworkManagementEventObserver.Stub {
}
@Override
- public void interfaceClassDataActivityChanged(int networkType, boolean active, long tsNanos,
+ public void interfaceClassDataActivityChanged(int transportType, boolean active, long tsNanos,
int uid) {
// default no-op
}
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 3156f71fa113..2ff474b140e0 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -14,15 +14,6 @@
* limitations under the License.
*/
-/*
- * Disable optimization of this file if we are compiling with the address
- * sanitizer. This is a mitigation for b/122921367 and can be removed once the
- * bug is fixed.
- */
-#if __has_feature(address_sanitizer)
-#pragma clang optimize off
-#endif
-
#define LOG_TAG "Zygote"
#define ATRACE_TAG ATRACE_TAG_DALVIK
diff --git a/core/proto/android/server/biometrics.proto b/core/proto/android/server/biometrics.proto
index 632c1e5d419e..14b5c52acce4 100644
--- a/core/proto/android/server/biometrics.proto
+++ b/core/proto/android/server/biometrics.proto
@@ -106,16 +106,25 @@ message SensorServiceStateProto {
// State of a single sensor.
message SensorStateProto {
+ enum Modality {
+ UNKNOWN = 0;
+ FINGERPRINT = 1;
+ FACE = 2;
+ IRIS = 3;
+ }
+
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
// Unique sensorId
optional int32 sensor_id = 1;
+ optional Modality modality = 2;
+
// State of the sensor's scheduler. True if currently handling an operation, false if idle.
- optional bool is_busy = 2;
+ optional bool is_busy = 3;
// User states for this sensor.
- repeated UserStateProto user_states = 3;
+ repeated UserStateProto user_states = 4;
}
// State of a specific user for a specific sensor.
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 30535185bdae..ce3ed9dc8ba6 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2676,11 +2676,11 @@
The app can check whether it has this authorization by calling
{@link android.provider.Settings#canDrawOverlays
Settings.canDrawOverlays()}.
- <p>Protection level: signature|preinstalled|appop|pre23|development -->
+ <p>Protection level: signature|appop|installer|pre23|development -->
<permission android:name="android.permission.SYSTEM_ALERT_WINDOW"
android:label="@string/permlab_systemAlertWindow"
android:description="@string/permdesc_systemAlertWindow"
- android:protectionLevel="signature|preinstalled|appop|pre23|development" />
+ android:protectionLevel="signature|appop|installer|pre23|development" />
<!-- @deprecated Use {@link android.Manifest.permission#REQUEST_COMPANION_RUN_IN_BACKGROUND}
@hide
@@ -3591,6 +3591,14 @@
<permission android:name="android.permission.BIND_CONTENT_CAPTURE_SERVICE"
android:protectionLevel="signature" />
+ <!-- Must be required by a android.service.translation.TranslationService,
+ to ensure that only the system can bind to it.
+ @SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.BIND_TRANSLATION_SERVICE"
+ android:protectionLevel="signature" />
+
<!-- Must be required by a android.service.contentsuggestions.ContentSuggestionsService,
to ensure that only the system can bind to it.
@SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 7ca3fafdca47..ef54db1a422c 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -8292,6 +8292,23 @@
</declare-styleable>
<!-- =============================== -->
+ <!-- Translation attributes -->
+ <!-- =============================== -->
+ <eat-comment />
+
+ <!-- Use <code>translation-service</code> as the root tag of the XML resource that describes
+ a {@link android.service.translation.TranslationService}, which is referenced from
+ its {@link android.service.translation.TranslationService#SERVICE_META_DATA} meta-data
+ entry.
+ @hide @SystemApi
+ -->
+ <declare-styleable name="TranslationService">
+ <!-- Fully qualified class name of an activity that allows the user to modify
+ the settings for this service. -->
+ <attr name="settingsActivity" />
+ </declare-styleable>
+
+ <!-- =============================== -->
<!-- Contacts meta-data attributes -->
<!-- =============================== -->
<eat-comment />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index da658cc3d525..487847a0b075 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -450,6 +450,10 @@
-->
</string-array>
+ <!-- Whether the internal vehicle network should remain active even when no
+ apps requested it. -->
+ <bool name="config_vehicleInternalNetworkAlwaysRequested">false</bool>
+
<!-- Configuration of network interfaces that support WakeOnLAN -->
<string-array translatable="false" name="config_wakeonlan_supported_interfaces">
<!--
@@ -3724,6 +3728,14 @@
-->
<string name="config_defaultAugmentedAutofillService" translatable="false"></string>
+ <!-- The package name for the system's translation service.
+ This service must be trusted, as it can be activated without explicit consent of the user.
+ If no service with the specified name exists on the device, translation wil be
+ disabled.
+ Example: "com.android.translation/.TranslationService"
+-->
+ <string name="config_defaultTranslationService" translatable="false"></string>
+
<!-- The package name for the system's app prediction service.
This service must be trusted, as it can be activated without explicit consent of the user.
Example: "com.android.intelligence/.AppPredictionService"
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 3a593b93346d..5715b31b94bc 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -292,6 +292,17 @@
<!-- WFC, summary for Wi-Fi Only -->
<string name="wfc_mode_wifi_only_summary">Wi-Fi only</string>
+ <!-- Template for showing mobile network operator name while Cross SIM calling is active -->
+ <string-array name="crossSimSpnFormats" translatable="false">
+ <item>@string/crossSimFormat_spn</item>
+ <item>@string/crossSimFormat_spn_cross_sim_calling</item>
+ </string-array>
+
+ <!-- Spn during Cross-SIM Calling: "<operator> " [CHAR LIMIT=NONE] -->
+ <string name="crossSimFormat_spn"><xliff:g id="spn" example="Operator">%s</xliff:g></string>
+ <!-- Spn during Cross SIM Calling: "<operator> Cross-SIM Calling" [CHAR LIMIT=NONE] -->
+ <string name="crossSimFormat_spn_cross_sim_calling"><xliff:g id="spn" example="Operator">%s</xliff:g> Cross-SIM Calling</string>
+
<!--
{0} is one of "bearerServiceCode*"
{1} is dialing number
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 31b4edd4e6e8..96ebdaa84698 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -684,6 +684,7 @@
<java-symbol type="string" name="config_ethernet_iface_regex" />
<java-symbol type="string" name="not_checked" />
<java-symbol type="array" name="config_ethernet_interfaces" />
+ <java-symbol type="bool" name="config_vehicleInternalNetworkAlwaysRequested" />
<java-symbol type="array" name="config_wakeonlan_supported_interfaces" />
<java-symbol type="string" name="config_forceVoiceInteractionServicePackage" />
<java-symbol type="string" name="config_mms_user_agent" />
@@ -874,6 +875,7 @@
<java-symbol type="string" name="wfc_mode_wifi_preferred_summary" />
<java-symbol type="string" name="wfc_mode_cellular_preferred_summary" />
<java-symbol type="string" name="wfc_mode_wifi_only_summary" />
+ <java-symbol type="array" name="crossSimSpnFormats" />
<java-symbol type="string" name="policydesc_disableCamera" />
<java-symbol type="string" name="policydesc_encryptedStorage" />
<java-symbol type="string" name="policydesc_expirePassword" />
@@ -3484,6 +3486,7 @@
<java-symbol type="string" name="config_defaultWellbeingPackage" />
<java-symbol type="string" name="config_defaultContentCaptureService" />
<java-symbol type="string" name="config_defaultAugmentedAutofillService" />
+ <java-symbol type="string" name="config_defaultTranslationService" />
<java-symbol type="string" name="config_defaultAppPredictionService" />
<java-symbol type="string" name="config_defaultContentSuggestionsService" />
<java-symbol type="string" name="config_defaultSearchUiService" />
diff --git a/core/tests/overlaytests/device/Android.bp b/core/tests/overlaytests/device/Android.bp
index f86ac9ce37e1..12a2b0815050 100644
--- a/core/tests/overlaytests/device/Android.bp
+++ b/core/tests/overlaytests/device/Android.bp
@@ -16,11 +16,7 @@ android_test {
name: "OverlayDeviceTests",
srcs: ["src/**/*.java"],
platform_apis: true,
- certificate: "platform",
- static_libs: [
- "androidx.test.rules",
- "testng",
- ],
+ static_libs: ["androidx.test.rules"],
test_suites: ["device-tests"],
data: [
":OverlayDeviceTests_AppOverlayOne",
diff --git a/core/tests/overlaytests/device/AndroidManifest.xml b/core/tests/overlaytests/device/AndroidManifest.xml
index a69911f8d827..4881636c7095 100644
--- a/core/tests/overlaytests/device/AndroidManifest.xml
+++ b/core/tests/overlaytests/device/AndroidManifest.xml
@@ -19,8 +19,6 @@
<uses-sdk android:minSdkVersion="21" />
- <uses-permission android:name="android.permission.CHANGE_OVERLAY_PACKAGES" />
-
<application>
<uses-library android:name="android.test.runner"/>
</application>
diff --git a/core/tests/overlaytests/device/AndroidTest.xml b/core/tests/overlaytests/device/AndroidTest.xml
index db45750ff6dc..6507839a4288 100644
--- a/core/tests/overlaytests/device/AndroidTest.xml
+++ b/core/tests/overlaytests/device/AndroidTest.xml
@@ -19,20 +19,9 @@
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-instrumentation" />
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
- <option name="cleanup" value="true" />
- <option name="remount-system" value="true" />
- <option name="push" value="OverlayDeviceTests.apk->/system/app/OverlayDeviceTests.apk" />
- </target_preparer>
-
- <!-- Reboot to have the test APK scanned by PM and reboot after to remove the test APK. -->
- <target_preparer class="com.android.tradefed.targetprep.RebootTargetPreparer">
- <option name="pre-reboot" value="true" />
- <option name="post-reboot" value="true" />
- </target_preparer>
-
<target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
<option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="OverlayDeviceTests.apk" />
<option name="test-file-name" value="OverlayDeviceTests_AppOverlayOne.apk" />
<option name="test-file-name" value="OverlayDeviceTests_AppOverlayTwo.apk" />
<option name="test-file-name" value="OverlayDeviceTests_FrameworkOverlay.apk" />
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java b/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java
index 76c01a7e1125..390bb766ab81 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java
@@ -18,76 +18,60 @@ package com.android.overlaytest;
import static java.util.concurrent.TimeUnit.SECONDS;
-import android.annotation.NonNull;
-import android.content.Context;
-import android.content.om.OverlayManager;
-import android.content.om.OverlayManagerTransaction;
-import android.os.UserHandle;
+import android.app.UiAutomation;
+import android.content.res.Resources;
+import android.os.ParcelFileDescriptor;
import androidx.test.InstrumentationRegistry;
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
import java.util.concurrent.Executor;
import java.util.concurrent.FutureTask;
class LocalOverlayManager {
private static final long TIMEOUT = 30;
- public static void toggleOverlaysAndWait(@NonNull final String[] overlaysToEnable,
- @NonNull final String[] overlaysToDisable) throws Exception {
- final int userId = UserHandle.myUserId();
- OverlayManagerTransaction.Builder builder = new OverlayManagerTransaction.Builder();
- for (String pkg : overlaysToEnable) {
- builder.setEnabled(pkg, true, userId);
+ public static void setEnabledAndWait(Executor executor, final String packageName,
+ boolean enable) throws Exception {
+ final String pattern = (enable ? "[x]" : "[ ]") + " " + packageName;
+ if (executeShellCommand("cmd overlay list").contains(pattern)) {
+ // nothing to do, overlay already in the requested state
+ return;
}
- for (String pkg : overlaysToDisable) {
- builder.setEnabled(pkg, false, userId);
- }
- OverlayManagerTransaction transaction = builder.build();
- final Context ctx = InstrumentationRegistry.getTargetContext();
+ final Resources res = InstrumentationRegistry.getContext().getResources();
+ final String[] oldApkPaths = res.getAssets().getApkPaths();
FutureTask<Boolean> task = new FutureTask<>(() -> {
while (true) {
- final String[] paths = ctx.getResources().getAssets().getApkPaths();
- if (arrayTailContains(paths, overlaysToEnable)
- && arrayDoesNotContain(paths, overlaysToDisable)) {
+ if (!Arrays.equals(oldApkPaths, res.getAssets().getApkPaths())) {
return true;
}
Thread.sleep(10);
}
});
-
- OverlayManager om = ctx.getSystemService(OverlayManager.class);
- om.commit(transaction);
-
- Executor executor = (cmd) -> new Thread(cmd).start();
executor.execute(task);
+ executeShellCommand("cmd overlay " + (enable ? "enable " : "disable ") + packageName);
task.get(TIMEOUT, SECONDS);
}
- private static boolean arrayTailContains(@NonNull final String[] array,
- @NonNull final String[] substrings) {
- if (array.length < substrings.length) {
- return false;
- }
- for (int i = 0; i < substrings.length; i++) {
- String a = array[array.length - substrings.length + i];
- String s = substrings[i];
- if (!a.contains(s)) {
- return false;
- }
- }
- return true;
- }
-
- private static boolean arrayDoesNotContain(@NonNull final String[] array,
- @NonNull final String[] substrings) {
- for (String s : substrings) {
- for (String a : array) {
- if (a.contains(s)) {
- return false;
- }
+ private static String executeShellCommand(final String command)
+ throws Exception {
+ final UiAutomation uiAutomation =
+ InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ final ParcelFileDescriptor pfd = uiAutomation.executeShellCommand(command);
+ try (InputStream in = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
+ final BufferedReader reader = new BufferedReader(
+ new InputStreamReader(in, StandardCharsets.UTF_8));
+ StringBuilder str = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ str.append(line);
}
+ return str.toString();
}
- return true;
}
}
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/TransactionTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/TransactionTest.java
deleted file mode 100644
index 0b4f5e227169..000000000000
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/TransactionTest.java
+++ /dev/null
@@ -1,133 +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.overlaytest;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.testng.Assert.assertThrows;
-
-import android.content.Context;
-import android.content.om.OverlayInfo;
-import android.content.om.OverlayManager;
-import android.content.om.OverlayManagerTransaction;
-import android.content.res.Resources;
-import android.os.UserHandle;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.MediumTest;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.util.List;
-
-@RunWith(JUnit4.class)
-@MediumTest
-public class TransactionTest {
- static final String APP_OVERLAY_ONE_PKG = "com.android.overlaytest.app_overlay_one";
- static final String APP_OVERLAY_TWO_PKG = "com.android.overlaytest.app_overlay_two";
-
- private Context mContext;
- private Resources mResources;
- private OverlayManager mOverlayManager;
- private int mUserId;
- private UserHandle mUserHandle;
-
- @Before
- public void setUp() throws Exception {
- mContext = InstrumentationRegistry.getContext();
- mResources = mContext.getResources();
- mOverlayManager = mContext.getSystemService(OverlayManager.class);
- mUserId = UserHandle.myUserId();
- mUserHandle = UserHandle.of(mUserId);
-
- LocalOverlayManager.toggleOverlaysAndWait(
- new String[]{},
- new String[]{APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG});
- }
-
- @Test
- public void testValidTransaction() throws Exception {
- assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, false, mUserId);
- assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, false, mUserId);
-
- OverlayManagerTransaction t = new OverlayManagerTransaction.Builder()
- .setEnabled(APP_OVERLAY_ONE_PKG, true)
- .setEnabled(APP_OVERLAY_TWO_PKG, true)
- .build();
- mOverlayManager.commit(t);
-
- assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, true, mUserId);
- assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, true, mUserId);
- List<OverlayInfo> ois =
- mOverlayManager.getOverlayInfosForTarget("com.android.overlaytest", mUserHandle);
- assertEquals(ois.size(), 2);
- assertEquals(ois.get(0).packageName, APP_OVERLAY_ONE_PKG);
- assertEquals(ois.get(1).packageName, APP_OVERLAY_TWO_PKG);
-
- OverlayManagerTransaction t2 = new OverlayManagerTransaction.Builder()
- .setEnabled(APP_OVERLAY_TWO_PKG, true)
- .setEnabled(APP_OVERLAY_ONE_PKG, true)
- .build();
- mOverlayManager.commit(t2);
-
- assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, true, mUserId);
- assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, true, mUserId);
- List<OverlayInfo> ois2 =
- mOverlayManager.getOverlayInfosForTarget("com.android.overlaytest", mUserHandle);
- assertEquals(ois2.size(), 2);
- assertEquals(ois2.get(0).packageName, APP_OVERLAY_TWO_PKG);
- assertEquals(ois2.get(1).packageName, APP_OVERLAY_ONE_PKG);
-
- OverlayManagerTransaction t3 = new OverlayManagerTransaction.Builder()
- .setEnabled(APP_OVERLAY_TWO_PKG, false)
- .build();
- mOverlayManager.commit(t3);
-
- assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, true, mUserId);
- assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, false, mUserId);
- List<OverlayInfo> ois3 =
- mOverlayManager.getOverlayInfosForTarget("com.android.overlaytest", mUserHandle);
- assertEquals(ois3.size(), 2);
- assertEquals(ois3.get(0).packageName, APP_OVERLAY_TWO_PKG);
- assertEquals(ois3.get(1).packageName, APP_OVERLAY_ONE_PKG);
- }
-
- @Test
- public void testInvalidRequestHasNoEffect() {
- assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, false, mUserId);
- assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, false, mUserId);
-
- OverlayManagerTransaction t = new OverlayManagerTransaction.Builder()
- .setEnabled(APP_OVERLAY_ONE_PKG, true)
- .setEnabled("does-not-exist", true)
- .setEnabled(APP_OVERLAY_TWO_PKG, true)
- .build();
- assertThrows(SecurityException.class, () -> mOverlayManager.commit(t));
-
- assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, false, mUserId);
- assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, false, mUserId);
- }
-
- private void assertOverlayIsEnabled(final String packageName, boolean enabled, int userId) {
- final OverlayInfo oi = mOverlayManager.getOverlayInfo(packageName, UserHandle.of(userId));
- assertNotNull(oi);
- assertEquals(oi.isEnabled(), enabled);
- }
-}
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
index 420f755c5251..d28c47d9c922 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
@@ -22,6 +22,8 @@ import org.junit.BeforeClass;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.concurrent.Executor;
+
@RunWith(JUnit4.class)
@MediumTest
public class WithMultipleOverlaysTest extends OverlayBaseTest {
@@ -31,8 +33,9 @@ public class WithMultipleOverlaysTest extends OverlayBaseTest {
@BeforeClass
public static void enableOverlay() throws Exception {
- LocalOverlayManager.toggleOverlaysAndWait(
- new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG},
- new String[]{});
+ Executor executor = (cmd) -> new Thread(cmd).start();
+ LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, true);
+ LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, true);
+ LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, true);
}
}
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
index a86255e96388..6566ad304c1c 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
@@ -22,6 +22,8 @@ import org.junit.BeforeClass;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.concurrent.Executor;
+
@RunWith(JUnit4.class)
@MediumTest
public class WithOverlayTest extends OverlayBaseTest {
@@ -30,9 +32,10 @@ public class WithOverlayTest extends OverlayBaseTest {
}
@BeforeClass
- public static void enableOverlays() throws Exception {
- LocalOverlayManager.toggleOverlaysAndWait(
- new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG},
- new String[]{APP_OVERLAY_TWO_PKG});
+ public static void enableOverlay() throws Exception {
+ Executor executor = (cmd) -> new Thread(cmd).start();
+ LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, true);
+ LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, false);
+ LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, true);
}
}
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
index 51c411819b87..48cfeab2fbff 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
@@ -22,6 +22,8 @@ import org.junit.BeforeClass;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.concurrent.Executor;
+
@RunWith(JUnit4.class)
@MediumTest
public class WithoutOverlayTest extends OverlayBaseTest {
@@ -31,8 +33,9 @@ public class WithoutOverlayTest extends OverlayBaseTest {
@BeforeClass
public static void disableOverlays() throws Exception {
- LocalOverlayManager.toggleOverlaysAndWait(
- new String[]{},
- new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG});
+ Executor executor = (cmd) -> new Thread(cmd).start();
+ LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, false);
+ LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, false);
+ LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, false);
}
}
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp b/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp
index 847b4915530b..da3aa007135a 100644
--- a/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp
@@ -15,6 +15,6 @@
android_test {
name: "OverlayDeviceTests_AppOverlayOne",
sdk_version: "current",
- certificate: "platform",
+
aaptflags: ["--no-resource-removal"],
}
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp
index 7d5f82a71b44..215b66da36dc 100644
--- a/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp
@@ -15,6 +15,6 @@
android_test {
name: "OverlayDeviceTests_AppOverlayTwo",
sdk_version: "current",
- certificate: "platform",
+
aaptflags: ["--no-resource-removal"],
}
diff --git a/data/etc/OWNERS b/data/etc/OWNERS
index 5efd0bd06b74..9867d810dba2 100644
--- a/data/etc/OWNERS
+++ b/data/etc/OWNERS
@@ -11,3 +11,5 @@ svetoslavganov@google.com
toddke@android.com
toddke@google.com
yamasani@google.com
+
+per-file preinstalled-packages* = file:/MULTIUSER_OWNERS \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index faa4a0ed2294..c9b38d00c0ae 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -262,12 +262,6 @@ public class ShellTaskOrganizer extends TaskOrganizer {
synchronized (mLock) {
ProtoLog.v(WM_SHELL_TASK_ORG, "Task info changed taskId=%d", taskInfo.taskId);
final TaskAppearedInfo data = mTasks.get(taskInfo.taskId);
- if (data == null) {
- // TODO(b/171749427): It means onTaskInfoChanged send before onTaskAppeared or
- // after onTaskVanished, it should be fixed in controller side.
- return;
- }
-
final TaskListener oldListener = getTaskListener(data.getTaskInfo());
final TaskListener newListener = getTaskListener(taskInfo);
mTasks.put(taskInfo.taskId, new TaskAppearedInfo(taskInfo, data.getLeash()));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java
index fa0a75c2d364..4874d3ccae7e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.common;
import android.annotation.NonNull;
import android.os.Handler;
+import android.os.Looper;
/** Executor implementation which is backed by a Handler. */
public class HandlerExecutor implements ShellExecutor {
@@ -49,4 +50,14 @@ public class HandlerExecutor implements ShellExecutor {
public void removeCallbacks(@NonNull Runnable r) {
mHandler.removeCallbacks(r);
}
+
+ @Override
+ public boolean hasCallback(Runnable r) {
+ return mHandler.hasCallbacks(r);
+ }
+
+ @Override
+ public Looper getLooper() {
+ return mHandler.getLooper();
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java
index f2c57f71f5b8..d37e628b9a51 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java
@@ -16,6 +16,8 @@
package com.android.wm.shell.common;
+import android.os.Looper;
+
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
@@ -69,4 +71,14 @@ public interface ShellExecutor extends Executor {
* See {@link android.os.Handler#removeCallbacks}.
*/
void removeCallbacks(Runnable r);
+
+ /**
+ * See {@link android.os.Handler#hasCallbacks(Runnable)}.
+ */
+ boolean hasCallback(Runnable r);
+
+ /**
+ * Returns the looper that this executor is running on.
+ */
+ Looper getLooper();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java
index ad05e6d3e6c4..a8cd1dd4fbde 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java
@@ -18,7 +18,10 @@ package com.android.wm.shell.legacysplitscreen;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
@@ -54,6 +57,17 @@ class WindowManagerProxy {
private static final String TAG = "WindowManagerProxy";
private static final int[] HOME_AND_RECENTS = {ACTIVITY_TYPE_HOME, ACTIVITY_TYPE_RECENTS};
+ private static final int[] CONTROLLED_ACTIVITY_TYPES = {
+ ACTIVITY_TYPE_STANDARD,
+ ACTIVITY_TYPE_HOME,
+ ACTIVITY_TYPE_RECENTS,
+ ACTIVITY_TYPE_UNDEFINED
+ };
+ private static final int[] CONTROLLED_WINDOWING_MODES = {
+ WINDOWING_MODE_FULLSCREEN,
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
+ WINDOWING_MODE_UNDEFINED
+ };
@GuardedBy("mDockedRect")
private final Rect mDockedRect = new Rect();
@@ -191,8 +205,9 @@ class WindowManagerProxy {
// Set launchtile first so that any stack created after
// getAllRootTaskInfos and before reparent (even if unlikely) are placed
// correctly.
- mTaskOrganizer.setLaunchRoot(DEFAULT_DISPLAY, tiles.mSecondary.token);
WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setLaunchRoot(tiles.mSecondary.token, CONTROLLED_WINDOWING_MODES,
+ CONTROLLED_ACTIVITY_TYPES);
final boolean isHomeResizable = buildEnterSplit(wct, tiles, layout);
applySyncTransaction(wct);
return isHomeResizable;
@@ -251,12 +266,12 @@ class WindowManagerProxy {
/** @see #buildDismissSplit */
void applyDismissSplit(LegacySplitScreenTaskListener tiles, LegacySplitDisplayLayout layout,
boolean dismissOrMaximize) {
- // Set launch root first so that any task created after getChildContainers and
- // before reparent (pretty unlikely) are put into fullscreen.
- mTaskOrganizer.setLaunchRoot(Display.DEFAULT_DISPLAY, null);
// TODO(task-org): Once task-org is more complete, consider using Appeared/Vanished
// plus specific APIs to clean this up.
final WindowContainerTransaction wct = new WindowContainerTransaction();
+ // Set launch root first so that any task created after getChildContainers and
+ // before reparent (pretty unlikely) are put into fullscreen.
+ wct.setLaunchRoot(tiles.mSecondary.token, null, null);
buildDismissSplit(wct, tiles, layout, dismissOrMaximize);
applySyncTransaction(wct);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java
index 146f231a8854..d22abe4dd19b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java
@@ -127,7 +127,6 @@ public class OneHandedAnimationController {
mSurfaceControlTransactionFactory;
private @TransitionDirection int mTransitionDirection;
- private int mTransitionOffset;
private OneHandedTransitionAnimator(SurfaceControl leash, T startValue, T endValue) {
mLeash = leash;
@@ -231,11 +230,6 @@ public class OneHandedAnimationController {
return this;
}
- OneHandedTransitionAnimator<T> setTransitionOffset(int offset) {
- mTransitionOffset = offset;
- return this;
- }
-
T getStartValue() {
return mStartValue;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index 00605d872e39..48d6a7b40ee1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -26,7 +26,6 @@ import android.content.om.OverlayInfo;
import android.database.ContentObserver;
import android.graphics.Point;
import android.os.Handler;
-import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
@@ -41,8 +40,10 @@ import androidx.annotation.VisibleForTesting;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayChangeController;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.onehanded.OneHandedGestureHandler.OneHandedGestureEventCallback;
import java.io.PrintWriter;
@@ -51,7 +52,7 @@ import java.util.concurrent.Executor;
/**
* Manages and manipulates the one handed states, transitions, and gesture for phones.
*/
-public class OneHandedController implements OneHanded {
+public class OneHandedController {
private static final String TAG = "OneHandedController";
private static final String ONE_HANDED_MODE_OFFSET_PERCENTAGE =
@@ -61,8 +62,8 @@ public class OneHandedController implements OneHanded {
static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
- private boolean mIsOneHandedEnabled;
- private boolean mIsSwipeToNotificationEnabled;
+ private volatile boolean mIsOneHandedEnabled;
+ private volatile boolean mIsSwipeToNotificationEnabled;
private boolean mTaskChangeToExit;
private float mOffSetFraction;
@@ -73,7 +74,9 @@ public class OneHandedController implements OneHanded {
private final OneHandedTouchHandler mTouchHandler;
private final OneHandedTutorialHandler mTutorialHandler;
private final IOverlayManager mOverlayManager;
- private final Handler mMainHandler = new Handler(Looper.getMainLooper());
+ private final ShellExecutor mMainExecutor;
+ private final Handler mMainHandler;
+ private final OneHandedImpl mImpl = new OneHandedImpl();
private OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer;
private final AccessibilityManager mAccessibilityManager;
@@ -89,83 +92,10 @@ public class OneHandedController implements OneHanded {
}
};
- private final ContentObserver mEnabledObserver = new ContentObserver(mMainHandler) {
- @Override
- public void onChange(boolean selfChange) {
- final boolean enabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
- mContext.getContentResolver());
- OneHandedEvents.writeEvent(enabled
- ? OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_ENABLED_ON
- : OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_ENABLED_OFF);
-
- setOneHandedEnabled(enabled);
-
- // Also checks swipe to notification settings since they all need gesture overlay.
- setEnabledGesturalOverlay(
- enabled || OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
- mContext.getContentResolver()));
- }
- };
-
- private final ContentObserver mTimeoutObserver = new ContentObserver(mMainHandler) {
- @Override
- public void onChange(boolean selfChange) {
- final int newTimeout = OneHandedSettingsUtil.getSettingsOneHandedModeTimeout(
- mContext.getContentResolver());
- int metricsId = OneHandedEvents.OneHandedSettingsTogglesEvent.INVALID.getId();
- switch (newTimeout) {
- case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER:
- metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_NEVER;
- break;
- case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS:
- metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_4;
- break;
- case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS:
- metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_8;
- break;
- case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_LONG_IN_SECONDS:
- metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_12;
- break;
- default:
- // do nothing
- break;
- }
- OneHandedEvents.writeEvent(metricsId);
-
- if (mTimeoutHandler != null) {
- mTimeoutHandler.setTimeout(newTimeout);
- }
- }
- };
-
- private final ContentObserver mTaskChangeExitObserver = new ContentObserver(mMainHandler) {
- @Override
- public void onChange(boolean selfChange) {
- final boolean enabled = OneHandedSettingsUtil.getSettingsTapsAppToExit(
- mContext.getContentResolver());
- OneHandedEvents.writeEvent(enabled
- ? OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_ON
- : OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_OFF);
-
- setTaskChangeToExit(enabled);
- }
- };
-
- private final ContentObserver mSwipeToNotificationEnabledObserver =
- new ContentObserver(mMainHandler) {
- @Override
- public void onChange(boolean selfChange) {
- final boolean enabled =
- OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
- mContext.getContentResolver());
- setSwipeToNotificationEnabled(enabled);
-
- // Also checks one handed mode settings since they all need gesture overlay.
- setEnabledGesturalOverlay(
- enabled || OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
- mContext.getContentResolver()));
- }
- };
+ private final ContentObserver mEnabledObserver;
+ private final ContentObserver mTimeoutObserver;
+ private final ContentObserver mTaskChangeExitObserver;
+ private final ContentObserver mSwipeToNotificationEnabledObserver;
private AccessibilityManager.AccessibilityStateChangeListener
mAccessibilityStateChangeListener =
@@ -188,33 +118,39 @@ public class OneHandedController implements OneHanded {
};
/**
- * Creates {@link OneHandedController}, returns {@code null} if the feature is not supported.
+ * Creates {@link OneHanded}, returns {@code null} if the feature is not supported.
*/
@Nullable
- public static OneHandedController create(
+ public static OneHanded create(
Context context, DisplayController displayController,
- TaskStackListenerImpl taskStackListener, Executor executor) {
+ TaskStackListenerImpl taskStackListener,
+ ShellExecutor mainExecutor,
+ Handler mainHandler) {
if (!SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) {
Slog.w(TAG, "Device doesn't support OneHanded feature");
return null;
}
- OneHandedTutorialHandler tutorialHandler = new OneHandedTutorialHandler(context);
+ OneHandedTimeoutHandler timeoutHandler = new OneHandedTimeoutHandler(mainExecutor);
+ OneHandedTutorialHandler tutorialHandler = new OneHandedTutorialHandler(context,
+ mainExecutor);
OneHandedAnimationController animationController =
new OneHandedAnimationController(context);
- OneHandedTouchHandler touchHandler = new OneHandedTouchHandler();
+ OneHandedTouchHandler touchHandler = new OneHandedTouchHandler(timeoutHandler,
+ mainExecutor);
OneHandedGestureHandler gestureHandler = new OneHandedGestureHandler(
- context, displayController);
+ context, displayController, mainExecutor);
OneHandedBackgroundPanelOrganizer oneHandedBackgroundPanelOrganizer =
- new OneHandedBackgroundPanelOrganizer(context, displayController, executor);
+ new OneHandedBackgroundPanelOrganizer(context, displayController, mainExecutor);
OneHandedDisplayAreaOrganizer organizer = new OneHandedDisplayAreaOrganizer(
- context, displayController, animationController, tutorialHandler, executor,
- oneHandedBackgroundPanelOrganizer);
+ context, displayController, animationController, tutorialHandler,
+ oneHandedBackgroundPanelOrganizer, mainExecutor);
IOverlayManager overlayManager = IOverlayManager.Stub.asInterface(
ServiceManager.getService(Context.OVERLAY_SERVICE));
return new OneHandedController(context, displayController,
oneHandedBackgroundPanelOrganizer, organizer, touchHandler, tutorialHandler,
- gestureHandler, overlayManager, taskStackListener);
+ gestureHandler, timeoutHandler, overlayManager, taskStackListener, mainExecutor,
+ mainHandler).mImpl;
}
@VisibleForTesting
@@ -225,8 +161,11 @@ public class OneHandedController implements OneHanded {
OneHandedTouchHandler touchHandler,
OneHandedTutorialHandler tutorialHandler,
OneHandedGestureHandler gestureHandler,
+ OneHandedTimeoutHandler timeoutHandler,
IOverlayManager overlayManager,
- TaskStackListenerImpl taskStackListener) {
+ TaskStackListenerImpl taskStackListener,
+ ShellExecutor mainExecutor,
+ Handler mainHandler) {
mContext = context;
mBackgroundPanelOrganizer = backgroundPanelOrganizer;
mDisplayAreaOrganizer = displayAreaOrganizer;
@@ -235,6 +174,8 @@ public class OneHandedController implements OneHanded {
mTutorialHandler = tutorialHandler;
mGestureHandler = gestureHandler;
mOverlayManager = overlayManager;
+ mMainExecutor = mainExecutor;
+ mMainHandler = mainHandler;
final float offsetPercentageConfig = context.getResources().getFraction(
R.fraction.config_one_handed_offset, 1, 1);
@@ -246,7 +187,13 @@ public class OneHandedController implements OneHanded {
mIsSwipeToNotificationEnabled =
OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
context.getContentResolver());
- mTimeoutHandler = OneHandedTimeoutHandler.get();
+ mTimeoutHandler = timeoutHandler;
+
+ mEnabledObserver = getObserver(this::onEnabledSettingChanged);
+ mTimeoutObserver = getObserver(this::onTimeoutSettingChanged);
+ mTaskChangeExitObserver = getObserver(this::onTaskChangeExitSettingChanged);
+ mSwipeToNotificationEnabledObserver =
+ getObserver(this::onSwipeToNotificationEnabledSettingChanged);
mDisplayController.addDisplayChangingController(mRotationController);
@@ -298,18 +245,8 @@ public class OneHandedController implements OneHanded {
updateOneHandedEnabled();
}
- @Override
- public boolean isOneHandedEnabled() {
- return mIsOneHandedEnabled;
- }
-
- @Override
- public boolean isSwipeToNotificationEnabled() {
- return mIsSwipeToNotificationEnabled;
- }
-
- @Override
- public void startOneHanded() {
+ @VisibleForTesting
+ void startOneHanded() {
if (!mDisplayAreaOrganizer.isInOneHanded()) {
final int yOffSet = Math.round(getDisplaySize().y * mOffSetFraction);
mDisplayAreaOrganizer.scheduleOffset(0, yOffSet);
@@ -319,16 +256,15 @@ public class OneHandedController implements OneHanded {
}
}
- @Override
- public void stopOneHanded() {
+ @VisibleForTesting
+ void stopOneHanded() {
if (mDisplayAreaOrganizer.isInOneHanded()) {
mDisplayAreaOrganizer.scheduleOffset(0, 0);
mTimeoutHandler.removeTimer();
}
}
- @Override
- public void stopOneHanded(int event) {
+ private void stopOneHanded(int event) {
if (!mTaskChangeToExit && event == OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_APP_TAPS_OUT) {
//Task change exit not enable, do nothing and return here.
return;
@@ -341,18 +277,16 @@ public class OneHandedController implements OneHanded {
stopOneHanded();
}
- @Override
- public void setThreeButtonModeEnabled(boolean enabled) {
+ private void setThreeButtonModeEnabled(boolean enabled) {
mGestureHandler.onThreeButtonModeEnabled(enabled);
}
- @Override
- public void registerTransitionCallback(OneHandedTransitionCallback callback) {
+ @VisibleForTesting
+ void registerTransitionCallback(OneHandedTransitionCallback callback) {
mDisplayAreaOrganizer.registerTransitionCallback(callback);
}
- @Override
- public void registerGestureCallback(OneHandedGestureEventCallback callback) {
+ private void registerGestureCallback(OneHandedGestureEventCallback callback) {
mGestureHandler.setGestureEventListener(callback);
}
@@ -388,6 +322,80 @@ public class OneHandedController implements OneHanded {
.getSettingsSwipeToNotificationEnabled(mContext.getContentResolver()));
}
+ private ContentObserver getObserver(Runnable onChangeRunnable) {
+ return new ContentObserver(mMainHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ onChangeRunnable.run();
+ }
+ };
+ }
+
+ private void onEnabledSettingChanged() {
+ final boolean enabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
+ mContext.getContentResolver());
+ OneHandedEvents.writeEvent(enabled
+ ? OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_ENABLED_ON
+ : OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_ENABLED_OFF);
+
+ setOneHandedEnabled(enabled);
+
+ // Also checks swipe to notification settings since they all need gesture overlay.
+ setEnabledGesturalOverlay(
+ enabled || OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
+ mContext.getContentResolver()));
+ }
+
+ private void onTimeoutSettingChanged() {
+ final int newTimeout = OneHandedSettingsUtil.getSettingsOneHandedModeTimeout(
+ mContext.getContentResolver());
+ int metricsId = OneHandedEvents.OneHandedSettingsTogglesEvent.INVALID.getId();
+ switch (newTimeout) {
+ case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER:
+ metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_NEVER;
+ break;
+ case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS:
+ metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_4;
+ break;
+ case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS:
+ metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_8;
+ break;
+ case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_LONG_IN_SECONDS:
+ metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_12;
+ break;
+ default:
+ // do nothing
+ break;
+ }
+ OneHandedEvents.writeEvent(metricsId);
+
+ if (mTimeoutHandler != null) {
+ mTimeoutHandler.setTimeout(newTimeout);
+ }
+ }
+
+ private void onTaskChangeExitSettingChanged() {
+ final boolean enabled = OneHandedSettingsUtil.getSettingsTapsAppToExit(
+ mContext.getContentResolver());
+ OneHandedEvents.writeEvent(enabled
+ ? OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_ON
+ : OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_OFF);
+
+ setTaskChangeToExit(enabled);
+ }
+
+ private void onSwipeToNotificationEnabledSettingChanged() {
+ final boolean enabled =
+ OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
+ mContext.getContentResolver());
+ setSwipeToNotificationEnabled(enabled);
+
+ // Also checks one handed mode settings since they all need gesture overlay.
+ setEnabledGesturalOverlay(
+ enabled || OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
+ mContext.getContentResolver()));
+ }
+
private void setupTimeoutListener() {
mTimeoutHandler.registerTimeoutListener(timeoutTime -> {
stopOneHanded(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_TIMEOUT_OUT);
@@ -451,8 +459,7 @@ public class OneHandedController implements OneHanded {
}
}
- @Override
- public void dump(@NonNull PrintWriter pw) {
+ private void dump(@NonNull PrintWriter pw) {
final String innerPrefix = " ";
pw.println(TAG + "states: ");
pw.print(innerPrefix + "mOffSetFraction=");
@@ -489,4 +496,68 @@ public class OneHandedController implements OneHanded {
}
}
}
+
+ @ExternalThread
+ private class OneHandedImpl implements OneHanded {
+ @Override
+ public boolean isOneHandedEnabled() {
+ // This is volatile so return directly
+ return mIsOneHandedEnabled;
+ }
+
+ @Override
+ public boolean isSwipeToNotificationEnabled() {
+ // This is volatile so return directly
+ return mIsSwipeToNotificationEnabled;
+ }
+
+ @Override
+ public void startOneHanded() {
+ mMainExecutor.execute(() -> {
+ OneHandedController.this.startOneHanded();
+ });
+ }
+
+ @Override
+ public void stopOneHanded() {
+ mMainExecutor.execute(() -> {
+ OneHandedController.this.stopOneHanded();
+ });
+ }
+
+ @Override
+ public void stopOneHanded(int event) {
+ mMainExecutor.execute(() -> {
+ OneHandedController.this.stopOneHanded(event);
+ });
+ }
+
+ @Override
+ public void setThreeButtonModeEnabled(boolean enabled) {
+ mMainExecutor.execute(() -> {
+ OneHandedController.this.setThreeButtonModeEnabled(enabled);
+ });
+ }
+
+ @Override
+ public void registerTransitionCallback(OneHandedTransitionCallback callback) {
+ mMainExecutor.execute(() -> {
+ OneHandedController.this.registerTransitionCallback(callback);
+ });
+ }
+
+ @Override
+ public void registerGestureCallback(OneHandedGestureEventCallback callback) {
+ mMainExecutor.execute(() -> {
+ OneHandedController.this.registerGestureCallback(callback);
+ });
+ }
+
+ @Override
+ public void dump(@NonNull PrintWriter pw) {
+ mMainExecutor.execute(() -> {
+ OneHandedController.this.dump(pw);
+ });
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
index 7873318fc82d..d2d5591100d4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -24,8 +24,6 @@ import static com.android.wm.shell.onehanded.OneHandedAnimationController.TRANSI
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
-import android.os.Handler;
-import android.os.Looper;
import android.os.SystemProperties;
import android.util.ArrayMap;
import android.util.Log;
@@ -39,9 +37,9 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
-import com.android.internal.os.SomeArgs;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.ShellExecutor;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -64,17 +62,9 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
private static final String ONE_HANDED_MODE_TRANSLATE_ANIMATION_DURATION =
"persist.debug.one_handed_translate_animation_duration";
- @VisibleForTesting
- static final int MSG_RESET_IMMEDIATE = 1;
- @VisibleForTesting
- static final int MSG_OFFSET_ANIMATE = 2;
- @VisibleForTesting
- static final int MSG_OFFSET_FINISH = 3;
-
private final Rect mLastVisualDisplayBounds = new Rect();
private final Rect mDefaultDisplayBounds = new Rect();
- private Handler mUpdateHandler;
private boolean mIsInOneHanded;
private int mEnterExitAnimationDurationMs;
@@ -101,8 +91,8 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
OneHandedAnimationController.OneHandedTransitionAnimator animator) {
mAnimationController.removeAnimator(animator.getLeash());
if (mAnimationController.isAnimatorsConsumed()) {
- mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_FINISH,
- obtainArgsFromAnimator(animator)));
+ finishOffset(animator.getDestinationOffset(),
+ animator.getTransitionDirection());
}
}
@@ -111,52 +101,22 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
OneHandedAnimationController.OneHandedTransitionAnimator animator) {
mAnimationController.removeAnimator(animator.getLeash());
if (mAnimationController.isAnimatorsConsumed()) {
- mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_FINISH,
- obtainArgsFromAnimator(animator)));
+ finishOffset(animator.getDestinationOffset(),
+ animator.getTransitionDirection());
}
}
};
- @SuppressWarnings("unchecked")
- private Handler.Callback mUpdateCallback = (msg) -> {
- SomeArgs args = (SomeArgs) msg.obj;
- final Rect currentBounds = args.arg1 != null ? (Rect) args.arg1 : mDefaultDisplayBounds;
- final WindowContainerTransaction wctFromRotate = (WindowContainerTransaction) args.arg2;
- final int yOffset = args.argi2;
- final int direction = args.argi3;
-
- switch (msg.what) {
- case MSG_RESET_IMMEDIATE:
- resetWindowsOffset(wctFromRotate);
- mDefaultDisplayBounds.set(currentBounds);
- mLastVisualDisplayBounds.set(currentBounds);
- finishOffset(0, TRANSITION_DIRECTION_EXIT);
- break;
- case MSG_OFFSET_ANIMATE:
- final Rect toBounds = new Rect(mDefaultDisplayBounds.left,
- mDefaultDisplayBounds.top + yOffset,
- mDefaultDisplayBounds.right,
- mDefaultDisplayBounds.bottom + yOffset);
- offsetWindows(currentBounds, toBounds, direction, mEnterExitAnimationDurationMs);
- break;
- case MSG_OFFSET_FINISH:
- finishOffset(yOffset, direction);
- break;
- }
- args.recycle();
- return true;
- };
-
/**
* Constructor of OneHandedDisplayAreaOrganizer
*/
public OneHandedDisplayAreaOrganizer(Context context,
DisplayController displayController,
OneHandedAnimationController animationController,
- OneHandedTutorialHandler tutorialHandler, Executor executor,
- OneHandedBackgroundPanelOrganizer oneHandedBackgroundGradientOrganizer) {
- super(executor);
- mUpdateHandler = new Handler(OneHandedThread.get().getLooper(), mUpdateCallback);
+ OneHandedTutorialHandler tutorialHandler,
+ OneHandedBackgroundPanelOrganizer oneHandedBackgroundGradientOrganizer,
+ ShellExecutor mainExecutor) {
+ super(mainExecutor);
mAnimationController = animationController;
mDisplayController = displayController;
mDefaultDisplayBounds.set(getDisplayBounds());
@@ -176,12 +136,10 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
@NonNull SurfaceControl leash) {
Objects.requireNonNull(displayAreaInfo, "displayAreaInfo must not be null");
Objects.requireNonNull(leash, "leash must not be null");
- synchronized (this) {
- if (mDisplayAreaMap.get(displayAreaInfo) == null) {
- // mDefaultDisplayBounds may out of date after removeDisplayChangingController()
- mDefaultDisplayBounds.set(getDisplayBounds());
- mDisplayAreaMap.put(displayAreaInfo, leash);
- }
+ if (mDisplayAreaMap.get(displayAreaInfo) == null) {
+ // mDefaultDisplayBounds may out of date after removeDisplayChangingController()
+ mDefaultDisplayBounds.set(getDisplayBounds());
+ mDisplayAreaMap.put(displayAreaInfo, leash);
}
}
@@ -189,13 +147,11 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
public void onDisplayAreaVanished(@NonNull DisplayAreaInfo displayAreaInfo) {
Objects.requireNonNull(displayAreaInfo,
"Requires valid displayArea, and displayArea must not be null");
- synchronized (this) {
- if (!mDisplayAreaMap.containsKey(displayAreaInfo)) {
- Log.w(TAG, "Unrecognized token: " + displayAreaInfo.token);
- return;
- }
- mDisplayAreaMap.remove(displayAreaInfo);
+ if (!mDisplayAreaMap.containsKey(displayAreaInfo)) {
+ Log.w(TAG, "Unrecognized token: " + displayAreaInfo.token);
+ return;
}
+ mDisplayAreaMap.remove(displayAreaInfo);
}
@Override
@@ -212,7 +168,7 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
@Override
public void unregisterOrganizer() {
super.unregisterOrganizer();
- mUpdateHandler.post(() -> resetWindowsOffset(null));
+ resetWindowsOffset(null);
}
/**
@@ -230,14 +186,10 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
final boolean isOrientationDiff = Math.abs(fromRotation - toRotation) % 2 == 1;
if (isOrientationDiff) {
- newBounds.set(newBounds.left, newBounds.top, newBounds.bottom, newBounds.right);
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = newBounds;
- args.arg2 = wct;
- args.argi1 = 0 /* xOffset */;
- args.argi2 = 0 /* yOffset */;
- args.argi3 = TRANSITION_DIRECTION_EXIT;
- mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESET_IMMEDIATE, args));
+ resetWindowsOffset(wct);
+ mDefaultDisplayBounds.set(newBounds);
+ mLastVisualDisplayBounds.set(newBounds);
+ finishOffset(0, TRANSITION_DIRECTION_EXIT);
}
}
@@ -246,79 +198,65 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
* Directly perform manipulation/offset on the leash.
*/
public void scheduleOffset(int xOffset, int yOffset) {
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = getLastVisualDisplayBounds();
- args.argi1 = xOffset;
- args.argi2 = yOffset;
- args.argi3 = yOffset > 0 ? TRANSITION_DIRECTION_TRIGGER : TRANSITION_DIRECTION_EXIT;
- mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_ANIMATE, args));
- }
-
- private void offsetWindows(Rect fromBounds, Rect toBounds, int direction, int durationMs) {
- if (Looper.myLooper() != mUpdateHandler.getLooper()) {
- throw new RuntimeException("Callers should call scheduleOffset() instead of this "
- + "directly");
- }
- synchronized (this) {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- mDisplayAreaMap.forEach(
- (key, leash) -> {
- animateWindows(leash, fromBounds, toBounds, direction, durationMs);
- wct.setBounds(key.token, toBounds);
- });
- applyTransaction(wct);
- }
+ final Rect toBounds = new Rect(mDefaultDisplayBounds.left,
+ mDefaultDisplayBounds.top + yOffset,
+ mDefaultDisplayBounds.right,
+ mDefaultDisplayBounds.bottom + yOffset);
+ final Rect fromBounds = getLastVisualDisplayBounds() != null
+ ? getLastVisualDisplayBounds()
+ : mDefaultDisplayBounds;
+ final int direction = yOffset > 0
+ ? TRANSITION_DIRECTION_TRIGGER
+ : TRANSITION_DIRECTION_EXIT;
+
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ mDisplayAreaMap.forEach(
+ (key, leash) -> {
+ animateWindows(leash, fromBounds, toBounds, direction,
+ mEnterExitAnimationDurationMs);
+ wct.setBounds(key.token, toBounds);
+ });
+ applyTransaction(wct);
}
private void resetWindowsOffset(WindowContainerTransaction wct) {
- synchronized (this) {
- final SurfaceControl.Transaction tx =
- mSurfaceControlTransactionFactory.getTransaction();
- mDisplayAreaMap.forEach(
- (key, leash) -> {
- final OneHandedAnimationController.OneHandedTransitionAnimator animator =
- mAnimationController.getAnimatorMap().remove(leash);
- if (animator != null && animator.isRunning()) {
- animator.cancel();
- }
- tx.setPosition(leash, 0, 0)
- .setWindowCrop(leash, -1/* reset */, -1/* reset */);
- // DisplayRotationController will applyTransaction() after finish rotating
- if (wct != null) {
- wct.setBounds(key.token, null/* reset */);
- }
- });
- tx.apply();
- }
+ final SurfaceControl.Transaction tx =
+ mSurfaceControlTransactionFactory.getTransaction();
+ mDisplayAreaMap.forEach(
+ (key, leash) -> {
+ final OneHandedAnimationController.OneHandedTransitionAnimator animator =
+ mAnimationController.getAnimatorMap().remove(leash);
+ if (animator != null && animator.isRunning()) {
+ animator.cancel();
+ }
+ tx.setPosition(leash, 0, 0)
+ .setWindowCrop(leash, -1/* reset */, -1/* reset */);
+ // DisplayRotationController will applyTransaction() after finish rotating
+ if (wct != null) {
+ wct.setBounds(key.token, null/* reset */);
+ }
+ });
+ tx.apply();
}
private void animateWindows(SurfaceControl leash, Rect fromBounds, Rect toBounds,
@OneHandedAnimationController.TransitionDirection int direction, int durationMs) {
- if (Looper.myLooper() != mUpdateHandler.getLooper()) {
- throw new RuntimeException("Callers should call scheduleOffset() instead of "
- + "this directly");
+ final OneHandedAnimationController.OneHandedTransitionAnimator animator =
+ mAnimationController.getAnimator(leash, fromBounds, toBounds);
+ if (animator != null) {
+ animator.setTransitionDirection(direction)
+ .addOneHandedAnimationCallback(mOneHandedAnimationCallback)
+ .addOneHandedAnimationCallback(mTutorialHandler.getAnimationCallback())
+ .addOneHandedAnimationCallback(
+ mBackgroundPanelOrganizer.getOneHandedAnimationCallback())
+ .setDuration(durationMs)
+ .start();
}
- mUpdateHandler.post(() -> {
- final OneHandedAnimationController.OneHandedTransitionAnimator animator =
- mAnimationController.getAnimator(leash, fromBounds, toBounds);
- if (animator != null) {
- animator.setTransitionDirection(direction)
- .addOneHandedAnimationCallback(mOneHandedAnimationCallback)
- .addOneHandedAnimationCallback(mTutorialHandler.getAnimationCallback())
- .addOneHandedAnimationCallback(
- mBackgroundPanelOrganizer.getOneHandedAnimationCallback())
- .setDuration(durationMs)
- .start();
- }
- });
}
- private void finishOffset(int offset,
+ @VisibleForTesting
+ void finishOffset(int offset,
@OneHandedAnimationController.TransitionDirection int direction) {
- if (Looper.myLooper() != mUpdateHandler.getLooper()) {
- throw new RuntimeException(
- "Callers should call scheduleOffset() instead of this directly.");
- }
// Only finishOffset() can update mIsInOneHanded to ensure the state is handle in sequence,
// the flag *MUST* be updated before dispatch mTransitionCallbacks
mIsInOneHanded = (offset > 0 || direction == TRANSITION_DIRECTION_TRIGGER);
@@ -361,11 +299,6 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
return new Rect(0, 0, realSize.x, realSize.y);
}
- @VisibleForTesting
- void setUpdateHandler(Handler updateHandler) {
- mUpdateHandler = updateHandler;
- }
-
/**
* Register transition callback
*/
@@ -373,16 +306,6 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
mTransitionCallbacks.add(callback);
}
- private SomeArgs obtainArgsFromAnimator(
- OneHandedAnimationController.OneHandedTransitionAnimator animator) {
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = animator.getDestinationBounds();
- args.argi1 = 0 /* xOffset */;
- args.argi2 = animator.getDestinationOffset();
- args.argi3 = animator.getTransitionDirection();
- return args;
- }
-
void dump(@NonNull PrintWriter pw) {
final String innerPrefix = " ";
pw.println(TAG + "states: ");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java
index aa4ec1788a88..1ed121f35a59 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java
@@ -41,6 +41,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayChangeController;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.ShellExecutor;
/**
* The class manage swipe up and down gesture for 3-Button mode navigation,
@@ -70,7 +71,8 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback,
InputMonitor mInputMonitor;
@VisibleForTesting
InputEventReceiver mInputEventReceiver;
- private DisplayController mDisplayController;
+ private final DisplayController mDisplayController;
+ private final ShellExecutor mMainExecutor;
@VisibleForTesting
@Nullable
OneHandedGestureEventCallback mGestureEventCallback;
@@ -84,8 +86,10 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback,
* @param context {@link Context}
* @param displayController {@link DisplayController}
*/
- public OneHandedGestureHandler(Context context, DisplayController displayController) {
+ public OneHandedGestureHandler(Context context, DisplayController displayController,
+ ShellExecutor mainExecutor) {
mDisplayController = displayController;
+ mMainExecutor = mainExecutor;
displayController.addDisplayChangingController(this);
mNavGestureHeight = context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.navigation_bar_gesture_larger_height);
@@ -93,6 +97,7 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback,
R.dimen.gestures_onehanded_drag_threshold);
final float slop = ViewConfiguration.get(context).getScaledTouchSlop();
mSquaredSlop = slop * slop;
+
updateIsEnabled();
}
@@ -217,7 +222,7 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback,
mInputMonitor = InputManager.getInstance().monitorGestureInput(
"onehanded-gesture-offset", DEFAULT_DISPLAY);
mInputEventReceiver = new EventReceiver(
- mInputMonitor.getInputChannel(), Looper.getMainLooper());
+ mInputMonitor.getInputChannel(), mMainExecutor.getLooper());
}
}
@@ -233,6 +238,7 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback,
mRotation = toRotation;
}
+ // TODO: Use BatchedInputEventReceiver
private class EventReceiver extends InputEventReceiver {
EventReceiver(InputChannel channel, Looper looper) {
super(channel, looper);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedThread.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedThread.java
deleted file mode 100644
index 24d33ede5d63..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedThread.java
+++ /dev/null
@@ -1,62 +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.wm.shell.onehanded;
-
-import android.os.Handler;
-import android.os.HandlerThread;
-
-/**
- * Similar to {@link com.android.internal.os.BackgroundThread}, this is a shared singleton
- * foreground thread for each process for updating one handed.
- */
-public class OneHandedThread extends HandlerThread {
- private static OneHandedThread sInstance;
- private static Handler sHandler;
-
- private OneHandedThread() {
- super("OneHanded");
- }
-
- private static void ensureThreadLocked() {
- if (sInstance == null) {
- sInstance = new OneHandedThread();
- sInstance.start();
- sHandler = new Handler(sInstance.getLooper());
- }
- }
-
- /**
- * @return the static update thread instance
- */
- public static OneHandedThread get() {
- synchronized (OneHandedThread.class) {
- ensureThreadLocked();
- return sInstance;
- }
- }
-
- /**
- * @return the static update thread handler instance
- */
- public static Handler getHandler() {
- synchronized (OneHandedThread.class) {
- ensureThreadLocked();
- return sHandler;
- }
- }
-
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandler.java
index 9c97cd7db71f..4a98941b7410 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandler.java
@@ -19,12 +19,12 @@ package com.android.wm.shell.onehanded;
import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS;
import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
+import com.android.wm.shell.common.ShellExecutor;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -35,18 +35,15 @@ import java.util.concurrent.TimeUnit;
*/
public class OneHandedTimeoutHandler {
private static final String TAG = "OneHandedTimeoutHandler";
- private static boolean sIsDragging = false;
+
+ private final ShellExecutor mMainExecutor;
+
// Default timeout is ONE_HANDED_TIMEOUT_MEDIUM
- private static @OneHandedSettingsUtil.OneHandedTimeout int sTimeout =
+ private @OneHandedSettingsUtil.OneHandedTimeout int mTimeout =
ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS;
- private static long sTimeoutMs = TimeUnit.SECONDS.toMillis(sTimeout);
- private static OneHandedTimeoutHandler sInstance;
- private static List<TimeoutListener> sListeners = new ArrayList<>();
-
- @VisibleForTesting
- static final int ONE_HANDED_TIMEOUT_STOP_MSG = 1;
- @VisibleForTesting
- static Handler sHandler;
+ private long mTimeoutMs = TimeUnit.SECONDS.toMillis(mTimeout);
+ private final Runnable mTimeoutRunnable = this::onStop;
+ private List<TimeoutListener> mListeners = new ArrayList<>();
/**
* Get the current config of timeout
@@ -54,7 +51,7 @@ public class OneHandedTimeoutHandler {
* @return timeout of current config
*/
public @OneHandedSettingsUtil.OneHandedTimeout int getTimeout() {
- return sTimeout;
+ return mTimeout;
}
/**
@@ -69,32 +66,36 @@ public class OneHandedTimeoutHandler {
void onTimeout(int timeoutTime);
}
+ public OneHandedTimeoutHandler(ShellExecutor mainExecutor) {
+ mMainExecutor = mainExecutor;
+ }
+
/**
* Set the specific timeout of {@link OneHandedSettingsUtil.OneHandedTimeout}
*/
- public static void setTimeout(@OneHandedSettingsUtil.OneHandedTimeout int timeout) {
- sTimeout = timeout;
- sTimeoutMs = TimeUnit.SECONDS.toMillis(sTimeout);
+ public void setTimeout(@OneHandedSettingsUtil.OneHandedTimeout int timeout) {
+ mTimeout = timeout;
+ mTimeoutMs = TimeUnit.SECONDS.toMillis(mTimeout);
resetTimer();
}
/**
* Reset the timer when one handed trigger or user is operating in some conditions
*/
- public static void removeTimer() {
- sHandler.removeMessages(ONE_HANDED_TIMEOUT_STOP_MSG);
+ public void removeTimer() {
+ mMainExecutor.removeCallbacks(mTimeoutRunnable);
}
/**
* Reset the timer when one handed trigger or user is operating in some conditions
*/
- public static void resetTimer() {
+ public void resetTimer() {
removeTimer();
- if (sTimeout == OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER) {
+ if (mTimeout == OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER) {
return;
}
- if (sTimeout != OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER) {
- sHandler.sendEmptyMessageDelayed(ONE_HANDED_TIMEOUT_STOP_MSG, sTimeoutMs);
+ if (mTimeout != OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER) {
+ mMainExecutor.executeDelayed(mTimeoutRunnable, mTimeoutMs);
}
}
@@ -103,47 +104,19 @@ public class OneHandedTimeoutHandler {
*
* @param listener the listener be sent events when times up
*/
- public static void registerTimeoutListener(TimeoutListener listener) {
- sListeners.add(listener);
+ public void registerTimeoutListener(TimeoutListener listener) {
+ mListeners.add(listener);
}
- /**
- * Private constructor due to Singleton pattern
- */
- private OneHandedTimeoutHandler() {
- }
-
- /**
- * Singleton pattern to get {@link OneHandedTimeoutHandler} instance
- *
- * @return the static update thread instance
- */
- public static OneHandedTimeoutHandler get() {
- synchronized (OneHandedTimeoutHandler.class) {
- if (sInstance == null) {
- sInstance = new OneHandedTimeoutHandler();
- }
- if (sHandler == null) {
- sHandler = new Handler(Looper.myLooper()) {
- @Override
- public void handleMessage(Message msg) {
- if (msg.what == ONE_HANDED_TIMEOUT_STOP_MSG) {
- onStop();
- }
- }
- };
- if (sTimeout != OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER) {
- sHandler.sendEmptyMessageDelayed(ONE_HANDED_TIMEOUT_STOP_MSG, sTimeoutMs);
- }
- }
- return sInstance;
- }
+ @VisibleForTesting
+ boolean hasScheduledTimeout() {
+ return mMainExecutor.hasCallback(mTimeoutRunnable);
}
- private static void onStop() {
- for (int i = sListeners.size() - 1; i >= 0; i--) {
- final TimeoutListener listener = sListeners.get(i);
- listener.onTimeout(sTimeout);
+ private void onStop() {
+ for (int i = mListeners.size() - 1; i >= 0; i--) {
+ final TimeoutListener listener = mListeners.get(i);
+ listener.onTimeout(mTimeout);
}
}
@@ -151,9 +124,9 @@ public class OneHandedTimeoutHandler {
final String innerPrefix = " ";
pw.println(TAG + "states: ");
pw.print(innerPrefix + "sTimeout=");
- pw.println(sTimeout);
+ pw.println(mTimeout);
pw.print(innerPrefix + "sListeners=");
- pw.println(sListeners);
+ pw.println(mListeners);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTouchHandler.java
index 721382d52717..60709bef4daf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTouchHandler.java
@@ -30,6 +30,8 @@ import android.view.MotionEvent;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
+import com.android.wm.shell.common.ShellExecutor;
+
import java.io.PrintWriter;
/**
@@ -41,7 +43,8 @@ public class OneHandedTouchHandler implements OneHandedTransitionCallback {
private static final String TAG = "OneHandedTouchHandler";
private final Rect mLastUpdatedBounds = new Rect();
- private OneHandedTimeoutHandler mTimeoutHandler;
+ private final OneHandedTimeoutHandler mTimeoutHandler;
+ private final ShellExecutor mMainExecutor;
@VisibleForTesting
InputMonitor mInputMonitor;
@@ -54,8 +57,10 @@ public class OneHandedTouchHandler implements OneHandedTransitionCallback {
private boolean mIsOnStopTransitioning;
private boolean mIsInOutsideRegion;
- public OneHandedTouchHandler() {
- mTimeoutHandler = OneHandedTimeoutHandler.get();
+ public OneHandedTouchHandler(OneHandedTimeoutHandler timeoutHandler,
+ ShellExecutor mainExecutor) {
+ mTimeoutHandler = timeoutHandler;
+ mMainExecutor = mainExecutor;
updateIsEnabled();
}
@@ -128,7 +133,7 @@ public class OneHandedTouchHandler implements OneHandedTransitionCallback {
mInputMonitor = InputManager.getInstance().monitorGestureInput(
"onehanded-touch", DEFAULT_DISPLAY);
mInputEventReceiver = new EventReceiver(
- mInputMonitor.getInputChannel(), Looper.getMainLooper());
+ mInputMonitor.getInputChannel(), mMainExecutor.getLooper());
}
}
@@ -150,6 +155,7 @@ public class OneHandedTouchHandler implements OneHandedTransitionCallback {
pw.println(mLastUpdatedBounds);
}
+ // TODO: Use BatchedInputEventReceiver
private class EventReceiver extends InputEventReceiver {
EventReceiver(InputChannel channel, Looper looper) {
super(channel, looper);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
index a944e3bc50cf..492130bebb30 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
@@ -21,7 +21,6 @@ import android.content.Context;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
-import android.os.Handler;
import android.os.SystemProperties;
import android.provider.Settings;
import android.view.Gravity;
@@ -36,6 +35,7 @@ import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import com.android.wm.shell.R;
+import com.android.wm.shell.common.ShellExecutor;
import java.io.PrintWriter;
@@ -57,7 +57,6 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback {
private View mTutorialView;
private Point mDisplaySize = new Point();
- private Handler mUpdateHandler;
private ContentResolver mContentResolver;
private boolean mCanShowTutorial;
private String mStartOneHandedDescription;
@@ -82,69 +81,67 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback {
private final OneHandedAnimationCallback mAnimationCallback = new OneHandedAnimationCallback() {
@Override
public void onTutorialAnimationUpdate(int offset) {
- mUpdateHandler.post(() -> onAnimationUpdate(offset));
+ onAnimationUpdate(offset);
}
@Override
public void onOneHandedAnimationStart(
OneHandedAnimationController.OneHandedTransitionAnimator animator) {
- mUpdateHandler.post(() -> {
- final Rect startValue = (Rect) animator.getStartValue();
- if (mTriggerState == ONE_HANDED_TRIGGER_STATE.UNSET) {
- mTriggerState = (startValue.top == 0)
- ? ONE_HANDED_TRIGGER_STATE.ENTERING : ONE_HANDED_TRIGGER_STATE.EXITING;
- if (mCanShowTutorial && mTriggerState == ONE_HANDED_TRIGGER_STATE.ENTERING) {
- createTutorialTarget();
- }
+ final Rect startValue = (Rect) animator.getStartValue();
+ if (mTriggerState == ONE_HANDED_TRIGGER_STATE.UNSET) {
+ mTriggerState = (startValue.top == 0)
+ ? ONE_HANDED_TRIGGER_STATE.ENTERING : ONE_HANDED_TRIGGER_STATE.EXITING;
+ if (mCanShowTutorial && mTriggerState == ONE_HANDED_TRIGGER_STATE.ENTERING) {
+ createTutorialTarget();
}
- });
+ }
}
};
- public OneHandedTutorialHandler(Context context) {
+ public OneHandedTutorialHandler(Context context, ShellExecutor mainExecutor) {
context.getDisplay().getRealSize(mDisplaySize);
mPackageName = context.getPackageName();
mContentResolver = context.getContentResolver();
- mUpdateHandler = new Handler();
mWindowManager = context.getSystemService(WindowManager.class);
mAccessibilityManager = (AccessibilityManager)
context.getSystemService(Context.ACCESSIBILITY_SERVICE);
- mTargetViewContainer = new FrameLayout(context);
- mTargetViewContainer.setClipChildren(false);
+
+ mStartOneHandedDescription = context.getResources().getString(
+ R.string.accessibility_action_start_one_handed);
+ mStopOneHandedDescription = context.getResources().getString(
+ R.string.accessibility_action_stop_one_handed);
+ mCanShowTutorial = (Settings.Secure.getInt(mContentResolver,
+ Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, 0) >= MAX_TUTORIAL_SHOW_COUNT)
+ ? false : true;
final float offsetPercentageConfig = context.getResources().getFraction(
R.fraction.config_one_handed_offset, 1, 1);
final int sysPropPercentageConfig = SystemProperties.getInt(
ONE_HANDED_MODE_OFFSET_PERCENTAGE, Math.round(offsetPercentageConfig * 100.0f));
mTutorialAreaHeight = Math.round(mDisplaySize.y * (sysPropPercentageConfig / 100.0f));
- mTutorialView = LayoutInflater.from(context).inflate(R.layout.one_handed_tutorial, null);
- mTargetViewContainer.addView(mTutorialView);
- mCanShowTutorial = (Settings.Secure.getInt(mContentResolver,
- Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, 0) >= MAX_TUTORIAL_SHOW_COUNT)
- ? false : true;
- mStartOneHandedDescription = context.getResources().getString(
- R.string.accessibility_action_start_one_handed);
- mStopOneHandedDescription = context.getResources().getString(
- R.string.accessibility_action_stop_one_handed);
+
+ mainExecutor.execute(() -> {
+ mTutorialView = LayoutInflater.from(context).inflate(R.layout.one_handed_tutorial,
+ null);
+ mTargetViewContainer = new FrameLayout(context);
+ mTargetViewContainer.setClipChildren(false);
+ mTargetViewContainer.addView(mTutorialView);
+ });
}
@Override
public void onStartFinished(Rect bounds) {
- mUpdateHandler.post(() -> {
- updateFinished(View.VISIBLE, 0f);
- updateTutorialCount();
- announcementForScreenReader(true);
- mTriggerState = ONE_HANDED_TRIGGER_STATE.UNSET;
- });
+ updateFinished(View.VISIBLE, 0f);
+ updateTutorialCount();
+ announcementForScreenReader(true);
+ mTriggerState = ONE_HANDED_TRIGGER_STATE.UNSET;
}
@Override
public void onStopFinished(Rect bounds) {
- mUpdateHandler.post(() -> {
- updateFinished(View.INVISIBLE, -mTargetViewContainer.getHeight());
- announcementForScreenReader(false);
- removeTutorialFromWindowManager();
- mTriggerState = ONE_HANDED_TRIGGER_STATE.UNSET;
- });
+ updateFinished(View.INVISIBLE, -mTargetViewContainer.getHeight());
+ announcementForScreenReader(false);
+ removeTutorialFromWindowManager();
+ mTriggerState = ONE_HANDED_TRIGGER_STATE.UNSET;
}
private void updateFinished(int visible, float finalPosition) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
index a7c34fd4465a..d96d4d0a6a3c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
@@ -138,7 +138,7 @@ public class PipMediaController {
public void onActivityPinned() {
// Once we enter PiP, try to find the active media controller for the top most activity
resolveActiveMediaController(mMediaSessionManager.getActiveSessionsForUser(null,
- UserHandle.USER_CURRENT));
+ UserHandle.CURRENT));
}
/**
@@ -245,7 +245,7 @@ public class PipMediaController {
public void registerSessionListenerForCurrentUser() {
mMediaSessionManager.removeOnActiveSessionsChangedListener(mSessionsChangedListener);
mMediaSessionManager.addOnActiveSessionsChangedListener(mSessionsChangedListener, null,
- UserHandle.USER_CURRENT, null);
+ UserHandle.CURRENT, null);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java
index de3bb2950c0a..f8b4dd9bc621 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java
@@ -41,12 +41,12 @@ public class PipUiEventLogger {
}
public void setTaskInfo(TaskInfo taskInfo) {
- if (taskInfo == null) {
- mPackageName = null;
- mPackageUid = INVALID_PACKAGE_UID;
- } else {
+ if (taskInfo != null && taskInfo.topActivity != null) {
mPackageName = taskInfo.topActivity.getPackageName();
mPackageUid = getUid(mPackageName, taskInfo.userId);
+ } else {
+ mPackageName = null;
+ mPackageUid = INVALID_PACKAGE_UID;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
index 7194fc70025c..3b65899364cd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
@@ -53,7 +53,7 @@ public class PipAccessibilityInteractionConnection {
private List<AccessibilityNodeInfo> mAccessibilityNodeInfoList;
private final Context mContext;
- private final ShellExecutor mShellMainExcutor;
+ private final ShellExecutor mMainExcutor;
private final @NonNull PipBoundsState mPipBoundsState;
private final PipMotionHelper mMotionHelper;
private final PipTaskOrganizer mTaskOrganizer;
@@ -72,9 +72,9 @@ public class PipAccessibilityInteractionConnection {
@NonNull PipBoundsState pipBoundsState, PipMotionHelper motionHelper,
PipTaskOrganizer taskOrganizer, PipSnapAlgorithm snapAlgorithm,
AccessibilityCallbacks callbacks, Runnable updateMovementBoundCallback,
- ShellExecutor shellMainExcutor) {
+ ShellExecutor mainExcutor) {
mContext = context;
- mShellMainExcutor = shellMainExcutor;
+ mMainExcutor = mainExcutor;
mPipBoundsState = pipBoundsState;
mMotionHelper = motionHelper;
mTaskOrganizer = taskOrganizer;
@@ -271,7 +271,7 @@ public class PipAccessibilityInteractionConnection {
IAccessibilityInteractionConnectionCallback callback, int flags,
int interrogatingPid, long interrogatingTid, MagnificationSpec spec,
Bundle arguments) throws RemoteException {
- mShellMainExcutor.execute(() -> {
+ mMainExcutor.execute(() -> {
PipAccessibilityInteractionConnection.this
.findAccessibilityNodeInfoByAccessibilityId(accessibilityNodeId, bounds,
interactionId, callback, flags, interrogatingPid, interrogatingTid,
@@ -285,7 +285,7 @@ public class PipAccessibilityInteractionConnection {
IAccessibilityInteractionConnectionCallback callback, int flags,
int interrogatingPid, long interrogatingTid, MagnificationSpec spec)
throws RemoteException {
- mShellMainExcutor.execute(() -> {
+ mMainExcutor.execute(() -> {
PipAccessibilityInteractionConnection.this.findAccessibilityNodeInfosByViewId(
accessibilityNodeId, viewId, bounds, interactionId, callback, flags,
interrogatingPid, interrogatingTid, spec);
@@ -298,7 +298,7 @@ public class PipAccessibilityInteractionConnection {
IAccessibilityInteractionConnectionCallback callback, int flags,
int interrogatingPid, long interrogatingTid, MagnificationSpec spec)
throws RemoteException {
- mShellMainExcutor.execute(() -> {
+ mMainExcutor.execute(() -> {
PipAccessibilityInteractionConnection.this.findAccessibilityNodeInfosByText(
accessibilityNodeId, text, bounds, interactionId, callback, flags,
interrogatingPid, interrogatingTid, spec);
@@ -310,7 +310,7 @@ public class PipAccessibilityInteractionConnection {
int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
int interrogatingPid, long interrogatingTid, MagnificationSpec spec)
throws RemoteException {
- mShellMainExcutor.execute(() -> {
+ mMainExcutor.execute(() -> {
PipAccessibilityInteractionConnection.this.findFocus(accessibilityNodeId, focusType,
bounds, interactionId, callback, flags, interrogatingPid, interrogatingTid,
spec);
@@ -322,7 +322,7 @@ public class PipAccessibilityInteractionConnection {
int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
int interrogatingPid, long interrogatingTid, MagnificationSpec spec)
throws RemoteException {
- mShellMainExcutor.execute(() -> {
+ mMainExcutor.execute(() -> {
PipAccessibilityInteractionConnection.this.focusSearch(accessibilityNodeId,
direction,
bounds, interactionId, callback, flags, interrogatingPid, interrogatingTid,
@@ -335,7 +335,7 @@ public class PipAccessibilityInteractionConnection {
Bundle arguments, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags,
int interrogatingPid, long interrogatingTid) throws RemoteException {
- mShellMainExcutor.execute(() -> {
+ mMainExcutor.execute(() -> {
PipAccessibilityInteractionConnection.this.performAccessibilityAction(
accessibilityNodeId, action, arguments, interactionId, callback, flags,
interrogatingPid, interrogatingTid);
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index 5ab1c390a92b..854385020f03 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -44,6 +44,18 @@ fun LayersAssertion.appPairsDividerIsInvisible(
}
@JvmOverloads
+fun LayersAssertion.appPairsDividerBecomesVisible(
+ bugId: Int = 0,
+ enabled: Boolean = bugId == 0
+) {
+ all("dividerLayerBecomesVisible") {
+ this.hidesLayer(FlickerTestBase.DOCKED_STACK_DIVIDER)
+ .then()
+ .showsLayer(FlickerTestBase.DOCKED_STACK_DIVIDER)
+ }
+}
+
+@JvmOverloads
fun LayersAssertion.dockedStackDividerIsVisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterLegacySplitScreenTest.kt
index b33fa55c2be1..85bf4a1f8c25 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterLegacySplitScreenTest.kt
@@ -21,7 +21,6 @@ import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.dsl.runWithFlicker
-import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.canSplitScreen
import com.android.server.wm.flicker.helpers.exitSplitScreen
import com.android.server.wm.flicker.helpers.isInSplitScreen
@@ -35,6 +34,7 @@ import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+
import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible
import org.junit.Assert
@@ -59,8 +59,6 @@ class EnterLegacySplitScreenTest(
rotationName: String,
rotation: Int
) : SplitScreenTestBase(rotationName, rotation) {
- private val letterBox = "Letterbox"
-
private val splitScreenSetup: FlickerBuilder
get() = FlickerBuilder(instrumentation).apply {
val testLaunchActivity = "launch_splitScreen_test_activity"
@@ -91,7 +89,6 @@ class EnterLegacySplitScreenTest(
windowManagerTrace {
navBarWindowIsAlwaysVisible()
statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(launcherPackageName))
}
}
}
@@ -114,7 +111,8 @@ class EnterLegacySplitScreenTest(
rotation, splitScreenApp.defaultWindowName, 169271943)
dockedStackDividerBecomesVisible()
visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(launcherPackageName, splitScreenApp.defaultWindowName)
+ listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
+ LIVE_WALLPAPER_PACKAGE_NAME)
)
}
windowManagerTrace {
@@ -148,7 +146,7 @@ class EnterLegacySplitScreenTest(
rotation, secondaryApp.defaultWindowName, 169271943)
dockedStackDividerBecomesVisible()
visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(launcherPackageName, splitScreenApp.defaultWindowName,
+ listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
secondaryApp.defaultWindowName)
)
}
@@ -157,6 +155,7 @@ class EnterLegacySplitScreenTest(
showsAppWindow(splitScreenApp.defaultWindowName)
.and().showsAppWindow(secondaryApp.defaultWindowName)
}
+ visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(LAUNCHER_PACKAGE_NAME))
}
}
}
@@ -181,85 +180,14 @@ class EnterLegacySplitScreenTest(
layersTrace {
dockedStackDividerIsInvisible()
visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(launcherPackageName, nonResizeableApp.defaultWindowName)
+ listOf(LAUNCHER_PACKAGE_NAME, nonResizeableApp.defaultWindowName)
)
}
windowManagerTrace {
end {
hidesAppWindow(nonResizeableApp.defaultWindowName)
}
- }
- }
- }
- }
-
- @Test
- fun testNonResizeableWhenAlreadyInSplitScreenPrimary() {
- val testTag = "testNonResizeableWhenAlreadyInSplitScreenPrimary"
- runWithFlicker(splitScreenSetup) {
- withTestName { testTag }
- repeat {
- TEST_REPETITIONS
- }
- transitions {
- nonResizeableApp.launchViaIntent()
- splitScreenApp.launchViaIntent()
- uiDevice.launchSplitScreen()
- nonResizeableApp.reopenAppFromOverview()
- }
- assertions {
- layersTrace {
- dockedStackDividerIsInvisible()
- end("appsEndingBounds", enabled = false) {
- val displayBounds = WindowUtils.getDisplayBounds(rotation)
- this.hasVisibleRegion(nonResizeableApp.defaultWindowName, displayBounds)
- }
- visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(launcherPackageName, splitScreenApp.defaultWindowName,
- nonResizeableApp.defaultWindowName, letterBox)
- )
- }
- windowManagerTrace {
- end {
- showsAppWindow(nonResizeableApp.defaultWindowName)
- hidesAppWindow(splitScreenApp.defaultWindowName)
- }
- }
- }
- }
- }
-
- @Test
- fun testNonResizeableWhenAlreadyInSplitScreenSecondary() {
- val testTag = "testNonResizeableWhenAlreadyInSplitScreenSecondary"
- runWithFlicker(splitScreenSetup) {
- withTestName { testTag }
- repeat {
- TEST_REPETITIONS
- }
- transitions {
- splitScreenApp.launchViaIntent()
- uiDevice.launchSplitScreen()
- uiDevice.pressBack()
- nonResizeableApp.launchViaIntent()
- }
- assertions {
- layersTrace {
- dockedStackDividerIsInvisible()
- end("appsEndingBounds", enabled = false) {
- val displayBounds = WindowUtils.getDisplayBounds(rotation)
- this.hasVisibleRegion(nonResizeableApp.defaultWindowName, displayBounds)
- }
- visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(launcherPackageName, splitScreenApp.defaultWindowName,
- nonResizeableApp.defaultWindowName, letterBox)
- )
- }
- windowManagerTrace {
- end {
- showsAppWindow(nonResizeableApp.defaultWindowName)
- hidesAppWindow(splitScreenApp.defaultWindowName)
- }
+ visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(LAUNCHER_PACKAGE_NAME))
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottomTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottomTest.kt
index 573ffc6c3299..9586fd139eb5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottomTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottomTest.kt
@@ -22,16 +22,20 @@ import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.Flicker
import com.android.server.wm.flicker.FlickerTestRunner
import com.android.server.wm.flicker.FlickerTestRunnerFactory
-import com.android.server.wm.flicker.helpers.StandardAppHelper
import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.buildTestTag
import com.android.server.wm.flicker.helpers.exitSplitScreen
import com.android.server.wm.flicker.helpers.exitSplitScreenFromBottom
import com.android.server.wm.flicker.helpers.isInSplitScreen
import com.android.server.wm.flicker.helpers.launchSplitScreen
+import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+
import com.android.server.wm.flicker.repetitions
+import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_PRIMARY_LABEL
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import com.android.wm.shell.flicker.testapp.Components
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -53,23 +57,24 @@ class ExitLegacySplitScreenFromBottomTest(
@JvmStatic
fun getParams(): Collection<Array<Any>> {
val instrumentation = InstrumentationRegistry.getInstrumentation()
- val testApp = StandardAppHelper(instrumentation,
- "com.android.wm.shell.flicker.testapp", "SimpleApp")
+ val splitScreenApp = SplitScreenHelper(instrumentation,
+ TEST_APP_SPLITSCREEN_PRIMARY_LABEL,
+ Components.SplitScreenActivity())
- // b/161435597 causes the test not to work on 90 degrees
- return FlickerTestRunnerFactory(instrumentation, listOf(Surface.ROTATION_0))
+ // TODO(b/162923992) Use of multiple segments of flicker spec for testing
+ return FlickerTestRunnerFactory(instrumentation,
+ listOf(Surface.ROTATION_0, Surface.ROTATION_90))
.buildTest { configuration ->
withTestName {
- buildTestTag("exitSplitScreenFromBottom", testApp,
+ buildTestTag("exitSplitScreenFromBottom", splitScreenApp,
configuration)
}
repeat { configuration.repetitions }
setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- }
eachRun {
- testApp.open()
+ device.wakeUpAndGoToHomeScreen()
+ device.openQuickStepAndClearRecentAppsFromOverview()
+ splitScreenApp.launchViaIntent()
device.launchSplitScreen()
device.waitForIdle()
this.setRotation(configuration.endRotation)
@@ -77,12 +82,10 @@ class ExitLegacySplitScreenFromBottomTest(
}
teardown {
eachRun {
- testApp.exit()
- }
- test {
if (device.isInSplitScreen()) {
device.exitSplitScreen()
}
+ splitScreenApp.exit()
}
}
transitions {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenTest.kt
index c51c73a9e248..84bfe9451e0a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenTest.kt
@@ -82,7 +82,7 @@ class ExitLegacySplitScreenTest(
}
layersTrace {
visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(launcherPackageName))
+ listOf(LAUNCHER_PACKAGE_NAME))
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt
new file mode 100644
index 000000000000..e9d3eb7f475d
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.legacysplitscreen
+
+import android.view.Surface
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.dsl.runWithFlicker
+import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.helpers.launchSplitScreen
+import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
+import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test open app to split screen.
+ * To run this test: `atest WMShellFlickerTests:NonResizableDismissInLegacySplitScreenTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class NonResizableDismissInLegacySplitScreenTest(
+ rotationName: String,
+ rotation: Int
+) : SplitScreenTestBase(rotationName, rotation) {
+
+ @Test
+ fun testNonResizableDismissInLegacySplitScreenTest() {
+ val testTag = "testNonResizableDismissInLegacySplitScreenTest"
+
+ runWithFlicker(transitionSetup) {
+ withTestName { testTag }
+ repeat { SplitScreenHelper.TEST_REPETITIONS }
+ transitions {
+ nonResizeableApp.launchViaIntent()
+ splitScreenApp.launchViaIntent()
+ device.launchSplitScreen()
+ nonResizeableApp.reopenAppFromOverview()
+ }
+ assertions {
+ layersTrace {
+ dockedStackDividerIsInvisible()
+ end("appsEndingBounds", enabled = false) {
+ val displayBounds = WindowUtils.getDisplayBounds(rotation)
+ this.hasVisibleRegion(nonResizeableApp.defaultWindowName, displayBounds)
+ }
+ visibleLayersShownMoreThanOneConsecutiveEntry(
+ listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
+ nonResizeableApp.defaultWindowName, LETTER_BOX_NAME)
+ )
+ }
+ windowManagerTrace {
+ end {
+ showsAppWindow(nonResizeableApp.defaultWindowName)
+ hidesAppWindow(splitScreenApp.defaultWindowName)
+ }
+ }
+ }
+ }
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<Array<Any>> {
+ val supportedRotations = intArrayOf(Surface.ROTATION_0, Surface.ROTATION_90)
+ return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
+ }
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt
new file mode 100644
index 000000000000..b5a36f5a31d4
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.legacysplitscreen
+
+import android.view.Surface
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.dsl.runWithFlicker
+import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.helpers.launchSplitScreen
+import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
+import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test open app to split screen.
+ * To run this test: `atest WMShellFlickerTests:NonResizableLaunchInLegacySplitScreenTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class NonResizableLaunchInLegacySplitScreenTest(
+ rotationName: String,
+ rotation: Int
+) : SplitScreenTestBase(rotationName, rotation) {
+
+ @Test
+ fun testNonResizableLaunchInLegacySplitScreenTest() {
+ val testTag = "NonResizableLaunchInLegacySplitScreenTest"
+
+ runWithFlicker(transitionSetup) {
+ withTestName { testTag }
+ repeat { SplitScreenHelper.TEST_REPETITIONS }
+ transitions {
+ nonResizeableApp.launchViaIntent()
+ splitScreenApp.launchViaIntent()
+ device.launchSplitScreen()
+ nonResizeableApp.reopenAppFromOverview()
+ }
+ assertions {
+ layersTrace {
+ dockedStackDividerIsInvisible()
+ end("appsEndingBounds", enabled = false) {
+ val displayBounds = WindowUtils.getDisplayBounds(rotation)
+ this.hasVisibleRegion(nonResizeableApp.defaultWindowName, displayBounds)
+ }
+ visibleLayersShownMoreThanOneConsecutiveEntry(
+ listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
+ nonResizeableApp.defaultWindowName, LETTER_BOX_NAME)
+ )
+ }
+ windowManagerTrace {
+ end {
+ showsAppWindow(nonResizeableApp.defaultWindowName)
+ hidesAppWindow(splitScreenApp.defaultWindowName)
+ }
+ }
+ }
+ }
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<Array<Any>> {
+ val supportedRotations = intArrayOf(Surface.ROTATION_0, Surface.ROTATION_90)
+ return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
+ }
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreenTest.kt
index af038698de80..90577ef19c1a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreenTest.kt
@@ -17,37 +17,22 @@
package com.android.wm.shell.flicker.legacysplitscreen
import android.platform.test.annotations.Presubmit
-import android.support.test.launcherhelper.LauncherStrategyFactory
import android.view.Surface
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.Flicker
-import com.android.server.wm.flicker.FlickerTestRunner
-import com.android.server.wm.flicker.FlickerTestRunnerFactory
-import com.android.server.wm.flicker.helpers.StandardAppHelper
-import com.android.server.wm.flicker.endRotation
-import com.android.server.wm.flicker.focusChanges
-import com.android.server.wm.flicker.helpers.buildTestTag
-import com.android.server.wm.flicker.helpers.exitSplitScreen
-import com.android.server.wm.flicker.helpers.isInSplitScreen
-import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview
-import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.appWindowBecomesVisible
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
import com.android.server.wm.flicker.layerBecomesVisible
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.focusChanges
+import com.android.server.wm.flicker.helpers.launchSplitScreen
+import com.android.server.wm.flicker.dsl.runWithFlicker
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.repetitions
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible
+import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.wm.shell.flicker.appPairsDividerBecomesVisible
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -56,85 +41,59 @@ import org.junit.runners.Parameterized
* Test open app to split screen.
* To run this test: `atest WMShellFlickerTests:OpenAppToLegacySplitScreenTest`
*/
-@Presubmit
+// TODO: Add back to pre-submit when stable.
+//@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class OpenAppToLegacySplitScreenTest(
- testName: String,
- flickerSpec: Flicker
-) : FlickerTestRunner(testName, flickerSpec) {
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val instrumentation = InstrumentationRegistry.getInstrumentation()
- val launcherPackageName = LauncherStrategyFactory.getInstance(instrumentation)
- .launcherStrategy.supportedLauncherPackage
- val testApp = StandardAppHelper(instrumentation,
- "com.android.wm.shell.flicker.testapp", "SimpleApp")
-
- // b/161435597 causes the test not to work on 90 degrees
- return FlickerTestRunnerFactory(instrumentation, listOf(Surface.ROTATION_0))
- .buildTest { configuration ->
- withTestName {
- buildTestTag("appToSplitScreen", testApp, configuration)
- }
- repeat { configuration.repetitions }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- device.openQuickStepAndClearRecentAppsFromOverview()
- }
- eachRun {
- testApp.open()
- device.pressHome()
- this.setRotation(configuration.endRotation)
- }
- }
- teardown {
- eachRun {
- if (device.isInSplitScreen()) {
- device.exitSplitScreen()
- }
- }
- test {
- testApp.exit()
- }
- }
- transitions {
- device.launchSplitScreen()
- }
- assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- appWindowBecomesVisible(testApp.getPackage())
- }
+ rotationName: String,
+ rotation: Int
+) : SplitScreenTestBase(rotationName, rotation) {
+ @Test
+ fun OpenAppToLegacySplitScreenTest() {
+ val testTag = "OpenAppToLegacySplitScreenTest"
- layersTrace {
- navBarLayerIsAlwaysVisible(bugId = 140855415)
- statusBarLayerIsAlwaysVisible()
- noUncoveredRegions(configuration.endRotation, enabled = false)
- navBarLayerRotatesAndScales(configuration.endRotation,
- bugId = 140855415)
- statusBarLayerRotatesScales(configuration.endRotation)
- visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(launcherPackageName))
+ runWithFlicker(transitionSetup) {
+ withTestName { testTag }
+ repeat { SplitScreenHelper.TEST_REPETITIONS }
+ transitions {
+ splitScreenApp.launchViaIntent()
+ device.pressHome()
+ this.setRotation(rotation)
+ device.launchSplitScreen()
+ }
+ assertions {
+ windowManagerTrace {
+ visibleWindowsShownMoreThanOneConsecutiveEntry()
+ appWindowBecomesVisible(splitScreenApp.getPackage())
+ }
- dockedStackDividerBecomesVisible()
- layerBecomesVisible(testApp.getPackage())
- }
+ layersTrace {
+ navBarLayerIsAlwaysVisible(bugId = 140855415)
+ noUncoveredRegions(rotation, enabled = false)
+ statusBarLayerIsAlwaysVisible(bugId = 140855415)
+ visibleLayersShownMoreThanOneConsecutiveEntry(
+ listOf(LAUNCHER_PACKAGE_NAME))
+ appPairsDividerBecomesVisible()
+ layerBecomesVisible(splitScreenApp.getPackage())
+ }
- eventLog {
- focusChanges(testApp.`package`,
- "recents_animation_input_consumer", "NexusLauncherActivity",
- bugId = 151179149)
- }
- }
+ eventLog {
+ focusChanges(splitScreenApp.`package`,
+ "recents_animation_input_consumer", "NexusLauncherActivity",
+ bugId = 151179149)
}
+ }
+ }
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<Array<Any>> {
+ val supportedRotations = intArrayOf(Surface.ROTATION_0, Surface.ROTATION_90)
+ return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/SplitScreenTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/SplitScreenTestBase.kt
index a536ec8e2e0b..2b94c5f3fee9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/SplitScreenTestBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/SplitScreenTestBase.kt
@@ -17,6 +17,15 @@
package com.android.wm.shell.flicker.legacysplitscreen
import android.support.test.launcherhelper.LauncherStrategyFactory
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.exitSplitScreen
+import com.android.server.wm.flicker.helpers.isInSplitScreen
+import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.wm.shell.flicker.NonRotationTestBase
import com.android.wm.shell.flicker.TEST_APP_NONRESIZEABLE_LABEL
import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_PRIMARY_LABEL
@@ -37,6 +46,39 @@ abstract class SplitScreenTestBase(
protected val nonResizeableApp = SplitScreenHelper(instrumentation,
TEST_APP_NONRESIZEABLE_LABEL,
Components.NonResizeableActivity())
- protected val launcherPackageName = LauncherStrategyFactory.getInstance(instrumentation)
+
+ protected val LAUNCHER_PACKAGE_NAME = LauncherStrategyFactory.getInstance(instrumentation)
.launcherStrategy.supportedLauncherPackage
+ protected val LIVE_WALLPAPER_PACKAGE_NAME =
+ "com.breel.wallpapers18.soundviz.wallpaper.variations.SoundVizWallpaperV2"
+ protected val LETTER_BOX_NAME = "Letterbox"
+
+ protected val transitionSetup: FlickerBuilder
+ get() = FlickerBuilder(instrumentation).apply {
+ setup {
+ eachRun {
+ uiDevice.wakeUpAndGoToHomeScreen()
+ uiDevice.openQuickStepAndClearRecentAppsFromOverview()
+ }
+ }
+ teardown {
+ eachRun {
+ if (uiDevice.isInSplitScreen()) {
+ uiDevice.exitSplitScreen()
+ }
+ splitScreenApp.exit()
+ nonResizeableApp.exit()
+ }
+ }
+ assertions {
+ layersTrace {
+ navBarLayerIsAlwaysVisible()
+ statusBarLayerIsAlwaysVisible()
+ }
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ }
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedAnimationControllerTest.java
index a8a3a9fd7da2..17fc0578dd2b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedAnimationControllerTest.java
@@ -25,6 +25,8 @@ import android.view.SurfaceControl;
import androidx.test.filters.SmallTest;
+import com.android.wm.shell.common.ShellExecutor;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -49,11 +51,14 @@ public class OneHandedAnimationControllerTest extends OneHandedTestCase {
@Mock
private SurfaceControl mMockLeash;
+ @Mock
+ private ShellExecutor mMainExecutor;
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mTutorialHandler = new OneHandedTutorialHandler(mContext);
+ mTutorialHandler = new OneHandedTutorialHandler(mContext, mMainExecutor);
mOneHandedAnimationController = new OneHandedAnimationController(mContext);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
index 20184bfd5541..16d13f40f840 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
@@ -25,6 +25,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.om.IOverlayManager;
+import android.os.Handler;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -33,6 +34,7 @@ import android.view.Display;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerImpl;
import org.junit.Before;
@@ -45,7 +47,6 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
public class OneHandedControllerTest extends OneHandedTestCase {
Display mDisplay;
OneHandedController mOneHandedController;
@@ -69,11 +70,16 @@ public class OneHandedControllerTest extends OneHandedTestCase {
IOverlayManager mMockOverlayManager;
@Mock
TaskStackListenerImpl mMockTaskStackListener;
+ @Mock
+ ShellExecutor mMockShellMainExecutor;
+ @Mock
+ Handler mMockShellMainHandler;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mDisplay = mContext.getDisplay();
+ mTimeoutHandler = Mockito.spy(new OneHandedTimeoutHandler(mMockShellMainExecutor));
OneHandedController oneHandedController = new OneHandedController(
mContext,
mMockDisplayController,
@@ -82,10 +88,12 @@ public class OneHandedControllerTest extends OneHandedTestCase {
mMockTouchHandler,
mMockTutorialHandler,
mMockGestureHandler,
+ mTimeoutHandler,
mMockOverlayManager,
- mMockTaskStackListener);
+ mMockTaskStackListener,
+ mMockShellMainExecutor,
+ mMockShellMainHandler);
mOneHandedController = Mockito.spy(oneHandedController);
- mTimeoutHandler = Mockito.spy(OneHandedTimeoutHandler.get());
when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false);
@@ -97,7 +105,7 @@ public class OneHandedControllerTest extends OneHandedTestCase {
mContext);
OneHandedDisplayAreaOrganizer displayAreaOrganizer = new OneHandedDisplayAreaOrganizer(
mContext, mMockDisplayController, animationController, mMockTutorialHandler,
- Runnable::run, mMockBackgroundOrganizer);
+ mMockBackgroundOrganizer, mMockShellMainExecutor);
assertThat(displayAreaOrganizer.isInOneHanded()).isFalse();
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
index 3d9fad9097f8..6cfd0c43724c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
@@ -44,6 +44,7 @@ import android.window.WindowContainerTransaction;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.ShellExecutor;
import org.junit.Before;
import org.junit.Test;
@@ -53,7 +54,6 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
static final int DISPLAY_WIDTH = 1000;
static final int DISPLAY_HEIGHT = 1000;
@@ -82,9 +82,8 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
WindowContainerTransaction mMockWindowContainerTransaction;
@Mock
OneHandedBackgroundPanelOrganizer mMockBackgroundOrganizer;
-
- Handler mSpyUpdateHandler;
- Handler.Callback mUpdateCallback = (msg) -> false;
+ @Mock
+ ShellExecutor mMockShellMainExecutor;
@Before
public void setUp() throws Exception {
@@ -110,19 +109,17 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
when(mMockLeash.getWidth()).thenReturn(DISPLAY_WIDTH);
when(mMockLeash.getHeight()).thenReturn(DISPLAY_HEIGHT);
- mDisplayAreaOrganizer = new OneHandedDisplayAreaOrganizer(mContext,
+ mDisplayAreaOrganizer = spy(new OneHandedDisplayAreaOrganizer(mContext,
mMockDisplayController,
mMockAnimationController,
mTutorialHandler,
- Runnable::run, mMockBackgroundOrganizer);
- mSpyUpdateHandler = spy(new Handler(OneHandedThread.get().getLooper(), mUpdateCallback));
- mDisplayAreaOrganizer.setUpdateHandler(mSpyUpdateHandler);
+ mMockBackgroundOrganizer,
+ mMockShellMainExecutor));
}
@Test
public void testOnDisplayAreaAppeared() {
mDisplayAreaOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
- mTestableLooper.processAllMessages();
verify(mMockAnimationController, never()).getAnimator(any(), any(), any());
}
@@ -130,31 +127,18 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
@Test
public void testOnDisplayAreaVanished() {
mDisplayAreaOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
- mTestableLooper.processAllMessages();
mDisplayAreaOrganizer.onDisplayAreaVanished(mDisplayAreaInfo);
assertThat(mDisplayAreaOrganizer.mDisplayAreaMap).isEmpty();
}
@Test
- public void testScheduleOffset() {
- final int xOffSet = 0;
- final int yOffSet = 100;
- mDisplayAreaOrganizer.scheduleOffset(xOffSet, yOffSet);
- mTestableLooper.processAllMessages();
-
- verify(mSpyUpdateHandler).sendMessage(any());
- }
-
- @Test
public void testRotation_portrait_0_to_landscape_90() {
when(mMockLeash.isValid()).thenReturn(false);
// Rotate 0 -> 90
mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_90,
mMockWindowContainerTransaction);
- mTestableLooper.processAllMessages();
-
- verify(mSpyUpdateHandler).sendMessage(any());
+ verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@Test
@@ -163,9 +147,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
// Rotate 0 -> 270
mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_270,
mMockWindowContainerTransaction);
- mTestableLooper.processAllMessages();
-
- verify(mSpyUpdateHandler).sendMessage(any());
+ verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@Test
@@ -174,9 +156,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
// Rotate 180 -> 90
mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_90,
mMockWindowContainerTransaction);
- mTestableLooper.processAllMessages();
-
- verify(mSpyUpdateHandler).sendMessage(any());
+ verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@Test
@@ -185,9 +165,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
// Rotate 180 -> 270
mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_270,
mMockWindowContainerTransaction);
- mTestableLooper.processAllMessages();
-
- verify(mSpyUpdateHandler).sendMessage(any());
+ verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@Test
@@ -196,9 +174,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
// Rotate 90 -> 0
mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_0,
mMockWindowContainerTransaction);
- mTestableLooper.processAllMessages();
-
- verify(mSpyUpdateHandler).sendMessage(any());
+ verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@Test
@@ -207,9 +183,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
// Rotate 90 -> 180
mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_180,
mMockWindowContainerTransaction);
- mTestableLooper.processAllMessages();
-
- verify(mSpyUpdateHandler).sendMessage(any());
+ verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@Test
@@ -218,9 +192,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
// Rotate 270 -> 0
mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_0,
mMockWindowContainerTransaction);
- mTestableLooper.processAllMessages();
-
- verify(mSpyUpdateHandler).sendMessage(any());
+ verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@Test
@@ -229,9 +201,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
// Rotate 270 -> 180
mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_180,
mMockWindowContainerTransaction);
- mTestableLooper.processAllMessages();
-
- verify(mSpyUpdateHandler).sendMessage(any());
+ verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@Test
@@ -240,9 +210,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
// Rotate 0 -> 0
mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_0,
mMockWindowContainerTransaction);
- mTestableLooper.processAllMessages();
-
- verify(mSpyUpdateHandler, never()).sendMessage(any());
+ verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
}
@Test
@@ -251,9 +219,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
// Rotate 0 -> 180
mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_180,
mMockWindowContainerTransaction);
- mTestableLooper.processAllMessages();
-
- verify(mSpyUpdateHandler, never()).sendMessage(any());
+ verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
}
@Test
@@ -262,9 +228,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
// Rotate 180 -> 180
mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_180,
mMockWindowContainerTransaction);
- mTestableLooper.processAllMessages();
-
- verify(mSpyUpdateHandler, never()).sendMessage(any());
+ verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
}
@Test
@@ -273,9 +237,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
// Rotate 180 -> 0
mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_0,
mMockWindowContainerTransaction);
- mTestableLooper.processAllMessages();
-
- verify(mSpyUpdateHandler, never()).sendMessage(any());
+ verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
}
@Test
@@ -284,9 +246,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
// Rotate 90 -> 90
mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_90,
mMockWindowContainerTransaction);
- mTestableLooper.processAllMessages();
-
- verify(mSpyUpdateHandler, never()).sendMessage(any());
+ verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
}
@Test
@@ -295,9 +255,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
// Rotate 90 -> 270
mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_270,
mMockWindowContainerTransaction);
- mTestableLooper.processAllMessages();
-
- verify(mSpyUpdateHandler, never()).sendMessage(any());
+ verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
}
@Test
@@ -306,9 +264,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
// Rotate 270 -> 270
mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_270,
mMockWindowContainerTransaction);
- mTestableLooper.processAllMessages();
-
- verify(mSpyUpdateHandler, never()).sendMessage(any());
+ verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
}
@Test
@@ -317,8 +273,6 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
// Rotate 270 -> 90
mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_90,
mMockWindowContainerTransaction);
- mTestableLooper.processAllMessages();
-
- verify(mSpyUpdateHandler, never()).sendMessage(any());
+ verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedGestureHandlerTest.java
index fb417c8ca5e8..e5f2ff717e37 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedGestureHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedGestureHandlerTest.java
@@ -26,6 +26,7 @@ import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.ShellExecutor;
import org.junit.Before;
import org.junit.Ignore;
@@ -36,17 +37,20 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
public class OneHandedGestureHandlerTest extends OneHandedTestCase {
OneHandedTutorialHandler mTutorialHandler;
OneHandedGestureHandler mGestureHandler;
@Mock
DisplayController mMockDisplayController;
+ @Mock
+ ShellExecutor mMockShellMainExecutor;
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mTutorialHandler = new OneHandedTutorialHandler(mContext);
- mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController);
+ mTutorialHandler = new OneHandedTutorialHandler(mContext, mMockShellMainExecutor);
+ mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController,
+ mMockShellMainExecutor);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedSettingsUtilTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedSettingsUtilTest.java
index 7c11138a47aa..f8c9d535ba94 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedSettingsUtilTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedSettingsUtilTest.java
@@ -38,7 +38,6 @@ import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
public class OneHandedSettingsUtilTest extends OneHandedTestCase {
ContentResolver mContentResolver;
ContentObserver mContentObserver;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java
index e2b70c3bcc70..9219f15afc7f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java
@@ -20,43 +20,46 @@ import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TI
import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS;
import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER;
import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS;
-import static com.android.wm.shell.onehanded.OneHandedTimeoutHandler.ONE_HANDED_TIMEOUT_STOP_MSG;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.verify;
+import android.os.Looper;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
+import com.android.wm.shell.common.ShellExecutor;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
public class OneHandedTimeoutHandlerTest extends OneHandedTestCase {
- OneHandedTimeoutHandler mTimeoutHandler;
+ private OneHandedTimeoutHandler mTimeoutHandler;
+ private ShellExecutor mMainExecutor;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mTimeoutHandler = Mockito.spy(OneHandedTimeoutHandler.get());
- }
-
- @Test
- public void testTimeoutHandler_isNotNull() {
- assertThat(OneHandedTimeoutHandler.get()).isNotNull();
+ mMainExecutor = new TestShellExecutor();
+ mTimeoutHandler = Mockito.spy(new OneHandedTimeoutHandler(mMainExecutor));
}
@Test
public void testTimeoutHandler_getTimeout_defaultMedium() {
- assertThat(OneHandedTimeoutHandler.get().getTimeout()).isEqualTo(
+ assertThat(mTimeoutHandler.getTimeout()).isEqualTo(
ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS);
}
@@ -64,28 +67,29 @@ public class OneHandedTimeoutHandlerTest extends OneHandedTestCase {
public void testTimeoutHandler_setNewTime_resetTimer() {
mTimeoutHandler.setTimeout(ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS);
verify(mTimeoutHandler).resetTimer();
- assertThat(mTimeoutHandler.sHandler.hasMessages(ONE_HANDED_TIMEOUT_STOP_MSG)).isNotNull();
+ assertTrue(mTimeoutHandler.hasScheduledTimeout());
}
@Test
public void testSetTimeoutNever_neverResetTimer() {
mTimeoutHandler.setTimeout(ONE_HANDED_TIMEOUT_NEVER);
- assertThat(!mTimeoutHandler.sHandler.hasMessages(ONE_HANDED_TIMEOUT_STOP_MSG)).isNotNull();
+ assertFalse(mTimeoutHandler.hasScheduledTimeout());
}
@Test
public void testSetTimeoutShort() {
mTimeoutHandler.setTimeout(ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS);
verify(mTimeoutHandler).resetTimer();
- assertThat(mTimeoutHandler.sHandler.hasMessages(ONE_HANDED_TIMEOUT_STOP_MSG)).isNotNull();
+ assertThat(mTimeoutHandler.getTimeout()).isEqualTo(ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS);
+ assertTrue(mTimeoutHandler.hasScheduledTimeout());
}
@Test
public void testSetTimeoutMedium() {
mTimeoutHandler.setTimeout(ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS);
verify(mTimeoutHandler).resetTimer();
- assertThat(mTimeoutHandler.sHandler.hasMessages(
- ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS)).isNotNull();
+ assertThat(mTimeoutHandler.getTimeout()).isEqualTo(ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS);
+ assertTrue(mTimeoutHandler.hasScheduledTimeout());
}
@Test
@@ -96,10 +100,38 @@ public class OneHandedTimeoutHandlerTest extends OneHandedTestCase {
@Test
public void testDragging_shouldRemoveAndSendEmptyMessageDelay() {
- final boolean isDragging = true;
mTimeoutHandler.setTimeout(ONE_HANDED_TIMEOUT_LONG_IN_SECONDS);
mTimeoutHandler.resetTimer();
- TestableLooper.get(this).processAllMessages();
- assertThat(mTimeoutHandler.sHandler.hasMessages(ONE_HANDED_TIMEOUT_STOP_MSG)).isNotNull();
+ assertTrue(mTimeoutHandler.hasScheduledTimeout());
+ }
+
+ private class TestShellExecutor implements ShellExecutor {
+ private ArrayList<Runnable> mExecuted = new ArrayList<>();
+ private ArrayList<Runnable> mDelayed = new ArrayList<>();
+
+ @Override
+ public void execute(Runnable runnable) {
+ mExecuted.add(runnable);
+ }
+
+ @Override
+ public void executeDelayed(Runnable r, long delayMillis) {
+ mDelayed.add(r);
+ }
+
+ @Override
+ public void removeCallbacks(Runnable r) {
+ mDelayed.remove(r);
+ }
+
+ @Override
+ public boolean hasCallback(Runnable r) {
+ return mDelayed.contains(r);
+ }
+
+ @Override
+ public Looper getLooper() {
+ return Looper.myLooper();
+ }
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTouchHandlerTest.java
index c69e385b2602..d3b02caf8b65 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTouchHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTouchHandlerTest.java
@@ -23,22 +23,30 @@ import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
+import com.android.wm.shell.common.ShellExecutor;
+
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;
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
public class OneHandedTouchHandlerTest extends OneHandedTestCase {
- OneHandedTouchHandler mTouchHandler;
+ private OneHandedTouchHandler mTouchHandler;
+
+ @Mock
+ private OneHandedTimeoutHandler mTimeoutHandler;
+
+ @Mock
+ private ShellExecutor mMainExecutor;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mTouchHandler = new OneHandedTouchHandler();
+ mTouchHandler = new OneHandedTouchHandler(mTimeoutHandler, mMainExecutor);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
index b187dc981bf8..c451b8b2d2d7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
@@ -19,12 +19,14 @@ package com.android.wm.shell.onehanded;
import static org.mockito.Mockito.verify;
import android.content.om.IOverlayManager;
+import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerImpl;
import org.junit.Before;
@@ -35,12 +37,12 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
public class OneHandedTutorialHandlerTest extends OneHandedTestCase {
@Mock
OneHandedTouchHandler mTouchHandler;
OneHandedTutorialHandler mTutorialHandler;
OneHandedGestureHandler mGestureHandler;
+ OneHandedTimeoutHandler mTimeoutHandler;
OneHandedController mOneHandedController;
@Mock
DisplayController mMockDisplayController;
@@ -52,12 +54,18 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase {
IOverlayManager mMockOverlayManager;
@Mock
TaskStackListenerImpl mMockTaskStackListener;
+ @Mock
+ ShellExecutor mMockShellMainExecutor;
+ @Mock
+ Handler mMockShellMainHandler;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mTutorialHandler = new OneHandedTutorialHandler(mContext);
- mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController);
+ mTutorialHandler = new OneHandedTutorialHandler(mContext, mMockShellMainExecutor);
+ mTimeoutHandler = new OneHandedTimeoutHandler(mMockShellMainExecutor);
+ mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController,
+ mMockShellMainExecutor);
mOneHandedController = new OneHandedController(
getContext(),
mMockDisplayController,
@@ -66,8 +74,11 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase {
mTouchHandler,
mTutorialHandler,
mGestureHandler,
+ mTimeoutHandler,
mMockOverlayManager,
- mMockTaskStackListener);
+ mMockTaskStackListener,
+ mMockShellMainExecutor,
+ mMockShellMainHandler);
}
@Test
diff --git a/libs/services/include/android/os/DropBoxManager.h b/libs/services/include/android/os/DropBoxManager.h
index 07472435d8a3..5689286f0b32 100644
--- a/libs/services/include/android/os/DropBoxManager.h
+++ b/libs/services/include/android/os/DropBoxManager.h
@@ -93,8 +93,6 @@ private:
enum {
HAS_BYTE_ARRAY = 8
};
-
- Status add(const Entry& entry);
};
}} // namespace android::os
diff --git a/libs/services/src/os/DropBoxManager.cpp b/libs/services/src/os/DropBoxManager.cpp
index 429f996bd65e..3716e019f69a 100644
--- a/libs/services/src/os/DropBoxManager.cpp
+++ b/libs/services/src/os/DropBoxManager.cpp
@@ -18,7 +18,9 @@
#include <android/os/DropBoxManager.h>
+#include <android-base/unique_fd.h>
#include <binder/IServiceManager.h>
+#include <binder/ParcelFileDescriptor.h>
#include <com/android/internal/os/IDropBoxManagerService.h>
#include <cutils/log.h>
@@ -178,18 +180,24 @@ DropBoxManager::~DropBoxManager()
Status
DropBoxManager::addText(const String16& tag, const string& text)
{
- Entry entry(tag, IS_TEXT);
- entry.mData.assign(text.c_str(), text.c_str() + text.size());
- return add(entry);
+ return addData(tag, reinterpret_cast<uint8_t const*>(text.c_str()), text.size(), IS_TEXT);
}
Status
DropBoxManager::addData(const String16& tag, uint8_t const* data,
size_t size, int flags)
{
- Entry entry(tag, flags);
- entry.mData.assign(data, data+size);
- return add(entry);
+ sp<IDropBoxManagerService> service = interface_cast<IDropBoxManagerService>(
+ defaultServiceManager()->getService(android::String16("dropbox")));
+ if (service == NULL) {
+ return Status::fromExceptionCode(Status::EX_NULL_POINTER, "can't find dropbox service");
+ }
+ ALOGD("About to call service->add()");
+ vector<uint8_t> dataArg;
+ dataArg.assign(data, data + size);
+ Status status = service->addData(tag, dataArg, flags);
+ ALOGD("service->add returned %s", status.toString8().string());
+ return status;
}
Status
@@ -213,20 +221,15 @@ DropBoxManager::addFile(const String16& tag, int fd, int flags)
ALOGW("DropboxManager: %s", message.c_str());
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, message.c_str());
}
- Entry entry(tag, flags, fd);
- return add(entry);
-}
-
-Status
-DropBoxManager::add(const Entry& entry)
-{
sp<IDropBoxManagerService> service = interface_cast<IDropBoxManagerService>(
defaultServiceManager()->getService(android::String16("dropbox")));
if (service == NULL) {
return Status::fromExceptionCode(Status::EX_NULL_POINTER, "can't find dropbox service");
}
ALOGD("About to call service->add()");
- Status status = service->add(entry);
+ android::base::unique_fd uniqueFd(fd);
+ android::os::ParcelFileDescriptor parcelFd(std::move(uniqueFd));
+ Status status = service->addFile(tag, parcelFd, flags);
ALOGD("service->add returned %s", status.toString8().string());
return status;
}
diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java
index 9957975f1692..582a28ee278e 100644
--- a/media/java/android/media/MediaCas.java
+++ b/media/java/android/media/MediaCas.java
@@ -716,8 +716,9 @@ public final class MediaCas implements AutoCloseable {
context.getSystemService(Context.TV_TUNER_RESOURCE_MGR_SERVICE);
if (mTunerResourceManager != null) {
int[] clientId = new int[1];
- ResourceClientProfile profile =
- new ResourceClientProfile(tvInputServiceSessionId, priorityHint);
+ ResourceClientProfile profile = new ResourceClientProfile();
+ profile.tvInputSessionId = tvInputServiceSessionId;
+ profile.useCase = priorityHint;
mTunerResourceManager.registerClientProfile(
profile, context.getMainExecutor(), mResourceListener, clientId);
mClientId = clientId[0];
@@ -921,7 +922,9 @@ public final class MediaCas implements AutoCloseable {
int[] sessionResourceHandle = new int[1];
sessionResourceHandle[0] = -1;
if (mTunerResourceManager != null) {
- CasSessionRequest casSessionRequest = new CasSessionRequest(mClientId, mCasSystemId);
+ CasSessionRequest casSessionRequest = new CasSessionRequest();
+ casSessionRequest.clientId = mClientId;
+ casSessionRequest.casSystemId = mCasSystemId;
if (!mTunerResourceManager
.requestCasSession(casSessionRequest, sessionResourceHandle)) {
throw new MediaCasException.InsufficientResourceException(
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 1fd132d00f10..f580ea5d57de 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -140,6 +140,8 @@ public final class MediaSessionManager {
@NonNull
public ISession createSession(@NonNull MediaSession.CallbackStub cbStub, @NonNull String tag,
@Nullable Bundle sessionInfo) {
+ Objects.requireNonNull(cbStub, "cbStub shouldn't be null");
+ Objects.requireNonNull(tag, "tag shouldn't be null");
try {
return mService.createSession(mContext.getPackageName(), cbStub, tag, sessionInfo,
UserHandle.myUserId());
@@ -163,9 +165,7 @@ public final class MediaSessionManager {
* @param token newly created session2 token
*/
public void notifySession2Created(@NonNull Session2Token token) {
- if (token == null) {
- throw new IllegalArgumentException("token shouldn't be null");
- }
+ Objects.requireNonNull(token, "token shouldn't be null");
if (token.getType() != Session2Token.TYPE_SESSION) {
throw new IllegalArgumentException("token's type should be TYPE_SESSION");
}
@@ -209,16 +209,24 @@ public final class MediaSessionManager {
* retrieve sessions for user ids that do not belong to current process.
*
* @param notificationListener The enabled notification listener component. May be null.
- * @param userId The user id to fetch sessions for.
+ * @param userHandle The user handle to fetch sessions for.
* @return A list of controllers for ongoing sessions.
* @hide
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @SuppressLint("UserHandle")
public @NonNull List<MediaController> getActiveSessionsForUser(
- @Nullable ComponentName notificationListener, int userId) {
+ @Nullable ComponentName notificationListener, @NonNull UserHandle userHandle) {
+ Objects.requireNonNull(userHandle, "userHandle shouldn't be null");
+ return getActiveSessionsForUser(notificationListener, userHandle.getIdentifier());
+ }
+
+ private List<MediaController> getActiveSessionsForUser(ComponentName notificationListener,
+ int userId) {
ArrayList<MediaController> controllers = new ArrayList<MediaController>();
try {
- List<MediaSession.Token> tokens = mService.getSessions(notificationListener, userId);
+ List<MediaSession.Token> tokens = mService.getSessions(notificationListener,
+ userId);
int size = tokens.size();
for (int i = 0; i < size; i++) {
MediaController controller = new MediaController(mContext, tokens.get(i));
@@ -257,12 +265,19 @@ public final class MediaSessionManager {
* {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission in order to
* retrieve session tokens for user ids that do not belong to current process.
*
- * @param userId The user id to fetch sessions for.
+ * @param userHandle The user handle to fetch sessions for.
* @return A list of {@link Session2Token}
* @hide
*/
@NonNull
- public List<Session2Token> getSession2Tokens(int userId) {
+ @SuppressLint("UserHandle")
+ public List<Session2Token> getSession2Tokens(@NonNull UserHandle userHandle) {
+ Objects.requireNonNull(userHandle, "userHandle shouldn't be null");
+ return getSession2Tokens(userHandle.getIdentifier());
+
+ }
+
+ private List<Session2Token> getSession2Tokens(int userId) {
try {
ParceledListSlice slice = mService.getSession2Tokens(userId);
return slice == null ? new ArrayList<>() : slice.getList();
@@ -324,18 +339,26 @@ public final class MediaSessionManager {
*
* @param sessionListener The listener to add.
* @param notificationListener The enabled notification listener component. May be null.
- * @param userId The userId to listen for changes on.
+ * @param userHandle The user handle to listen for changes on.
* @param handler The handler to post updates on.
* @hide
*/
- @SuppressLint({"ExecutorRegistration", "SamShouldBeLast"})
+ @SuppressLint({"ExecutorRegistration", "SamShouldBeLast", "UserHandle"})
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public void addOnActiveSessionsChangedListener(
@NonNull OnActiveSessionsChangedListener sessionListener,
- @Nullable ComponentName notificationListener, int userId, @Nullable Handler handler) {
- if (sessionListener == null) {
- throw new IllegalArgumentException("listener may not be null");
- }
+ @Nullable ComponentName notificationListener, @NonNull UserHandle userHandle,
+ @Nullable Handler handler) {
+ Objects.requireNonNull(userHandle, "userHandle shouldn't be null");
+ addOnActiveSessionsChangedListener(sessionListener, notificationListener,
+ userHandle.getIdentifier(), handler);
+ }
+
+ private void addOnActiveSessionsChangedListener(
+ @NonNull OnActiveSessionsChangedListener sessionListener,
+ @Nullable ComponentName notificationListener, int userId,
+ @Nullable Handler handler) {
+ Objects.requireNonNull(sessionListener, "sessionListener shouldn't be null");
if (handler == null) {
handler = new Handler();
}
@@ -358,15 +381,13 @@ public final class MediaSessionManager {
/**
* Stop receiving active sessions updates on the specified listener.
*
- * @param listener The listener to remove.
+ * @param sessionListener The listener to remove.
*/
public void removeOnActiveSessionsChangedListener(
- @NonNull OnActiveSessionsChangedListener listener) {
- if (listener == null) {
- throw new IllegalArgumentException("listener may not be null");
- }
+ @NonNull OnActiveSessionsChangedListener sessionListener) {
+ Objects.requireNonNull(sessionListener, "sessionListener shouldn't be null");
synchronized (mLock) {
- SessionsChangedWrapper wrapper = mListeners.remove(listener);
+ SessionsChangedWrapper wrapper = mListeners.remove(sessionListener);
if (wrapper != null) {
try {
mService.removeSessionsListener(wrapper.mStub);
@@ -422,17 +443,23 @@ public final class MediaSessionManager {
* {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission in order to
* add listeners for user ids that do not belong to current process.
*
- * @param userId The userId to listen for changes on
+ * @param userHandle The userHandle to listen for changes on
* @param listener The listener to add
* @param handler The handler to call listener on. If {@code null}, calling thread's looper will
* be used.
* @hide
*/
- public void addOnSession2TokensChangedListener(int userId,
- @NonNull OnSession2TokensChangedListener listener, @Nullable Handler handler) {
- if (listener == null) {
- throw new IllegalArgumentException("listener shouldn't be null");
- }
+ @SuppressLint("UserHandle")
+ public void addOnSession2TokensChangedListener(@NonNull UserHandle userHandle,
+ @NonNull OnSession2TokensChangedListener listener, @NonNull Handler handler) {
+ Objects.requireNonNull(userHandle, "userHandle shouldn't be null");
+ addOnSession2TokensChangedListener(userHandle.getIdentifier(), listener, handler);
+ }
+
+ private void addOnSession2TokensChangedListener(int userId,
+ OnSession2TokensChangedListener listener, Handler handler) {
+ Objects.requireNonNull(handler, "handler shouldn't be null");
+ Objects.requireNonNull(listener, "listener shouldn't be null");
synchronized (mLock) {
if (mSession2TokensListeners.get(listener) != null) {
Log.w(TAG, "Attempted to add session listener twice, ignoring.");
@@ -462,9 +489,7 @@ public final class MediaSessionManager {
*/
public void removeOnSession2TokensChangedListener(
@NonNull OnSession2TokensChangedListener listener) {
- if (listener == null) {
- throw new IllegalArgumentException("listener may not be null");
- }
+ Objects.requireNonNull(listener, "listener shouldn't be null");
final Session2TokensChangedWrapper wrapper;
synchronized (mLock) {
wrapper = mSession2TokensListeners.remove(listener);
@@ -581,9 +606,7 @@ public final class MediaSessionManager {
private void dispatchMediaKeyEventInternal(KeyEvent keyEvent, boolean asSystemService,
boolean needWakeLock) {
- if (keyEvent == null) {
- throw new NullPointerException("keyEvent shouldn't be null");
- }
+ Objects.requireNonNull(keyEvent, "keyEvent shouldn't be null");
try {
mService.dispatchMediaKeyEvent(mContext.getPackageName(), asSystemService, keyEvent,
needWakeLock);
@@ -606,12 +629,8 @@ public final class MediaSessionManager {
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public boolean dispatchMediaKeyEventToSessionAsSystemService(@NonNull KeyEvent keyEvent,
@NonNull MediaSession.Token sessionToken) {
- if (sessionToken == null) {
- throw new NullPointerException("sessionToken shouldn't be null");
- }
- if (keyEvent == null) {
- throw new NullPointerException("keyEvent shouldn't be null");
- }
+ Objects.requireNonNull(sessionToken, "sessionToken shouldn't be null");
+ Objects.requireNonNull(keyEvent, "keyEvent shouldn't be null");
if (!KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) {
return false;
}
@@ -662,9 +681,7 @@ public final class MediaSessionManager {
private void dispatchVolumeKeyEventInternal(@NonNull KeyEvent keyEvent, int stream,
boolean musicOnly, boolean asSystemService) {
- if (keyEvent == null) {
- throw new NullPointerException("keyEvent shouldn't be null");
- }
+ Objects.requireNonNull(keyEvent, "keyEvent shouldn't be null");
try {
mService.dispatchVolumeKeyEvent(mContext.getPackageName(), mContext.getOpPackageName(),
asSystemService, keyEvent, stream, musicOnly);
@@ -686,12 +703,8 @@ public final class MediaSessionManager {
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public void dispatchVolumeKeyEventToSessionAsSystemService(@NonNull KeyEvent keyEvent,
@NonNull MediaSession.Token sessionToken) {
- if (sessionToken == null) {
- throw new NullPointerException("sessionToken shouldn't be null");
- }
- if (keyEvent == null) {
- throw new NullPointerException("keyEvent shouldn't be null");
- }
+ Objects.requireNonNull(sessionToken, "sessionToken shouldn't be null");
+ Objects.requireNonNull(keyEvent, "keyEvent shouldn't be null");
try {
mService.dispatchVolumeKeyEventToSessionAsSystemService(mContext.getPackageName(),
mContext.getOpPackageName(), keyEvent, sessionToken);
@@ -735,9 +748,7 @@ public final class MediaSessionManager {
* {@code false} otherwise.
*/
public boolean isTrustedForMediaControl(@NonNull RemoteUserInfo userInfo) {
- if (userInfo == null) {
- throw new IllegalArgumentException("userInfo may not be null");
- }
+ Objects.requireNonNull(userInfo, "userInfo shouldn't be null");
if (userInfo.getPackageName() == null) {
return false;
}
@@ -845,12 +856,8 @@ public final class MediaSessionManager {
public void addOnMediaKeyEventDispatchedListener(
@NonNull @CallbackExecutor Executor executor,
@NonNull OnMediaKeyEventDispatchedListener listener) {
- if (executor == null) {
- throw new NullPointerException("executor shouldn't be null");
- }
- if (listener == null) {
- throw new NullPointerException("listener shouldn't be null");
- }
+ Objects.requireNonNull(executor, "executor shouldn't be null");
+ Objects.requireNonNull(listener, "listener shouldn't be null");
synchronized (mLock) {
try {
mOnMediaKeyEventDispatchedListeners.put(listener, executor);
@@ -874,9 +881,7 @@ public final class MediaSessionManager {
@RequiresPermission(value = android.Manifest.permission.MEDIA_CONTENT_CONTROL)
public void removeOnMediaKeyEventDispatchedListener(
@NonNull OnMediaKeyEventDispatchedListener listener) {
- if (listener == null) {
- throw new NullPointerException("listener shouldn't be null");
- }
+ Objects.requireNonNull(listener, "listener shouldn't be null");
synchronized (mLock) {
try {
mOnMediaKeyEventDispatchedListeners.remove(listener);
@@ -902,12 +907,8 @@ public final class MediaSessionManager {
public void addOnMediaKeyEventSessionChangedListener(
@NonNull @CallbackExecutor Executor executor,
@NonNull OnMediaKeyEventSessionChangedListener listener) {
- if (executor == null) {
- throw new NullPointerException("executor shouldn't be null");
- }
- if (listener == null) {
- throw new NullPointerException("listener shouldn't be null");
- }
+ Objects.requireNonNull(executor, "executor shouldn't be null");
+ Objects.requireNonNull(listener, "listener shouldn't be null");
synchronized (mLock) {
try {
mMediaKeyEventSessionChangedCallbacks.put(listener, executor);
@@ -934,9 +935,7 @@ public final class MediaSessionManager {
@RequiresPermission(value = android.Manifest.permission.MEDIA_CONTENT_CONTROL)
public void removeOnMediaKeyEventSessionChangedListener(
@NonNull OnMediaKeyEventSessionChangedListener listener) {
- if (listener == null) {
- throw new NullPointerException("listener shouldn't be null");
- }
+ Objects.requireNonNull(listener, "listener shouldn't be null");
synchronized (mLock) {
try {
mMediaKeyEventSessionChangedCallbacks.remove(listener);
diff --git a/media/java/android/media/tv/tuner/Lnb.java b/media/java/android/media/tv/tuner/Lnb.java
index 5e9579430e11..59ef4b890320 100644
--- a/media/java/android/media/tv/tuner/Lnb.java
+++ b/media/java/android/media/tv/tuner/Lnb.java
@@ -145,7 +145,6 @@ public class Lnb implements AutoCloseable {
private static final String TAG = "Lnb";
- int mId;
LnbCallback mCallback;
Executor mExecutor;
Tuner mTuner;
@@ -162,9 +161,7 @@ public class Lnb implements AutoCloseable {
private Boolean mIsClosed = false;
private final Object mLock = new Object();
- private Lnb(int id) {
- mId = id;
- }
+ private Lnb() {}
void setCallback(Executor executor, @Nullable LnbCallback callback, Tuner tuner) {
mCallback = callback;
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 02b6571b0802..46b29f5bc90a 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -44,9 +44,9 @@ import android.media.tv.tuner.frontend.FrontendStatus.FrontendStatusType;
import android.media.tv.tuner.frontend.OnTuneEventListener;
import android.media.tv.tuner.frontend.ScanCallback;
import android.media.tv.tunerresourcemanager.ResourceClientProfile;
+import android.media.tv.tunerresourcemanager.TunerCiCamRequest;
import android.media.tv.tunerresourcemanager.TunerDemuxRequest;
import android.media.tv.tunerresourcemanager.TunerDescramblerRequest;
-import android.media.tv.tunerresourcemanager.TunerFrontendInfo;
import android.media.tv.tunerresourcemanager.TunerFrontendRequest;
import android.media.tv.tunerresourcemanager.TunerLnbRequest;
import android.media.tv.tunerresourcemanager.TunerResourceManager;
@@ -298,6 +298,8 @@ public class Tuner implements AutoCloseable {
private Executor mOnResourceLostListenerExecutor;
private Integer mDemuxHandle;
+ private Integer mFrontendCiCamHandle;
+ private Integer mFrontendCiCamId;
private Map<Integer, WeakReference<Descrambler>> mDescramblers = new HashMap<>();
private List<WeakReference<Filter>> mFilters = new ArrayList<WeakReference<Filter>>();
@@ -343,34 +345,14 @@ public class Tuner implements AutoCloseable {
mHandler = createEventHandler();
int[] clientId = new int[1];
- ResourceClientProfile profile = new ResourceClientProfile(tvInputSessionId, useCase);
+ ResourceClientProfile profile = new ResourceClientProfile();
+ profile.tvInputSessionId = tvInputSessionId;
+ profile.useCase = useCase;
mTunerResourceManager.registerClientProfile(
profile, new HandlerExecutor(mHandler), mResourceListener, clientId);
mClientId = clientId[0];
mUserId = ActivityManager.getCurrentUser();
-
- setFrontendInfoList();
- setLnbIds();
- }
-
- private void setFrontendInfoList() {
- List<Integer> ids = getFrontendIds();
- if (ids == null) {
- return;
- }
- TunerFrontendInfo[] infos = new TunerFrontendInfo[ids.size()];
- for (int i = 0; i < ids.size(); i++) {
- int id = ids.get(i);
- FrontendInfo frontendInfo = getFrontendInfoById(id);
- if (frontendInfo == null) {
- continue;
- }
- TunerFrontendInfo tunerFrontendInfo = new TunerFrontendInfo(
- id, frontendInfo.getType(), frontendInfo.getExclusiveGroupId());
- infos[i] = tunerFrontendInfo;
- }
- mTunerResourceManager.setFrontendInfoList(infos);
}
/**
@@ -405,14 +387,6 @@ public class Tuner implements AutoCloseable {
return nativeGetFrontendIds();
}
- private void setLnbIds() {
- int[] ids = nativeGetLnbIds();
- if (ids == null) {
- return;
- }
- mTunerResourceManager.setLnbInfoList(ids);
- }
-
/**
* Sets the listener for resource lost.
*
@@ -498,6 +472,14 @@ public class Tuner implements AutoCloseable {
if (mLnb != null) {
mLnb.close();
}
+ if (mFrontendCiCamHandle != null) {
+ int result = nativeUnlinkCiCam(mFrontendCiCamId);
+ if (result == RESULT_SUCCESS) {
+ mTunerResourceManager.releaseCiCam(mFrontendCiCamHandle, mClientId);
+ mFrontendCiCamId = null;
+ mFrontendCiCamHandle = null;
+ }
+ }
synchronized (mDescramblers) {
if (!mDescramblers.isEmpty()) {
for (Map.Entry<Integer, WeakReference<Descrambler>> d : mDescramblers.entrySet()) {
@@ -561,7 +543,7 @@ public class Tuner implements AutoCloseable {
private native int nativeStopTune();
private native int nativeScan(int settingsType, FrontendSettings settings, int scanType);
private native int nativeStopScan();
- private native int nativeSetLnb(int lnbId);
+ private native int nativeSetLnb(Lnb lnb);
private native int nativeSetLna(boolean enable);
private native FrontendStatus nativeGetFrontendStatus(int[] statusTypes);
private native Integer nativeGetAvSyncHwId(Filter filter);
@@ -574,7 +556,6 @@ public class Tuner implements AutoCloseable {
private native Filter nativeOpenFilter(int type, int subType, long bufferSize);
private native TimeFilter nativeOpenTimeFilter();
- private native int[] nativeGetLnbIds();
private native Lnb nativeOpenLnbByHandle(int handle);
private native Lnb nativeOpenLnbByName(String name);
@@ -814,7 +795,9 @@ public class Tuner implements AutoCloseable {
private boolean requestFrontend() {
int[] feHandle = new int[1];
- TunerFrontendRequest request = new TunerFrontendRequest(mClientId, mFrontendType);
+ TunerFrontendRequest request = new TunerFrontendRequest();
+ request.clientId = mClientId;
+ request.frontendType = mFrontendType;
boolean granted = mTunerResourceManager.requestFrontend(request, feHandle);
if (granted) {
mFrontendHandle = feHandle[0];
@@ -835,7 +818,7 @@ public class Tuner implements AutoCloseable {
*/
@Result
private int setLnb(@NonNull Lnb lnb) {
- return nativeSetLnb(lnb.mId);
+ return nativeSetLnb(lnb);
}
/**
@@ -945,7 +928,8 @@ public class Tuner implements AutoCloseable {
public int connectFrontendToCiCam(int ciCamId) {
if (TunerVersionChecker.checkHigherOrEqualVersionTo(TunerVersionChecker.TUNER_VERSION_1_1,
"linkFrontendToCiCam")) {
- if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) {
+ if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)
+ && checkCiCamResource(ciCamId)) {
return nativeLinkCiCam(ciCamId);
}
}
@@ -964,7 +948,7 @@ public class Tuner implements AutoCloseable {
*/
@Result
public int disconnectCiCam() {
- if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) {
+ if (mDemuxHandle != null) {
return nativeDisconnectCiCam();
}
return RESULT_UNAVAILABLE;
@@ -990,8 +974,14 @@ public class Tuner implements AutoCloseable {
public int disconnectFrontendToCiCam(int ciCamId) {
if (TunerVersionChecker.checkHigherOrEqualVersionTo(TunerVersionChecker.TUNER_VERSION_1_1,
"unlinkFrontendToCiCam")) {
- if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) {
- return nativeUnlinkCiCam(ciCamId);
+ if (mFrontendCiCamHandle != null && mFrontendCiCamId == ciCamId) {
+ int result = nativeUnlinkCiCam(ciCamId);
+ if (result == RESULT_SUCCESS) {
+ mTunerResourceManager.releaseCiCam(mFrontendCiCamHandle, mClientId);
+ mFrontendCiCamId = null;
+ mFrontendCiCamHandle = null;
+ }
+ return result;
}
}
return RESULT_UNAVAILABLE;
@@ -1268,7 +1258,8 @@ public class Tuner implements AutoCloseable {
private boolean requestLnb() {
int[] lnbHandle = new int[1];
- TunerLnbRequest request = new TunerLnbRequest(mClientId);
+ TunerLnbRequest request = new TunerLnbRequest();
+ request.clientId = mClientId;
boolean granted = mTunerResourceManager.requestLnb(request, lnbHandle);
if (granted) {
mLnbHandle = lnbHandle[0];
@@ -1356,7 +1347,8 @@ public class Tuner implements AutoCloseable {
private boolean requestDemux() {
int[] demuxHandle = new int[1];
- TunerDemuxRequest request = new TunerDemuxRequest(mClientId);
+ TunerDemuxRequest request = new TunerDemuxRequest();
+ request.clientId = mClientId;
boolean granted = mTunerResourceManager.requestDemux(request, demuxHandle);
if (granted) {
mDemuxHandle = demuxHandle[0];
@@ -1367,7 +1359,8 @@ public class Tuner implements AutoCloseable {
private Descrambler requestDescrambler() {
int[] descramblerHandle = new int[1];
- TunerDescramblerRequest request = new TunerDescramblerRequest(mClientId);
+ TunerDescramblerRequest request = new TunerDescramblerRequest();
+ request.clientId = mClientId;
boolean granted = mTunerResourceManager.requestDescrambler(request, descramblerHandle);
if (!granted) {
return null;
@@ -1385,6 +1378,18 @@ public class Tuner implements AutoCloseable {
return descrambler;
}
+ private boolean requestFrontendCiCam(int ciCamId) {
+ int[] ciCamHandle = new int[1];
+ TunerCiCamRequest request = new TunerCiCamRequest();
+ request.clientId = mClientId;
+ request.ciCamId = ciCamId;
+ boolean granted = mTunerResourceManager.requestCiCam(request, ciCamHandle);
+ if (granted) {
+ mFrontendCiCamHandle = ciCamHandle[0];
+ }
+ return granted;
+ }
+
private boolean checkResource(int resourceType) {
switch (resourceType) {
case TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND: {
@@ -1411,6 +1416,13 @@ public class Tuner implements AutoCloseable {
return true;
}
+ private boolean checkCiCamResource(int ciCamId) {
+ if (mFrontendCiCamHandle == null && !requestFrontendCiCam(ciCamId)) {
+ return false;
+ }
+ return true;
+ }
+
/* package */ void releaseLnb() {
if (mLnbHandle != null) {
// LNB handle can be null if it's opened by name.
diff --git a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
index c4b622d0fba9..2f2cd964d6f3 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
@@ -47,7 +47,7 @@ public class DvrRecorder implements AutoCloseable {
private static int sInstantId = 0;
private int mSegmentId = 0;
private int mOverflow;
- private Boolean mIsStopped = null;
+ private Boolean mIsStopped = true;
private native int nativeAttachFilter(Filter filter);
private native int nativeDetachFilter(Filter filter);
diff --git a/media/java/android/media/tv/tunerresourcemanager/Android.bp b/media/java/android/media/tv/tunerresourcemanager/Android.bp
index c65d25a03813..02390bb7c31b 100644
--- a/media/java/android/media/tv/tunerresourcemanager/Android.bp
+++ b/media/java/android/media/tv/tunerresourcemanager/Android.bp
@@ -1,17 +1,36 @@
filegroup {
- name: "framework-media-tv-tunerresourcemanager-sources",
+ name: "framework-media-tv-tunerresourcemanager-sources-aidl",
srcs: [
- "*.java",
- "*.aidl",
+ "aidl/android/media/tv/tunerresourcemanager/CasSessionRequest.aidl",
+ "aidl/android/media/tv/tunerresourcemanager/IResourcesReclaimListener.aidl",
+ "aidl/android/media/tv/tunerresourcemanager/ResourceClientProfile.aidl",
+ "aidl/android/media/tv/tunerresourcemanager/TunerCiCamRequest.aidl",
+ "aidl/android/media/tv/tunerresourcemanager/TunerDemuxRequest.aidl",
+ "aidl/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.aidl",
+ "aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl",
+ "aidl/android/media/tv/tunerresourcemanager/TunerFrontendRequest.aidl",
+ "aidl/android/media/tv/tunerresourcemanager/TunerLnbRequest.aidl",
+ "aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl",
],
- path: ".",
+ path: "aidl",
}
-java_library {
- name: "framework-media-tv-trm-sources",
- srcs: [":framework-media-tv-tunerresourcemanager-sources"],
- installable: true,
- visibility: [
- "//frameworks/base",
+aidl_interface {
+ name: "tv_tuner_resource_manager_aidl_interface",
+ unstable: true,
+ local_include_dir: "aidl",
+ backend: {
+ java: {
+ sdk_version: "current",
+ },
+ cpp: {
+ enabled: true,
+ },
+ ndk: {
+ enabled: true,
+ },
+ },
+ srcs: [
+ ":framework-media-tv-tunerresourcemanager-sources-aidl",
],
-} \ No newline at end of file
+}
diff --git a/media/java/android/media/tv/tunerresourcemanager/CasSessionRequest.java b/media/java/android/media/tv/tunerresourcemanager/CasSessionRequest.java
deleted file mode 100644
index 59802ff8c3f8..000000000000
--- a/media/java/android/media/tv/tunerresourcemanager/CasSessionRequest.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * 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.tv.tunerresourcemanager;
-
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.Log;
-
-/**
- * Information required to request a Cas Session.
- *
- * @hide
- */
-public final class CasSessionRequest implements Parcelable {
- static final String TAG = "CasSessionRequest";
-
- public static final
- @NonNull
- Parcelable.Creator<CasSessionRequest> CREATOR =
- new Parcelable.Creator<CasSessionRequest>() {
- @Override
- public CasSessionRequest createFromParcel(Parcel source) {
- try {
- return new CasSessionRequest(source);
- } catch (Exception e) {
- Log.e(TAG, "Exception creating CasSessionRequest from parcel", e);
- return null;
- }
- }
-
- @Override
- public CasSessionRequest[] newArray(int size) {
- return new CasSessionRequest[size];
- }
- };
-
- /**
- * Client id of the client that sends the request.
- */
- private final int mClientId;
-
- /**
- * System id of the requested cas.
- */
- private final int mCasSystemId;
-
- private CasSessionRequest(@NonNull Parcel source) {
- mClientId = source.readInt();
- mCasSystemId = source.readInt();
- }
-
- /**
- * Constructs a new {@link CasSessionRequest} with the given parameters.
- *
- * @param clientId id of the client.
- * @param casSystemId the cas system id that the client is requesting.
- */
- public CasSessionRequest(int clientId,
- int casSystemId) {
- mClientId = clientId;
- mCasSystemId = casSystemId;
- }
-
- /**
- * Returns the id of the client.
- */
- public int getClientId() {
- return mClientId;
- }
-
- /**
- * Returns the cas system id requested.
- */
- public int getCasSystemId() {
- return mCasSystemId;
- }
-
- // Parcelable
- @Override
- public int describeContents() {
- return 0;
- }
-
- @NonNull
- @Override
- public String toString() {
- StringBuilder b = new StringBuilder(128);
- b.append("CasSessionRequest {clientId=").append(mClientId);
- b.append(", casSystemId=").append(mCasSystemId);
- b.append("}");
- return b.toString();
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeInt(mClientId);
- dest.writeInt(mCasSystemId);
- }
-}
diff --git a/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java b/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java
deleted file mode 100644
index 28f1ac916690..000000000000
--- a/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * 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.tv.tunerresourcemanager;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.Log;
-
-/**
- * A profile of a resource client. This profile is used to register the client info
- * with the Tuner Resource Manager(TRM).
- *
- * @hide
- */
-public final class ResourceClientProfile implements Parcelable {
- static final String TAG = "ResourceClientProfile";
-
- public static final
- @NonNull
- Parcelable.Creator<ResourceClientProfile> CREATOR =
- new Parcelable.Creator<ResourceClientProfile>() {
- @Override
- public ResourceClientProfile createFromParcel(Parcel source) {
- try {
- return new ResourceClientProfile(source);
- } catch (Exception e) {
- Log.e(TAG, "Exception creating ResourceClientProfile from parcel", e);
- return null;
- }
- }
-
- @Override
- public ResourceClientProfile[] newArray(int size) {
- return new ResourceClientProfile[size];
- }
- };
-
- /**
- * This is used by TRM to get TV App’s processId from TIF.
- * The processId will be used to identify foreground applications.
- *
- * <p>MediaCas, Tuner and TvInputHardwareManager get tvInputSessionId from TIS.
- * If mTvInputSessionId is UNKNOWN, the client is always background.
- */
- private final String mTvInputSessionId;
-
- /**
- * Usage of the client.
- */
- private final int mUseCase;
-
- private ResourceClientProfile(@NonNull Parcel source) {
- mTvInputSessionId = source.readString();
- mUseCase = source.readInt();
- }
-
- /**
- * Constructs a new {@link ResourceClientProfile} with the given parameters.
- *
- * @param tvInputSessionId the unique id of the session owned by the client.
- * @param useCase the usage of the client. Suggested priority hints are
- * {@link android.media.tv.TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK}
- * {@link android.media.tv.TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE}
- * {@link android.media.tv.TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD}.
- * New [use case : priority value] pair can be defined in the manifest by the
- * OEM. The id of the useCaseVendor should be passed through this parameter. Any
- * undefined use case would cause IllegalArgumentException.
- */
- public ResourceClientProfile(@Nullable String tvInputSessionId,
- int useCase) {
- mTvInputSessionId = tvInputSessionId;
- mUseCase = useCase;
- }
-
- /**
- * Returns the tv input session id of the client.
- *
- * @return the value of the tv input session id.
- */
- @Nullable
- public String getTvInputSessionId() {
- return mTvInputSessionId;
- }
-
- /**
- * Returns the user usage of the client.
- *
- * @return the value of use case.
- */
- public int getUseCase() {
- return mUseCase;
- }
-
- // Parcelable
- @Override
- public int describeContents() {
- return 0;
- }
-
- @NonNull
- @Override
- public String toString() {
- StringBuilder b = new StringBuilder(128);
- b.append("ResourceClientProfile {tvInputSessionId=").append(mTvInputSessionId);
- b.append(", useCase=").append(mUseCase);
- b.append("}");
- return b.toString();
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeString(mTvInputSessionId);
- dest.writeInt(mUseCase);
- }
-}
diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerDemuxRequest.java b/media/java/android/media/tv/tunerresourcemanager/TunerDemuxRequest.java
deleted file mode 100644
index 34a77616f62e..000000000000
--- a/media/java/android/media/tv/tunerresourcemanager/TunerDemuxRequest.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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.tv.tunerresourcemanager;
-
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.Log;
-
-/**
- * Information required to request a Tuner Demux.
- *
- * @hide
- */
-public final class TunerDemuxRequest implements Parcelable {
- static final String TAG = "TunerDemuxRequest";
-
- public static final
- @NonNull
- Parcelable.Creator<TunerDemuxRequest> CREATOR =
- new Parcelable.Creator<TunerDemuxRequest>() {
- @Override
- public TunerDemuxRequest createFromParcel(Parcel source) {
- try {
- return new TunerDemuxRequest(source);
- } catch (Exception e) {
- Log.e(TAG, "Exception creating TunerDemuxRequest from parcel", e);
- return null;
- }
- }
-
- @Override
- public TunerDemuxRequest[] newArray(int size) {
- return new TunerDemuxRequest[size];
- }
- };
-
- /**
- * Client id of the client that sends the request.
- */
- private final int mClientId;
-
- private TunerDemuxRequest(@NonNull Parcel source) {
- mClientId = source.readInt();
- }
-
- /**
- * Constructs a new {@link TunerDemuxRequest} with the given parameters.
- *
- * @param clientId id of the client.
- */
- public TunerDemuxRequest(int clientId) {
- mClientId = clientId;
- }
-
- /**
- * Returns the id of the client.
- */
- public int getClientId() {
- return mClientId;
- }
-
- // Parcelable
- @Override
- public int describeContents() {
- return 0;
- }
-
- @NonNull
- @Override
- public String toString() {
- StringBuilder b = new StringBuilder(128);
- b.append("TunerDemuxRequest {clientId=").append(mClientId);
- b.append("}");
- return b.toString();
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeInt(mClientId);
- }
-}
diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.java b/media/java/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.java
deleted file mode 100644
index 58162879f8cf..000000000000
--- a/media/java/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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.tv.tunerresourcemanager;
-
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.Log;
-
-/**
- * Information required to request a Tuner Descrambler.
- *
- * @hide
- */
-public final class TunerDescramblerRequest implements Parcelable {
- static final String TAG = "TunerDescramblerRequest";
-
- public static final
- @NonNull
- Parcelable.Creator<TunerDescramblerRequest> CREATOR =
- new Parcelable.Creator<TunerDescramblerRequest>() {
- @Override
- public TunerDescramblerRequest createFromParcel(Parcel source) {
- try {
- return new TunerDescramblerRequest(source);
- } catch (Exception e) {
- Log.e(TAG, "Exception creating TunerDescramblerRequest from parcel", e);
- return null;
- }
- }
-
- @Override
- public TunerDescramblerRequest[] newArray(int size) {
- return new TunerDescramblerRequest[size];
- }
- };
-
- /**
- * Client id of the client that sends the request.
- */
- private final int mClientId;
-
- private TunerDescramblerRequest(@NonNull Parcel source) {
- mClientId = source.readInt();
- }
-
- /**
- * Constructs a new {@link TunerDescramblerRequest} with the given parameters.
- *
- * @param clientId id of the client.
- */
- public TunerDescramblerRequest(int clientId) {
- mClientId = clientId;
- }
-
- /**
- * Returns the id of the client.
- */
- public int getClientId() {
- return mClientId;
- }
-
- // Parcelable
- @Override
- public int describeContents() {
- return 0;
- }
-
- @NonNull
- @Override
- public String toString() {
- StringBuilder b = new StringBuilder(128);
- b.append("TunerDescramblerRequest {clientId=").append(mClientId);
- b.append("}");
- return b.toString();
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeInt(mClientId);
- }
-}
diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerFrontendInfo.java b/media/java/android/media/tv/tunerresourcemanager/TunerFrontendInfo.java
deleted file mode 100644
index ef50aacf43b5..000000000000
--- a/media/java/android/media/tv/tunerresourcemanager/TunerFrontendInfo.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * 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.tv.tunerresourcemanager;
-
-import android.annotation.NonNull;
-import android.media.tv.tuner.frontend.FrontendSettings.Type;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.Log;
-
-/**
- * Simple container of the FrontendInfo struct defined in the TunerHAL 1.0 interface.
- *
- * <p>Note that this object is defined to pass necessary frontend info between the
- * Tuner Resource Manager and the client. It includes partial information in
- * {@link FrontendInfo}.
- *
- * @hide
- */
-public final class TunerFrontendInfo implements Parcelable {
- static final String TAG = "TunerFrontendInfo";
-
- public static final
- @NonNull
- Parcelable.Creator<TunerFrontendInfo> CREATOR =
- new Parcelable.Creator<TunerFrontendInfo>() {
- @Override
- public TunerFrontendInfo createFromParcel(Parcel source) {
- try {
- return new TunerFrontendInfo(source);
- } catch (Exception e) {
- Log.e(TAG, "Exception creating TunerFrontendInfo from parcel", e);
- return null;
- }
- }
-
- @Override
- public TunerFrontendInfo[] newArray(int size) {
- return new TunerFrontendInfo[size];
- }
- };
-
- private final int mHandle;
-
- @Type
- private final int mFrontendType;
-
- /**
- * Frontends are assigned with the same exclusiveGroupId if they can't
- * function at same time. For instance, they share same hardware module.
- */
- private final int mExclusiveGroupId;
-
- private TunerFrontendInfo(@NonNull Parcel source) {
- mHandle = source.readInt();
- mFrontendType = source.readInt();
- mExclusiveGroupId = source.readInt();
- }
-
- /**
- * Constructs a new {@link TunerFrontendInfo} with the given parameters.
- *
- * @param handle frontend handle
- * @param frontendType the type of the frontend.
- * @param exclusiveGroupId the group id of the frontend. FE with the same
- group id can't function at the same time.
- */
- public TunerFrontendInfo(int handle,
- @Type int frontendType,
- int exclusiveGroupId) {
- mHandle = handle;
- mFrontendType = frontendType;
- mExclusiveGroupId = exclusiveGroupId;
- }
-
- /**
- * Returns the frontend handle.
- *
- * @return the value of the frontend handle.
- */
- public int getHandle() {
- return mHandle;
- }
-
- /**
- * Returns the application id that requests the tuner frontend resource.
- *
- * @return the value of the frontend type.
- */
- @Type
- public int getFrontendType() {
- return mFrontendType;
- }
-
- /**
- * Returns the exclusiveGroupId. Frontends with the same exclusiveGroupId
- * can't function at same time.
- *
- * @return the value of the exclusive group id.
- */
- public int getExclusiveGroupId() {
- return mExclusiveGroupId;
- }
-
- // Parcelable
- @Override
- public int describeContents() {
- return 0;
- }
-
- @NonNull
- @Override
- public String toString() {
- StringBuilder b = new StringBuilder(128);
- b.append("TunerFrontendInfo {handle=").append(mHandle);
- b.append(", frontendType=").append(mFrontendType);
- b.append(", exclusiveGroupId=").append(mExclusiveGroupId);
- b.append("}");
- return b.toString();
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeInt(mHandle);
- dest.writeInt(mFrontendType);
- dest.writeInt(mExclusiveGroupId);
- }
-}
diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerFrontendRequest.java b/media/java/android/media/tv/tunerresourcemanager/TunerFrontendRequest.java
deleted file mode 100644
index 12f8032ab99b..000000000000
--- a/media/java/android/media/tv/tunerresourcemanager/TunerFrontendRequest.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * 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.tv.tunerresourcemanager;
-
-import android.annotation.NonNull;
-import android.media.tv.tuner.frontend.FrontendSettings.Type;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.Log;
-
-/**
- * Information required to request a Tuner Frontend.
- *
- * @hide
- */
-public final class TunerFrontendRequest implements Parcelable {
- static final String TAG = "TunerFrontendRequest";
-
- public static final
- @NonNull
- Parcelable.Creator<TunerFrontendRequest> CREATOR =
- new Parcelable.Creator<TunerFrontendRequest>() {
- @Override
- public TunerFrontendRequest createFromParcel(Parcel source) {
- try {
- return new TunerFrontendRequest(source);
- } catch (Exception e) {
- Log.e(TAG, "Exception creating TunerFrontendRequest from parcel", e);
- return null;
- }
- }
-
- @Override
- public TunerFrontendRequest[] newArray(int size) {
- return new TunerFrontendRequest[size];
- }
- };
-
- private final int mClientId;
- @Type
- private final int mFrontendType;
-
- private TunerFrontendRequest(@NonNull Parcel source) {
- mClientId = source.readInt();
- mFrontendType = source.readInt();
- }
-
- /**
- * Constructs a new {@link TunerFrontendRequest} with the given parameters.
- *
- * @param clientId the unique id of the client returned when registering profile.
- * @param frontendType the type of the requested frontend.
- */
- public TunerFrontendRequest(int clientId,
- @Type int frontendType) {
- mClientId = clientId;
- mFrontendType = frontendType;
- }
-
- /**
- * Returns the client id that requests the tuner frontend resource.
- *
- * @return the value of the client id.
- */
- public int getClientId() {
- return mClientId;
- }
-
- /**
- * Returns the frontend type that the client requests for.
- *
- * @return the value of the requested frontend type.
- */
- @Type
- public int getFrontendType() {
- return mFrontendType;
- }
-
- // Parcelable
- @Override
- public int describeContents() {
- return 0;
- }
-
- @NonNull
- @Override
- public String toString() {
- StringBuilder b = new StringBuilder(128);
- b.append("TunerFrontendRequest {clientId=").append(mClientId);
- b.append(", frontendType=").append(mFrontendType);
- b.append("}");
- return b.toString();
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeInt(mClientId);
- dest.writeInt(mFrontendType);
- }
-}
diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerLnbRequest.java b/media/java/android/media/tv/tunerresourcemanager/TunerLnbRequest.java
deleted file mode 100644
index 5ed7f3f546f4..000000000000
--- a/media/java/android/media/tv/tunerresourcemanager/TunerLnbRequest.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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.tv.tunerresourcemanager;
-
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.Log;
-
-/**
- * Information required to request a Tuner Lnb.
- *
- * @hide
- */
-public final class TunerLnbRequest implements Parcelable {
- static final String TAG = "TunerLnbRequest";
-
- public static final
- @NonNull
- Parcelable.Creator<TunerLnbRequest> CREATOR =
- new Parcelable.Creator<TunerLnbRequest>() {
- @Override
- public TunerLnbRequest createFromParcel(Parcel source) {
- try {
- return new TunerLnbRequest(source);
- } catch (Exception e) {
- Log.e(TAG, "Exception creating TunerLnbRequest from parcel", e);
- return null;
- }
- }
-
- @Override
- public TunerLnbRequest[] newArray(int size) {
- return new TunerLnbRequest[size];
- }
- };
-
- /**
- * Client id of the client that sends the request.
- */
- private final int mClientId;
-
- private TunerLnbRequest(@NonNull Parcel source) {
- mClientId = source.readInt();
- }
-
- /**
- * Constructs a new {@link TunerLnbRequest} with the given parameters.
- *
- * @param clientId the id of the client.
- */
- public TunerLnbRequest(int clientId) {
- mClientId = clientId;
- }
-
- /**
- * Returns the id of the client
- */
- public int getClientId() {
- return mClientId;
- }
-
- // Parcelable
- @Override
- public int describeContents() {
- return 0;
- }
-
- @NonNull
- @Override
- public String toString() {
- StringBuilder b = new StringBuilder(128);
- b.append("TunerLnbRequest {clientId=").append(mClientId);
- b.append("}");
- return b.toString();
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeInt(mClientId);
- }
-}
diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
index be102d8acc10..6f7adbc65318 100644
--- a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
+++ b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
@@ -74,6 +74,7 @@ public class TunerResourceManager {
TUNER_RESOURCE_TYPE_DESCRAMBLER,
TUNER_RESOURCE_TYPE_LNB,
TUNER_RESOURCE_TYPE_CAS_SESSION,
+ TUNER_RESOURCE_TYPE_FRONTEND_CICAM,
TUNER_RESOURCE_TYPE_MAX,
})
@Retention(RetentionPolicy.SOURCE)
@@ -84,7 +85,8 @@ public class TunerResourceManager {
public static final int TUNER_RESOURCE_TYPE_DESCRAMBLER = 2;
public static final int TUNER_RESOURCE_TYPE_LNB = 3;
public static final int TUNER_RESOURCE_TYPE_CAS_SESSION = 4;
- public static final int TUNER_RESOURCE_TYPE_MAX = 5;
+ public static final int TUNER_RESOURCE_TYPE_FRONTEND_CICAM = 5;
+ public static final int TUNER_RESOURCE_TYPE_MAX = 6;
private final ITunerResourceManager mService;
private final int mUserId;
@@ -379,6 +381,38 @@ public class TunerResourceManager {
}
/**
+ * Requests a CiCam resource.
+ *
+ * <p>There are three possible scenarios:
+ * <ul>
+ * <li>If there is CiCam available, the API would send the id back.
+ *
+ * <li>If no CiCam is available but the current request info can show higher priority than
+ * other uses of the CiCam, the API will send
+ * {@link IResourcesReclaimListener#onReclaimResources()} to the {@link Tuner}. Tuner would
+ * handle the resource reclaim on the holder of lower priority and notify the holder of its
+ * resource loss.
+ *
+ * <p><strong>Note:</strong> {@link #updateCasInfo(int, int)} must be called before this
+ * request.
+ *
+ * @param request {@link TunerCiCamRequest} information of the current request.
+ * @param ciCamHandle a one-element array to return the granted ciCam handle.
+ * If no ciCam granted, this will return {@link #INVALID_RESOURCE_HANDLE}.
+ *
+ * @return true if there is ciCam granted.
+ */
+ public boolean requestCiCam(TunerCiCamRequest request, int[] ciCamHandle) {
+ boolean result = false;
+ try {
+ result = mService.requestCiCam(request, ciCamHandle);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ return result;
+ }
+
+ /**
* Requests a Tuner Lnb resource.
*
* <p>There are three possible scenarios:
@@ -482,6 +516,25 @@ public class TunerResourceManager {
}
/**
+ * Notifies the TRM that the given CiCam has been released.
+ *
+ * <p>Client must call this whenever it releases a CiCam.
+ *
+ * <p><strong>Note:</strong> {@link #updateCasInfo(int, int)} must be called before this
+ * release.
+ *
+ * @param ciCamHandle the handle of the releasing CiCam.
+ * @param clientId the id of the client that is releasing the CiCam.
+ */
+ public void releaseCiCam(int ciCamHandle, int clientId) {
+ try {
+ mService.releaseCiCam(ciCamHandle, clientId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Notifies the TRM that the Lnb with the given id has been released.
*
* <p>Client must call this whenever it releases an Lnb.
diff --git a/media/java/android/media/tv/tunerresourcemanager/CasSessionRequest.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/CasSessionRequest.aidl
index c918d88b479a..88f591551e8a 100644
--- a/media/java/android/media/tv/tunerresourcemanager/CasSessionRequest.aidl
+++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/CasSessionRequest.aidl
@@ -21,4 +21,8 @@ package android.media.tv.tunerresourcemanager;
*
* @hide
*/
-parcelable CasSessionRequest; \ No newline at end of file
+parcelable CasSessionRequest {
+ int clientId;
+
+ int casSystemId;
+} \ No newline at end of file
diff --git a/media/java/android/media/tv/tunerresourcemanager/IResourcesReclaimListener.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/IResourcesReclaimListener.aidl
index 1a4eb2946b8c..1a4eb2946b8c 100644
--- a/media/java/android/media/tv/tunerresourcemanager/IResourcesReclaimListener.aidl
+++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/IResourcesReclaimListener.aidl
diff --git a/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
index 487b444eb627..a1f6687a1b81 100644
--- a/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
+++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
@@ -19,6 +19,7 @@ package android.media.tv.tunerresourcemanager;
import android.media.tv.tunerresourcemanager.CasSessionRequest;
import android.media.tv.tunerresourcemanager.IResourcesReclaimListener;
import android.media.tv.tunerresourcemanager.ResourceClientProfile;
+import android.media.tv.tunerresourcemanager.TunerCiCamRequest;
import android.media.tv.tunerresourcemanager.TunerDemuxRequest;
import android.media.tv.tunerresourcemanager.TunerDescramblerRequest;
import android.media.tv.tunerresourcemanager.TunerFrontendInfo;
@@ -225,6 +226,31 @@ interface ITunerResourceManager {
boolean requestCasSession(in CasSessionRequest request, out int[] casSessionHandle);
/*
+ * This API is used by the Tuner framework to request an available CuCam.
+ *
+ * <p>There are three possible scenarios:
+ * <ul>
+ * <li>If there is CiCam available, the API would send the handle back.
+ *
+ * <li>If no CiCma is available but the current request info can show higher priority than
+ * other uses of the ciCam, the API will send
+ * {@link ITunerResourceManagerCallback#onReclaimResources()} to the {@link Tuner}. Tuner would
+ * handle the resource reclaim on the holder of lower priority and notify the holder of its
+ * resource loss.
+ *
+ * <li>If no CiCam can be granted, the API would return false.
+ * <ul>
+ *
+ * <p><strong>Note:</strong> {@link #updateCasInfo(int, int)} must be called before this request.
+ *
+ * @param request {@link TunerCiCamRequest} information of the current request.
+ * @param ciCamHandle a one-element array to return the granted ciCam handle.
+ *
+ * @return true if there is CiCam granted.
+ */
+ boolean requestCiCam(in TunerCiCamRequest request, out int[] ciCamHandle);
+
+ /*
* This API is used by the Tuner framework to request an available Lnb from the TunerHAL.
*
* <p>There are three possible scenarios:
@@ -293,6 +319,19 @@ interface ITunerResourceManager {
*/
void releaseCasSession(in int casSessionHandle, int clientId);
+ /**
+ * Notifies the TRM that the given CiCam has been released.
+ *
+ * <p>Client must call this whenever it releases a CiCam.
+ *
+ * <p><strong>Note:</strong> {@link #updateCasInfo(int, int)} must be called before this
+ * release.
+ *
+ * @param ciCamHandle the handle of the releasing CiCam.
+ * @param clientId the id of the client that is releasing the CiCam.
+ */
+ void releaseCiCam(in int ciCamHandle, int clientId);
+
/*
* Notifies the TRM that the Lnb with the given handle was released.
*
diff --git a/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ResourceClientProfile.aidl
index ed90c1dc3996..08c2bb85c0c4 100644
--- a/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.aidl
+++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ResourceClientProfile.aidl
@@ -22,4 +22,8 @@ package android.media.tv.tunerresourcemanager;
*
* @hide
*/
-parcelable ResourceClientProfile; \ No newline at end of file
+parcelable ResourceClientProfile {
+ String tvInputSessionId;
+
+ int useCase;
+} \ No newline at end of file
diff --git a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerCiCamRequest.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerCiCamRequest.aidl
new file mode 100644
index 000000000000..76f9f83ead82
--- /dev/null
+++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerCiCamRequest.aidl
@@ -0,0 +1,28 @@
+/*
+ * 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.tv.tunerresourcemanager;
+
+/**
+ * A wrapper of a ciCam requests that contains all the request info of the client.
+ *
+ * @hide
+ */
+parcelable TunerCiCamRequest {
+ int clientId;
+
+ int ciCamId;
+} \ No newline at end of file
diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerDemuxRequest.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerDemuxRequest.aidl
index 919a215a9ce5..457f90ce866d 100644
--- a/media/java/android/media/tv/tunerresourcemanager/TunerDemuxRequest.aidl
+++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerDemuxRequest.aidl
@@ -21,4 +21,6 @@ package android.media.tv.tunerresourcemanager;
*
* @hide
*/
-parcelable TunerDemuxRequest; \ No newline at end of file
+parcelable TunerDemuxRequest {
+ int clientId;
+} \ No newline at end of file
diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.aidl
index fbafb3bc010e..98ab7301bac9 100644
--- a/media/java/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.aidl
+++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.aidl
@@ -21,4 +21,6 @@ package android.media.tv.tunerresourcemanager;
*
* @hide
*/
-parcelable TunerDescramblerRequest; \ No newline at end of file
+parcelable TunerDescramblerRequest {
+ int clientId;
+} \ No newline at end of file
diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl
index e649c2aa3fd6..edf96ddd4e5a 100644
--- a/media/java/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl
+++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl
@@ -21,4 +21,10 @@ package android.media.tv.tunerresourcemanager;
*
* @hide
*/
-parcelable TunerFrontendInfo; \ No newline at end of file
+parcelable TunerFrontendInfo {
+ int handle;
+
+ int frontendType;
+
+ int exclusiveGroupId;
+}
diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerFrontendRequest.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendRequest.aidl
index 5e48adc075b8..4d9822215842 100644
--- a/media/java/android/media/tv/tunerresourcemanager/TunerFrontendRequest.aidl
+++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendRequest.aidl
@@ -21,4 +21,8 @@ package android.media.tv.tunerresourcemanager;
*
* @hide
*/
-parcelable TunerFrontendRequest; \ No newline at end of file
+parcelable TunerFrontendRequest {
+ int clientId;
+
+ int frontendType;
+} \ No newline at end of file
diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerLnbRequest.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerLnbRequest.aidl
index 0e6fcde51642..1a059ea632f2 100644
--- a/media/java/android/media/tv/tunerresourcemanager/TunerLnbRequest.aidl
+++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerLnbRequest.aidl
@@ -21,4 +21,6 @@ package android.media.tv.tunerresourcemanager;
*
* @hide
*/
-parcelable TunerLnbRequest; \ No newline at end of file
+parcelable TunerLnbRequest {
+ int clientId;
+} \ No newline at end of file
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 25b1b4026265..decf68f26c0e 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -137,12 +137,16 @@ cc_library_shared {
cc_library_shared {
name: "libmedia_tv_tuner",
+
srcs: [
"android_media_tv_Tuner.cpp",
"tuner/DemuxClient.cpp",
+ "tuner/DescramblerClient.cpp",
"tuner/DvrClient.cpp",
"tuner/FilterClient.cpp",
"tuner/FrontendClient.cpp",
+ "tuner/LnbClient.cpp",
+ "tuner/TimeFilterClient.cpp",
"tuner/TunerClient.cpp",
],
@@ -160,6 +164,7 @@ cc_library_shared {
"libnativehelper",
"libutils",
"tv_tuner_aidl_interface-ndk_platform",
+ "tv_tuner_resource_manager_aidl_interface-ndk_platform"
],
defaults: [
"libcodec2-impl-defaults",
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index a4abf3693096..ee2d83b9ca77 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -211,59 +211,46 @@ void DestroyCallback(const C2Buffer * buf, void *arg) {
}
namespace android {
-/////////////// LnbCallback ///////////////////////
-LnbCallback::LnbCallback(jobject lnbObj, LnbId id) : mId(id) {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
- mLnb = env->NewWeakGlobalRef(lnbObj);
-}
-LnbCallback::~LnbCallback() {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
- env->DeleteWeakGlobalRef(mLnb);
- mLnb = NULL;
-}
+/////////////// LnbClientCallbackImpl ///////////////////////
-Return<void> LnbCallback::onEvent(LnbEventType lnbEventType) {
- ALOGD("LnbCallback::onEvent, type=%d", lnbEventType);
+void LnbClientCallbackImpl::onEvent(const LnbEventType lnbEventType) {
+ ALOGD("LnbClientCallbackImpl::onEvent, type=%d", lnbEventType);
JNIEnv *env = AndroidRuntime::getJNIEnv();
env->CallVoidMethod(
- mLnb,
+ mLnbObj,
gFields.onLnbEventID,
(jint)lnbEventType);
- return Void();
}
-Return<void> LnbCallback::onDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage) {
- ALOGD("LnbCallback::onDiseqcMessage");
+
+void LnbClientCallbackImpl::onDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage) {
+ ALOGD("LnbClientCallbackImpl::onDiseqcMessage");
JNIEnv *env = AndroidRuntime::getJNIEnv();
jbyteArray array = env->NewByteArray(diseqcMessage.size());
env->SetByteArrayRegion(
array, 0, diseqcMessage.size(), reinterpret_cast<jbyte*>(diseqcMessage[0]));
env->CallVoidMethod(
- mLnb,
+ mLnbObj,
gFields.onLnbDiseqcMessageID,
array);
- return Void();
}
-/////////////// Lnb ///////////////////////
-
-Lnb::Lnb(sp<ILnb> sp, jobject obj) : mLnbSp(sp) {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
- mLnbObj = env->NewWeakGlobalRef(obj);
+void LnbClientCallbackImpl::setLnb(jweak lnbObj) {
+ ALOGD("LnbClientCallbackImpl::setLnb");
+ mLnbObj = lnbObj;
}
-Lnb::~Lnb() {
+LnbClientCallbackImpl::~LnbClientCallbackImpl() {
JNIEnv *env = AndroidRuntime::getJNIEnv();
- env->DeleteWeakGlobalRef(mLnbObj);
- mLnbObj = NULL;
-}
-
-sp<ILnb> Lnb::getILnb() {
- return mLnbSp;
+ if (mLnbObj != NULL) {
+ env->DeleteWeakGlobalRef(mLnbObj);
+ mLnbObj = NULL;
+ }
}
/////////////// DvrClientCallbackImpl ///////////////////////
+
void DvrClientCallbackImpl::onRecordStatus(RecordStatus status) {
ALOGD("DvrClientCallbackImpl::onRecordStatus");
JNIEnv *env = AndroidRuntime::getJNIEnv();
@@ -854,29 +841,9 @@ void FilterClientCallbackImpl::setFilter(jweak filterObj, sp<FilterClient> filte
mFilterClient = filterClient;
}
-/////////////// TimeFilter ///////////////////////
-
-TimeFilter::TimeFilter(sp<ITimeFilter> sp, jobject obj) : mTimeFilterSp(sp) {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
- mTimeFilterObj = env->NewWeakGlobalRef(obj);
-}
-
-TimeFilter::~TimeFilter() {
- ALOGD("~TimeFilter");
- JNIEnv *env = AndroidRuntime::getJNIEnv();
-
- env->DeleteWeakGlobalRef(mTimeFilterObj);
- mTimeFilterObj = NULL;
-}
-
-sp<ITimeFilter> TimeFilter::getITimeFilter() {
- return mTimeFilterSp;
-}
-
/////////////// FrontendClientCallbackImpl ///////////////////////
-FrontendClientCallbackImpl::FrontendClientCallbackImpl(
- jweak tunerObj, FrontendId id) : mObject(tunerObj), mId(id) {}
+FrontendClientCallbackImpl::FrontendClientCallbackImpl(jweak tunerObj) : mObject(tunerObj) {}
void FrontendClientCallbackImpl::onEvent(FrontendEventType frontendEventType) {
ALOGD("FrontendClientCallbackImpl::onEvent, type=%d", frontendEventType);
@@ -1118,8 +1085,6 @@ void FrontendClientCallbackImpl::onScanMessageExt1_1(FrontendScanMessageTypeExt1
/////////////// Tuner ///////////////////////
-sp<ITuner> JTuner::mTuner;
-sp<::android::hardware::tv::tuner::V1_1::ITuner> JTuner::mTuner_1_1;
sp<TunerClient> JTuner::mTunerClient;
JTuner::JTuner(JNIEnv *env, jobject thiz)
@@ -1129,29 +1094,22 @@ JTuner::JTuner(JNIEnv *env, jobject thiz)
mClass = (jclass)env->NewGlobalRef(clazz);
mObject = env->NewWeakGlobalRef(thiz);
- // TODO: remove after migrate to client lib
- if (mTuner == NULL) {
- mTuner = getTunerService();
- }
if (mTunerClient == NULL) {
mTunerClient = new TunerClient();
}
}
JTuner::~JTuner() {
- if (mFe != NULL) {
- mFe->close();
+ if (mFeClient != NULL) {
+ mFeClient->close();
}
- if (mDemux != NULL) {
- mDemux->close();
+ if (mDemuxClient != NULL) {
+ mDemuxClient->close();
}
JNIEnv *env = AndroidRuntime::getJNIEnv();
env->DeleteWeakGlobalRef(mObject);
env->DeleteGlobalRef(mClass);
- mTuner = NULL;
- mFe = NULL;
- mDemux = NULL;
mTunerClient = NULL;
mFeClient = NULL;
mDemuxClient = NULL;
@@ -1159,23 +1117,6 @@ JTuner::~JTuner() {
mObject = NULL;
}
-sp<ITuner> JTuner::getTunerService() {
- if (mTuner == nullptr) {
- mTuner_1_1 = ::android::hardware::tv::tuner::V1_1::ITuner::getService();
-
- if (mTuner_1_1 == nullptr) {
- ALOGW("Failed to get tuner 1.1 service.");
- mTuner = ITuner::getService();
- if (mTuner == nullptr) {
- ALOGW("Failed to get tuner 1.0 service.");
- }
- } else {
- mTuner = static_cast<sp<ITuner>>(mTuner_1_1);
- }
- }
- return mTuner;
-}
-
jint JTuner::getTunerVersion() {
ALOGD("JTuner::getTunerVersion()");
return (jint) mTunerClient->getHalTunerVersion();
@@ -1205,27 +1146,6 @@ jobject JTuner::getFrontendIds() {
}
jobject JTuner::openFrontendByHandle(int feHandle) {
- sp<IFrontend> fe;
- Result res;
- uint32_t id = getResourceIdFromHandle(feHandle);
-
- mTuner->openFrontendById(id, [&](Result r, const sp<IFrontend>& frontend) {
- fe = frontend;
- res = r;
- });
- if (res != Result::SUCCESS || fe == nullptr) {
- ALOGE("Failed to open frontend");
- return NULL;
- }
- mFe = fe;
- mFe_1_1 = ::android::hardware::tv::tuner::V1_1::IFrontend::castFrom(mFe);
- mFeId = id;
- if (mDemux != NULL) {
- mDemux->setFrontendDataSource(mFeId);
- }
-
- jint jId = (jint) id;
-
// TODO: Handle reopening frontend with different handle
sp<FrontendClient> feClient = mTunerClient->openFrontend(feHandle);
if (feClient == NULL) {
@@ -1235,11 +1155,10 @@ jobject JTuner::openFrontendByHandle(int feHandle) {
mFeClient = feClient;
mFeId = mFeClient->getId();
- jId = (jint) id;
if (mDemuxClient != NULL) {
mDemuxClient->setFrontendDataSource(mFeClient);
}
- sp<FrontendClientCallbackImpl> feClientCb = new FrontendClientCallbackImpl(mObject, id);
+ sp<FrontendClientCallbackImpl> feClientCb = new FrontendClientCallbackImpl(mObject);
mFeClient->setCallback(feClientCb);
JNIEnv *env = AndroidRuntime::getJNIEnv();
@@ -1248,7 +1167,7 @@ jobject JTuner::openFrontendByHandle(int feHandle) {
env->FindClass("android/media/tv/tuner/Tuner$Frontend"),
gFields.frontendInitID,
mObject,
- (jint) jId);
+ (jint) mFeId);
}
jobject JTuner::getAnalogFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps) {
@@ -1473,85 +1392,63 @@ jobject JTuner::getFrontendInfo(int id) {
maxSymbolRate, acquireRange, exclusiveGroupId, statusCaps, jcaps);
}
-jintArray JTuner::getLnbIds() {
- ALOGD("JTuner::getLnbIds()");
- Result res;
- hidl_vec<LnbId> lnbIds;
- mTuner->getLnbIds([&](Result r, const hidl_vec<LnbId>& ids) {
- lnbIds = ids;
- res = r;
- });
- if (res != Result::SUCCESS || lnbIds.size() == 0) {
- ALOGW("Lnb isn't available");
+jobject JTuner::openLnbByHandle(int handle) {
+ if (mTunerClient == NULL) {
return NULL;
}
- mLnbIds = lnbIds;
- JNIEnv *env = AndroidRuntime::getJNIEnv();
-
- jintArray ids = env->NewIntArray(mLnbIds.size());
- env->SetIntArrayRegion(ids, 0, mLnbIds.size(), reinterpret_cast<jint*>(&mLnbIds[0]));
-
- return ids;
-}
+ sp<LnbClient> lnbClient;
+ sp<LnbClientCallbackImpl> callback = new LnbClientCallbackImpl();
+ lnbClient = mTunerClient->openLnb(handle);
+ if (lnbClient == NULL) {
+ ALOGD("Failed to open lnb, handle = %d", handle);
+ return NULL;
+ }
-jobject JTuner::openLnbById(int id) {
- sp<ILnb> iLnbSp;
- Result r;
- mTuner->openLnbById(id, [&](Result res, const sp<ILnb>& lnb) {
- r = res;
- iLnbSp = lnb;
- });
- if (r != Result::SUCCESS || iLnbSp == nullptr) {
- ALOGE("Failed to open lnb");
+ if (lnbClient->setCallback(callback) != Result::SUCCESS) {
+ ALOGD("Failed to set lnb callback");
return NULL;
}
- mLnb = iLnbSp;
JNIEnv *env = AndroidRuntime::getJNIEnv();
jobject lnbObj = env->NewObject(
env->FindClass("android/media/tv/tuner/Lnb"),
- gFields.lnbInitID,
- (jint) id);
-
- sp<LnbCallback> lnbCb = new LnbCallback(lnbObj, id);
- mLnb->setCallback(lnbCb);
+ gFields.lnbInitID);
- sp<Lnb> lnbSp = new Lnb(iLnbSp, lnbObj);
- lnbSp->incStrong(lnbObj);
- env->SetLongField(lnbObj, gFields.lnbContext, (jlong) lnbSp.get());
+ lnbClient->incStrong(lnbObj);
+ env->SetLongField(lnbObj, gFields.lnbContext, (jlong)lnbClient.get());
+ callback->setLnb(env->NewWeakGlobalRef(lnbObj));
return lnbObj;
}
jobject JTuner::openLnbByName(jstring name) {
+ if (mTunerClient == NULL) {
+ return NULL;
+ }
+
JNIEnv *env = AndroidRuntime::getJNIEnv();
std::string lnbName(env->GetStringUTFChars(name, nullptr));
- sp<ILnb> iLnbSp;
- Result res;
- LnbId id;
- mTuner->openLnbByName(lnbName, [&](Result r, LnbId lnbId, const sp<ILnb>& lnb) {
- res = r;
- iLnbSp = lnb;
- id = lnbId;
- });
- if (res != Result::SUCCESS || iLnbSp == nullptr) {
- ALOGE("Failed to open lnb");
+ sp<LnbClient> lnbClient;
+ sp<LnbClientCallbackImpl> callback = new LnbClientCallbackImpl();
+ lnbClient = mTunerClient->openLnbByName(lnbName);
+ if (lnbClient == NULL) {
+ ALOGD("Failed to open lnb by name, name = %s", lnbName.c_str());
+ return NULL;
+ }
+
+ if (lnbClient->setCallback(callback) != Result::SUCCESS) {
+ ALOGD("Failed to set lnb callback");
return NULL;
}
- mLnb = iLnbSp;
jobject lnbObj = env->NewObject(
env->FindClass("android/media/tv/tuner/Lnb"),
- gFields.lnbInitID,
- id);
-
- sp<LnbCallback> lnbCb = new LnbCallback(lnbObj, id);
- mLnb->setCallback(lnbCb);
+ gFields.lnbInitID);
- sp<Lnb> lnbSp = new Lnb(iLnbSp, lnbObj);
- lnbSp->incStrong(lnbObj);
- env->SetLongField(lnbObj, gFields.lnbContext, (jlong) lnbSp.get());
+ lnbClient->incStrong(lnbObj);
+ env->SetLongField(lnbObj, gFields.lnbContext, (jlong)lnbClient.get());
+ callback->setLnb(env->NewWeakGlobalRef(lnbObj));
return lnbObj;
}
@@ -1574,109 +1471,66 @@ int JTuner::stopTune() {
int JTuner::scan(const FrontendSettings& settings, FrontendScanType scanType,
const FrontendSettingsExt1_1& settingsExt1_1) {
- if (mFe == NULL) {
- ALOGE("frontend is not initialized");
+ if (mFeClient == NULL) {
+ ALOGE("frontend client is not initialized");
return (int)Result::INVALID_STATE;
}
- Result result;
- sp<::android::hardware::tv::tuner::V1_1::IFrontend> fe_1_1 =
- ::android::hardware::tv::tuner::V1_1::IFrontend::castFrom(mFe);
- if (fe_1_1 == NULL) {
- ALOGD("1.1 frontend is not found. Using 1.0 instead.");
- result = mFe->scan(settings, scanType);
- return (int)result;
- }
-
- result = fe_1_1->scan_1_1(settings, scanType, settingsExt1_1);
+ Result result = mFeClient->scan(settings, scanType, settingsExt1_1);
return (int)result;
}
int JTuner::stopScan() {
- if (mFe == NULL) {
- ALOGE("frontend is not initialized");
+ if (mFeClient == NULL) {
+ ALOGE("frontend client is not initialized");
return (int)Result::INVALID_STATE;
}
- Result result = mFe->stopScan();
+ Result result = mFeClient->stopScan();
return (int)result;
}
-int JTuner::setLnb(int id) {
- if (mFe == NULL) {
- ALOGE("frontend is not initialized");
+int JTuner::setLnb(sp<LnbClient> lnbClient) {
+ if (mFeClient == NULL) {
+ ALOGE("frontend client is not initialized");
return (int)Result::INVALID_STATE;
}
- Result result = mFe->setLnb(id);
+ if (lnbClient == NULL) {
+ ALOGE("lnb is not initialized");
+ return (int)Result::INVALID_STATE;
+ }
+ Result result = mFeClient->setLnb(lnbClient);
return (int)result;
}
int JTuner::setLna(bool enable) {
- if (mFe == NULL) {
- ALOGE("frontend is not initialized");
+ if (mFeClient == NULL) {
+ ALOGE("frontend client is not initialized");
return (int)Result::INVALID_STATE;
}
- Result result = mFe->setLna(enable);
+ Result result = mFeClient->setLna(enable);
return (int)result;
}
-Result JTuner::openDemux() {
- if (mTuner == nullptr || mTunerClient == nullptr) {
+Result JTuner::openDemux(int handle) {
+ if (mTunerClient == nullptr) {
return Result::NOT_INITIALIZED;
}
- Result res = Result::SUCCESS;
-
- if (mDemux == nullptr) {
- uint32_t id;
- sp<IDemux> demuxSp;
- mTuner->openDemux([&](Result r, uint32_t demuxId, const sp<IDemux>& demux) {
- demuxSp = demux;
- id = demuxId;
- res = r;
- ALOGD("open demux, id = %d", demuxId);
- });
- if (res == Result::SUCCESS) {
- mDemux = demuxSp;
- mDemuxId = id;
- if (mFe != NULL) {
- mDemux->setFrontendDataSource(mFeId);
- }
- } else {
- return res;
- }
- }
-
- // TODO: replace demux opening with mTunerClient->openDemux(handle)
- // when DemuxClient is fully ready
if (mDemuxClient == nullptr) {
- sp<DemuxClient> demuxClient = new DemuxClient();
- if (demuxClient == NULL) {
+ mDemuxClient = mTunerClient->openDemux(handle);
+ if (mDemuxClient == NULL) {
ALOGE("Failed to open demux");
return Result::UNKNOWN_ERROR;
}
- mDemuxClient = demuxClient;
- mDemuxClient->setHidlDemux(mDemux);
if (mFeClient != NULL) {
mDemuxClient->setFrontendDataSource(mFeClient);
}
}
- return res;
+ return Result::SUCCESS;
}
jint JTuner::close() {
Result res = Result::SUCCESS;
- if (mFe != NULL) {
- res = mFe->close();
- if (res != Result::SUCCESS) {
- return (jint) res;
- }
- }
- if (mDemux != NULL) {
- res = mDemux->close();
- if (res != Result::SUCCESS) {
- return (jint) res;
- }
- }
if (mFeClient != NULL) {
res = mFeClient->close();
@@ -1726,42 +1580,23 @@ jobject JTuner::getAvSyncTime(jint id) {
int JTuner::connectCiCam(jint id) {
if (mDemuxClient == NULL) {
- Result r = openDemux();
- if (r != Result::SUCCESS) {
- return (int) r;
- }
+ return (int)Result::NOT_INITIALIZED;
}
Result r = mDemuxClient->connectCiCam((int)id);
return (int) r;
}
int JTuner::linkCiCam(int id) {
- if (mFe_1_1 == NULL) {
- ALOGE("frontend 1.1 is not initialized");
+ if (mFeClient == NULL) {
+ ALOGE("frontend client is not initialized");
return (int)Constant::INVALID_LTS_ID;
}
-
- Result res;
- uint32_t ltsId;
- mFe_1_1->linkCiCam(static_cast<uint32_t>(id),
- [&](Result r, uint32_t id) {
- res = r;
- ltsId = id;
- });
-
- if (res != Result::SUCCESS) {
- return (int)Constant::INVALID_LTS_ID;
- }
-
- return (int) ltsId;
+ return mFeClient->linkCiCamToFrontend(id);
}
int JTuner::disconnectCiCam() {
if (mDemuxClient == NULL) {
- Result r = openDemux();
- if (r != Result::SUCCESS) {
- return (int) r;
- }
+ return (int)Result::NOT_INITIALIZED;
}
Result r = mDemuxClient->disconnectCiCam();
return (int) r;
@@ -1769,33 +1604,29 @@ int JTuner::disconnectCiCam() {
int JTuner::unlinkCiCam(int id) {
- if (mFe_1_1 == NULL) {
- ALOGE("frontend 1.1 is not initialized");
+ if (mFeClient == NULL) {
+ ALOGE("frontend client is not initialized");
return (int)Result::INVALID_STATE;
}
- Result r = mFe_1_1->unlinkCiCam(static_cast<uint32_t>(id));
+ Result r = mFeClient->unlinkCiCamToFrontend(id);
return (int) r;
}
jobject JTuner::openDescrambler() {
ALOGD("JTuner::openDescrambler");
- if (mTuner == nullptr || mDemux == nullptr) {
+ if (mTunerClient == nullptr || mDemuxClient == nullptr) {
return NULL;
}
- sp<IDescrambler> descramblerSp;
- Result res;
- mTuner->openDescrambler([&](Result r, const sp<IDescrambler>& descrambler) {
- res = r;
- descramblerSp = descrambler;
- });
+ sp<DescramblerClient> descramblerClient = mTunerClient->openDescrambler(0/*unused*/);
- if (res != Result::SUCCESS || descramblerSp == NULL) {
+ if (descramblerClient == NULL) {
+ ALOGD("Failed to open descrambler");
return NULL;
}
- descramblerSp->setDemuxSource(mDemuxId);
+ descramblerClient->setDemuxSource(mDemuxClient);
JNIEnv *env = AndroidRuntime::getJNIEnv();
jobject descramblerObj =
@@ -1803,14 +1634,14 @@ jobject JTuner::openDescrambler() {
env->FindClass("android/media/tv/tuner/Descrambler"),
gFields.descramblerInitID);
- descramblerSp->incStrong(descramblerObj);
- env->SetLongField(descramblerObj, gFields.descramblerContext, (jlong)descramblerSp.get());
+ descramblerClient->incStrong(descramblerObj);
+ env->SetLongField(descramblerObj, gFields.descramblerContext, (jlong)descramblerClient.get());
return descramblerObj;
}
jobject JTuner::openFilter(DemuxFilterType type, int bufferSize) {
- if (mDemux == NULL || mDemuxClient == NULL) {
+ if (mDemuxClient == NULL) {
return NULL;
}
@@ -1844,20 +1675,7 @@ jobject JTuner::openFilter(DemuxFilterType type, int bufferSize) {
}
jobject JTuner::openTimeFilter() {
- if (mDemux == NULL) {
- if (openDemux() != Result::SUCCESS) {
- return NULL;
- }
- }
- sp<ITimeFilter> iTimeFilterSp;
- Result res;
- mDemux->openTimeFilter(
- [&](Result r, const sp<ITimeFilter>& filter) {
- iTimeFilterSp = filter;
- res = r;
- });
-
- if (res != Result::SUCCESS || iTimeFilterSp == NULL) {
+ if (mDemuxClient == NULL) {
return NULL;
}
@@ -1866,9 +1684,13 @@ jobject JTuner::openTimeFilter() {
env->NewObject(
env->FindClass("android/media/tv/tuner/filter/TimeFilter"),
gFields.timeFilterInitID);
- sp<TimeFilter> timeFilterSp = new TimeFilter(iTimeFilterSp, timeFilterObj);
- timeFilterSp->incStrong(timeFilterObj);
- env->SetLongField(timeFilterObj, gFields.timeFilterContext, (jlong)timeFilterSp.get());
+ sp<TimeFilterClient> timeFilterClient = mDemuxClient->openTimeFilter();
+ if (timeFilterClient == NULL) {
+ ALOGD("Failed to open time filter.");
+ return NULL;
+ }
+ timeFilterClient->incStrong(timeFilterObj);
+ env->SetLongField(timeFilterObj, gFields.timeFilterContext, (jlong)timeFilterClient.get());
return timeFilterObj;
}
@@ -1912,35 +1734,36 @@ jobject JTuner::openDvr(DvrType type, jlong bufferSize) {
}
jobject JTuner::getDemuxCaps() {
- DemuxCapabilities caps;
- Result res;
- mTuner->getDemuxCaps([&](Result r, const DemuxCapabilities& demuxCaps) {
- caps = demuxCaps;
- res = r;
- });
- if (res != Result::SUCCESS) {
+ if (mTunerClient == NULL) {
return NULL;
}
+
+ shared_ptr<DemuxCapabilities> caps;
+ caps = mTunerClient->getDemuxCaps();
+ if (caps == NULL) {
+ return NULL;
+ }
+
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass clazz = env->FindClass("android/media/tv/tuner/DemuxCapabilities");
jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(IIIIIIIIIJI[IZ)V");
- jint numDemux = caps.numDemux;
- jint numRecord = caps.numRecord;
- jint numPlayback = caps.numPlayback;
- jint numTsFilter = caps.numTsFilter;
- jint numSectionFilter = caps.numSectionFilter;
- jint numAudioFilter = caps.numAudioFilter;
- jint numVideoFilter = caps.numVideoFilter;
- jint numPesFilter = caps.numPesFilter;
- jint numPcrFilter = caps.numPcrFilter;
- jlong numBytesInSectionFilter = caps.numBytesInSectionFilter;
- jint filterCaps = static_cast<jint>(caps.filterCaps);
- jboolean bTimeFilter = caps.bTimeFilter;
-
- jintArray linkCaps = env->NewIntArray(caps.linkCaps.size());
+ jint numDemux = caps->numDemux;
+ jint numRecord = caps->numRecord;
+ jint numPlayback = caps->numPlayback;
+ jint numTsFilter = caps->numTsFilter;
+ jint numSectionFilter = caps->numSectionFilter;
+ jint numAudioFilter = caps->numAudioFilter;
+ jint numVideoFilter = caps->numVideoFilter;
+ jint numPesFilter = caps->numPesFilter;
+ jint numPcrFilter = caps->numPcrFilter;
+ jlong numBytesInSectionFilter = caps->numBytesInSectionFilter;
+ jint filterCaps = static_cast<jint>(caps->filterCaps);
+ jboolean bTimeFilter = caps->bTimeFilter;
+
+ jintArray linkCaps = env->NewIntArray(caps->linkCaps.size());
env->SetIntArrayRegion(
- linkCaps, 0, caps.linkCaps.size(), reinterpret_cast<jint*>(&caps.linkCaps[0]));
+ linkCaps, 0, caps->linkCaps.size(), reinterpret_cast<jint*>(&caps->linkCaps[0]));
return env->NewObject(clazz, capsInit, numDemux, numRecord, numPlayback, numTsFilter,
numSectionFilter, numAudioFilter, numVideoFilter, numPesFilter, numPcrFilter,
@@ -1948,7 +1771,7 @@ jobject JTuner::getDemuxCaps() {
}
jobject JTuner::getFrontendStatus(jintArray types) {
- if (mFe == NULL) {
+ if (mFeClient == NULL) {
return NULL;
}
JNIEnv *env = AndroidRuntime::getJNIEnv();
@@ -1965,38 +1788,8 @@ jobject JTuner::getFrontendStatus(jintArray types) {
}
}
- Result res;
- hidl_vec<FrontendStatus> status;
- hidl_vec<FrontendStatusExt1_1> status_1_1;
-
- if (v.size() > 0) {
- mFe->getStatus(v,
- [&](Result r, const hidl_vec<FrontendStatus>& s) {
- res = r;
- status = s;
- });
- if (res != Result::SUCCESS) {
- return NULL;
- }
- }
-
- if (v_1_1.size() > 0) {
- sp<::android::hardware::tv::tuner::V1_1::IFrontend> iFeSp_1_1;
- iFeSp_1_1 = ::android::hardware::tv::tuner::V1_1::IFrontend::castFrom(mFe);
-
- if (iFeSp_1_1 != NULL) {
- iFeSp_1_1->getStatusExt1_1(v_1_1,
- [&](Result r, const hidl_vec<FrontendStatusExt1_1>& s) {
- res = r;
- status_1_1 = s;
- });
- if (res != Result::SUCCESS) {
- return NULL;
- }
- } else {
- ALOGW("getStatusExt1_1 is not supported with the current HAL implementation.");
- }
- }
+ hidl_vec<FrontendStatus> status = mFeClient->getStatus(v);
+ hidl_vec<FrontendStatusExt1_1> status_1_1 = mFeClient->getStatusExtended_1_1(v_1_1);
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/FrontendStatus");
jmethodID init = env->GetMethodID(clazz, "<init>", "()V");
@@ -2542,15 +2335,6 @@ bool JTuner::isV1_1ExtendedStatusType(int type) {
jint JTuner::closeFrontend() {
Result r = Result::SUCCESS;
- if (mFe != NULL) {
- r = mFe->close();
- }
- if (r == Result::SUCCESS) {
- mFe = NULL;
- mFe_1_1 = NULL;
- } else {
- return (jint) r;
- }
if (mFeClient != NULL) {
r = mFeClient->close();
@@ -2563,14 +2347,6 @@ jint JTuner::closeFrontend() {
jint JTuner::closeDemux() {
Result r = Result::SUCCESS;
- if (mDemux != NULL) {
- r = mDemux->close();
- }
- if (r == Result::SUCCESS) {
- mDemux = NULL;
- } else {
- return (jint) r;
- }
if (mDemuxClient != NULL) {
r = mDemuxClient->close();
@@ -2604,12 +2380,8 @@ static sp<JTuner> getTuner(JNIEnv *env, jobject thiz) {
return (JTuner *)env->GetLongField(thiz, gFields.tunerContext);
}
-static sp<IDescrambler> getDescrambler(JNIEnv *env, jobject descrambler) {
- return (IDescrambler *)env->GetLongField(descrambler, gFields.descramblerContext);
-}
-
-static uint32_t getResourceIdFromHandle(jint handle) {
- return (handle & 0x00ff0000) >> 16;
+static sp<DescramblerClient> getDescramblerClient(JNIEnv *env, jobject descrambler) {
+ return (DescramblerClient *)env->GetLongField(descrambler, gFields.descramblerContext);
}
static DemuxPid getDemuxPid(int pidType, int pid) {
@@ -3224,6 +2996,10 @@ static sp<FilterClient> getFilterClient(JNIEnv *env, jobject filter) {
return (FilterClient *)env->GetLongField(filter, gFields.filterContext);
}
+static sp<LnbClient> getLnbClient(JNIEnv *env, jobject lnb) {
+ return (LnbClient *)env->GetLongField(lnb, gFields.lnbContext);
+}
+
static DvrSettings getDvrSettings(JNIEnv *env, jobject settings, bool isRecorder) {
DvrSettings dvrSettings;
jclass clazz = env->FindClass("android/media/tv/tuner/dvr/DvrSettings");
@@ -3287,7 +3063,7 @@ static void android_media_tv_Tuner_native_init(JNIEnv *env) {
jclass lnbClazz = env->FindClass("android/media/tv/tuner/Lnb");
gFields.lnbContext = env->GetFieldID(lnbClazz, "mNativeContext", "J");
- gFields.lnbInitID = env->GetMethodID(lnbClazz, "<init>", "(I)V");
+ gFields.lnbInitID = env->GetMethodID(lnbClazz, "<init>", "()V");
gFields.onLnbEventID = env->GetMethodID(lnbClazz, "onEvent", "(I)V");
gFields.onLnbDiseqcMessageID = env->GetMethodID(lnbClazz, "onDiseqcMessage", "([B)V");
@@ -3376,9 +3152,14 @@ static int android_media_tv_Tuner_stop_scan(JNIEnv *env, jobject thiz) {
return tuner->stopScan();
}
-static int android_media_tv_Tuner_set_lnb(JNIEnv *env, jobject thiz, jint id) {
+static int android_media_tv_Tuner_set_lnb(JNIEnv *env, jobject thiz, jobject lnb) {
sp<JTuner> tuner = getTuner(env, thiz);
- return tuner->setLnb(id);
+ sp<LnbClient> lnbClient = getLnbClient(env, lnb);
+ if (lnbClient == NULL) {
+ ALOGE("lnb is not initialized");
+ return (int)Result::INVALID_STATE;
+ }
+ return tuner->setLnb(lnbClient);
}
static int android_media_tv_Tuner_set_lna(JNIEnv *env, jobject thiz, jboolean enable) {
@@ -3433,15 +3214,9 @@ static jobject android_media_tv_Tuner_get_frontend_info(JNIEnv *env, jobject thi
return tuner->getFrontendInfo(id);
}
-static jintArray android_media_tv_Tuner_get_lnb_ids(JNIEnv *env, jobject thiz) {
- sp<JTuner> tuner = getTuner(env, thiz);
- return tuner->getLnbIds();
-}
-
static jobject android_media_tv_Tuner_open_lnb_by_handle(JNIEnv *env, jobject thiz, jint handle) {
sp<JTuner> tuner = getTuner(env, thiz);
- uint32_t id = getResourceIdFromHandle(handle);
- return tuner->openLnbById(id);
+ return tuner->openLnbByHandle(handle);
}
static jobject android_media_tv_Tuner_open_lnb_by_name(JNIEnv *env, jobject thiz, jstring name) {
@@ -4027,49 +3802,39 @@ static jint android_media_tv_Tuner_close_filter(JNIEnv *env, jobject filter) {
return (jint) filterClient->close();
}
-static sp<TimeFilter> getTimeFilter(JNIEnv *env, jobject filter) {
- return (TimeFilter *)env->GetLongField(filter, gFields.timeFilterContext);
+static sp<TimeFilterClient> getTimeFilterClient(JNIEnv *env, jobject filter) {
+ return (TimeFilterClient *)env->GetLongField(filter, gFields.timeFilterContext);
}
static int android_media_tv_Tuner_time_filter_set_timestamp(
JNIEnv *env, jobject filter, jlong timestamp) {
- sp<TimeFilter> filterSp = getTimeFilter(env, filter);
- if (filterSp == NULL) {
- ALOGD("Failed set timestamp: time filter not found");
+ sp<TimeFilterClient> timeFilterClient = getTimeFilterClient(env, filter);
+ if (timeFilterClient == NULL) {
+ ALOGD("Failed set timestamp: time filter client not found");
return (int) Result::INVALID_STATE;
}
- sp<ITimeFilter> iFilterSp = filterSp->getITimeFilter();
- Result r = iFilterSp->setTimeStamp(static_cast<uint64_t>(timestamp));
+ Result r = timeFilterClient->setTimeStamp(static_cast<uint64_t>(timestamp));
return (int) r;
}
static int android_media_tv_Tuner_time_filter_clear_timestamp(JNIEnv *env, jobject filter) {
- sp<TimeFilter> filterSp = getTimeFilter(env, filter);
- if (filterSp == NULL) {
- ALOGD("Failed clear timestamp: time filter not found");
+ sp<TimeFilterClient> timeFilterClient = getTimeFilterClient(env, filter);
+ if (timeFilterClient == NULL) {
+ ALOGD("Failed clear timestamp: time filter client not found");
return (int) Result::INVALID_STATE;
}
- sp<ITimeFilter> iFilterSp = filterSp->getITimeFilter();
- Result r = iFilterSp->clearTimeStamp();
+ Result r = timeFilterClient->clearTimeStamp();
return (int) r;
}
static jobject android_media_tv_Tuner_time_filter_get_timestamp(JNIEnv *env, jobject filter) {
- sp<TimeFilter> filterSp = getTimeFilter(env, filter);
- if (filterSp == NULL) {
- ALOGD("Failed get timestamp: time filter not found");
+ sp<TimeFilterClient> timeFilterClient = getTimeFilterClient(env, filter);
+ if (timeFilterClient == NULL) {
+ ALOGD("Failed get timestamp: time filter client not found");
return NULL;
}
-
- sp<ITimeFilter> iFilterSp = filterSp->getITimeFilter();
- Result res;
- uint64_t timestamp;
- iFilterSp->getTimeStamp(
- [&](Result r, uint64_t t) {
- res = r;
- timestamp = t;
- });
- if (res != Result::SUCCESS) {
+ uint64_t timestamp = timeFilterClient->getTimeStamp();
+ if (timestamp == (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP) {
return NULL;
}
@@ -4081,21 +3846,13 @@ static jobject android_media_tv_Tuner_time_filter_get_timestamp(JNIEnv *env, job
}
static jobject android_media_tv_Tuner_time_filter_get_source_time(JNIEnv *env, jobject filter) {
- sp<TimeFilter> filterSp = getTimeFilter(env, filter);
- if (filterSp == NULL) {
- ALOGD("Failed get source time: time filter not found");
+ sp<TimeFilterClient> timeFilterClient = getTimeFilterClient(env, filter);
+ if (timeFilterClient == NULL) {
+ ALOGD("Failed get source time: time filter client not found");
return NULL;
}
-
- sp<ITimeFilter> iFilterSp = filterSp->getITimeFilter();
- Result res;
- uint64_t timestamp;
- iFilterSp->getSourceTime(
- [&](Result r, uint64_t t) {
- res = r;
- timestamp = t;
- });
- if (res != Result::SUCCESS) {
+ uint64_t timestamp = timeFilterClient->getSourceTime();
+ if (timestamp == (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP) {
return NULL;
}
@@ -4107,15 +3864,15 @@ static jobject android_media_tv_Tuner_time_filter_get_source_time(JNIEnv *env, j
}
static int android_media_tv_Tuner_time_filter_close(JNIEnv *env, jobject filter) {
- sp<TimeFilter> filterSp = getTimeFilter(env, filter);
- if (filterSp == NULL) {
- ALOGD("Failed close time filter: time filter not found");
+ sp<TimeFilterClient> timeFilterClient = getTimeFilterClient(env, filter);
+ if (timeFilterClient == NULL) {
+ ALOGD("Failed close time filter: time filter client not found");
return (int) Result::INVALID_STATE;
}
- Result r = filterSp->getITimeFilter()->close();
+ Result r = timeFilterClient->close();
if (r == Result::SUCCESS) {
- filterSp->decStrong(filter);
+ timeFilterClient->decStrong(filter);
env->SetLongField(filter, gFields.timeFilterContext, 0);
}
return (int) r;
@@ -4128,49 +3885,47 @@ static jobject android_media_tv_Tuner_open_descrambler(JNIEnv *env, jobject thiz
static jint android_media_tv_Tuner_descrambler_add_pid(
JNIEnv *env, jobject descrambler, jint pidType, jint pid, jobject filter) {
- sp<IDescrambler> descramblerSp = getDescrambler(env, descrambler);
- if (descramblerSp == NULL) {
+ sp<DescramblerClient> descramblerClient = getDescramblerClient(env, descrambler);
+ if (descramblerClient == NULL) {
return (jint) Result::NOT_INITIALIZED;
}
- // TODO: use filter client once descramblerClient is ready
- sp<IFilter> iFilterSp = getFilterClient(env, filter)->getHalFilter();
- Result result = descramblerSp->addPid(getDemuxPid((int)pidType, (int)pid), iFilterSp);
+ sp<FilterClient> filterClient = getFilterClient(env, filter);
+ Result result = descramblerClient->addPid(getDemuxPid((int)pidType, (int)pid), filterClient);
return (jint) result;
}
static jint android_media_tv_Tuner_descrambler_remove_pid(
JNIEnv *env, jobject descrambler, jint pidType, jint pid, jobject filter) {
- sp<IDescrambler> descramblerSp = getDescrambler(env, descrambler);
- if (descramblerSp == NULL) {
+ sp<DescramblerClient> descramblerClient = getDescramblerClient(env, descrambler);
+ if (descramblerClient == NULL) {
return (jint) Result::NOT_INITIALIZED;
}
- // TODO: use filter client once descramblerClient is ready
- sp<IFilter> iFilterSp = getFilterClient(env, filter)->getHalFilter();
- Result result = descramblerSp->removePid(getDemuxPid((int)pidType, (int)pid), iFilterSp);
+ sp<FilterClient> filterClient = getFilterClient(env, filter);
+ Result result = descramblerClient->removePid(getDemuxPid((int)pidType, (int)pid), filterClient);
return (jint) result;
}
static jint android_media_tv_Tuner_descrambler_set_key_token(
JNIEnv* env, jobject descrambler, jbyteArray keyToken) {
- sp<IDescrambler> descramblerSp = getDescrambler(env, descrambler);
- if (descramblerSp == NULL) {
+ sp<DescramblerClient> descramblerClient = getDescramblerClient(env, descrambler);
+ if (descramblerClient == NULL) {
return (jint) Result::NOT_INITIALIZED;
}
int size = env->GetArrayLength(keyToken);
std::vector<uint8_t> v(size);
env->GetByteArrayRegion(keyToken, 0, size, reinterpret_cast<jbyte*>(&v[0]));
- Result result = descramblerSp->setKeyToken(v);
+ Result result = descramblerClient->setKeyToken(v);
return (jint) result;
}
static jint android_media_tv_Tuner_close_descrambler(JNIEnv* env, jobject descrambler) {
- sp<IDescrambler> descramblerSp = getDescrambler(env, descrambler);
- if (descramblerSp == NULL) {
+ sp<DescramblerClient> descramblerClient = getDescramblerClient(env, descrambler);
+ if (descramblerClient == NULL) {
return (jint) Result::NOT_INITIALIZED;
}
- Result r = descramblerSp->close();
+ Result r = descramblerClient->close();
if (r == Result::SUCCESS) {
- descramblerSp->decStrong(descrambler);
+ descramblerClient->decStrong(descrambler);
}
return (jint) r;
}
@@ -4192,9 +3947,9 @@ static jobject android_media_tv_Tuner_get_demux_caps(JNIEnv* env, jobject thiz)
return tuner->getDemuxCaps();
}
-static jint android_media_tv_Tuner_open_demux(JNIEnv* env, jobject thiz, jint /* handle */) {
+static jint android_media_tv_Tuner_open_demux(JNIEnv* env, jobject thiz, jint handle) {
sp<JTuner> tuner = getTuner(env, thiz);
- return (jint) tuner->openDemux();
+ return (jint) tuner->openDemux(handle);
}
static jint android_media_tv_Tuner_close_tuner(JNIEnv* env, jobject thiz) {
@@ -4289,42 +4044,38 @@ static jint android_media_tv_Tuner_close_dvr(JNIEnv* env, jobject dvr) {
return (jint) dvrClient->close();
}
-static sp<Lnb> getLnb(JNIEnv *env, jobject lnb) {
- return (Lnb *)env->GetLongField(lnb, gFields.lnbContext);
-}
-
static jint android_media_tv_Tuner_lnb_set_voltage(JNIEnv* env, jobject lnb, jint voltage) {
- sp<ILnb> iLnbSp = getLnb(env, lnb)->getILnb();
- Result r = iLnbSp->setVoltage(static_cast<LnbVoltage>(voltage));
+ sp<LnbClient> lnbClient = getLnbClient(env, lnb);
+ Result r = lnbClient->setVoltage(static_cast<LnbVoltage>(voltage));
return (jint) r;
}
static int android_media_tv_Tuner_lnb_set_tone(JNIEnv* env, jobject lnb, jint tone) {
- sp<ILnb> iLnbSp = getLnb(env, lnb)->getILnb();
- Result r = iLnbSp->setTone(static_cast<LnbTone>(tone));
+ sp<LnbClient> lnbClient = getLnbClient(env, lnb);
+ Result r = lnbClient->setTone(static_cast<LnbTone>(tone));
return (jint) r;
}
static int android_media_tv_Tuner_lnb_set_position(JNIEnv* env, jobject lnb, jint position) {
- sp<ILnb> iLnbSp = getLnb(env, lnb)->getILnb();
- Result r = iLnbSp->setSatellitePosition(static_cast<LnbPosition>(position));
+ sp<LnbClient> lnbClient = getLnbClient(env, lnb);
+ Result r = lnbClient->setSatellitePosition(static_cast<LnbPosition>(position));
return (jint) r;
}
static int android_media_tv_Tuner_lnb_send_diseqc_msg(JNIEnv* env, jobject lnb, jbyteArray msg) {
- sp<ILnb> iLnbSp = getLnb(env, lnb)->getILnb();
+ sp<LnbClient> lnbClient = getLnbClient(env, lnb);
int size = env->GetArrayLength(msg);
std::vector<uint8_t> v(size);
env->GetByteArrayRegion(msg, 0, size, reinterpret_cast<jbyte*>(&v[0]));
- Result r = iLnbSp->sendDiseqcMessage(v);
+ Result r = lnbClient->sendDiseqcMessage(v);
return (jint) r;
}
static int android_media_tv_Tuner_close_lnb(JNIEnv* env, jobject lnb) {
- sp<Lnb> lnbSp = getLnb(env, lnb);
- Result r = lnbSp->getILnb()->close();
+ sp<LnbClient> lnbClient = getLnbClient(env, lnb);
+ Result r = lnbClient->close();
if (r == Result::SUCCESS) {
- lnbSp->decStrong(lnb);
+ lnbClient->decStrong(lnb);
env->SetLongField(lnb, gFields.lnbContext, 0);
}
return (jint) r;
@@ -4464,7 +4215,7 @@ static const JNINativeMethod gTunerMethods[] = {
{ "nativeScan", "(ILandroid/media/tv/tuner/frontend/FrontendSettings;I)I",
(void *)android_media_tv_Tuner_scan },
{ "nativeStopScan", "()I", (void *)android_media_tv_Tuner_stop_scan },
- { "nativeSetLnb", "(I)I", (void *)android_media_tv_Tuner_set_lnb },
+ { "nativeSetLnb", "(Landroid/media/tv/tuner/Lnb;)I", (void *)android_media_tv_Tuner_set_lnb },
{ "nativeSetLna", "(Z)I", (void *)android_media_tv_Tuner_set_lna },
{ "nativeGetFrontendStatus", "([I)Landroid/media/tv/tuner/frontend/FrontendStatus;",
(void *)android_media_tv_Tuner_get_frontend_status },
@@ -4484,7 +4235,6 @@ static const JNINativeMethod gTunerMethods[] = {
(void *)android_media_tv_Tuner_open_filter },
{ "nativeOpenTimeFilter", "()Landroid/media/tv/tuner/filter/TimeFilter;",
(void *)android_media_tv_Tuner_open_time_filter },
- { "nativeGetLnbIds", "()[I", (void *)android_media_tv_Tuner_get_lnb_ids },
{ "nativeOpenLnbByHandle", "(I)Landroid/media/tv/tuner/Lnb;",
(void *)android_media_tv_Tuner_open_lnb_by_handle },
{ "nativeOpenLnbByName", "(Ljava/lang/String;)Landroid/media/tv/tuner/Lnb;",
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index 9dc4ddfa1b61..0e30b18eb2d4 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -17,11 +17,6 @@
#ifndef _ANDROID_MEDIA_TV_TUNER_H_
#define _ANDROID_MEDIA_TV_TUNER_H_
-#include <android/hardware/tv/tuner/1.1/IFilter.h>
-#include <android/hardware/tv/tuner/1.1/IFilterCallback.h>
-#include <android/hardware/tv/tuner/1.1/IFrontend.h>
-#include <android/hardware/tv/tuner/1.1/IFrontendCallback.h>
-#include <android/hardware/tv/tuner/1.1/ITuner.h>
#include <android/hardware/tv/tuner/1.1/types.h>
#include <C2BlockInternal.h>
@@ -35,10 +30,14 @@
#include <utils/RefBase.h>
#include "tuner/DemuxClient.h"
+#include "tuner/DescramblerClient.h"
#include "tuner/FilterClient.h"
#include "tuner/FilterClientCallback.h"
#include "tuner/FrontendClient.h"
#include "tuner/FrontendClientCallback.h"
+#include "tuner/LnbClient.h"
+#include "tuner/LnbClientCallback.h"
+#include "tuner/TimeFilterClient.h"
#include "tuner/TunerClient.h"
#include "jni.h"
@@ -62,17 +61,6 @@ using ::android::hardware::tv::tuner::V1_0::FrontendScanMessageType;
using ::android::hardware::tv::tuner::V1_0::FrontendScanType;
using ::android::hardware::tv::tuner::V1_0::FrontendSettings;
using ::android::hardware::tv::tuner::V1_1::FrontendSettingsExt1_1;
-using ::android::hardware::tv::tuner::V1_0::IDemux;
-using ::android::hardware::tv::tuner::V1_0::IDescrambler;
-using ::android::hardware::tv::tuner::V1_0::IDvr;
-using ::android::hardware::tv::tuner::V1_0::IDvrCallback;
-using ::android::hardware::tv::tuner::V1_0::IFilter;
-using ::android::hardware::tv::tuner::V1_1::IFilterCallback;
-using ::android::hardware::tv::tuner::V1_0::IFrontend;
-using ::android::hardware::tv::tuner::V1_0::ILnb;
-using ::android::hardware::tv::tuner::V1_0::ILnbCallback;
-using ::android::hardware::tv::tuner::V1_0::ITimeFilter;
-using ::android::hardware::tv::tuner::V1_0::ITuner;
using ::android::hardware::tv::tuner::V1_0::LnbEventType;
using ::android::hardware::tv::tuner::V1_0::LnbId;
using ::android::hardware::tv::tuner::V1_0::PlaybackStatus;
@@ -87,21 +75,13 @@ using MQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
namespace android {
-struct LnbCallback : public ILnbCallback {
- LnbCallback(jweak tunerObj, LnbId id);
- ~LnbCallback();
- virtual Return<void> onEvent(LnbEventType lnbEventType);
- virtual Return<void> onDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage);
- jweak mLnb;
- LnbId mId;
-};
+struct LnbClientCallbackImpl : public LnbClientCallback {
+ ~LnbClientCallbackImpl();
+ virtual void onEvent(LnbEventType lnbEventType);
+ virtual void onDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage);
-struct Lnb : public RefBase {
- Lnb(sp<ILnb> sp, jobject obj);
- ~Lnb();
- sp<ILnb> getILnb();
- // TODO: remove after migrate to client lib
- sp<ILnb> mLnbSp;
+ void setLnb(jweak lnbObj);
+private:
jweak mLnbObj;
};
@@ -174,7 +154,7 @@ private:
};
struct FrontendClientCallbackImpl : public FrontendClientCallback {
- FrontendClientCallbackImpl(jweak tunerObj, FrontendId id);
+ FrontendClientCallbackImpl(jweak tunerObj);
virtual void onEvent(FrontendEventType frontendEventType);
virtual void onScanMessage(
@@ -183,22 +163,10 @@ struct FrontendClientCallbackImpl : public FrontendClientCallback {
FrontendScanMessageTypeExt1_1 type, const FrontendScanMessageExt1_1& messageExt);
jweak mObject;
- FrontendId mId;
-};
-
-struct TimeFilter : public RefBase {
- TimeFilter(sp<ITimeFilter> sp, jweak obj);
- ~TimeFilter();
- sp<ITimeFilter> getITimeFilter();
- // TODO: remove after migrate to client lib
- sp<ITimeFilter> mTimeFilterSp;
- jweak mTimeFilterObj;
};
struct JTuner : public RefBase {
JTuner(JNIEnv *env, jobject thiz);
- // TODO: modify after migrate to client lib
- sp<ITuner> getTunerService();
int getTunerVersion();
jobject getAvSyncHwId(sp<FilterClient> filter);
jobject getAvSyncTime(jint id);
@@ -215,10 +183,9 @@ struct JTuner : public RefBase {
int scan(const FrontendSettings& settings, FrontendScanType scanType,
const FrontendSettingsExt1_1& settingsExt1_1);
int stopScan();
- int setLnb(int id);
+ int setLnb(sp<LnbClient> lnbClient);
int setLna(bool enable);
- jintArray getLnbIds();
- jobject openLnbById(int id);
+ jobject openLnbByHandle(int handle);
jobject openLnbByName(jstring name);
jobject openFilter(DemuxFilterType type, int bufferSize);
jobject openTimeFilter();
@@ -226,7 +193,7 @@ struct JTuner : public RefBase {
jobject openDvr(DvrType type, jlong bufferSize);
jobject getDemuxCaps();
jobject getFrontendStatus(jintArray types);
- Result openDemux();
+ Result openDemux(int handle);
jint close();
jint closeFrontend();
jint closeDemux();
@@ -237,23 +204,11 @@ protected:
private:
jclass mClass;
jweak mObject;
- // TODO: remove after migrate to client lib
- static sp<ITuner> mTuner;
- static sp<::android::hardware::tv::tuner::V1_1::ITuner> mTuner_1_1;
static sp<TunerClient> mTunerClient;
- // TODO: remove after migrate to client lib
- sp<IFrontend> mFe;
- // TODO: remove after migrate to client lib
- sp<::android::hardware::tv::tuner::V1_1::IFrontend> mFe_1_1;
sp<FrontendClient> mFeClient;
int mFeId;
- hidl_vec<LnbId> mLnbIds;
- // TODO: remove after migrate to client lib
- sp<ILnb> mLnb;
- // TODO: remove after migrate to client lib
- sp<IDemux> mDemux;
+ sp<LnbClient> mLnbClient;
sp<DemuxClient> mDemuxClient;
- uint32_t mDemuxId;
static jobject getAnalogFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps);
static jobject getAtsc3FrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps);
static jobject getAtscFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps);
@@ -266,9 +221,6 @@ private:
static jobject getDtmbFrontendCaps(JNIEnv *env, int id);
bool isV1_1ExtendedStatusType(jint type);
- static uint32_t getResourceIdFromHandle(jint handle) {
- return (handle & 0x00ff0000) >> 16;
- }
};
class C2DataIdInfo : public C2Param {
diff --git a/media/jni/tuner/DemuxClient.cpp b/media/jni/tuner/DemuxClient.cpp
index 59dfd704179e..08b739854378 100644
--- a/media/jni/tuner/DemuxClient.cpp
+++ b/media/jni/tuner/DemuxClient.cpp
@@ -32,11 +32,13 @@ namespace android {
// TODO: pending aidl interface
DemuxClient::DemuxClient() {
//mTunerDemux = tunerDemux;
+ mId = -1;
}
DemuxClient::~DemuxClient() {
//mTunerDemux = NULL;
mDemux = NULL;
+ mId = -1;
}
// TODO: remove after migration to Tuner Service is done.
@@ -77,6 +79,21 @@ sp<FilterClient> DemuxClient::openFilter(DemuxFilterType type, int bufferSize,
return NULL;
}
+sp<TimeFilterClient> DemuxClient::openTimeFilter() {
+ // TODO: pending aidl interface
+
+ if (mDemux != NULL) {
+ sp<ITimeFilter> hidlTimeFilter = openHidlTimeFilter();
+ if (hidlTimeFilter != NULL) {
+ sp<TimeFilterClient> timeFilterClient = new TimeFilterClient();
+ timeFilterClient->setHidlTimeFilter(hidlTimeFilter);
+ return timeFilterClient;
+ }
+ }
+
+ return NULL;
+}
+
int DemuxClient::getAvSyncHwId(sp<FilterClient> filterClient) {
// pending aidl interface
@@ -188,6 +205,26 @@ sp<IFilter> DemuxClient::openHidlFilter(DemuxFilterType type, int bufferSize,
return hidlFilter;
}
+sp<ITimeFilter> DemuxClient::openHidlTimeFilter() {
+ if (mDemux == NULL) {
+ return NULL;
+ }
+
+ sp<ITimeFilter> timeFilter;
+ Result res;
+ mDemux->openTimeFilter(
+ [&](Result r, const sp<ITimeFilter>& timeFilterSp) {
+ timeFilter = timeFilterSp;
+ res = r;
+ });
+
+ if (res != Result::SUCCESS || timeFilter == NULL) {
+ return NULL;
+ }
+
+ return timeFilter;
+}
+
sp<IDvr> DemuxClient::openHidlDvr(DvrType dvrType, int bufferSize,
sp<HidlDvrCallback> callback) {
if (mDemux == NULL) {
diff --git a/media/jni/tuner/DemuxClient.h b/media/jni/tuner/DemuxClient.h
index f11f2c6cc22b..2950dd449a10 100644
--- a/media/jni/tuner/DemuxClient.h
+++ b/media/jni/tuner/DemuxClient.h
@@ -26,12 +26,14 @@
#include "FilterClient.h"
#include "FilterClientCallback.h"
#include "FrontendClient.h"
+#include "TimeFilterClient.h"
//using ::aidl::android::media::tv::tuner::ITunerDemux;
using ::android::hardware::tv::tuner::V1_0::DemuxFilterType;
using ::android::hardware::tv::tuner::V1_0::DvrType;
using ::android::hardware::tv::tuner::V1_0::IDemux;
+using ::android::hardware::tv::tuner::V1_0::ITimeFilter;
using namespace std;
@@ -56,7 +58,10 @@ public:
*/
sp<FilterClient> openFilter(DemuxFilterType type, int bufferSize, sp<FilterClientCallback> cb);
- // TODO: handle TimeFilterClient
+ /**
+ * Open time filter of the demux.
+ */
+ sp<TimeFilterClient> openTimeFilter();
/**
* Get hardware sync ID for audio and video.
@@ -88,8 +93,12 @@ public:
*/
Result close();
+ void setId(int id) { mId = id; }
+ int getId() { return mId; }
+
private:
sp<IFilter> openHidlFilter(DemuxFilterType type, int bufferSize, sp<HidlFilterCallback> cb);
+ sp<ITimeFilter> openHidlTimeFilter();
sp<IDvr> openHidlDvr(DvrType type, int bufferSize, sp<HidlDvrCallback> cb);
/**
@@ -105,6 +114,8 @@ private:
* Default null when the HAL service does not exist.
*/
sp<IDemux> mDemux;
+
+ int mId;
};
} // namespace android
diff --git a/media/jni/tuner/DescramblerClient.cpp b/media/jni/tuner/DescramblerClient.cpp
new file mode 100644
index 000000000000..979beeac6b3a
--- /dev/null
+++ b/media/jni/tuner/DescramblerClient.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "DescramblerClient"
+
+#include <android-base/logging.h>
+#include <utils/Log.h>
+
+#include "DescramblerClient.h"
+
+using ::android::hardware::tv::tuner::V1_0::Result;
+
+namespace android {
+
+/////////////// DescramblerClient ///////////////////////
+
+// TODO: pending aidl interface
+DescramblerClient::DescramblerClient() {
+ //mTunerDescrambler = tunerDescrambler;
+}
+
+DescramblerClient::~DescramblerClient() {
+ //mTunerDescrambler = NULL;
+ mDescrambler = NULL;
+}
+
+// TODO: remove after migration to Tuner Service is done.
+void DescramblerClient::setHidlDescrambler(sp<IDescrambler> descrambler) {
+ mDescrambler = descrambler;
+}
+
+Result DescramblerClient::setDemuxSource(sp<DemuxClient> demuxClient) {
+ if (demuxClient == NULL) {
+ return Result::INVALID_ARGUMENT;
+ }
+
+ // TODO: pending aidl interface
+
+ if (mDescrambler != NULL) {
+ return mDescrambler->setDemuxSource(demuxClient->getId());
+ }
+
+ return Result::INVALID_STATE;
+}
+
+Result DescramblerClient::setKeyToken(vector<uint8_t> keyToken) {
+ // TODO: pending aidl interface
+
+ if (mDescrambler != NULL) {
+ return mDescrambler->setKeyToken(keyToken);
+ }
+
+ return Result::INVALID_STATE;
+}
+
+Result DescramblerClient::addPid(DemuxPid pid, sp<FilterClient> optionalSourceFilter) {
+ // TODO: pending aidl interface
+
+ if (mDescrambler != NULL) {
+ return mDescrambler->addPid(pid, optionalSourceFilter->getHalFilter());
+ }
+
+ return Result::INVALID_STATE;}
+
+Result DescramblerClient::removePid(DemuxPid pid, sp<FilterClient> optionalSourceFilter) {
+ // TODO: pending aidl interface
+
+ if (mDescrambler != NULL) {
+ return mDescrambler->addPid(pid, optionalSourceFilter->getHalFilter());
+ }
+
+ return Result::INVALID_STATE;}
+
+Result DescramblerClient::close() {
+ // TODO: pending aidl interface
+
+ if (mDescrambler != NULL) {
+ return mDescrambler->close();
+ }
+
+ return Result::INVALID_STATE;}
+
+/////////////// DescramblerClient Helper Methods ///////////////////////
+
+} // namespace android
diff --git a/media/jni/tuner/DescramblerClient.h b/media/jni/tuner/DescramblerClient.h
new file mode 100644
index 000000000000..8af688314db1
--- /dev/null
+++ b/media/jni/tuner/DescramblerClient.h
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROID_MEDIA_TV_DESCRAMBLER_CLIENT_H_
+#define _ANDROID_MEDIA_TV_DESCRAMBLER_CLIENT_H_
+
+//#include <aidl/android/media/tv/tuner/ITunerDescrambler.h>
+#include <android/hardware/tv/tuner/1.0/IDescrambler.h>
+#include <android/hardware/tv/tuner/1.1/types.h>
+
+#include "DemuxClient.h"
+#include "FilterClient.h"
+
+//using ::aidl::android::media::tv::tuner::ITunerDescrambler;
+
+using ::android::hardware::tv::tuner::V1_0::IDescrambler;
+using ::android::hardware::tv::tuner::V1_0::Result;
+using ::android::hardware::tv::tuner::V1_0::DemuxPid;
+
+using namespace std;
+
+namespace android {
+
+struct DescramblerClient : public RefBase {
+
+public:
+ // TODO: pending hidl interface
+ DescramblerClient();
+ ~DescramblerClient();
+
+ // TODO: remove after migration to Tuner Service is done.
+ void setHidlDescrambler(sp<IDescrambler> descrambler);
+
+ /**
+ * Set a demux as source of the descrambler.
+ */
+ Result setDemuxSource(sp<DemuxClient> demuxClient);
+
+ /**
+ * Set a key token to link descrambler to a key slot.
+ */
+ Result setKeyToken(vector<uint8_t> keyToken);
+
+ /**
+ * Add packets' PID to the descrambler for descrambling.
+ */
+ Result addPid(DemuxPid pid, sp<FilterClient> optionalSourceFilter);
+
+ /**
+ * Remove packets' PID from the descrambler.
+ */
+ Result removePid(DemuxPid pid, sp<FilterClient> optionalSourceFilter);
+
+ /**
+ * Close a new interface of ITunerDescrambler.
+ */
+ Result close();
+
+private:
+ /**
+ * An AIDL Tuner Descrambler Singleton assigned at the first time the Tuner Client
+ * opens a descrambler. Default null when descrambler is not opened.
+ */
+ // TODO: pending on aidl interface
+ //shared_ptr<ITunerDescrambler> mTunerDescrambler;
+
+ /**
+ * A Descrambler HAL interface that is ready before migrating to the TunerDescrambler.
+ * This is a temprary interface before Tuner Framework migrates to use TunerService.
+ * Default null when the HAL service does not exist.
+ */
+ sp<IDescrambler> mDescrambler;
+};
+} // namespace android
+
+#endif // _ANDROID_MEDIA_TV_DESCRAMBLER_CLIENT_H_
diff --git a/media/jni/tuner/FrontendClient.cpp b/media/jni/tuner/FrontendClient.cpp
index 44b46f0a0b47..14761a6f92a8 100644
--- a/media/jni/tuner/FrontendClient.cpp
+++ b/media/jni/tuner/FrontendClient.cpp
@@ -22,16 +22,17 @@
#include "FrontendClient.h"
using ::aidl::android::media::tv::tuner::TunerFrontendSettings;
+using ::android::hardware::tv::tuner::V1_1::Constant;
namespace android {
/////////////// FrontendClient ///////////////////////
-FrontendClient::FrontendClient(shared_ptr<ITunerFrontend> tunerFrontend, int frontendHandle) {
+FrontendClient::FrontendClient(shared_ptr<ITunerFrontend> tunerFrontend, int id) {
mTunerFrontend = tunerFrontend;
mAidlCallback = NULL;
mHidlCallback = NULL;
- mFrontendHandle = frontendHandle;
+ mId = id;
}
FrontendClient::~FrontendClient() {
@@ -40,7 +41,7 @@ FrontendClient::~FrontendClient() {
mFrontend_1_1 = NULL;
mAidlCallback = NULL;
mHidlCallback = NULL;
- mFrontendHandle = -1;
+ mId = -1;
}
Result FrontendClient::setCallback(sp<FrontendClientCallback> frontendClientCallback) {
@@ -99,6 +100,164 @@ Result FrontendClient::stopTune() {
return Result::INVALID_STATE;
}
+Result FrontendClient::scan(const FrontendSettings& settings, FrontendScanType type,
+ const FrontendSettingsExt1_1& settingsExt1_1) {
+ if (mTunerFrontend != NULL) {
+ // TODO: parse hidl settings to aidl settings
+ // TODO: aidl frontend settings to include Tuner HAL 1.1 settings
+ TunerFrontendSettings settings;
+ // TODO: handle error message.
+ mTunerFrontend->scan(settings, (int)type);
+ return Result::SUCCESS;
+ }
+
+ Result result;
+ if (mFrontend_1_1 != NULL) {
+ result = mFrontend_1_1->scan_1_1(settings, type, settingsExt1_1);
+ return result;
+ }
+
+ if (mFrontend != NULL) {
+ result = mFrontend->scan(settings, type);
+ return result;
+ }
+
+ return Result::INVALID_STATE;
+}
+
+Result FrontendClient::stopScan() {
+ if (mTunerFrontend != NULL) {
+ // TODO: handle error message.
+ mTunerFrontend->stopScan();
+ return Result::SUCCESS;
+ }
+
+ if (mFrontend != NULL) {
+ Result result = mFrontend->stopScan();
+ return result;
+ }
+
+ return Result::INVALID_STATE;
+}
+
+vector<FrontendStatus> FrontendClient::getStatus(vector<FrontendStatusType> statusTypes) {
+ vector<FrontendStatus> status;
+
+ if (mTunerFrontend != NULL) {
+ // TODO: handle error message.
+ /*status = mTunerFrontend->getStatus(statusTypes);
+ return status;*/
+ }
+
+ if (mFrontend != NULL && statusTypes.size() > 0) {
+ Result res;
+ mFrontend->getStatus(statusTypes,
+ [&](Result r, const hidl_vec<FrontendStatus>& s) {
+ res = r;
+ status = s;
+ });
+ if (res != Result::SUCCESS) {
+ status.clear();
+ return status;
+ }
+ }
+
+ return status;
+}
+vector<FrontendStatusExt1_1> FrontendClient::getStatusExtended_1_1(
+ vector<FrontendStatusTypeExt1_1> statusTypes) {
+ vector<FrontendStatusExt1_1> status;
+
+ if (mTunerFrontend != NULL) {
+ // TODO: handle error message.
+ /*status = mTunerFrontend->getStatusExtended_1_1(statusTypes);
+ return status;*/
+ }
+
+ if (mFrontend_1_1 != NULL && statusTypes.size() > 0) {
+ Result res;
+ mFrontend_1_1->getStatusExt1_1(statusTypes,
+ [&](Result r, const hidl_vec<FrontendStatusExt1_1>& s) {
+ res = r;
+ status = s;
+ });
+ if (res != Result::SUCCESS) {
+ status.clear();
+ return status;
+ }
+ }
+
+ return status;
+}
+
+Result FrontendClient::setLnb(sp<LnbClient> lnbClient) {
+ if (mTunerFrontend != NULL) {
+ // TODO: handle error message.
+ /*mTunerFrontend->setLnb(lnbClient->getAidlLnb());
+ return Result::SUCCESS;*/
+ }
+
+ if (mFrontend != NULL) {
+ Result result = mFrontend->setLnb(lnbClient->getId());
+ return result;
+ }
+
+ return Result::INVALID_STATE;
+}
+
+Result FrontendClient::setLna(bool bEnable) {
+ if (mTunerFrontend != NULL) {
+ // TODO: handle error message.
+ /*mTunerFrontend->setLna(bEnable);
+ return Result::SUCCESS;*/
+ }
+
+ if (mFrontend != NULL) {
+ Result result = mFrontend->setLna(bEnable);
+ return result;
+ }
+
+ return Result::INVALID_STATE;
+}
+
+int FrontendClient::linkCiCamToFrontend(int ciCamId) {
+ int ltsId = (int)Constant::INVALID_LTS_ID;
+
+ if (mTunerFrontend != NULL) {
+ // TODO: handle error message.
+ /*mTunerFrontend->linkCiCamToFrontend(ciCamId, ltsId);
+ return ltsId;*/
+ }
+
+ if (mFrontend_1_1 != NULL) {
+ Result res;
+ mFrontend_1_1->linkCiCam(static_cast<uint32_t>(ciCamId),
+ [&](Result r, uint32_t id) {
+ res = r;
+ ltsId = id;
+ });
+ if (res != Result::SUCCESS) {
+ return (int)Constant::INVALID_LTS_ID;
+ }
+ }
+
+ return ltsId;
+}
+
+Result FrontendClient::unlinkCiCamToFrontend(int ciCamId) {
+ if (mTunerFrontend != NULL) {
+ // TODO: handle error message.
+ /*mTunerFrontend->unlinkCiCamToFrontend(ciCamId);
+ return Result::SUCCESS;*/
+ }
+
+ if (mFrontend_1_1 != NULL) {
+ return mFrontend_1_1->unlinkCiCam(static_cast<uint32_t>(ciCamId));
+ }
+
+ return Result::INVALID_STATE;
+}
+
Result FrontendClient::close() {
if (mTunerFrontend != NULL) {
// TODO: handle error message.
@@ -123,7 +282,7 @@ shared_ptr<ITunerFrontend> FrontendClient::getAidlFrontend() {
}
int FrontendClient::getId() {
- return getResourceIdFromHandle(mFrontendHandle);
+ return mId;
}
/////////////// TunerFrontendCallback ///////////////////////
diff --git a/media/jni/tuner/FrontendClient.h b/media/jni/tuner/FrontendClient.h
index 7db572ba0987..32356744c839 100644
--- a/media/jni/tuner/FrontendClient.h
+++ b/media/jni/tuner/FrontendClient.h
@@ -24,6 +24,7 @@
#include <android/hardware/tv/tuner/1.1/types.h>
#include "FrontendClientCallback.h"
+#include "LnbClient.h"
using Status = ::ndk::ScopedAStatus;
@@ -37,13 +38,18 @@ using ::android::hardware::tv::tuner::V1_0::FrontendInfo;
using ::android::hardware::tv::tuner::V1_0::FrontendEventType;
using ::android::hardware::tv::tuner::V1_0::FrontendScanMessage;
using ::android::hardware::tv::tuner::V1_0::FrontendScanMessageType;
+using ::android::hardware::tv::tuner::V1_0::FrontendScanType;
using ::android::hardware::tv::tuner::V1_0::FrontendSettings;
+using ::android::hardware::tv::tuner::V1_0::FrontendStatus;
+using ::android::hardware::tv::tuner::V1_0::FrontendStatusType;
using ::android::hardware::tv::tuner::V1_0::IFrontend;
using ::android::hardware::tv::tuner::V1_0::Result;
using ::android::hardware::tv::tuner::V1_1::FrontendScanMessageExt1_1;
using ::android::hardware::tv::tuner::V1_1::FrontendScanMessageTypeExt1_1;
using ::android::hardware::tv::tuner::V1_1::FrontendSettingsExt1_1;
+using ::android::hardware::tv::tuner::V1_1::FrontendStatusExt1_1;
+using ::android::hardware::tv::tuner::V1_1::FrontendStatusTypeExt1_1;
using ::android::hardware::tv::tuner::V1_1::IFrontendCallback;
using namespace std;
@@ -105,7 +111,7 @@ private:
struct FrontendClient : public RefBase {
public:
- FrontendClient(shared_ptr<ITunerFrontend> tunerFrontend, int frontendHandle);
+ FrontendClient(shared_ptr<ITunerFrontend> tunerFrontend, int id);
~FrontendClient();
/**
@@ -127,6 +133,50 @@ public:
Result stopTune();
/**
+ * Scan the frontend to use the settings given.
+ */
+ Result scan(const FrontendSettings& settings, FrontendScanType frontendScanType,
+ const FrontendSettingsExt1_1& settingsExt1_1);
+
+ /**
+ * Stop the previous scanning.
+ */
+ Result stopScan();
+
+ /**
+ * Gets the statuses of the frontend.
+ */
+ vector<FrontendStatus> getStatus(vector<FrontendStatusType> statusTypes);
+
+ /**
+ * Gets the 1.1 extended statuses of the frontend.
+ */
+ vector<FrontendStatusExt1_1> getStatusExtended_1_1(
+ vector<FrontendStatusTypeExt1_1> statusTypes);
+
+ /**
+ * Sets Low-Noise Block downconverter (LNB) for satellite frontend.
+ */
+ Result setLnb(sp<LnbClient> lnbClient);
+
+ /**
+ * Enable or Disable Low Noise Amplifier (LNA).
+ */
+ Result setLna(bool bEnable);
+
+ /**
+ * Link Frontend to the cicam with given id.
+ *
+ * @return lts id
+ */
+ int linkCiCamToFrontend(int ciCamId);
+
+ /**
+ * Unink Frontend to the cicam with given id.
+ */
+ Result unlinkCiCamToFrontend(int ciCamId);
+
+ /**
* Close Frontend.
*/
Result close();
@@ -135,10 +185,6 @@ public:
int getId();
- static int getResourceIdFromHandle(int handle) {
- return (handle & 0x00ff0000) >> 16;
- }
-
private:
/**
* An AIDL Tuner Frontend Singleton assigned at the first time when the Tuner Client
@@ -163,7 +209,7 @@ private:
shared_ptr<TunerFrontendCallback> mAidlCallback;
sp<HidlFrontendCallback> mHidlCallback;
- int mFrontendHandle;
+ int mId;
};
} // namespace android
diff --git a/media/jni/tuner/LnbClient.cpp b/media/jni/tuner/LnbClient.cpp
new file mode 100644
index 000000000000..7f3916fe634d
--- /dev/null
+++ b/media/jni/tuner/LnbClient.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "LnbClient"
+
+#include <android-base/logging.h>
+#include <utils/Log.h>
+
+#include "LnbClient.h"
+
+using ::android::hardware::tv::tuner::V1_0::Result;
+
+namespace android {
+
+/////////////// LnbClient ///////////////////////
+
+// TODO: pending aidl interface
+LnbClient::LnbClient() {
+ //mTunerLnb = tunerLnb;
+ mId = -1;
+}
+
+LnbClient::~LnbClient() {
+ //mTunerLnb = NULL;
+ mLnb = NULL;
+ mId = -1;
+}
+
+// TODO: remove after migration to Tuner Service is done.
+void LnbClient::setHidlLnb(sp<ILnb> lnb) {
+ mLnb = lnb;
+}
+
+Result LnbClient::setCallback(sp<LnbClientCallback> cb) {
+ // TODO: pending aidl interface
+ /*if (mTunerFrontend != NULL) {
+ mAidlCallback = ::ndk::SharedRefBase::make<TunerLnbCallback>(cb);
+ mTunerLnb->setCallback(mAidlCallback);
+ return Result::SUCCESS;
+ }*/
+
+ mHidlCallback = new HidlLnbCallback(cb);
+ return mLnb->setCallback(mHidlCallback);
+}
+
+Result LnbClient::setVoltage(LnbVoltage voltage) {
+ // TODO: pending aidl interface
+
+ if (mLnb != NULL) {
+ return mLnb->setVoltage(voltage);
+ }
+
+ return Result::INVALID_STATE;
+}
+
+Result LnbClient::setTone(LnbTone tone) {
+ // TODO: pending aidl interface
+
+ if (mLnb != NULL) {
+ return mLnb->setTone(tone);
+ }
+
+ return Result::INVALID_STATE;
+}
+
+Result LnbClient::setSatellitePosition(LnbPosition position) {
+ // TODO: pending aidl interface
+
+ if (mLnb != NULL) {
+ return mLnb->setSatellitePosition(position);
+ }
+
+ return Result::INVALID_STATE;
+}
+
+Result LnbClient::sendDiseqcMessage(vector<uint8_t> diseqcMessage) {
+ // TODO: pending aidl interface
+
+ if (mLnb != NULL) {
+ return mLnb->sendDiseqcMessage(diseqcMessage);
+ }
+
+ return Result::INVALID_STATE;
+}
+
+Result LnbClient::close() {
+ // TODO: pending aidl interface
+
+ if (mLnb != NULL) {
+ return mLnb->close();
+ }
+
+ return Result::INVALID_STATE;
+}
+
+/////////////// ILnbCallback ///////////////////////
+
+HidlLnbCallback::HidlLnbCallback(sp<LnbClientCallback> lnbClientCallback)
+ : mLnbClientCallback(lnbClientCallback) {}
+
+Return<void> HidlLnbCallback::onEvent(const LnbEventType lnbEventType) {
+ if (mLnbClientCallback != NULL) {
+ mLnbClientCallback->onEvent(lnbEventType);
+ }
+ return Void();
+}
+
+Return<void> HidlLnbCallback::onDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage) {
+ if (mLnbClientCallback != NULL) {
+ mLnbClientCallback->onDiseqcMessage(diseqcMessage);
+ }
+ return Void();
+}
+
+/////////////// LnbClient Helper Methods ///////////////////////
+
+} // namespace android
diff --git a/media/jni/tuner/LnbClient.h b/media/jni/tuner/LnbClient.h
new file mode 100644
index 000000000000..533a99678efe
--- /dev/null
+++ b/media/jni/tuner/LnbClient.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_MEDIA_TV_LNB_CLIENT_H_
+#define _ANDROID_MEDIA_TV_LNB_CLIENT_H_
+
+//#include <aidl/android/media/tv/tuner/ITunerLnb.h>
+#include <android/hardware/tv/tuner/1.0/ILnb.h>
+#include <android/hardware/tv/tuner/1.0/ILnbCallback.h>
+#include <android/hardware/tv/tuner/1.1/types.h>
+
+#include "LnbClientCallback.h"
+
+//using ::aidl::android::media::tv::tuner::ITunerLnb;
+
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::tv::tuner::V1_0::ILnb;
+using ::android::hardware::tv::tuner::V1_0::ILnbCallback;
+using ::android::hardware::tv::tuner::V1_0::LnbId;
+using ::android::hardware::tv::tuner::V1_0::LnbPosition;
+using ::android::hardware::tv::tuner::V1_0::LnbTone;
+using ::android::hardware::tv::tuner::V1_0::LnbVoltage;
+using ::android::hardware::tv::tuner::V1_0::Result;
+
+using namespace std;
+
+namespace android {
+
+// TODO: pending aidl interface
+/*class TunerLnbCallback : public BnTunerLnbCallback {
+
+public:
+ TunerLnbCallback(sp<LnbClientCallback> lnbClientCallback);
+
+ Status onEvent(int lnbEventType);
+ Status onDiseqcMessage(vector<uint8_t> diseqcMessage);
+
+private:
+ sp<LnbClientCallback> mLnbClientCallback;
+};*/
+
+struct HidlLnbCallback : public ILnbCallback {
+
+public:
+ HidlLnbCallback(sp<LnbClientCallback> lnbClientCallback);
+ virtual Return<void> onEvent(const LnbEventType lnbEventType);
+ virtual Return<void> onDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage);
+
+private:
+ sp<LnbClientCallback> mLnbClientCallback;
+};
+
+struct LnbClient : public RefBase {
+
+public:
+ // TODO: add TunerLnb as parameter.
+ LnbClient();
+ ~LnbClient();
+
+ // TODO: remove after migration to Tuner Service is done.
+ void setHidlLnb(sp<ILnb> lnb);
+
+ /**
+ * Set the lnb callback.
+ */
+ Result setCallback(sp<LnbClientCallback> cb);
+
+ /**
+ * Set the lnb's power voltage.
+ */
+ Result setVoltage(LnbVoltage voltage);
+
+ /**
+ * Set the lnb's tone mode.
+ */
+ Result setTone(LnbTone tone);
+
+ /**
+ * Select the lnb's position.
+ */
+ Result setSatellitePosition(LnbPosition position);
+
+ /**
+ * Sends DiSEqC (Digital Satellite Equipment Control) message.
+ */
+ Result sendDiseqcMessage(vector<uint8_t> diseqcMessage);
+
+ /**
+ * Releases the LNB instance.
+ */
+ Result close();
+
+ //shared_ptr<ITunerLnb> getAidlLnb() { return mTunerLnb; }
+ void setId(LnbId id) { mId = id; }
+ LnbId getId() { return mId; }
+
+private:
+ /**
+ * An AIDL Tuner Lnb Singleton assigned at the first time the Tuner Client
+ * opens an Lnb. Default null when lnb is not opened.
+ */
+ // TODO: pending on aidl interface
+ //shared_ptr<ITunerLnb> mTunerLnb;
+
+ /**
+ * A Lnb HAL interface that is ready before migrating to the TunerLnb.
+ * This is a temprary interface before Tuner Framework migrates to use TunerService.
+ * Default null when the HAL service does not exist.
+ */
+ sp<ILnb> mLnb;
+
+ //shared_ptr<TunerLnbCallback> mAidlCallback;
+ sp<HidlLnbCallback> mHidlCallback;
+
+ LnbId mId;
+};
+} // namespace android
+
+#endif // _ANDROID_MEDIA_TV_LNB_CLIENT_H_
diff --git a/media/jni/tuner/LnbClientCallback.h b/media/jni/tuner/LnbClientCallback.h
new file mode 100644
index 000000000000..253d7ef110e3
--- /dev/null
+++ b/media/jni/tuner/LnbClientCallback.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_MEDIA_TV_LNB_CLIENT_CALLBACK_H_
+#define _ANDROID_MEDIA_TV_LNB_CLIENT_CALLBACK_H_
+
+using ::android::hardware::hidl_vec;
+using ::android::hardware::tv::tuner::V1_0::LnbEventType;
+
+using namespace std;
+
+namespace android {
+
+struct LnbClientCallback : public RefBase {
+ virtual void onEvent(const LnbEventType lnbEventType);
+ virtual void onDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage);
+};
+} // namespace android
+
+#endif // _ANDROID_MEDIA_TV_LNB_CLIENT_CALLBACK_H_ \ No newline at end of file
diff --git a/media/jni/tuner/TimeFilterClient.cpp b/media/jni/tuner/TimeFilterClient.cpp
new file mode 100644
index 000000000000..27ea6e596204
--- /dev/null
+++ b/media/jni/tuner/TimeFilterClient.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "TimeFilterClient"
+
+#include <android-base/logging.h>
+#include <utils/Log.h>
+
+#include "TimeFilterClient.h"
+
+using ::android::hardware::tv::tuner::V1_0::Result;
+using ::android::hardware::tv::tuner::V1_1::Constant64Bit;
+
+namespace android {
+
+/////////////// TimeFilterClient ///////////////////////
+
+// TODO: pending aidl interface
+TimeFilterClient::TimeFilterClient() {
+ //mTunerTimeFilter = tunerTimeFilter;
+}
+
+TimeFilterClient::~TimeFilterClient() {
+ //mTunerTimeFilter = NULL;
+ mTimeFilter = NULL;
+}
+
+// TODO: remove after migration to Tuner Service is done.
+void TimeFilterClient::setHidlTimeFilter(sp<ITimeFilter> timeFilter) {
+ mTimeFilter = timeFilter;
+}
+
+Result TimeFilterClient::setTimeStamp(long timeStamp) {
+ // TODO: pending aidl interface
+
+ if (mTimeFilter != NULL) {
+ return mTimeFilter->setTimeStamp(timeStamp);
+ }
+
+ return Result::INVALID_STATE;
+}
+
+Result TimeFilterClient::clearTimeStamp() {
+ // TODO: pending aidl interface
+
+ if (mTimeFilter != NULL) {
+ return mTimeFilter->clearTimeStamp();
+ }
+
+ return Result::INVALID_STATE;
+}
+
+long TimeFilterClient::getTimeStamp() {
+ // TODO: pending aidl interface
+
+ if (mTimeFilter != NULL) {
+ Result res;
+ long timestamp;
+ mTimeFilter->getTimeStamp(
+ [&](Result r, uint64_t t) {
+ res = r;
+ timestamp = t;
+ });
+ if (res != Result::SUCCESS) {
+ return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
+ }
+ return timestamp;
+ }
+
+ return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
+}
+
+long TimeFilterClient::getSourceTime() {
+ // TODO: pending aidl interface
+
+ if (mTimeFilter != NULL) {
+ Result res;
+ long timestamp;
+ mTimeFilter->getSourceTime(
+ [&](Result r, uint64_t t) {
+ res = r;
+ timestamp = t;
+ });
+ if (res != Result::SUCCESS) {
+ return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
+ }
+ return timestamp;
+ }
+
+ return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
+}
+
+Result TimeFilterClient::close() {
+ // TODO: pending aidl interface
+
+ if (mTimeFilter != NULL) {
+ return mTimeFilter->close();
+ }
+
+ return Result::INVALID_STATE;
+}
+} // namespace android
diff --git a/media/jni/tuner/TimeFilterClient.h b/media/jni/tuner/TimeFilterClient.h
new file mode 100644
index 000000000000..9a9d172665ec
--- /dev/null
+++ b/media/jni/tuner/TimeFilterClient.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_MEDIA_TV_TIME_FILTER_CLIENT_H_
+#define _ANDROID_MEDIA_TV_TIME_FILTER_CLIENT_H_
+
+//#include <aidl/android/media/tv/tuner/ITunerTimeFilter.h>
+#include <android/hardware/tv/tuner/1.0/ITimeFilter.h>
+#include <android/hardware/tv/tuner/1.1/types.h>
+
+//using ::aidl::android::media::tv::tuner::ITunerTimeFilter;
+
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::tv::tuner::V1_0::ITimeFilter;
+using ::android::hardware::tv::tuner::V1_0::Result;
+
+using namespace std;
+
+namespace android {
+
+struct TimeFilterClient : public RefBase {
+
+public:
+ // TODO: add TunerTimeFilter as parameter.
+ TimeFilterClient();
+ ~TimeFilterClient();
+
+ // TODO: remove after migration to Tuner Service is done.
+ void setHidlTimeFilter(sp<ITimeFilter> timeFilter);
+
+ /**
+ * Set time stamp for time based filter.
+ */
+ Result setTimeStamp(long timeStamp);
+
+ /**
+ * Clear the time stamp in the time filter.
+ */
+ Result clearTimeStamp();
+
+ /**
+ * Get the current time in the time filter.
+ */
+ long getTimeStamp();
+
+ /**
+ * Get the time from the beginning of current data source.
+ */
+ long getSourceTime();
+
+ /**
+ * Releases the Time Filter instance.
+ */
+ Result close();
+
+private:
+ /**
+ * An AIDL Tuner TimeFilter Singleton assigned at the first time the Tuner Client
+ * opens an TimeFilter. Default null when time filter is not opened.
+ */
+ // TODO: pending on aidl interface
+ //shared_ptr<ITunerTimeFilter> mTunerTimeFilter;
+
+ /**
+ * A TimeFilter HAL interface that is ready before migrating to the TunerTimeFilter.
+ * This is a temprary interface before Tuner Framework migrates to use TunerService.
+ * Default null when the HAL service does not exist.
+ */
+ sp<ITimeFilter> mTimeFilter;
+};
+} // namespace android
+
+#endif // _ANDROID_MEDIA_TV_TIME_FILTER_CLIENT_H_
diff --git a/media/jni/tuner/TunerClient.cpp b/media/jni/tuner/TunerClient.cpp
index bd18c707f18b..39e6ba27e56f 100644
--- a/media/jni/tuner/TunerClient.cpp
+++ b/media/jni/tuner/TunerClient.cpp
@@ -25,6 +25,8 @@
using ::android::hardware::tv::tuner::V1_0::FrontendId;
using ::android::hardware::tv::tuner::V1_0::FrontendType;
+using ::aidl::android::media::tv::tunerresourcemanager::TunerFrontendInfo;
+
namespace android {
sp<ITuner> TunerClient::mTuner;
@@ -37,6 +39,7 @@ int TunerClient::mTunerVersion;
TunerClient::TunerClient() {
// Get HIDL Tuner in migration stage.
getHidlTuner();
+ updateTunerResources();
// Connect with Tuner Service.
::ndk::SpAIBinder binder(AServiceManager_getService("media.tuner"));
mTunerService = ITunerService::fromBinder(binder);
@@ -99,9 +102,10 @@ sp<FrontendClient> TunerClient::openFrontend(int frontendHandle) {
}
if (mTuner != NULL) {
- sp<IFrontend> hidlFrontend = openHidlFrontendByHandle(frontendHandle);
+ int id = getResourceIdFromHandle(frontendHandle, FRONTEND);
+ sp<IFrontend> hidlFrontend = openHidlFrontendById(id);
if (hidlFrontend != NULL) {
- sp<FrontendClient> frontendClient = new FrontendClient(NULL, frontendHandle);
+ sp<FrontendClient> frontendClient = new FrontendClient(NULL, id);
frontendClient->setHidlFrontend(hidlFrontend);
return frontendClient;
}
@@ -160,9 +164,11 @@ sp<DemuxClient> TunerClient::openDemux(int /*demuxHandle*/) {
if (mTuner != NULL) {
// TODO: pending aidl interface
sp<DemuxClient> demuxClient = new DemuxClient();
- sp<IDemux> hidlDemux = openHidlDemux();
+ int demuxId;
+ sp<IDemux> hidlDemux = openHidlDemux(demuxId);
if (hidlDemux != NULL) {
demuxClient->setHidlDemux(hidlDemux);
+ demuxClient->setId(demuxId);
return demuxClient;
}
}
@@ -170,8 +176,135 @@ sp<DemuxClient> TunerClient::openDemux(int /*demuxHandle*/) {
return NULL;
}
+shared_ptr<DemuxCapabilities> TunerClient::getDemuxCaps() {
+ // pending aidl interface
+
+ if (mTuner != NULL) {
+ Result res;
+ DemuxCapabilities caps;
+ mTuner->getDemuxCaps([&](Result r, const DemuxCapabilities& demuxCaps) {
+ caps = demuxCaps;
+ res = r;
+ });
+ if (res == Result::SUCCESS) {
+ return make_shared<DemuxCapabilities>(caps);
+ }
+ }
+
+ return NULL;
+}
+
+sp<DescramblerClient> TunerClient::openDescrambler(int /*descramblerHandle*/) {
+ if (mTunerService != NULL) {
+ // TODO: handle error code
+ /*shared_ptr<ITunerDescrambler> tunerDescrambler;
+ mTunerService->openDescrambler(demuxHandle, &tunerDescrambler);
+ return new DescramblerClient(tunerDescrambler);*/
+ }
+
+ if (mTuner != NULL) {
+ // TODO: pending aidl interface
+ sp<DescramblerClient> descramblerClient = new DescramblerClient();
+ sp<IDescrambler> hidlDescrambler = openHidlDescrambler();
+ if (hidlDescrambler != NULL) {
+ descramblerClient->setHidlDescrambler(hidlDescrambler);
+ return descramblerClient;
+ }
+ }
+
+ return NULL;}
+
+sp<LnbClient> TunerClient::openLnb(int lnbHandle) {
+ if (mTunerService != NULL) {
+ // TODO: handle error code
+ /*shared_ptr<ITunerLnb> tunerLnb;
+ mTunerService->openLnb(demuxHandle, &tunerLnb);
+ return new LnbClient(tunerLnb);*/
+ }
+
+ if (mTuner != NULL) {
+ int id = getResourceIdFromHandle(lnbHandle, LNB);
+ // TODO: pending aidl interface
+ sp<LnbClient> lnbClient = new LnbClient();
+ sp<ILnb> hidlLnb = openHidlLnbById(id);
+ if (hidlLnb != NULL) {
+ lnbClient->setHidlLnb(hidlLnb);
+ lnbClient->setId(id);
+ return lnbClient;
+ }
+ }
+
+ return NULL;
+}
+
+sp<LnbClient> TunerClient::openLnbByName(string lnbName) {
+ if (mTunerService != NULL) {
+ // TODO: handle error code
+ /*shared_ptr<ITunerLnb> tunerLnb;
+ mTunerService->openLnbByName(lnbName, &tunerLnb);
+ return new LnbClient(tunerLnb);*/
+ }
+
+ if (mTuner != NULL) {
+ // TODO: pending aidl interface
+ sp<LnbClient> lnbClient = new LnbClient();
+ LnbId id;
+ sp<ILnb> hidlLnb = openHidlLnbByName(lnbName, id);
+ if (hidlLnb != NULL) {
+ lnbClient->setHidlLnb(hidlLnb);
+ lnbClient->setId(id);
+ return lnbClient;
+ }
+ }
+
+ return NULL;
+}
+
/////////////// TunerClient Helper Methods ///////////////////////
+void TunerClient::updateTunerResources() {
+ if (mTuner == NULL) {
+ return;
+ }
+
+ // Connect with Tuner Resource Manager.
+ ::ndk::SpAIBinder binder(AServiceManager_getService("tv_tuner_resource_mgr"));
+ mTunerResourceManager = ITunerResourceManager::fromBinder(binder);
+
+ updateFrontendResources();
+ updateLnbResources();
+ // TODO: update Demux, Descrambler.
+}
+
+void TunerClient::updateFrontendResources() {
+ vector<FrontendId> ids = getFrontendIds();
+ if (ids.size() == 0) {
+ return;
+ }
+ vector<TunerFrontendInfo> infos;
+ for (int i = 0; i < ids.size(); i++) {
+ shared_ptr<FrontendInfo> frontendInfo = getFrontendInfo((int)ids[i]);
+ if (frontendInfo == NULL) {
+ continue;
+ }
+ TunerFrontendInfo tunerFrontendInfo{
+ .handle = getResourceHandleFromId((int)ids[i], FRONTEND),
+ .frontendType = static_cast<int>(frontendInfo->type),
+ .exclusiveGroupId = static_cast<int>(frontendInfo->exclusiveGroupId),
+ };
+ infos.push_back(tunerFrontendInfo);
+ }
+ mTunerResourceManager->setFrontendInfoList(infos);
+}
+
+void TunerClient::updateLnbResources() {
+ vector<int> handles = getLnbHandles();
+ if (handles.size() == 0) {
+ return;
+ }
+ mTunerResourceManager->setLnbInfoList(handles);
+}
+
sp<ITuner> TunerClient::getHidlTuner() {
if (mTuner == NULL) {
mTunerVersion = 0;
@@ -193,10 +326,9 @@ sp<ITuner> TunerClient::getHidlTuner() {
return mTuner;
}
-sp<IFrontend> TunerClient::openHidlFrontendByHandle(int frontendHandle) {
+sp<IFrontend> TunerClient::openHidlFrontendById(int id) {
sp<IFrontend> fe;
Result res;
- uint32_t id = getResourceIdFromHandle(frontendHandle);
mTuner->openFrontendById(id, [&](Result r, const sp<IFrontend>& frontend) {
fe = frontend;
res = r;
@@ -217,12 +349,13 @@ Result TunerClient::getHidlFrontendInfo(int id, FrontendInfo& feInfo) {
return res;
}
-sp<IDemux> TunerClient::openHidlDemux() {
+sp<IDemux> TunerClient::openHidlDemux(int& demuxId) {
sp<IDemux> demux;
Result res;
- mTuner->openDemux([&](Result result, uint32_t /*id*/, const sp<IDemux>& demuxSp) {
+ mTuner->openDemux([&](Result result, uint32_t id, const sp<IDemux>& demuxSp) {
demux = demuxSp;
+ demuxId = id;
res = result;
});
if (res != Result::SUCCESS || demux == nullptr) {
@@ -232,6 +365,79 @@ sp<IDemux> TunerClient::openHidlDemux() {
return demux;
}
+sp<ILnb> TunerClient::openHidlLnbById(int id) {
+ sp<ILnb> lnb;
+ Result res;
+
+ mTuner->openLnbById(id, [&](Result r, const sp<ILnb>& lnbSp) {
+ res = r;
+ lnb = lnbSp;
+ });
+ if (res != Result::SUCCESS || lnb == nullptr) {
+ ALOGE("Failed to open lnb by id");
+ return NULL;
+ }
+ return lnb;
+}
+
+sp<ILnb> TunerClient::openHidlLnbByName(string name, LnbId& lnbId) {
+ sp<ILnb> lnb;
+ Result res;
+
+ mTuner->openLnbByName(name, [&](Result r, LnbId id, const sp<ILnb>& lnbSp) {
+ res = r;
+ lnb = lnbSp;
+ lnbId = id;
+ });
+ if (res != Result::SUCCESS || lnb == nullptr) {
+ ALOGE("Failed to open lnb by name");
+ return NULL;
+ }
+ return lnb;
+}
+
+sp<IDescrambler> TunerClient::openHidlDescrambler() {
+ sp<IDescrambler> descrambler;
+ Result res;
+
+ mTuner->openDescrambler([&](Result r, const sp<IDescrambler>& descramblerSp) {
+ res = r;
+ descrambler = descramblerSp;
+ });
+
+ if (res != Result::SUCCESS || descrambler == NULL) {
+ return NULL;
+ }
+
+ return descrambler;
+}
+
+vector<int> TunerClient::getLnbHandles() {
+ vector<int> lnbHandles;
+
+ if (mTunerService != NULL) {
+ // TODO: pending hidl interface
+ }
+
+ if (mTuner != NULL) {
+ Result res;
+ vector<LnbId> lnbIds;
+ mTuner->getLnbIds([&](Result r, const hardware::hidl_vec<LnbId>& ids) {
+ lnbIds = ids;
+ res = r;
+ });
+ if (res != Result::SUCCESS || lnbIds.size() == 0) {
+ ALOGW("Lnb isn't available");
+ } else {
+ for (int i = 0; i < lnbIds.size(); i++) {
+ lnbHandles.push_back(getResourceHandleFromId((int)lnbIds[i], LNB));
+ }
+ }
+ }
+
+ return lnbHandles;
+}
+
FrontendInfo TunerClient::FrontendInfoAidlToHidl(TunerServiceFrontendInfo aidlFrontendInfo) {
FrontendInfo hidlFrontendInfo {
.type = static_cast<FrontendType>(aidlFrontendInfo.type),
@@ -246,4 +452,15 @@ FrontendInfo TunerClient::FrontendInfoAidlToHidl(TunerServiceFrontendInfo aidlFr
return hidlFrontendInfo;
}
+
+int TunerClient::getResourceIdFromHandle(int handle, int /*resourceType*/) {
+ return (handle & 0x00ff0000) >> 16;
+}
+
+int TunerClient::getResourceHandleFromId(int id, int resourceType) {
+ // TODO: build up randomly generated id to handle mapping
+ return (resourceType & 0x000000ff) << 24
+ | (id << 16)
+ | (mResourceRequestCount++ & 0xffff);
+}
} // namespace android
diff --git a/media/jni/tuner/TunerClient.h b/media/jni/tuner/TunerClient.h
index 197b1100acd6..a3d2d02c7ef6 100644
--- a/media/jni/tuner/TunerClient.h
+++ b/media/jni/tuner/TunerClient.h
@@ -17,19 +17,25 @@
#ifndef _ANDROID_MEDIA_TV_TUNER_CLIENT_H_
#define _ANDROID_MEDIA_TV_TUNER_CLIENT_H_
+#include <aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.h>
+#include <aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.h>
#include <aidl/android/media/tv/tuner/ITunerService.h>
#include <android/hardware/tv/tuner/1.1/ITuner.h>
#include <android/hardware/tv/tuner/1.1/types.h>
#include "FrontendClient.h"
#include "DemuxClient.h"
+#include "DescramblerClient.h"
+#include "LnbClient.h"
using ::aidl::android::media::tv::tuner::ITunerService;
using ::aidl::android::media::tv::tuner::TunerServiceFrontendInfo;
+using ::aidl::android::media::tv::tunerresourcemanager::ITunerResourceManager;
using ::android::hardware::tv::tuner::V1_0::DemuxCapabilities;
using ::android::hardware::tv::tuner::V1_0::FrontendId;
using ::android::hardware::tv::tuner::V1_0::ITuner;
+using ::android::hardware::tv::tuner::V1_0::LnbId;
using ::android::hardware::tv::tuner::V1_0::Result;
using ::android::hardware::tv::tuner::V1_1::FrontendDtmbCapabilities;
@@ -37,6 +43,13 @@ using namespace std;
namespace android {
+typedef enum {
+ FRONTEND,
+ LNB,
+ DEMUX,
+ DESCRAMBLER,
+} TunerResourceType;
+
struct TunerClient : public RefBase {
public:
@@ -87,7 +100,31 @@ public:
*
* @return the demux’s capabilities.
*/
- //DemuxCapabilities getDemuxCaps() {};
+ shared_ptr<DemuxCapabilities> getDemuxCaps();
+
+ /**
+ * Open a new interface of DescramblerClient given a descramblerHandle.
+ *
+ * @param descramblerHandle the handle of the descrambler granted by TRM.
+ * @return a newly created DescramblerClient interface.
+ */
+ sp<DescramblerClient> openDescrambler(int descramblerHandle);
+
+ /**
+ * Open a new interface of LnbClient given an lnbHandle.
+ *
+ * @param lnbHandle the handle of the LNB granted by TRM.
+ * @return a newly created LnbClient interface.
+ */
+ sp<LnbClient> openLnb(int lnbHandle);
+
+ /**
+ * Open a new interface of LnbClient given a LNB name.
+ *
+ * @param lnbName the name for an external LNB to be opened.
+ * @return a newly created LnbClient interface.
+ */
+ sp<LnbClient> openLnbByName(string lnbName);
/**
* Get the current Tuner HAL version. The high 16 bits are the major version number
@@ -95,11 +132,24 @@ public:
*/
int getHalTunerVersion() { return mTunerVersion; }
- static int getResourceIdFromHandle(int handle) {
- return (handle & 0x00ff0000) >> 16;
- }
-
private:
+ sp<ITuner> getHidlTuner();
+ sp<IFrontend> openHidlFrontendById(int id);
+ sp<IDemux> openHidlDemux(int& demuxId);
+ Result getHidlFrontendInfo(int id, FrontendInfo& info);
+ sp<ILnb> openHidlLnbById(int id);
+ sp<ILnb> openHidlLnbByName(string name, LnbId& lnbId);
+ sp<IDescrambler> openHidlDescrambler();
+ vector<int> getLnbHandles();
+ FrontendInfo FrontendInfoAidlToHidl(TunerServiceFrontendInfo aidlFrontendInfo);
+ void updateTunerResources();
+ void updateFrontendResources();
+ void updateLnbResources();
+
+ int getResourceIdFromHandle(int handle, int resourceType);
+
+ int getResourceHandleFromId(int id, int resourceType);
+
/**
* An AIDL Tuner Service Singleton assigned at the first time the Tuner Client
* connects with the Tuner Service. Default null when the service does not exist.
@@ -124,11 +174,9 @@ private:
// while the low 16 bits are the minor version. Default value is unknown version 0.
static int mTunerVersion;
- sp<ITuner> getHidlTuner();
- sp<IFrontend> openHidlFrontendByHandle(int frontendHandle);
- sp<IDemux> openHidlDemux();
- Result getHidlFrontendInfo(int id, FrontendInfo& info);
- FrontendInfo FrontendInfoAidlToHidl(TunerServiceFrontendInfo aidlFrontendInfo);
+ shared_ptr<ITunerResourceManager> mTunerResourceManager;
+
+ int mResourceRequestCount = 0;
};
} // namespace android
diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml
index 1b96b0045ef0..731bdccb914f 100644
--- a/packages/CompanionDeviceManager/res/values/strings.xml
+++ b/packages/CompanionDeviceManager/res/values/strings.xml
@@ -25,11 +25,14 @@
<!-- The generic placeholder for a device type when nothing specific is known about it [CHAR LIMIT=30] -->
<string name="profile_name_generic">device</string>
+ <!-- The name of the "watch" device type [CHAR LIMIT=30] -->
+ <string name="profile_name_watch">watch</string>
+
<!-- Title of the device association confirmation dialog. -->
<string name="confirmation_title">Set &lt;strong&gt;<xliff:g id="app_name" example="Android Wear">%1$s</xliff:g>&lt;/strong&gt; to manage your <xliff:g id="profile_name" example="watch">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="device_name" example="ASUS ZenWatch 2">%3$s</xliff:g>&lt;/strong&gt;</string>
<!-- Text of the device profile permissions explanation in the association dialog. -->
- <string name="profile_summary"><xliff:g id="app_name" example="Android Wear">%1$s</xliff:g> is needed to manage your <xliff:g id="profile_name" example="watch">%2$s</xliff:g>. <xliff:g id="app_name2" example="Android Wear">%3$s</xliff:g> will get access to <xliff:g id="permissions" example="Notifications, Calendar and Phone">%4$s</xliff:g> while the <xliff:g id="profile_name2" example="watch">%5$s</xliff:g> is connected.</string>
+ <string name="profile_summary"><xliff:g id="app_name" example="Android Wear">%1$s</xliff:g> is needed to manage your <xliff:g id="profile_name" example="watch">%2$s</xliff:g>. <xliff:g id="privileges_discplaimer" example="Android Wear will get access to your Notifications, Calendar and Contacts.">%3$s</xliff:g></string>
<!-- Positive button for the device-app association consent dialog [CHAR LIMIT=30] -->
<string name="consent_yes">Yes</string>
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
index f42a51d6593a..620c7ae96ebf 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
@@ -17,11 +17,13 @@
package com.android.companiondevicemanager;
import static android.companion.BluetoothDeviceFilterUtils.getDeviceMacAddress;
+import static android.text.TextUtils.emptyIfNull;
import static android.text.TextUtils.withoutPrefix;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import static java.util.Objects.requireNonNull;
+import android.annotation.Nullable;
import android.app.Activity;
import android.companion.AssociationRequest;
import android.companion.CompanionDeviceManager;
@@ -65,10 +67,7 @@ public class DeviceChooserActivity extends Activity {
getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
String deviceProfile = getRequest().getDeviceProfile();
- String profileName = deviceProfile == null
- ? getString(R.string.profile_name_generic)
- //TODO introduce PermissionController APIs to resolve UI values
- : withoutPrefix("android.app.role.COMPANION_DEVICE_", deviceProfile).toLowerCase();
+ String profileName = getDeviceProfileName(deviceProfile);
if (getRequest().isSingleDevice()) {
setContentView(R.layout.device_confirmation);
@@ -112,15 +111,14 @@ public class DeviceChooserActivity extends Activity {
TextView profileSummary = findViewById(R.id.profile_summary);
if (deviceProfile != null) {
- //TODO introduce PermissionController APIs to resolve UI values
- String privileges = "Notifications, Phone, Contacts and Calendar";
+ String privacyDisclaimer = emptyIfNull(getRequest()
+ .getDeviceProfilePrivilegesDescription())
+ .replace("APP_NAME", getCallingAppName());
profileSummary.setVisibility(View.VISIBLE);
profileSummary.setText(getString(R.string.profile_summary,
getCallingAppName(),
profileName,
- getCallingAppName(),
- privileges,
- profileName));
+ privacyDisclaimer));
} else {
profileSummary.setVisibility(View.GONE);
}
@@ -135,6 +133,24 @@ public class DeviceChooserActivity extends Activity {
return getService().mRequest;
}
+ private String getDeviceProfileName(@Nullable String deviceProfile) {
+ if (deviceProfile == null) {
+ return getString(R.string.profile_name_generic);
+ }
+ switch (deviceProfile) {
+ case AssociationRequest.DEVICE_PROFILE_WATCH: {
+ return getString(R.string.profile_name_watch);
+ }
+ default: {
+ Log.wtf(LOG_TAG,
+ "No localized profile name found for device profile: " + deviceProfile);
+ return withoutPrefix("android.app.role.COMPANION_DEVICE_", deviceProfile)
+ .toLowerCase()
+ .replace('_', ' ');
+ }
+ }
+ }
+
private void cancel() {
getService().onCancel();
setResult(RESULT_CANCELED);
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index a06bb931771f..e036d87f2492 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -78,6 +78,7 @@
<uses-permission android:name="android.permission.REQUEST_NETWORK_SCORES" />
<uses-permission android:name="android.permission.CONTROL_VPN" />
<uses-permission android:name="android.permission.PEERS_MAC_ADDRESS"/>
+ <uses-permission android:name="android.permission.READ_WIFI_CREDENTIAL"/>
<!-- Physical hardware -->
<uses-permission android:name="android.permission.MANAGE_USB" />
<uses-permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS" />
diff --git a/packages/SystemUI/res/layout/qs_paged_page_side_labels.xml b/packages/SystemUI/res/layout/qs_paged_page_side_labels.xml
new file mode 100644
index 000000000000..c83077371bb0
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_paged_page_side_labels.xml
@@ -0,0 +1,23 @@
+<?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.
+-->
+<com.android.systemui.qs.SideLabelTileLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/tile_page"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipChildren="false"
+ android:clipToPadding="false" />
diff --git a/packages/SystemUI/res/layout/qs_paged_tile_layout_side_labels.xml b/packages/SystemUI/res/layout/qs_paged_tile_layout_side_labels.xml
new file mode 100644
index 000000000000..efa240362f67
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_paged_tile_layout_side_labels.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+
+<com.android.systemui.qs.PagedTileLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:systemui="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/qs_pager"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:clipChildren="true"
+ android:paddingBottom="@dimen/qs_paged_tile_layout_padding_bottom"
+ systemui:sideLabels="true" />
diff --git a/packages/SystemUI/res/layout/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml
index 81d44cfa49dd..571cbbcc77db 100644
--- a/packages/SystemUI/res/layout/qs_tile_label.xml
+++ b/packages/SystemUI/res/layout/qs_tile_label.xml
@@ -34,7 +34,8 @@
<Space
android:id="@+id/expand_space"
android:layout_width="22dp"
- android:layout_height="0dp" />
+ android:layout_height="0dp"
+ android:visibility="gone" />
<TextView
android:id="@+id/tile_label"
diff --git a/packages/SystemUI/res/layout/qs_tile_label_divider.xml b/packages/SystemUI/res/layout/qs_tile_label_divider.xml
new file mode 100644
index 000000000000..0d6460c22f0f
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_tile_label_divider.xml
@@ -0,0 +1,27 @@
+<?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.
+ -->
+
+<View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="1px"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_vertical"
+ android:layout_marginBottom="10dp"
+ android:layout_marginTop="10dp"
+ android:layout_marginStart="0dp"
+ android:layout_marginEnd="0dp"
+ android:background="?android:attr/textColorSecondary"
+/> \ No newline at end of file
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 897e3902b55c..4059b49ec486 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -167,5 +167,9 @@
<attr name="android:drawable" />
<attr name="android:alpha" />
</declare-styleable>
+
+ <declare-styleable name="PagedTileLayout">
+ <attr name="sideLabels" format="boolean"/>
+ </declare-styleable>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 8888ad6b0411..72dd72410af3 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -519,6 +519,7 @@
Scaled @dimen/qs_page_indicator-width by .4f.
-->
<dimen name="qs_page_indicator_dot_width">6.4dp</dimen>
+ <dimen name="qs_tile_side_label_padding">6dp</dimen>
<dimen name="qs_tile_icon_size">24dp</dimen>
<dimen name="qs_tile_text_size">12sp</dimen>
<dimen name="qs_tile_divider_height">1dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java b/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
index 0053fea35262..e822dd58fb98 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
@@ -98,6 +98,7 @@ public class PageIndicator extends ViewGroup {
}
// Refresh state.
setIndex(mPosition >> 1);
+ requestLayout();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 321f73295e2e..eaf212362320 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -8,6 +8,7 @@ import android.animation.PropertyValuesHolder;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.graphics.Rect;
import android.os.Bundle;
import android.util.AttributeSet;
@@ -47,7 +48,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
};
private final ArrayList<TileRecord> mTiles = new ArrayList<>();
- private final ArrayList<TilePage> mPages = new ArrayList<>();
+ private final ArrayList<TileLayout> mPages = new ArrayList<>();
private PageIndicator mPageIndicator;
private float mPageIndicatorPosition;
@@ -71,6 +72,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
private int mMaxColumns = TileLayout.NO_MAX_COLUMNS;
private boolean mShowLabels = true;
+ private final boolean mSideLabels;
public PagedTileLayout(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -81,13 +83,18 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
mLayoutOrientation = getResources().getConfiguration().orientation;
mLayoutDirection = getLayoutDirection();
mClippingRect = new Rect();
+
+ TypedArray t = context.getTheme().obtainStyledAttributes(
+ attrs, R.styleable.PagedTileLayout, 0, 0);
+ mSideLabels = t.getBoolean(R.styleable.PagedTileLayout_sideLabels, false);
+ t.recycle();
}
private int mLastMaxHeight = -1;
@Override
public void setShowLabels(boolean show) {
mShowLabels = show;
- for (TilePage p : mPages) {
+ for (TileLayout p : mPages) {
p.setShowLabels(show);
}
mDistributeTiles = true;
@@ -145,7 +152,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
}
// This will dump to the ui log all the tiles that are visible in this page
- private void logVisibleTiles(TilePage page) {
+ private void logVisibleTiles(TileLayout page) {
for (int i = 0; i < page.mRecords.size(); i++) {
QSTile t = page.mRecords.get(i).tile;
mUiEventLogger.logWithInstanceId(QSEvent.QS_TILE_VISIBLE, 0, t.getMetricsSpec(),
@@ -161,7 +168,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
}
private void updateListening() {
- for (TilePage tilePage : mPages) {
+ for (TileLayout tilePage : mPages) {
tilePage.setListening(tilePage.getParent() != null && mListening);
}
}
@@ -222,13 +229,14 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mPages.add(createTilePage());
+ mPages.add(createTileLayout());
mAdapter.notifyDataSetChanged();
}
- private TilePage createTilePage() {
- TilePage page = (TilePage) LayoutInflater.from(getContext())
- .inflate(R.layout.qs_paged_page, this, false);
+ private TileLayout createTileLayout() {
+ TileLayout page = (TileLayout) LayoutInflater.from(getContext())
+ .inflate(mSideLabels ? R.layout.qs_paged_page_side_labels
+ : R.layout.qs_paged_page, this, false);
page.setMinRows(mMinRows);
page.setMaxColumns(mMaxColumns);
page.setShowLabels(mShowLabels);
@@ -283,7 +291,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
int currentItem = getCurrentPageNumber();
for (int i = 0; i < mPages.size(); i++) {
- TilePage page = mPages.get(i);
+ TileLayout page = mPages.get(i);
page.setSelected(i == currentItem ? selected : false);
if (page.isSelected()) {
logVisibleTiles(page);
@@ -325,7 +333,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
}
while (mPages.size() < numPages) {
if (DEBUG) Log.d(TAG, "Adding page");
- mPages.add(createTilePage());
+ mPages.add(createTileLayout());
}
while (mPages.size() > numPages) {
if (DEBUG) Log.d(TAG, "Removing page");
@@ -422,7 +430,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
final int nRows = mPages.get(0).mRows;
for (int i = 0; i < mPages.size(); i++) {
- TilePage t = mPages.get(i);
+ TileLayout t = mPages.get(i);
t.mRows = nRows;
}
}
@@ -465,19 +473,19 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
public int getNumVisibleTiles() {
if (mPages.size() == 0) return 0;
- TilePage currentPage = mPages.get(getCurrentPageNumber());
+ TileLayout currentPage = mPages.get(getCurrentPageNumber());
return currentPage.mRecords.size();
}
public void startTileReveal(Set<String> tileSpecs, final Runnable postAnimation) {
if (tileSpecs.isEmpty() || mPages.size() < 2 || getScrollX() != 0 || !beginFakeDrag()) {
// Do not start the reveal animation unless there are tiles to animate, multiple
- // TilePages available and the user has not already started dragging.
+ // TileLayouts available and the user has not already started dragging.
return;
}
final int lastPageNumber = mPages.size() - 1;
- final TilePage lastPage = mPages.get(lastPageNumber);
+ final TileLayout lastPage = mPages.get(lastPageNumber);
final ArrayList<Animator> bounceAnims = new ArrayList<>();
for (TileRecord tr : lastPage.mRecords) {
if (tileSpecs.contains(tr.tile.getTileSpec())) {
@@ -557,12 +565,6 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
return mRecords.size() >= maxTiles();
}
- public int maxTiles() {
- // Each page should be able to hold at least one tile. If there's not enough room to
- // show even 1 or there are no tiles, it probably means we are in the middle of setting
- // up.
- return Math.max(mColumns * mRows, 1);
- }
}
private final PagerAdapter mAdapter = new PagerAdapter() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 65f174c508e8..5eba147ab279 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -26,6 +26,7 @@ import android.content.res.Resources;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
+import android.provider.Settings;
import android.util.AttributeSet;
import android.util.Pair;
import android.view.Gravity;
@@ -112,6 +113,7 @@ public class QSPanel extends LinearLayout implements Tunable {
private int mMediaTotalBottomMargin;
private int mFooterMarginStartHorizontal;
private Consumer<Boolean> mMediaVisibilityChangedListener;
+ private final boolean mSideLabels;
public QSPanel(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -119,6 +121,8 @@ public class QSPanel extends LinearLayout implements Tunable {
mMediaTotalBottomMargin = getResources().getDimensionPixelSize(
R.dimen.quick_settings_bottom_margin_media);
mContext = context;
+ mSideLabels = Settings.Secure.getInt(
+ mContext.getContentResolver(), "sysui_side_labels", 0) != 0;
setOrientation(VERTICAL);
@@ -174,8 +178,9 @@ public class QSPanel extends LinearLayout implements Tunable {
/** */
public QSTileLayout createRegularTileLayout() {
if (mRegularTileLayout == null) {
- mRegularTileLayout = (QSTileLayout) LayoutInflater.from(mContext).inflate(
- R.layout.qs_paged_tile_layout, this, false);
+ mRegularTileLayout = (QSTileLayout) LayoutInflater.from(mContext)
+ .inflate(mSideLabels ? R.layout.qs_paged_tile_layout_side_labels
+ : R.layout.qs_paged_tile_layout, this, false);
}
return mRegularTileLayout;
}
@@ -748,7 +753,13 @@ public class QSPanel extends LinearLayout implements Tunable {
if (needsDynamicRowsAndColumns()) {
newLayout.setMinRows(horizontal ? 2 : 1);
// Let's use 3 columns to match the current layout
- newLayout.setMaxColumns(horizontal ? 3 : TileLayout.NO_MAX_COLUMNS);
+ int columns;
+ if (mSideLabels) {
+ columns = horizontal ? 1 : 2;
+ } else {
+ columns = horizontal ? 3 : TileLayout.NO_MAX_COLUMNS;
+ }
+ newLayout.setMaxColumns(columns);
}
updateMargins(mediaHostView);
}
@@ -763,6 +774,10 @@ public class QSPanel extends LinearLayout implements Tunable {
updatePadding();
}
+ boolean useSideLabels() {
+ return mSideLabels;
+ }
+
private class H extends Handler {
private static final int SHOW_DETAIL = 1;
private static final int SET_TILE_VISIBILITY = 2;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index cca0e1b0c0f0..e2d7d201a5de 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -90,14 +90,26 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel>
@Override
public void setTiles() {
- List<QSTile> tiles = new ArrayList();
+ List<QSTile> tiles = new ArrayList<>();
for (QSTile tile : mHost.getTiles()) {
tiles.add(tile);
if (tiles.size() == mView.getNumQuickTiles()) {
break;
}
}
- super.setTiles(tiles, true);
+ if (mView.useSideLabels()) {
+ List<QSTile> newTiles = new ArrayList<>();
+ for (int i = 0; i < tiles.size(); i += 2) {
+ newTiles.add(tiles.get(i));
+ }
+ for (int i = 1; i < tiles.size(); i += 2) {
+ newTiles.add(tiles.get(i));
+ }
+ super.setTiles(newTiles, true);
+
+ } else {
+ super.setTiles(tiles, true);
+ }
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
new file mode 100644
index 000000000000..74a7ac1cb6dd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
@@ -0,0 +1,40 @@
+/*
+ * 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.qs
+
+import android.content.Context
+import android.util.AttributeSet
+import com.android.systemui.R
+
+open class SideLabelTileLayout(context: Context, attrs: AttributeSet) : TileLayout(context, attrs) {
+
+ override fun updateResources(): Boolean {
+ return super.updateResources().also {
+ mResourceColumns = 2
+ mMaxAllowedRows = 4
+ mCellMarginHorizontal = (mCellMarginHorizontal * 1.2).toInt()
+ mCellMarginVertical = mCellMarginHorizontal
+ mMaxCellHeight = context.resources.getDimensionPixelSize(R.dimen.qs_quick_tile_size)
+ }
+ }
+
+ override fun setShowLabels(show: Boolean) { }
+
+ override fun isFull(): Boolean {
+ return mRecords.size >= maxTiles()
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index e38c931287b1..911261a01143 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -42,7 +42,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout {
private final boolean mLessRows;
private int mMinRows = 1;
private int mMaxColumns = NO_MAX_COLUMNS;
- private int mResourceColumns;
+ protected int mResourceColumns;
public TileLayout(Context context) {
this(context, null);
@@ -216,7 +216,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout {
return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
}
- private int getCellHeight() {
+ protected int getCellHeight() {
return mShowLabels ? mMaxCellHeight : mMaxCellHeight / 2;
}
@@ -260,4 +260,18 @@ public class TileLayout extends ViewGroup implements QSTileLayout {
public int getNumVisibleTiles() {
return mRecords.size();
}
+
+ public boolean isFull() {
+ return false;
+ }
+
+ /**
+ * @return The maximum number of tiles this layout can hold
+ */
+ public int maxTiles() {
+ // Each layout should be able to hold at least one tile. If there's not enough room to
+ // show even 1 or there are no tiles, it probably means we are in the middle of setting
+ // up.
+ return Math.max(mColumns * mRows, 1);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index e9d481b2e7f0..ba71fa6a8fb3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -49,6 +49,7 @@ import com.android.systemui.qs.tiles.UserTile;
import com.android.systemui.qs.tiles.WifiTile;
import com.android.systemui.qs.tiles.WorkModeTile;
import com.android.systemui.util.leak.GarbageMonitor;
+import com.android.systemui.util.settings.SecureSettings;
import javax.inject.Inject;
import javax.inject.Provider;
@@ -86,9 +87,12 @@ public class QSFactoryImpl implements QSFactory {
private final Lazy<QSHost> mQsHostLazy;
private final Provider<CustomTile.Builder> mCustomTileBuilderProvider;
+ private final boolean mSideLabels;
+
@Inject
public QSFactoryImpl(
Lazy<QSHost> qsHostLazy,
+ SecureSettings settings,
Provider<CustomTile.Builder> customTileBuilderProvider,
Provider<WifiTile> wifiTileProvider,
Provider<InternetTile> internetTileProvider,
@@ -115,6 +119,8 @@ public class QSFactoryImpl implements QSFactory {
mQsHostLazy = qsHostLazy;
mCustomTileBuilderProvider = customTileBuilderProvider;
+ mSideLabels = settings.getInt("sysui_side_labels", 0) != 0;
+
mWifiTileProvider = wifiTileProvider;
mInternetTileProvider = internetTileProvider;
mBluetoothTileProvider = bluetoothTileProvider;
@@ -218,6 +224,8 @@ public class QSFactoryImpl implements QSFactory {
QSIconView icon = tile.createTileView(context);
if (collapsedView) {
return new QSTileBaseView(context, icon, collapsedView);
+ } else if (mSideLabels) {
+ return new QSTileViewHorizontal(context, icon);
} else {
return new com.android.systemui.qs.tileimpl.QSTileView(context, icon);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index 655e4e2684e4..38e2ba4df79a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -61,15 +61,15 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
private final FrameLayout mIconFrame;
protected QSIconView mIcon;
protected RippleDrawable mRipple;
- private Drawable mTileBackground;
+ protected Drawable mTileBackground;
private String mAccessibilityClass;
private boolean mTileState;
private boolean mCollapsedView;
- private boolean mShowRippleEffect = true;
+ protected boolean mShowRippleEffect = true;
private float mStrokeWidthActive;
private float mStrokeWidthInactive;
- private final ImageView mBg;
+ protected final ImageView mBg;
private final int mColorActive;
private final int mColorInactive;
private final int mColorDisabled;
@@ -162,7 +162,7 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
}
}
- private void updateRippleSize() {
+ protected void updateRippleSize() {
// center the touch feedback on the center of the icon, and dial it down a bit
final int cx = mIconFrame.getMeasuredWidth() / 2 + mIconFrame.getLeft();
final int cy = mIconFrame.getMeasuredHeight() / 2 + mIconFrame.getTop();
@@ -311,7 +311,7 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
return mLocInScreen[1] >= -getHeight();
}
- private int getCircleColor(int state) {
+ protected int getCircleColor(int state) {
switch (state) {
case Tile.STATE_ACTIVE:
return mColorActive;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
index 650206672c1e..2dbd2cfe9c10 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
@@ -37,7 +37,6 @@ import java.util.Objects;
/** View that represents a standard quick settings tile. **/
public class QSTileView extends QSTileBaseView {
private static final int MAX_LABEL_LINES = 2;
- private static final boolean DUAL_TARGET_ALLOWED = false;
private View mDivider;
protected TextView mLabel;
protected TextView mSecondLine;
@@ -46,8 +45,10 @@ public class QSTileView extends QSTileBaseView {
protected ViewGroup mLabelContainer;
private View mExpandIndicator;
private View mExpandSpace;
- private ColorStateList mColorLabelDefault;
+ protected ColorStateList mColorLabelActive;
+ protected ColorStateList mColorLabelInactive;
private ColorStateList mColorLabelUnavailable;
+ protected boolean mDualTargetAllowed = false;
public QSTileView(Context context, QSIconView icon) {
this(context, icon, false);
@@ -64,7 +65,8 @@ public class QSTileView extends QSTileBaseView {
createLabel();
setOrientation(VERTICAL);
setGravity(Gravity.CENTER_HORIZONTAL | Gravity.TOP);
- mColorLabelDefault = Utils.getColorAttr(getContext(), android.R.attr.textColorPrimary);
+ mColorLabelActive = Utils.getColorAttr(getContext(), android.R.attr.textColorPrimary);
+ mColorLabelInactive = mColorLabelActive;
// The text color for unavailable tiles is textColorSecondary, same as secondaryLabel for
// contrast purposes
mColorLabelUnavailable = Utils.getColorAttr(getContext(),
@@ -118,8 +120,15 @@ public class QSTileView extends QSTileBaseView {
protected void handleStateChanged(QSTile.State state) {
super.handleStateChanged(state);
if (!Objects.equals(mLabel.getText(), state.label) || mState != state.state) {
- mLabel.setTextColor(state.state == Tile.STATE_UNAVAILABLE ? mColorLabelUnavailable
- : mColorLabelDefault);
+ ColorStateList labelColor;
+ if (state.state == Tile.STATE_ACTIVE) {
+ labelColor = mColorLabelActive;
+ } else if (state.state == Tile.STATE_INACTIVE) {
+ labelColor = mColorLabelInactive;
+ } else {
+ labelColor = mColorLabelUnavailable;
+ }
+ mLabel.setTextColor(labelColor);
mState = state.state;
mLabel.setText(state.label);
}
@@ -128,9 +137,8 @@ public class QSTileView extends QSTileBaseView {
mSecondLine.setVisibility(TextUtils.isEmpty(state.secondaryLabel) ? View.GONE
: View.VISIBLE);
}
- boolean dualTarget = DUAL_TARGET_ALLOWED && state.dualTarget;
- mExpandIndicator.setVisibility(dualTarget ? View.VISIBLE : View.GONE);
- mExpandSpace.setVisibility(dualTarget ? View.VISIBLE : View.GONE);
+ boolean dualTarget = mDualTargetAllowed && state.dualTarget;
+ handleExpand(dualTarget);
mLabelContainer.setContentDescription(dualTarget ? state.dualLabelContentDescription
: null);
if (dualTarget != mLabelContainer.isClickable()) {
@@ -142,6 +150,11 @@ public class QSTileView extends QSTileBaseView {
mPadLock.setVisibility(state.disabledByPolicy ? View.VISIBLE : View.GONE);
}
+ protected void handleExpand(boolean dualTarget) {
+ mExpandIndicator.setVisibility(dualTarget ? View.VISIBLE : View.GONE);
+ mExpandSpace.setVisibility(dualTarget ? View.VISIBLE : View.GONE);
+ }
+
@Override
public void init(OnClickListener click, OnClickListener secondaryClick,
OnLongClickListener longClick) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
new file mode 100644
index 000000000000..2ef78c249246
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
@@ -0,0 +1,116 @@
+/*
+ * 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.qs.tileimpl
+
+import android.content.Context
+import android.content.res.ColorStateList
+import android.graphics.Color
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.PaintDrawable
+import android.graphics.drawable.RippleDrawable
+import android.service.quicksettings.Tile.STATE_ACTIVE
+import android.view.Gravity
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.LinearLayout
+import com.android.systemui.R
+import com.android.systemui.plugins.qs.QSIconView
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.tileimpl.QSTileImpl.getColorForState
+
+class QSTileViewHorizontal(
+ context: Context,
+ icon: QSIconView
+) : QSTileView(context, icon, false) {
+
+ private var paintDrawable: PaintDrawable? = null
+ private var divider: View? = null
+
+ init {
+ orientation = HORIZONTAL
+ mDualTargetAllowed = true
+ mBg.setImageDrawable(null)
+ createDivider()
+ mColorLabelActive = ColorStateList.valueOf(getColorForState(getContext(), STATE_ACTIVE))
+ }
+
+ override fun createLabel() {
+ super.createLabel()
+ findViewById<LinearLayout>(R.id.label_group)?.gravity = Gravity.START
+ mLabel.gravity = Gravity.START
+ mSecondLine.gravity = Gravity.START
+ val padding = context.resources.getDimensionPixelSize(R.dimen.qs_tile_side_label_padding)
+ mLabelContainer.setPadding(padding, padding, padding, padding)
+ (mLabelContainer.layoutParams as LayoutParams).gravity = Gravity.CENTER_VERTICAL
+ }
+
+ fun createDivider() {
+ divider = LayoutInflater.from(context).inflate(R.layout.qs_tile_label_divider, this, false)
+ val position = indexOfChild(mLabelContainer)
+ addView(divider, position)
+ }
+
+ override fun init(
+ click: OnClickListener?,
+ secondaryClick: OnClickListener?,
+ longClick: OnLongClickListener?
+ ) {
+ super.init(click, secondaryClick, longClick)
+ mLabelContainer.setOnClickListener {
+ longClick?.onLongClick(it)
+ }
+ mLabelContainer.isClickable = false
+ }
+
+ override fun updateRippleSize() {
+ }
+
+ override fun newTileBackground(): Drawable? {
+ val d = super.newTileBackground()
+ if (paintDrawable == null) {
+ paintDrawable = PaintDrawable(Color.WHITE).apply {
+ setCornerRadius(30f)
+ }
+ }
+ if (d is RippleDrawable) {
+ d.addLayer(paintDrawable)
+ return d
+ } else {
+ return paintDrawable
+ }
+ }
+
+ override fun setClickable(clickable: Boolean) {
+ super.setClickable(clickable)
+ background = mTileBackground
+ if (clickable && mShowRippleEffect) {
+ mRipple?.setHotspotBounds(left, top, right, bottom)
+ } else {
+ mRipple?.setHotspotBounds(0, 0, 0, 0)
+ }
+ }
+
+ override fun handleStateChanged(state: QSTile.State) {
+ super.handleStateChanged(state)
+ paintDrawable?.setTint(getCircleColor(state.state))
+ mSecondLine.setTextColor(mLabel.textColors)
+ mLabelContainer.background = null
+ divider?.backgroundTintList = mLabel.textColors
+ }
+
+ override fun handleExpand(dualTarget: Boolean) {}
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 4d89dea7cb70..19eac77136c6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -54,6 +54,7 @@ import com.android.systemui.statusbar.policy.NetworkController.AccessPointContro
import com.android.systemui.statusbar.policy.NetworkController.IconState;
import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
import com.android.systemui.statusbar.policy.WifiIcons;
+import com.android.wifitrackerlib.WifiEntry;
import java.util.List;
@@ -80,12 +81,13 @@ public class WifiTile extends QSTileImpl<SignalState> {
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
- NetworkController networkController
+ NetworkController networkController,
+ AccessPointController accessPointController
) {
super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
activityStarter, qsLogger);
mController = networkController;
- mWifiController = mController.getAccessPointController();
+ mWifiController = accessPointController;
mDetailAdapter = (WifiDetailAdapter) createDetailAdapter();
mController.observe(getLifecycle(), mSignalCallback);
}
@@ -325,7 +327,7 @@ public class WifiTile extends QSTileImpl<SignalState> {
NetworkController.AccessPointController.AccessPointCallback, QSDetailItems.Callback {
private QSDetailItems mItems;
- private AccessPoint[] mAccessPoints;
+ private WifiEntry[] mAccessPoints;
@Override
public CharSequence getTitle() {
@@ -366,8 +368,8 @@ public class WifiTile extends QSTileImpl<SignalState> {
}
@Override
- public void onAccessPointsChanged(final List<AccessPoint> accessPoints) {
- mAccessPoints = accessPoints.toArray(new AccessPoint[accessPoints.size()]);
+ public void onAccessPointsChanged(final List<WifiEntry> accessPoints) {
+ mAccessPoints = accessPoints.toArray(new WifiEntry[accessPoints.size()]);
filterUnreachableAPs();
updateItems();
@@ -376,15 +378,15 @@ public class WifiTile extends QSTileImpl<SignalState> {
/** Filter unreachable APs from mAccessPoints */
private void filterUnreachableAPs() {
int numReachable = 0;
- for (AccessPoint ap : mAccessPoints) {
- if (ap.isReachable()) numReachable++;
+ for (WifiEntry ap : mAccessPoints) {
+ if (isWifiEntryReachable(ap)) numReachable++;
}
if (numReachable != mAccessPoints.length) {
- AccessPoint[] unfiltered = mAccessPoints;
- mAccessPoints = new AccessPoint[numReachable];
+ WifiEntry[] unfiltered = mAccessPoints;
+ mAccessPoints = new WifiEntry[numReachable];
int i = 0;
- for (AccessPoint ap : unfiltered) {
- if (ap.isReachable()) mAccessPoints[i++] = ap;
+ for (WifiEntry ap : unfiltered) {
+ if (isWifiEntryReachable(ap)) mAccessPoints[i++] = ap;
}
}
}
@@ -397,8 +399,8 @@ public class WifiTile extends QSTileImpl<SignalState> {
@Override
public void onDetailItemClick(Item item) {
if (item == null || item.tag == null) return;
- final AccessPoint ap = (AccessPoint) item.tag;
- if (!ap.isActive()) {
+ final WifiEntry ap = (WifiEntry) item.tag;
+ if (ap.getConnectedState() == WifiEntry.CONNECTED_STATE_DISCONNECTED) {
if (mWifiController.connect(ap)) {
mHost.collapsePanels();
}
@@ -442,12 +444,12 @@ public class WifiTile extends QSTileImpl<SignalState> {
if (mAccessPoints != null) {
items = new Item[mAccessPoints.length];
for (int i = 0; i < mAccessPoints.length; i++) {
- final AccessPoint ap = mAccessPoints[i];
+ final WifiEntry ap = mAccessPoints[i];
final Item item = new Item();
item.tag = ap;
item.iconResId = mWifiController.getIcon(ap);
item.line1 = ap.getSsid();
- item.line2 = ap.isActive() ? ap.getSummary() : null;
+ item.line2 = ap.getSummary();
item.icon2 = ap.getSecurity() != AccessPoint.SECURITY_NONE
? R.drawable.qs_ic_wifi_lock
: -1;
@@ -457,4 +459,8 @@ public class WifiTile extends QSTileImpl<SignalState> {
mItems.setItems(items);
}
}
+
+ private static boolean isWifiEntryReachable(WifiEntry ap) {
+ return ap.getLevel() != WifiEntry.WIFI_LEVEL_UNREACHABLE;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
index 3811ca929f6e..bbc4b780bc52 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
@@ -50,18 +50,26 @@ public class FeatureFlags {
@Inject
public FeatureFlags(@Background Executor executor) {
DeviceConfig.addOnPropertiesChangedListener(
- "systemui",
+ /* namespace= */ "systemui",
executor,
this::onPropertiesChanged);
}
public boolean isNewNotifPipelineEnabled() {
- return getDeviceConfigFlag("notification.newpipeline.enabled", true);
+ return getDeviceConfigFlag("notification.newpipeline.enabled", /* defaultValue= */ true);
}
public boolean isNewNotifPipelineRenderingEnabled() {
return isNewNotifPipelineEnabled()
- && getDeviceConfigFlag("notification.newpipeline.rendering", false);
+ && getDeviceConfigFlag("notification.newpipeline.rendering", /* defaultValue= */
+ false);
+ }
+
+ /**
+ * Flag used for guarding development of b/171917882.
+ */
+ public boolean isTwoColumnNotificationShadeEnabled() {
+ return getDeviceConfigFlag("notification.twocolumn", /* defaultValue= */ false);
}
private void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
@@ -76,7 +84,7 @@ public class FeatureFlags {
synchronized (mCachedDeviceConfigFlags) {
Boolean flag = mCachedDeviceConfigFlags.get(key);
if (flag == null) {
- flag = DeviceConfig.getBoolean("systemui", key, defaultValue);
+ flag = DeviceConfig.getBoolean(/* namespace= */ "systemui", key, defaultValue);
mCachedDeviceConfigFlags.put(key, flag);
}
return flag;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index bb76ac0b26bd..ca3923f06a13 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -504,7 +504,7 @@ public class NotificationMediaManager implements Dumpable {
// TODO: Should this really be for all users? It appears that inactive users
// can't have active sessions, which would mean it is fine.
final List<MediaController> sessions =
- mMediaSessionManager.getActiveSessionsForUser(null, UserHandle.USER_ALL);
+ mMediaSessionManager.getActiveSessionsForUser(null, UserHandle.ALL);
for (MediaController aController : sessions) {
// now to see if we have one like this
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index f8e361fb5b6c..ab96a6d26dfd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -1003,6 +1003,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
}
private void updateThemeColors() {
+ if (mScrimBehind == null) return;
int background = Utils.getColorAttr(mScrimBehind.getContext(),
android.R.attr.colorBackgroundFloating).getDefaultColor();
int accent = Utils.getColorAccent(mScrimBehind.getContext()).getDefaultColor();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
index 53d02280a03b..ab58286859cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
@@ -16,25 +16,47 @@
package com.android.systemui.statusbar.policy;
-import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
-import android.net.wifi.WifiManager.ActionListener;
+import android.net.ConnectivityManager;
+import android.net.NetworkScoreManager;
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+import android.os.SimpleClock;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
+import android.util.IndentingPrintWriter;
import android.util.Log;
-import com.android.settingslib.wifi.AccessPoint;
-import com.android.settingslib.wifi.WifiTracker;
-import com.android.settingslib.wifi.WifiTracker.WifiListener;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LifecycleRegistry;
+
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.settings.UserTracker;
+import com.android.wifitrackerlib.WifiEntry;
+import com.android.wifitrackerlib.WifiPickerTracker;
import java.io.PrintWriter;
+import java.time.Clock;
+import java.time.ZoneOffset;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
+import java.util.concurrent.Executor;
+
+import javax.inject.Inject;
public class AccessPointControllerImpl
- implements NetworkController.AccessPointController, WifiListener {
+ implements NetworkController.AccessPointController,
+ WifiPickerTracker.WifiPickerTrackerCallback, LifecycleOwner {
private static final String TAG = "AccessPointController";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -44,24 +66,51 @@ public class AccessPointControllerImpl
private static final int[] ICONS = WifiIcons.WIFI_FULL_ICONS;
- private final Context mContext;
private final ArrayList<AccessPointCallback> mCallbacks = new ArrayList<AccessPointCallback>();
- private final WifiTracker mWifiTracker;
private final UserManager mUserManager;
+ private final Executor mMainExecutor;
+
+ private @Nullable WifiPickerTracker mWifiPickerTracker;
+ private WifiPickerTrackerFactory mWifiPickerTrackerFactory;
+
+ private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
private int mCurrentUser;
- public AccessPointControllerImpl(Context context) {
- mContext = context;
- mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- mWifiTracker = new WifiTracker(context, this, false, true);
- mCurrentUser = ActivityManager.getCurrentUser();
+ public AccessPointControllerImpl(
+ UserManager userManager,
+ UserTracker userTracker,
+ Executor mainExecutor,
+ WifiPickerTrackerFactory wifiPickerTrackerFactory
+ ) {
+ mUserManager = userManager;
+ mCurrentUser = userTracker.getUserId();
+ mMainExecutor = mainExecutor;
+ mWifiPickerTrackerFactory = wifiPickerTrackerFactory;
+ mMainExecutor.execute(() -> mLifecycle.setCurrentState(Lifecycle.State.CREATED));
+ }
+
+ /**
+ * Initializes the controller.
+ *
+ * Will create a WifiPickerTracker associated to this controller.
+ */
+ public void init() {
+ if (mWifiPickerTracker == null) {
+ mWifiPickerTracker = mWifiPickerTrackerFactory.create(this.getLifecycle(), this);
+ }
+ }
+
+ @NonNull
+ @Override
+ public Lifecycle getLifecycle() {
+ return mLifecycle;
}
@Override
protected void finalize() throws Throwable {
+ mMainExecutor.execute(() -> mLifecycle.setCurrentState(Lifecycle.State.DESTROYED));
super.finalize();
- mWifiTracker.onDestroy();
}
public boolean canConfigWifi() {
@@ -79,7 +128,7 @@ public class AccessPointControllerImpl
if (DEBUG) Log.d(TAG, "addCallback " + callback);
mCallbacks.add(callback);
if (mCallbacks.size() == 1) {
- mWifiTracker.onStart();
+ mMainExecutor.execute(() -> mLifecycle.setCurrentState(Lifecycle.State.STARTED));
}
}
@@ -89,37 +138,59 @@ public class AccessPointControllerImpl
if (DEBUG) Log.d(TAG, "removeCallback " + callback);
mCallbacks.remove(callback);
if (mCallbacks.isEmpty()) {
- mWifiTracker.onStop();
+ mMainExecutor.execute(() -> mLifecycle.setCurrentState(Lifecycle.State.CREATED));
}
}
@Override
public void scanForAccessPoints() {
- fireAcccessPointsCallback(mWifiTracker.getAccessPoints());
+ if (mWifiPickerTracker == null) {
+ fireAcccessPointsCallback(Collections.emptyList());
+ return;
+ }
+ List<WifiEntry> entries = mWifiPickerTracker.getWifiEntries();
+ WifiEntry connectedEntry = mWifiPickerTracker.getConnectedWifiEntry();
+ if (connectedEntry != null) {
+ entries.add(0, connectedEntry);
+ }
+ fireAcccessPointsCallback(entries);
}
@Override
- public int getIcon(AccessPoint ap) {
+ public int getIcon(WifiEntry ap) {
int level = ap.getLevel();
- return ICONS[level >= 0 ? level : 0];
+ return ICONS[Math.max(0, level)];
}
- public boolean connect(AccessPoint ap) {
+ /**
+ * Connects to a {@link WifiEntry} if it's saved or does not require security.
+ *
+ * If the entry is not saved and requires security, will trigger
+ * {@link AccessPointCallback#onSettingsActivityTriggered}.
+ * @param ap
+ * @return {@code true} if {@link AccessPointCallback#onSettingsActivityTriggered} is triggered
+ */
+ public boolean connect(WifiEntry ap) {
if (ap == null) return false;
- if (DEBUG) Log.d(TAG, "connect networkId=" + ap.getConfig().networkId);
+ if (DEBUG) {
+ if (ap.getWifiConfiguration() != null) {
+ Log.d(TAG, "connect networkId=" + ap.getWifiConfiguration().networkId);
+ } else {
+ Log.d(TAG, "connect to unsaved network " + ap.getTitle());
+ }
+ }
if (ap.isSaved()) {
- mWifiTracker.getManager().connect(ap.getConfig().networkId, mConnectListener);
+ ap.connect(mConnectCallback);
} else {
// Unknown network, need to add it.
- if (ap.getSecurity() != AccessPoint.SECURITY_NONE) {
+ if (ap.getSecurity() != WifiEntry.SECURITY_NONE) {
Intent intent = new Intent(Settings.ACTION_WIFI_SETTINGS);
- intent.putExtra(EXTRA_START_CONNECT_SSID, ap.getSsidStr());
+ intent.putExtra(EXTRA_START_CONNECT_SSID, ap.getSsid());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
fireSettingsIntentCallback(intent);
return true;
} else {
- ap.generateOpenNetworkConfig();
- mWifiTracker.getManager().connect(ap.getConfig(), mConnectListener);
+ ap.connect(mConnectCallback);
}
}
return false;
@@ -131,39 +202,129 @@ public class AccessPointControllerImpl
}
}
- private void fireAcccessPointsCallback(List<AccessPoint> aps) {
+ private void fireAcccessPointsCallback(List<WifiEntry> aps) {
for (AccessPointCallback callback : mCallbacks) {
callback.onAccessPointsChanged(aps);
}
}
public void dump(PrintWriter pw) {
- mWifiTracker.dump(pw);
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
+ ipw.println("AccessPointControllerImpl:");
+ ipw.increaseIndent();
+ ipw.println("Callbacks: " + Arrays.toString(mCallbacks.toArray()));
+ ipw.println("WifiPickerTracker: " + mWifiPickerTracker.toString());
+ if (mWifiPickerTracker != null && !mCallbacks.isEmpty()) {
+ ipw.println("Connected: " + mWifiPickerTracker.getConnectedWifiEntry());
+ ipw.println("Other wifi entries: "
+ + Arrays.toString(mWifiPickerTracker.getWifiEntries().toArray()));
+ } else if (mWifiPickerTracker != null) {
+ ipw.println("WifiPickerTracker not started, cannot get reliable entries");
+ }
+ ipw.decreaseIndent();
}
@Override
- public void onWifiStateChanged(int state) {
+ public void onWifiStateChanged() {
+ scanForAccessPoints();
}
@Override
- public void onConnectedChanged() {
- fireAcccessPointsCallback(mWifiTracker.getAccessPoints());
+ public void onWifiEntriesChanged() {
+ scanForAccessPoints();
}
@Override
- public void onAccessPointsChanged() {
- fireAcccessPointsCallback(mWifiTracker.getAccessPoints());
+ public void onNumSavedNetworksChanged() {
+ // Do nothing
}
- private final ActionListener mConnectListener = new ActionListener() {
- @Override
- public void onSuccess() {
- if (DEBUG) Log.d(TAG, "connect success");
- }
+ @Override
+ public void onNumSavedSubscriptionsChanged() {
+ // Do nothing
+ }
+ private final WifiEntry.ConnectCallback mConnectCallback = new WifiEntry.ConnectCallback() {
@Override
- public void onFailure(int reason) {
- if (DEBUG) Log.d(TAG, "connect failure reason=" + reason);
+ public void onConnectResult(int status) {
+ if (status == CONNECT_STATUS_SUCCESS) {
+ if (DEBUG) Log.d(TAG, "connect success");
+ } else {
+ if (DEBUG) Log.d(TAG, "connect failure reason=" + status);
+ }
}
};
+
+ /**
+ * Factory for creating {@link WifiPickerTracker}.
+ *
+ * Uses the same time intervals as the Settings page for Wifi.
+ */
+ @SysUISingleton
+ public static class WifiPickerTrackerFactory {
+
+ // Max age of tracked WifiEntries
+ private static final long MAX_SCAN_AGE_MILLIS = 15_000;
+ // Interval between initiating WifiPickerTracker scans
+ private static final long SCAN_INTERVAL_MILLIS = 10_000;
+
+ private final Context mContext;
+ private final @Nullable WifiManager mWifiManager;
+ private final ConnectivityManager mConnectivityManager;
+ private final NetworkScoreManager mNetworkScoreManager;
+ private final Handler mMainHandler;
+ private final Handler mWorkerHandler;
+ private final Clock mClock = new SimpleClock(ZoneOffset.UTC) {
+ @Override
+ public long millis() {
+ return SystemClock.elapsedRealtime();
+ }
+ };
+
+ @Inject
+ public WifiPickerTrackerFactory(
+ Context context,
+ @Nullable WifiManager wifiManager,
+ ConnectivityManager connectivityManager,
+ NetworkScoreManager networkScoreManager,
+ @Main Handler mainHandler,
+ @Background Handler workerHandler
+ ) {
+ mContext = context;
+ mWifiManager = wifiManager;
+ mConnectivityManager = connectivityManager;
+ mNetworkScoreManager = networkScoreManager;
+ mMainHandler = mainHandler;
+ mWorkerHandler = workerHandler;
+ }
+
+ /**
+ * Create a {@link WifiPickerTracker}
+ *
+ * @param lifecycle
+ * @param listener
+ * @return a new {@link WifiPickerTracker} or {@code null} if {@link WifiManager} is null.
+ */
+ public @Nullable WifiPickerTracker create(
+ Lifecycle lifecycle,
+ WifiPickerTracker.WifiPickerTrackerCallback listener
+ ) {
+ if (mWifiManager == null) {
+ return null;
+ }
+ return new WifiPickerTracker(
+ lifecycle,
+ mContext,
+ mWifiManager,
+ mConnectivityManager,
+ mNetworkScoreManager,
+ mMainHandler,
+ mWorkerHandler,
+ mClock,
+ MAX_SCAN_AGE_MILLIS,
+ SCAN_INTERVAL_MILLIS,
+ listener
+ );
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index f92860b70116..b012dc4c7159 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -21,9 +21,9 @@ import android.content.Intent;
import android.telephony.SubscriptionInfo;
import com.android.settingslib.net.DataUsageController;
-import com.android.settingslib.wifi.AccessPoint;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
+import com.android.wifitrackerlib.WifiEntry;
import java.util.List;
@@ -123,12 +123,12 @@ public interface NetworkController extends CallbackController<SignalCallback>, D
void addAccessPointCallback(AccessPointCallback callback);
void removeAccessPointCallback(AccessPointCallback callback);
void scanForAccessPoints();
- int getIcon(AccessPoint ap);
- boolean connect(AccessPoint ap);
+ int getIcon(WifiEntry ap);
+ boolean connect(WifiEntry ap);
boolean canConfigWifi();
public interface AccessPointCallback {
- void onAccessPointsChanged(List<AccessPoint> accessPoints);
+ void onAccessPointsChanged(List<WifiEntry> accessPoints);
void onSettingsActivityTriggered(Intent settingsIntent);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index e41996604c99..5f5a83c5c50c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -187,6 +187,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
TelephonyManager telephonyManager,
@Nullable WifiManager wifiManager,
NetworkScoreManager networkScoreManager,
+ AccessPointControllerImpl accessPointController,
DemoModeController demoModeController) {
this(context, connectivityManager,
telephonyManager,
@@ -194,7 +195,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
networkScoreManager,
SubscriptionManager.from(context), Config.readConfig(context), bgLooper,
new CallbackHandler(),
- new AccessPointControllerImpl(context),
+ accessPointController,
new DataUsageController(context),
new SubscriptionDefaults(),
deviceProvisionedController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
index 914105fdc4c4..069b4051af50 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
@@ -16,6 +16,12 @@
package com.android.systemui.statusbar.policy.dagger;
+import android.os.UserManager;
+
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.policy.AccessPointControllerImpl;
import com.android.systemui.statusbar.policy.BluetoothController;
import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
import com.android.systemui.statusbar.policy.CastController;
@@ -45,8 +51,11 @@ import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
+import java.util.concurrent.Executor;
+
import dagger.Binds;
import dagger.Module;
+import dagger.Provides;
/** Dagger Module for code in the statusbar.policy package. */
@@ -109,4 +118,27 @@ public interface StatusBarPolicyModule {
@Binds
ZenModeController provideZenModeController(ZenModeControllerImpl controllerImpl);
+ /** */
+ @Binds
+ NetworkController.AccessPointController provideAccessPointController(
+ AccessPointControllerImpl accessPointControllerImpl);
+
+ /** */
+ @SysUISingleton
+ @Provides
+ static AccessPointControllerImpl provideAccessPointControllerImpl(
+ UserManager userManager,
+ UserTracker userTracker,
+ @Main Executor mainExecutor,
+ AccessPointControllerImpl.WifiPickerTrackerFactory wifiPickerTrackerFactory
+ ) {
+ AccessPointControllerImpl controller = new AccessPointControllerImpl(
+ userManager,
+ userTracker,
+ mainExecutor,
+ wifiPickerTrackerFactory
+ );
+ controller.init();
+ return controller;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
index e20cd44974bb..f0ccc6d15661 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
@@ -45,9 +45,9 @@ public class TvWMShellModule {
@WMSingleton
@Provides
static DisplayImeController provideDisplayImeController(IWindowManager wmService,
- DisplayController displayController, @ShellMainThread ShellExecutor shellMainExecutor,
+ DisplayController displayController, @ShellMainThread ShellExecutor mainExecutor,
TransactionPool transactionPool) {
- return new DisplayImeController(wmService, displayController, shellMainExecutor,
+ return new DisplayImeController(wmService, displayController, mainExecutor,
transactionPool);
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 08194291ce2f..715b0a25e461 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -43,6 +43,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.Dependency;
import com.android.systemui.SystemUI;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationModeController;
@@ -67,6 +68,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Optional;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -86,18 +88,21 @@ public final class WMShell extends SystemUI
| SYSUI_STATE_BUBBLES_EXPANDED
| SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
+ // Shell interfaces
+ private final Optional<Pip> mPipOptional;
+ private final Optional<LegacySplitScreen> mSplitScreenOptional;
+ private final Optional<OneHanded> mOneHandedOptional;
+ private final Optional<HideDisplayCutout> mHideDisplayCutoutOptional;
+ private final Optional<ShellCommandHandler> mShellCommandHandler;
+
private final CommandQueue mCommandQueue;
private final ConfigurationController mConfigurationController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final NavigationModeController mNavigationModeController;
private final ScreenLifecycle mScreenLifecycle;
private final SysUiState mSysUiState;
- private final Optional<Pip> mPipOptional;
- private final Optional<LegacySplitScreen> mSplitScreenOptional;
- private final Optional<OneHanded> mOneHandedOptional;
- private final Optional<HideDisplayCutout> mHideDisplayCutoutOptional;
private final ProtoTracer mProtoTracer;
- private final Optional<ShellCommandHandler> mShellCommandHandler;
+ private final Executor mSysUiMainExecutor;
private boolean mIsSysUiStateValid;
private KeyguardUpdateMonitorCallback mSplitScreenKeyguardCallback;
@@ -105,18 +110,20 @@ public final class WMShell extends SystemUI
private KeyguardUpdateMonitorCallback mOneHandedKeyguardCallback;
@Inject
- public WMShell(Context context, CommandQueue commandQueue,
+ public WMShell(Context context,
+ Optional<Pip> pipOptional,
+ Optional<LegacySplitScreen> splitScreenOptional,
+ Optional<OneHanded> oneHandedOptional,
+ Optional<HideDisplayCutout> hideDisplayCutoutOptional,
+ Optional<ShellCommandHandler> shellCommandHandler,
+ CommandQueue commandQueue,
ConfigurationController configurationController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
NavigationModeController navigationModeController,
ScreenLifecycle screenLifecycle,
SysUiState sysUiState,
- Optional<Pip> pipOptional,
- Optional<LegacySplitScreen> splitScreenOptional,
- Optional<OneHanded> oneHandedOptional,
- Optional<HideDisplayCutout> hideDisplayCutoutOptional,
ProtoTracer protoTracer,
- Optional<ShellCommandHandler> shellCommandHandler) {
+ @Main Executor sysUiMainExecutor) {
super(context);
mCommandQueue = commandQueue;
mConfigurationController = configurationController;
@@ -129,12 +136,13 @@ public final class WMShell extends SystemUI
mOneHandedOptional = oneHandedOptional;
mHideDisplayCutoutOptional = hideDisplayCutoutOptional;
mProtoTracer = protoTracer;
- mProtoTracer.add(this);
mShellCommandHandler = shellCommandHandler;
+ mSysUiMainExecutor = sysUiMainExecutor;
}
@Override
public void start() {
+ mProtoTracer.add(this);
mCommandQueue.addCallback(this);
mPipOptional.ifPresent(this::initPip);
mSplitScreenOptional.ifPresent(this::initSplitScreen);
@@ -213,34 +221,43 @@ public final class WMShell extends SystemUI
oneHanded.registerTransitionCallback(new OneHandedTransitionCallback() {
@Override
public void onStartFinished(Rect bounds) {
- mSysUiState.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE,
- true).commitUpdate(DEFAULT_DISPLAY);
+ mSysUiMainExecutor.execute(() -> {
+ mSysUiState.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE,
+ true).commitUpdate(DEFAULT_DISPLAY);
+ });
}
@Override
public void onStopFinished(Rect bounds) {
- mSysUiState.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE,
- false).commitUpdate(DEFAULT_DISPLAY);
+ mSysUiMainExecutor.execute(() -> {
+ mSysUiState.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE,
+ false).commitUpdate(DEFAULT_DISPLAY);
+ });
}
});
oneHanded.registerGestureCallback(new OneHandedGestureEventCallback() {
@Override
public void onStart() {
- if (oneHanded.isOneHandedEnabled()) {
- oneHanded.startOneHanded();
- } else if (oneHanded.isSwipeToNotificationEnabled()) {
- mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN);
- }
+ mSysUiMainExecutor.execute(() -> {
+ if (oneHanded.isOneHandedEnabled()) {
+ oneHanded.startOneHanded();
+ } else if (oneHanded.isSwipeToNotificationEnabled()) {
+ mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN);
+ }
+ });
}
@Override
public void onStop() {
- if (oneHanded.isOneHandedEnabled()) {
- oneHanded.stopOneHanded(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_GESTURE_OUT);
- } else if (oneHanded.isSwipeToNotificationEnabled()) {
- mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP);
- }
+ mSysUiMainExecutor.execute(() -> {
+ if (oneHanded.isOneHandedEnabled()) {
+ oneHanded.stopOneHanded(
+ OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_GESTURE_OUT);
+ } else if (oneHanded.isSwipeToNotificationEnabled()) {
+ mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP);
+ }
+ });
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index 572b15d5215a..bcb4386c2ceb 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -110,9 +110,9 @@ public abstract class WMShellBaseModule {
@ShellMainThread
public static Handler provideShellMainHandler(@Main Handler sysuiMainHandler) {
if (ENABLE_SHELL_MAIN_THREAD) {
- HandlerThread shellMainThread = new HandlerThread("wmshell.main");
- shellMainThread.start();
- return shellMainThread.getThreadHandler();
+ HandlerThread mainThread = new HandlerThread("wmshell.main");
+ mainThread.start();
+ return mainThread.getThreadHandler();
}
return sysuiMainHandler;
}
@@ -123,10 +123,10 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
@ShellMainThread
- public static ShellExecutor provideShellMainExecutor(@ShellMainThread Handler shellMainHandler,
+ public static ShellExecutor provideShellMainExecutor(@ShellMainThread Handler mainHandler,
@Main ShellExecutor sysuiMainExecutor) {
if (ENABLE_SHELL_MAIN_THREAD) {
- return new HandlerExecutor(shellMainHandler);
+ return new HandlerExecutor(mainHandler);
}
return sysuiMainExecutor;
}
@@ -177,7 +177,7 @@ public abstract class WMShellBaseModule {
Optional<AppPairs> appPairsOptional,
FullscreenTaskListener fullscreenTaskListener,
Transitions transitions,
- @ShellMainThread ShellExecutor shellMainExecutor) {
+ @ShellMainThread ShellExecutor mainExecutor) {
return ShellInitImpl.create(displayImeController,
dragAndDropController,
shellTaskOrganizer,
@@ -185,7 +185,7 @@ public abstract class WMShellBaseModule {
appPairsOptional,
fullscreenTaskListener,
transitions,
- shellMainExecutor);
+ mainExecutor);
}
/**
@@ -201,10 +201,10 @@ public abstract class WMShellBaseModule {
Optional<OneHanded> oneHandedOptional,
Optional<HideDisplayCutout> hideDisplayCutout,
Optional<AppPairs> appPairsOptional,
- @ShellMainThread ShellExecutor shellMainExecutor) {
+ @ShellMainThread ShellExecutor mainExecutor) {
return Optional.of(ShellCommandHandlerImpl.create(shellTaskOrganizer,
legacySplitScreenOptional, pipOptional, oneHandedOptional, hideDisplayCutout,
- appPairsOptional, shellMainExecutor));
+ appPairsOptional, mainExecutor));
}
@WMSingleton
@@ -216,8 +216,8 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
static DisplayController provideDisplayController(Context context,
- IWindowManager wmService, @ShellMainThread ShellExecutor shellMainExecutor) {
- return new DisplayController(context, wmService, shellMainExecutor);
+ IWindowManager wmService, @ShellMainThread ShellExecutor mainExecutor) {
+ return new DisplayController(context, wmService, mainExecutor);
}
@WMSingleton
@@ -236,8 +236,8 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
static WindowManagerShellWrapper provideWindowManagerShellWrapper(
- @ShellMainThread ShellExecutor shellMainExecutor) {
- return new WindowManagerShellWrapper(shellMainExecutor);
+ @ShellMainThread ShellExecutor mainExecutor) {
+ return new WindowManagerShellWrapper(mainExecutor);
}
@WMSingleton
@@ -277,8 +277,8 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
static SyncTransactionQueue provideSyncTransactionQueue(TransactionPool pool,
- @ShellMainThread ShellExecutor shellMainExecutor) {
- return new SyncTransactionQueue(pool, shellMainExecutor);
+ @ShellMainThread ShellExecutor mainExecutor) {
+ return new SyncTransactionQueue(pool, mainExecutor);
}
@WMSingleton
@@ -299,8 +299,8 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
static TaskStackListenerImpl providerTaskStackListenerImpl(
- @ShellMainThread Handler shellMainHandler) {
- return new TaskStackListenerImpl(shellMainHandler);
+ @ShellMainThread Handler mainHandler) {
+ return new TaskStackListenerImpl(mainHandler);
}
@BindsOptionalOf
@@ -319,20 +319,22 @@ public abstract class WMShellBaseModule {
LauncherApps launcherApps,
UiEventLogger uiEventLogger,
ShellTaskOrganizer organizer,
- @ShellMainThread ShellExecutor shellMainExecutor) {
+ @ShellMainThread ShellExecutor mainExecutor) {
return Optional.of(BubbleController.create(context, null /* synchronizer */,
floatingContentCoordinator, statusBarService, windowManager,
windowManagerShellWrapper, launcherApps, uiEventLogger, organizer,
- shellMainExecutor));
+ mainExecutor));
}
+ // Needs the shell main handler for ContentObserver callbacks
@WMSingleton
@Provides
static Optional<OneHanded> provideOneHandedController(Context context,
DisplayController displayController, TaskStackListenerImpl taskStackListener,
- @ShellMainThread ShellExecutor mainExecutor) {
+ @ShellMainThread ShellExecutor mainExecutor,
+ @ShellMainThread Handler mainHandler) {
return Optional.ofNullable(OneHandedController.create(context, displayController,
- taskStackListener, mainExecutor));
+ taskStackListener, mainExecutor, mainHandler));
}
@WMSingleton
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index e635e1708841..88f6e1ffac0c 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -62,9 +62,9 @@ public class WMShellModule {
@WMSingleton
@Provides
static DisplayImeController provideDisplayImeController(IWindowManager wmService,
- DisplayController displayController, @ShellMainThread ShellExecutor shellMainExecutor,
+ DisplayController displayController, @ShellMainThread ShellExecutor mainExecutor,
TransactionPool transactionPool) {
- return new DisplayImeController(wmService, displayController, shellMainExecutor,
+ return new DisplayImeController(wmService, displayController, mainExecutor,
transactionPool);
}
@@ -96,11 +96,11 @@ public class WMShellModule {
PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer,
PipTouchHandler pipTouchHandler, WindowManagerShellWrapper windowManagerShellWrapper,
TaskStackListenerImpl taskStackListener,
- @ShellMainThread ShellExecutor shellMainExecutor) {
+ @ShellMainThread ShellExecutor mainExecutor) {
return Optional.ofNullable(PipController.create(context, displayController,
pipAppOpsListener, pipBoundsAlgorithm, pipBoundsState, pipMediaController,
phonePipMenuController, pipTaskOrganizer, pipTouchHandler,
- windowManagerShellWrapper, taskStackListener, shellMainExecutor));
+ windowManagerShellWrapper, taskStackListener, mainExecutor));
}
@WMSingleton
@@ -131,10 +131,10 @@ public class WMShellModule {
PipTaskOrganizer pipTaskOrganizer,
FloatingContentCoordinator floatingContentCoordinator,
PipUiEventLogger pipUiEventLogger,
- @ShellMainThread ShellExecutor shellMainExecutor) {
+ @ShellMainThread ShellExecutor mainExecutor) {
return new PipTouchHandler(context, menuPhoneController, pipBoundsAlgorithm,
pipBoundsState, pipTaskOrganizer, floatingContentCoordinator, pipUiEventLogger,
- shellMainExecutor);
+ mainExecutor);
}
@WMSingleton
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/AccessPointControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/AccessPointControllerImplTest.kt
new file mode 100644
index 000000000000..4068f93f5ee3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/AccessPointControllerImplTest.kt
@@ -0,0 +1,229 @@
+/*
+ * 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.policy
+
+import android.os.UserManager
+import android.test.suitebuilder.annotation.SmallTest
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.lifecycle.Lifecycle
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.mockito.capture
+import com.android.wifitrackerlib.WifiEntry
+import com.android.wifitrackerlib.WifiPickerTracker
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyList
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import java.util.concurrent.Executor
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper(setAsMainLooper = true)
+class AccessPointControllerImplTest : SysuiTestCase() {
+
+ @Mock
+ private lateinit var userManager: UserManager
+ @Mock
+ private lateinit var userTracker: UserTracker
+ @Mock
+ private lateinit var wifiPickerTrackerFactory:
+ AccessPointControllerImpl.WifiPickerTrackerFactory
+ @Mock
+ private lateinit var wifiPickerTracker: WifiPickerTracker
+ @Mock
+ private lateinit var callback: NetworkController.AccessPointController.AccessPointCallback
+ @Mock
+ private lateinit var otherCallback: NetworkController.AccessPointController.AccessPointCallback
+ @Mock
+ private lateinit var wifiEntryConnected: WifiEntry
+ @Mock
+ private lateinit var wifiEntryOther: WifiEntry
+ @Captor
+ private lateinit var wifiEntryListCaptor: ArgumentCaptor<List<WifiEntry>>
+
+ private val instantExecutor = Executor { it.run() }
+ private lateinit var controller: AccessPointControllerImpl
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ `when`(wifiPickerTrackerFactory.create(any(), any())).thenReturn(wifiPickerTracker)
+
+ `when`(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntryConnected)
+ `when`(wifiPickerTracker.wifiEntries).thenReturn(ArrayList<WifiEntry>().apply {
+ add(wifiEntryOther)
+ })
+
+ controller = AccessPointControllerImpl(
+ userManager,
+ userTracker,
+ instantExecutor,
+ wifiPickerTrackerFactory
+ )
+
+ controller.init()
+ }
+
+ @Test
+ fun testInitialLifecycleStateCreated() {
+ assertThat(controller.lifecycle.currentState).isEqualTo(Lifecycle.State.CREATED)
+ }
+
+ @Test
+ fun testLifecycleStartedAfterFirstCallback() {
+ controller.addAccessPointCallback(callback)
+ assertThat(controller.lifecycle.currentState).isEqualTo(Lifecycle.State.STARTED)
+ }
+
+ @Test
+ fun testLifecycleBackToCreatedAfterRemovingOnlyCallback() {
+ controller.addAccessPointCallback(callback)
+ controller.removeAccessPointCallback(callback)
+
+ assertThat(controller.lifecycle.currentState).isEqualTo(Lifecycle.State.CREATED)
+ }
+
+ @Test
+ fun testLifecycleStillStartedAfterRemovingSecondCallback() {
+ controller.addAccessPointCallback(callback)
+ controller.addAccessPointCallback(otherCallback)
+ controller.removeAccessPointCallback(callback)
+
+ assertThat(controller.lifecycle.currentState).isEqualTo(Lifecycle.State.STARTED)
+ }
+
+ @Test
+ fun testScanForAccessPointsTriggersCallbackWithEntriesInOrder() {
+ controller.addAccessPointCallback(callback)
+ controller.scanForAccessPoints()
+
+ verify(callback).onAccessPointsChanged(capture(wifiEntryListCaptor))
+
+ assertThat(wifiEntryListCaptor.value).containsExactly(wifiEntryConnected, wifiEntryOther)
+ }
+
+ @Test
+ fun testOnWifiStateChangedTriggersCallbackWithEntriesInOrder() {
+ controller.addAccessPointCallback(callback)
+ controller.onWifiStateChanged()
+
+ verify(callback).onAccessPointsChanged(capture(wifiEntryListCaptor))
+
+ assertThat(wifiEntryListCaptor.value).containsExactly(wifiEntryConnected, wifiEntryOther)
+ }
+
+ @Test
+ fun testOnWifiEntriesChangedTriggersCallbackWithEntriesInOrder() {
+ controller.addAccessPointCallback(callback)
+ controller.onWifiEntriesChanged()
+
+ verify(callback).onAccessPointsChanged(capture(wifiEntryListCaptor))
+
+ assertThat(wifiEntryListCaptor.value).containsExactly(wifiEntryConnected, wifiEntryOther)
+ }
+
+ @Test
+ fun testOnNumSavedNetworksChangedDoesntTriggerCallback() {
+ controller.addAccessPointCallback(callback)
+ controller.onNumSavedNetworksChanged()
+
+ verify(callback, never()).onAccessPointsChanged(anyList())
+ }
+
+ @Test
+ fun testOnNumSavedSubscriptionsChangedDoesntTriggerCallback() {
+ controller.addAccessPointCallback(callback)
+ controller.onNumSavedSubscriptionsChanged()
+
+ verify(callback, never()).onAccessPointsChanged(anyList())
+ }
+
+ @Test
+ fun testReturnEmptyListWhenNoWifiPickerTracker() {
+ `when`(wifiPickerTrackerFactory.create(any(), any())).thenReturn(null)
+ val otherController = AccessPointControllerImpl(
+ userManager,
+ userTracker,
+ instantExecutor,
+ wifiPickerTrackerFactory
+ )
+ otherController.init()
+
+ otherController.addAccessPointCallback(callback)
+ otherController.scanForAccessPoints()
+
+ verify(callback).onAccessPointsChanged(capture(wifiEntryListCaptor))
+
+ assertThat(wifiEntryListCaptor.value).isEmpty()
+ }
+
+ @Test
+ fun connectToNullEntry() {
+ controller.addAccessPointCallback(callback)
+
+ assertThat(controller.connect(null)).isFalse()
+
+ verify(callback, never()).onSettingsActivityTriggered(any())
+ }
+
+ @Test
+ fun connectToSavedWifiEntry() {
+ controller.addAccessPointCallback(callback)
+ `when`(wifiEntryOther.isSaved).thenReturn(true)
+
+ assertThat(controller.connect(wifiEntryOther)).isFalse()
+
+ verify(wifiEntryOther).connect(any())
+ verify(callback, never()).onSettingsActivityTriggered(any())
+ }
+
+ @Test
+ fun connectToSecuredNotSavedWifiEntry() {
+ controller.addAccessPointCallback(callback)
+ `when`(wifiEntryOther.isSaved).thenReturn(false)
+ `when`(wifiEntryOther.security).thenReturn(WifiEntry.SECURITY_EAP)
+
+ // True means we will launch WifiSettings
+ assertThat(controller.connect(wifiEntryOther)).isTrue()
+
+ verify(wifiEntryOther, never()).connect(any())
+ verify(callback).onSettingsActivityTriggered(any())
+ }
+
+ @Test
+ fun connectToNotSecuredNotSavedWifiEntry() {
+ controller.addAccessPointCallback(callback)
+ `when`(wifiEntryOther.isSaved).thenReturn(false)
+ `when`(wifiEntryOther.security).thenReturn(WifiEntry.SECURITY_NONE)
+
+ assertThat(controller.connect(wifiEntryOther)).isFalse()
+
+ verify(wifiEntryOther).connect(any())
+ verify(callback, never()).onSettingsActivityTriggered(any())
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
index 31bf7120900f..446d3f2f72a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -34,6 +34,7 @@ import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.tracing.ProtoTracer;
import com.android.wm.shell.ShellCommandHandler;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.onehanded.OneHanded;
@@ -63,22 +64,22 @@ public class WMShellTest extends SysuiTestCase {
@Mock SysUiState mSysUiState;
@Mock Pip mPip;
@Mock PipTouchHandler mPipTouchHandler;
- @Mock
- LegacySplitScreen mLegacySplitScreen;
+ @Mock LegacySplitScreen mLegacySplitScreen;
@Mock OneHanded mOneHanded;
@Mock HideDisplayCutout mHideDisplayCutout;
@Mock ProtoTracer mProtoTracer;
@Mock ShellCommandHandler mShellCommandHandler;
+ @Mock ShellExecutor mSysUiMainExecutor;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mWMShell = new WMShell(mContext, mCommandQueue, mConfigurationController,
+ mWMShell = new WMShell(mContext, Optional.of(mPip), Optional.of(mLegacySplitScreen),
+ Optional.of(mOneHanded), Optional.of(mHideDisplayCutout),
+ Optional.of(mShellCommandHandler), mCommandQueue, mConfigurationController,
mKeyguardUpdateMonitor, mNavigationModeController,
- mScreenLifecycle, mSysUiState, Optional.of(mPip), Optional.of(mLegacySplitScreen),
- Optional.of(mOneHanded), Optional.of(mHideDisplayCutout), mProtoTracer,
- Optional.of(mShellCommandHandler));
+ mScreenLifecycle, mSysUiState, mProtoTracer, mSysUiMainExecutor);
when(mPip.getPipTouchHandler()).thenReturn(mPipTouchHandler);
}
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_apps.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_apps.xml
index 228e2ccfcfb9..95c08678ca9a 100644
--- a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_apps.xml
+++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_apps.xml
@@ -20,30 +20,30 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 6 4 C 7.10456949966 4 8 4.89543050034 8 6 C 8 7.10456949966 7.10456949966 8 6 8 C 4.89543050034 8 4 7.10456949966 4 6 C 4 4.89543050034 4.89543050034 4 6 4 Z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 12 4 C 13.1045694997 4 14 4.89543050034 14 6 C 14 7.10456949966 13.1045694997 8 12 8 C 10.8954305003 8 10 7.10456949966 10 6 C 10 4.89543050034 10.8954305003 4 12 4 Z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 18 4 C 19.1045694997 4 20 4.89543050034 20 6 C 20 7.10456949966 19.1045694997 8 18 8 C 16.8954305003 8 16 7.10456949966 16 6 C 16 4.89543050034 16.8954305003 4 18 4 Z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 6 10 C 7.10456949966 10 8 10.8954305003 8 12 C 8 13.1045694997 7.10456949966 14 6 14 C 4.89543050034 14 4 13.1045694997 4 12 C 4 10.8954305003 4.89543050034 10 6 10 Z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 12 10 C 13.1045694997 10 14 10.8954305003 14 12 C 14 13.1045694997 13.1045694997 14 12 14 C 10.8954305003 14 10 13.1045694997 10 12 C 10 10.8954305003 10.8954305003 10 12 10 Z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 18 10 C 19.1045694997 10 20 10.8954305003 20 12 C 20 13.1045694997 19.1045694997 14 18 14 C 16.8954305003 14 16 13.1045694997 16 12 C 16 10.8954305003 16.8954305003 10 18 10 Z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 6 16 C 7.10456949966 16 8 16.8954305003 8 18 C 8 19.1045694997 7.10456949966 20 6 20 C 4.89543050034 20 4 19.1045694997 4 18 C 4 16.8954305003 4.89543050034 16 6 16 Z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 12 16 C 13.1045694997 16 14 16.8954305003 14 18 C 14 19.1045694997 13.1045694997 20 12 20 C 10.8954305003 20 10 19.1045694997 10 18 C 10 16.8954305003 10.8954305003 16 12 16 Z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 18 16 C 19.1045694997 16 20 16.8954305003 20 18 C 20 19.1045694997 19.1045694997 20 18 20 C 16.8954305003 20 16 19.1045694997 16 18 C 16 16.8954305003 16.8954305003 16 18 16 Z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_devices_other.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_devices_other.xml
index 14898c465a31..454b2e260334 100644
--- a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_devices_other.xml
+++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_devices_other.xml
@@ -21,12 +21,12 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M7.25,18.25c0-0.41-0.34-0.75-0.75-0.75H3V8.25C3,7.01,4.01,6,5.25,6h16C21.66,6,22,5.66,22,5.25S21.66,4.5,21.25,4.5h-16 C3.18,4.5,1.5,6.18,1.5,8.25V19h5C6.91,19,7.25,18.66,7.25,18.25z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M14,16c0-1.66-1.34-3-3-3s-3,1.34-3,3s1.34,3,3,3S14,17.66,14,16z M9.5,16c0-0.83,0.67-1.5,1.5-1.5s1.5,0.67,1.5,1.5 s-0.67,1.5-1.5,1.5S9.5,16.83,9.5,16z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M20,19c1.1,0,2-0.9,2-2v-7c0-1.1-0.9-2-2-2h-3c-1.1,0-2,0.9-2,2v7c0,1.1,0.9,2,2,2H20z M16.5,17v-7 c0-0.28,0.22-0.5,0.5-0.5h3c0.28,0,0.5,0.22,0.5,0.5v7c0,0.28-0.22,0.5-0.5,0.5h-3C16.72,17.5,16.5,17.28,16.5,17z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_help.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_help.xml
index 2fa1520df832..4e99add13424 100644
--- a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_help.xml
+++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_help.xml
@@ -20,12 +20,12 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M12,22c0,0,0.01,0,0.01,0c5.51,0,9.98-4.46,9.99-9.98c0-0.01,0-0.01,0-0.02c0-5.52-4.48-10-10-10S2,6.48,2,12 S6.48,22,12,22z M12,3.5c4.69,0,8.5,3.81,8.5,8.52c-0.01,4.68-3.81,8.48-8.5,8.48c-4.69,0-8.5-3.81-8.5-8.5 C3.5,7.31,7.31,3.5,12,3.5z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M8.57,9.89c0.4,0.1,0.81-0.15,0.9-0.56c0.12-0.5,0.36-0.94,0.71-1.29c1.06-1.06,2.78-1.06,3.84,0 c0.53,0.53,0.79,1.23,0.72,1.92c-0.06,0.62-0.39,1.15-0.92,1.5c-0.17,0.11-0.35,0.19-0.52,0.27c-0.7,0.33-1.67,0.78-1.93,2.37 c-0.07,0.41,0.21,0.8,0.61,0.86C12.02,14.99,12.06,15,12.1,15c0.36,0,0.68-0.26,0.74-0.62c0.14-0.82,0.48-0.98,1.09-1.26 c0.25-0.11,0.49-0.23,0.72-0.38c0.91-0.6,1.48-1.53,1.58-2.61c0.12-1.14-0.31-2.28-1.15-3.13c-1.64-1.64-4.32-1.64-5.96,0 c-0.54,0.54-0.93,1.24-1.11,2C7.92,9.39,8.16,9.8,8.57,9.89z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 12 16.5 C 12.5522847498 16.5 13 16.9477152502 13 17.5 C 13 18.0522847498 12.5522847498 18.5 12 18.5 C 11.4477152502 18.5 11 18.0522847498 11 17.5 C 11 16.9477152502 11.4477152502 16.5 12 16.5 Z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_phone_info.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_phone_info.xml
index efc300ab8ac5..1cafbfe562c9 100644
--- a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_phone_info.xml
+++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_phone_info.xml
@@ -20,12 +20,12 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M15,22c1.66,0,3-1.34,3-3V5c0-1.66-1.34-3-3-3H9C7.34,2,6,3.34,6,5v14c0,1.66,1.34,3,3,3H15z M7.5,6.5h9v11h-9V6.5z M9,3.5 h6c0.83,0,1.5,0.67,1.5,1.5h-9C7.5,4.17,8.17,3.5,9,3.5z M7.5,19h9c0,0.83-0.67,1.5-1.5,1.5H9C8.17,20.5,7.5,19.83,7.5,19z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M12,11.5c-0.41,0-0.75,0.34-0.75,0.75v3c0,0.41,0.34,0.75,0.75,0.75s0.75-0.34,0.75-0.75v-3 C12.75,11.84,12.41,11.5,12,11.5z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 12 8 C 12.5522847498 8 13 8.44771525017 13 9 C 13 9.55228474983 12.5522847498 10 12 10 C 11.4477152502 10 11 9.55228474983 11 9 C 11 8.44771525017 11.4477152502 8 12 8 Z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_accessibility.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_accessibility.xml
index 72814775e90a..4c57d8db3a2f 100644
--- a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_accessibility.xml
+++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_accessibility.xml
@@ -20,18 +20,18 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M3.03,5.54c-0.11,0.4,0.13,0.81,0.53,0.92C5.24,6.91,7.07,7.2,9,7.35v8.15v3.75C9,19.66,9.34,20,9.75,20 s0.75-0.34,0.75-0.75V15.5c0-0.83,0.67-1.5,1.5-1.5s1.5,0.67,1.5,1.5v3.75c0,0.41,0.34,0.75,0.75,0.75S15,19.66,15,19.25V15.5V7.35 c1.93-0.15,3.76-0.44,5.44-0.89c0.4-0.11,0.64-0.52,0.53-0.92c-0.11-0.4-0.51-0.64-0.92-0.53C17.64,5.66,14.93,5.98,12,5.98 S6.36,5.66,3.94,5.01C3.54,4.9,3.13,5.14,3.03,5.54z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 8 22 C 8.55228474983 22 9 22.4477152502 9 23 C 9 23.5522847498 8.55228474983 24 8 24 C 7.44771525017 24 7 23.5522847498 7 23 C 7 22.4477152502 7.44771525017 22 8 22 Z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 16 22 C 16.5522847498 22 17 22.4477152502 17 23 C 17 23.5522847498 16.5522847498 24 16 24 C 15.4477152502 24 15 23.5522847498 15 23 C 15 22.4477152502 15.4477152502 22 16 22 Z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 12 22 C 12.5522847498 22 13 22.4477152502 13 23 C 13 23.5522847498 12.5522847498 24 12 24 C 11.4477152502 24 11 23.5522847498 11 23 C 11 22.4477152502 11.4477152502 22 12 22 Z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 12 1 C 13.1045694997 1 14 1.89543050034 14 3 C 14 4.10456949966 13.1045694997 5 12 5 C 10.8954305003 5 10 4.10456949966 10 3 C 10 1.89543050034 10.8954305003 1 12 1 Z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_accounts.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_accounts.xml
index 6b5c4e4dbdff..c63ec5b91418 100644
--- a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_accounts.xml
+++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_accounts.xml
@@ -20,9 +20,9 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M17,3H7C4.79,3,3,4.79,3,7v10c0,2.21,1.79,4,4,4h10c2.21,0,4-1.79,4-4V7C21,4.79,19.21,3,17,3z M17,19.5H7 c-0.93,0-1.73-0.52-2.16-1.27C6.59,16.47,9.11,15.5,12,15.5s5.41,0.97,7.16,2.73C18.73,18.98,17.93,19.5,17,19.5z M19.5,16.51 C17.52,14.89,14.92,14,12,14s-5.52,0.89-7.5,2.51V7c0-1.38,1.12-2.5,2.5-2.5h10c1.38,0,2.5,1.12,2.5,2.5V16.51z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M12,6c-1.93,0-3.5,1.57-3.5,3.5S10.07,13,12,13s3.5-1.57,3.5-3.5S13.93,6,12,6z M12,11.5c-1.1,0-2-0.9-2-2 c0-1.1,0.9-2,2-2c1.1,0,2,0.9,2,2C14,10.6,13.1,11.5,12,11.5z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_battery_white.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_battery_white.xml
index d91afadb7f40..780fa2ee4241 100644
--- a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_battery_white.xml
+++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_battery_white.xml
@@ -20,6 +20,6 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M14,4c0-0.55-0.45-1-1-1h-2c-0.55,0-1,0.45-1,1H9C7.34,4,6,5.34,6,7v12c0,1.66,1.34,3,3,3h6c1.66,0,3-1.34,3-3V7 c0-1.66-1.34-3-3-3H14z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_display_white.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_display_white.xml
index f52653497e20..8dabc535d4e8 100644
--- a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_display_white.xml
+++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_display_white.xml
@@ -20,9 +20,9 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M17,12c0-2.76-2.24-5-5-5v10C14.76,17,17,14.76,17,12z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M4,15.31V18c0,1.1,0.9,2,2,2h2.69l1.9,1.9c0.39,0.39,0.9,0.59,1.41,0.59s1.02-0.2,1.41-0.59l1.9-1.9H18c1.1,0,2-0.9,2-2 v-2.69l1.9-1.9c0.78-0.78,0.78-2.05,0-2.83L20,8.69V6c0-1.1-0.9-2-2-2h-2.69l-1.9-1.9c-0.39-0.39-0.9-0.59-1.41-0.59 s-1.02,0.2-1.41,0.59L8.69,4H6C4.9,4,4,4.9,4,6v2.69l-1.9,1.9c-0.78,0.78-0.78,2.05,0,2.83L4,15.31z M3.16,11.65l1.9-1.9L5.5,9.31 V8.69V6c0-0.28,0.22-0.5,0.5-0.5h2.69h0.62l0.44-0.44l1.9-1.9c0.13-0.13,0.28-0.15,0.35-0.15c0.08,0,0.23,0.02,0.35,0.15l1.9,1.9 l0.44,0.44h0.62H18c0.28,0,0.5,0.22,0.5,0.5v2.69v0.62l0.44,0.44l1.9,1.9c0.13,0.13,0.15,0.28,0.15,0.35s-0.02,0.23-0.15,0.35 l-1.9,1.9l-0.44,0.44v0.62V18c0,0.28-0.22,0.5-0.5,0.5h-2.69h-0.62l-0.44,0.44l-1.9,1.9c-0.13,0.13-0.28,0.15-0.35,0.15 c-0.08,0-0.23-0.02-0.35-0.15l-1.9-1.9L9.31,18.5H8.69H6c-0.28,0-0.5-0.22-0.5-0.5v-2.69v-0.62l-0.44-0.44l-1.9-1.9 C3.04,12.23,3.02,12.08,3.02,12S3.04,11.77,3.16,11.65z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_location.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_location.xml
index 213b01b04eef..32234a1a28f1 100644
--- a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_location.xml
+++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_location.xml
@@ -20,9 +20,9 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M12,21.5c0,0,7-5.34,7-11.25c0-4-3.13-7.25-7-7.25c-3.87,0-7,3.25-7,7.25C5,16.16,12,21.5,12,21.5z M12,4.5 c3.03,0,5.5,2.58,5.5,5.75c0,3.91-3.74,7.72-5.51,9.29C9.9,17.68,6.5,13.89,6.5,10.25C6.5,7.08,8.97,4.5,12,4.5z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M15,10c0-1.66-1.34-3-3-3c-1.66,0-3,1.34-3,3c0,1.66,1.34,3,3,3C13.66,13,15,11.66,15,10z M10.5,10 c0-0.83,0.67-1.5,1.5-1.5s1.5,0.67,1.5,1.5s-0.67,1.5-1.5,1.5S10.5,10.83,10.5,10z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_privacy.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_privacy.xml
index ce4c1a45a66c..86b9a1d99f6a 100644
--- a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_privacy.xml
+++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_privacy.xml
@@ -20,12 +20,12 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M12,16.5c-3.74,0-6.89-1.9-8.37-5c1.47-3.1,4.62-5,8.37-5c3.53,0,6.52,1.71,8.08,4.5h1.7C20.09,7.3,16.35,5,12,5 C7.45,5,3.57,7.51,2,11.5C3.57,15.49,7.45,18,12,18c1.41,0,2.76-0.24,4-0.7v-1.62C14.79,16.21,13.44,16.5,12,16.5z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M12,8c-1.93,0-3.5,1.57-3.5,3.5S10.07,15,12,15s3.5-1.57,3.5-3.5S13.93,8,12,8z M12,13.5c-1.1,0-2-0.9-2-2s0.9-2,2-2 c1.1,0,2,0.9,2,2S13.1,13.5,12,13.5z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M22,14c0-1.1-0.9-2-2-2c-1.1,0-2,0.9-2,2c0,0.37,0,0.7,0,1h-1v4c0,0.55,0.45,1,1,1h4c0.55,0,1-0.45,1-1v-4h-1 C22,14.65,22,14.28,22,14z M19,14c0-0.55,0.45-1,1-1s1,0.45,1,1v1h-2V14z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_security_white.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_security_white.xml
index 612611593793..6fc58fa2533a 100644
--- a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_security_white.xml
+++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_security_white.xml
@@ -20,9 +20,9 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M11.25,14.79v1.46c0,0.41,0.34,0.75,0.75,0.75s0.75-0.34,0.75-0.75v-1.46c0.45-0.26,0.75-0.74,0.75-1.29 c0-0.83-0.67-1.5-1.5-1.5s-1.5,0.67-1.5,1.5C10.5,14.05,10.8,14.53,11.25,14.79z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M19,2.5c1.35,0,2.5,1.18,2.5,2.57c0,0.41,0.34,0.75,0.75,0.75S23,5.48,23,5.07C23,2.86,21.17,1,19,1s-4,1.86-4,4.07V8H5v10 c0,1.66,1.34,3,3,3h8c1.66,0,3-1.34,3-3V8h-2.5V5.07C16.5,3.68,17.65,2.5,19,2.5z M17.5,18c0,0.83-0.67,1.5-1.5,1.5H8 c-0.83,0-1.5-0.67-1.5-1.5V9.5h11V18z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml
index 4adc9ce2923f..67ddf46439e6 100644
--- a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml
+++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml
@@ -20,12 +20,12 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M11.99,2C6.47,2,2,6.48,2,12c0,5.52,4.47,10,9.99,10C17.52,22,22,17.52,22,12C22,6.48,17.52,2,11.99,2z M11.99,20.5 c-4.68,0-8.49-3.81-8.49-8.5c0-4.69,3.81-8.5,8.49-8.5c4.69,0,8.51,3.81,8.51,8.5C20.5,16.69,16.68,20.5,11.99,20.5z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M12,10.5c-0.41,0-0.75,0.34-0.75,0.75v5c0,0.41,0.34,0.75,0.75,0.75s0.75-0.34,0.75-0.75v-5 C12.75,10.84,12.41,10.5,12,10.5z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 12 7 C 12.5522847498 7 13 7.44771525017 13 8 C 13 8.55228474983 12.5522847498 9 12 9 C 11.4477152502 9 11 8.55228474983 11 8 C 11 7.44771525017 11.4477152502 7 12 7 Z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_wireless.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_wireless.xml
index d9203d2d0af3..91670fca1be0 100644
--- a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_wireless.xml
+++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_wireless.xml
@@ -21,15 +21,15 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 12 17 C 12.8284271247 17 13.5 17.6715728753 13.5 18.5 C 13.5 19.3284271247 12.8284271247 20 12 20 C 11.1715728753 20 10.5 19.3284271247 10.5 18.5 C 10.5 17.6715728753 11.1715728753 17 12 17 Z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M19.42,11.84c-0.19,0-0.38-0.07-0.53-0.22C17.05,9.77,14.6,8.75,12,8.75s-5.05,1.02-6.89,2.86 c-0.29,0.29-0.77,0.29-1.06,0c-0.29-0.29-0.29-0.77,0-1.06C6.17,8.43,9,7.25,12,7.25s5.83,1.17,7.95,3.3 c0.29,0.29,0.29,0.77,0,1.06C19.8,11.76,19.61,11.84,19.42,11.84z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M22.61,8.65c-0.19,0-0.38-0.07-0.53-0.22C19.38,5.74,15.81,4.25,12,4.25S4.62,5.74,1.92,8.43c-0.29,0.29-0.77,0.29-1.06,0 s-0.29-0.77,0-1.06C3.84,4.39,7.79,2.75,12,2.75s8.16,1.64,11.14,4.61c0.29,0.29,0.29,0.77,0,1.06 C22.99,8.57,22.8,8.65,22.61,8.65z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M16.25,15c-0.19,0-0.38-0.07-0.53-0.22c-1-0.99-2.32-1.53-3.73-1.53s-2.73,0.54-3.73,1.53c-0.29,0.29-0.77,0.29-1.06-0.01 s-0.29-0.77,0.01-1.06c1.28-1.27,2.98-1.96,4.78-1.96s3.5,0.7,4.78,1.96c0.29,0.29,0.3,0.77,0.01,1.06 C16.64,14.93,16.45,15,16.25,15z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_storage_white.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_storage_white.xml
index cf9db6817903..807c3bf3e533 100644
--- a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_storage_white.xml
+++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_storage_white.xml
@@ -20,21 +20,21 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M21,17c0-1.1-0.9-2-2-2H5c-1.1,0-2,0.9-2,2v3h18V17z M19.5,18.5h-15V17c0-0.28,0.22-0.5,0.5-0.5h14 c0.28,0,0.5,0.22,0.5,0.5V18.5z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M21,5c0-1.1-0.9-2-2-2H5C3.9,3,3,3.9,3,5v3h18V5z M19.5,6.5h-15V5c0-0.28,0.22-0.5,0.5-0.5h14c0.28,0,0.5,0.22,0.5,0.5V6.5 z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M21,11c0-1.1-0.9-2-2-2H5c-1.1,0-2,0.9-2,2v3h18V11z M19.5,12.5h-15V11c0-0.28,0.22-0.5,0.5-0.5h14 c0.28,0,0.5,0.22,0.5,0.5V12.5z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 6.01 4.75 C 6.42421356237 4.75 6.76 5.08578643763 6.76 5.5 C 6.76 5.91421356237 6.42421356237 6.25 6.01 6.25 C 5.59578643763 6.25 5.26 5.91421356237 5.26 5.5 C 5.26 5.08578643763 5.59578643763 4.75 6.01 4.75 Z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 6.01 10.75 C 6.42421356237 10.75 6.76 11.0857864376 6.76 11.5 C 6.76 11.9142135624 6.42421356237 12.25 6.01 12.25 C 5.59578643763 12.25 5.26 11.9142135624 5.26 11.5 C 5.26 11.0857864376 5.59578643763 10.75 6.01 10.75 Z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 6.01 16.75 C 6.42421356237 16.75 6.76 17.0857864376 6.76 17.5 C 6.76 17.9142135624 6.42421356237 18.25 6.01 18.25 C 5.59578643763 18.25 5.26 17.9142135624 5.26 17.5 C 5.26 17.0857864376 5.59578643763 16.75 6.01 16.75 Z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_volume_up_24dp.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_volume_up_24dp.xml
index ace87e03c8c8..1a0613712df5 100644
--- a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_volume_up_24dp.xml
+++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_volume_up_24dp.xml
@@ -20,12 +20,12 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M14.44,13.56c-0.38,0.17-0.54,0.62-0.37,0.99c0.13,0.28,0.4,0.44,0.68,0.44c0.1,0,0.21-0.02,0.31-0.07 C16.26,14.37,17,13.25,17,12c0-1.25-0.74-2.37-1.93-2.93c-0.37-0.17-0.82-0.01-1,0.37c-0.17,0.38-0.01,0.82,0.36,1 c0.66,0.3,1.07,0.9,1.07,1.57S15.09,13.26,14.44,13.56z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M14.59,17.42c-0.4,0.09-0.66,0.49-0.57,0.9c0.08,0.35,0.39,0.59,0.73,0.59c0.05,0,0.11-0.01,0.16-0.02 c3.29-0.74,5.59-3.57,5.59-6.89s-2.3-6.15-5.59-6.89c-0.41-0.08-0.81,0.17-0.9,0.57s0.16,0.8,0.57,0.9C17.19,7.16,19,9.39,19,12 S17.19,16.84,14.59,17.42z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M7,15l4.15,4.15c0.1,0.1,0.23,0.15,0.35,0.15c0.26,0,0.5-0.2,0.5-0.5V5.21c0-0.3-0.25-0.5-0.5-0.5 c-0.12,0-0.25,0.05-0.35,0.15L7,9H5c-1.1,0-2,0.9-2,2v2c0,1.1,0.9,2,2,2H7z M4.5,13v-2c0-0.28,0.22-0.5,0.5-0.5h2.62l2.88-2.88 v8.76L7.62,13.5H5C4.72,13.5,4.5,13.28,4.5,13z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_apps.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_apps.xml
index 60b511622b0d..74b13fd4c1ec 100644
--- a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_apps.xml
+++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_apps.xml
@@ -20,30 +20,30 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M7.5,4h-3C4.22,4,4,4.22,4,4.5v3C4,7.78,4.22,8,4.5,8h3C7.78,8,8,7.78,8,7.5v-3C8,4.22,7.78,4,7.5,4z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M13.5,4h-3C10.22,4,10,4.22,10,4.5v3C10,7.78,10.22,8,10.5,8h3C13.78,8,14,7.78,14,7.5v-3C14,4.22,13.78,4,13.5,4z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M19.5,4h-3C16.22,4,16,4.22,16,4.5v3C16,7.78,16.22,8,16.5,8h3C19.78,8,20,7.78,20,7.5v-3C20,4.22,19.78,4,19.5,4z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M7.5,10h-3C4.22,10,4,10.22,4,10.5v3C4,13.78,4.22,14,4.5,14h3C7.78,14,8,13.78,8,13.5v-3C8,10.22,7.78,10,7.5,10z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M13.5,10h-3c-0.28,0-0.5,0.22-0.5,0.5v3c0,0.28,0.22,0.5,0.5,0.5h3c0.28,0,0.5-0.22,0.5-0.5v-3C14,10.22,13.78,10,13.5,10 z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M19.5,10h-3c-0.28,0-0.5,0.22-0.5,0.5v3c0,0.28,0.22,0.5,0.5,0.5h3c0.28,0,0.5-0.22,0.5-0.5v-3C20,10.22,19.78,10,19.5,10 z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M7.5,16h-3C4.22,16,4,16.22,4,16.5v3C4,19.78,4.22,20,4.5,20h3C7.78,20,8,19.78,8,19.5v-3C8,16.22,7.78,16,7.5,16z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M13.5,16h-3c-0.28,0-0.5,0.22-0.5,0.5v3c0,0.28,0.22,0.5,0.5,0.5h3c0.28,0,0.5-0.22,0.5-0.5v-3C14,16.22,13.78,16,13.5,16 z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M19.5,16h-3c-0.28,0-0.5,0.22-0.5,0.5v3c0,0.28,0.22,0.5,0.5,0.5h3c0.28,0,0.5-0.22,0.5-0.5v-3C20,16.22,19.78,16,19.5,16 z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_devices_other.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_devices_other.xml
index a451ef831e78..33a4b29aba00 100644
--- a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_devices_other.xml
+++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_devices_other.xml
@@ -21,12 +21,12 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M6,18H3V6h17c0.55,0,1-0.45,1-1s-0.45-1-1-1H3C1.9,4,1,4.9,1,6v12c0,1.1,0.9,2,2,2h3c0.55,0,1-0.45,1-1S6.55,18,6,18z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M13,12H9v1.78C8.39,14.33,8,15.11,8,16s0.39,1.67,1,2.22V20h4v-1.78c0.61-0.55,1-1.34,1-2.22s-0.39-1.67-1-2.22V12z M11,17.5c-0.83,0-1.5-0.67-1.5-1.5c0-0.83,0.67-1.5,1.5-1.5s1.5,0.67,1.5,1.5C12.5,16.83,11.83,17.5,11,17.5z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M22,8h-6c-0.5,0-1,0.5-1,1v10c0,0.5,0.5,1,1,1h6c0.5,0,1-0.5,1-1V9C23,8.5,22.5,8,22,8z M21,18h-4v-8h4V18z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_help.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_help.xml
index 25bb10306d25..42854a4777d2 100644
--- a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_help.xml
+++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_help.xml
@@ -20,6 +20,6 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M12,22c5.52,0,10-4.48,10-10c0-5.52-4.48-10-10-10S2,6.48,2,12C2,17.52,6.48,22,12,22z M12,18.96 c-0.69,0-1.25-0.56-1.25-1.25c0-0.69,0.56-1.25,1.25-1.25s1.25,0.56,1.25,1.25C13.25,18.4,12.69,18.96,12,18.96z M8.16,7.92 c0.63-2.25,2.91-3.38,5.05-2.74c1.71,0.51,2.84,2.16,2.78,3.95c-0.07,2.44-2.49,2.61-2.92,5.06c-0.09,0.52-0.59,0.87-1.13,0.79 c-0.57-0.08-0.94-0.66-0.83-1.23c0.52-2.61,2.66-2.84,2.87-4.5c0.12-0.96-0.42-1.87-1.34-2.17c-1.04-0.33-2.21,0.16-2.55,1.37 C9.97,8.9,9.57,9.19,9.12,9.19C8.46,9.19,7.99,8.56,8.16,7.92z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_phone_info.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_phone_info.xml
index 682125974fab..e932b9b88a0f 100644
--- a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_phone_info.xml
+++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_phone_info.xml
@@ -20,12 +20,12 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M17,1.01L7,1C5.9,1,5,1.9,5,3v18c0,1.1,0.9,2,2,2h10c1.1,0,2-0.9,2-2V3C19,1.9,18.1,1.01,17,1.01z M17,19H7V5h10V19z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 12 7 C 12.6903559373 7 13.25 7.55964406271 13.25 8.25 C 13.25 8.94035593729 12.6903559373 9.5 12 9.5 C 11.3096440627 9.5 10.75 8.94035593729 10.75 8.25 C 10.75 7.55964406271 11.3096440627 7 12 7 Z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M12,11c-0.55,0-1,0.4-1,0.9v4.21c0,0.5,0.45,0.9,1,0.9s1-0.4,1-0.9V11.9C13,11.4,12.55,11,12,11z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_accessibility.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_accessibility.xml
index 762b7d4979bf..db456387f369 100644
--- a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_accessibility.xml
+++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_accessibility.xml
@@ -20,18 +20,18 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M20.76,5.02l-0.02-0.06c-0.13-0.53-0.67-0.85-1.2-0.73C17.16,4.77,14.49,5,12,5S6.84,4.77,4.46,4.24 c-0.54-0.12-1.07,0.19-1.2,0.73L3.24,5.02C3.11,5.56,3.43,6.12,3.97,6.24C5.59,6.61,7.34,6.86,9,7v12c0,0.55,0.45,1,1,1 s1-0.45,1-1v-5h2v5c0,0.55,0.45,1,1,1s1-0.45,1-1V7c1.66-0.14,3.41-0.39,5.03-0.76C20.57,6.12,20.89,5.56,20.76,5.02z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 12 0 C 13.1045694997 0 14 0.895430500338 14 2 C 14 3.10456949966 13.1045694997 4 12 4 C 10.8954305003 4 10 3.10456949966 10 2 C 10 0.895430500338 10.8954305003 0 12 0 Z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 12 22 C 12.5522847498 22 13 22.4477152502 13 23 C 13 23.5522847498 12.5522847498 24 12 24 C 11.4477152502 24 11 23.5522847498 11 23 C 11 22.4477152502 11.4477152502 22 12 22 Z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 16 22 C 16.5522847498 22 17 22.4477152502 17 23 C 17 23.5522847498 16.5522847498 24 16 24 C 15.4477152502 24 15 23.5522847498 15 23 C 15 22.4477152502 15.4477152502 22 16 22 Z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 8 22 C 8.55228474983 22 9 22.4477152502 9 23 C 9 23.5522847498 8.55228474983 24 8 24 C 7.44771525017 24 7 23.5522847498 7 23 C 7 22.4477152502 7.44771525017 22 8 22 Z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_accounts.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_accounts.xml
index 5409d0da8316..0d4a244e7040 100644
--- a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_accounts.xml
+++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_accounts.xml
@@ -20,6 +20,6 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M19,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M12,6c1.93,0,3.5,1.57,3.5,3.5 S13.93,13,12,13s-3.5-1.57-3.5-3.5S10.07,6,12,6z M19,19H5v-1.36c0-0.74,0.41-1.44,1.07-1.77C7.24,15.28,9.3,14.5,12,14.5 s4.76,0.78,5.93,1.37C18.59,16.2,19,16.9,19,17.64V19z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_battery_white.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_battery_white.xml
index 0fea7ae6ee31..bb1138831db7 100644
--- a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_battery_white.xml
+++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_battery_white.xml
@@ -20,6 +20,6 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M10,2v2H8.33C7.6,4,7,4.6,7,5.33v15.33C7,21.4,7.6,22,8.33,22h7.33C16.4,22,17,21.4,17,20.67V5.33C17,4.6,16.4,4,15.67,4 H14V2H10z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_display_white.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_display_white.xml
index b75787b3b745..2c931e48fa15 100644
--- a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_display_white.xml
+++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_display_white.xml
@@ -20,6 +20,6 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M4,15.3V19c0,0.55,0.45,1,1,1h3.69l2.6,2.6c0.39,0.39,1.02,0.39,1.41,0l2.6-2.6H19c0.55,0,1-0.45,1-1v-3.69l2.6-2.6 c0.39-0.39,0.39-1.02,0-1.41L20,8.69V5c0-0.55-0.45-1-1-1h-3.69l-2.6-2.6c-0.39-0.39-1.02-0.39-1.41,0L8.69,4H5C4.45,4,4,4.45,4,5 v3.69l-2.6,2.6c-0.39,0.39-0.39,1.02,0,1.41L4,15.3z M12,6c3.31,0,6,2.69,6,6s-2.69,6-6,6V6z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_location.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_location.xml
index ecab3a3d9b21..8732ea5cb99d 100644
--- a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_location.xml
+++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_location.xml
@@ -20,6 +20,6 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M12.77,21.11C14.58,18.92,19,13.17,19,9c0-3.87-3.13-7-7-7S5,5.13,5,9c0,4.17,4.42,9.92,6.24,12.11 C11.64,21.59,12.37,21.59,12.77,21.11z M9.5,9c0-1.38,1.12-2.5,2.5-2.5s2.5,1.12,2.5,2.5c0,1.38-1.12,2.5-2.5,2.5S9.5,10.38,9.5,9z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_privacy.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_privacy.xml
index 4404530a9580..2ebdc8f3fc96 100644
--- a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_privacy.xml
+++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_privacy.xml
@@ -20,12 +20,12 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M16.48,11.7c0.74-0.44,1.6-0.7,2.52-0.7h3.78C20.93,6.88,16.81,4,12,4C7,4,2.73,7.11,1,11.5C2.73,15.89,7,19,12,19 c0.68,0,1.35-0.06,2-0.17V16c0-0.18,0.03-0.34,0.05-0.51C13.43,15.8,12.74,16,12,16c-2.49,0-4.5-2.01-4.5-4.5 C7.5,9.01,9.51,7,12,7s4.5,2.01,4.5,4.5C16.5,11.57,16.48,11.64,16.48,11.7z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 12 9 C 13.3807118746 9 14.5 10.1192881254 14.5 11.5 C 14.5 12.8807118746 13.3807118746 14 12 14 C 10.6192881254 14 9.5 12.8807118746 9.5 11.5 C 9.5 10.1192881254 10.6192881254 9 12 9 Z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M22,16v-0.5c0-1.4-1.1-2.5-2.5-2.5S17,14.1,17,15.5V16c-0.5,0-1,0.5-1,1v4c0,0.5,0.5,1,1,1h5c0.5,0,1-0.5,1-1v-4 C23,16.5,22.5,16,22,16z M20.5,16h-2v-0.5c0-0.53,0.47-1,1-1s1,0.47,1,1V16z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_security_white.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_security_white.xml
index 86147c26c469..ecaed01a945f 100644
--- a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_security_white.xml
+++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_security_white.xml
@@ -20,6 +20,6 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M18,1c-2.21,0-4,1.79-4,4v3H6c-1.1,0-2,0.9-2,2v10c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V10c0-1.1-0.9-2-2-2h-2V5 c0-1.1,0.9-2,2-2s2,0.9,2,2c0,0.55,0.45,1,1,1s1-0.45,1-1C22,2.79,20.21,1,18,1z M12,17c-1.1,0-2-0.9-2-2c0-1.1,0.9-2,2-2 s2,0.9,2,2C14,16.1,13.1,17,12,17z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml
index ce233b7f2614..659a926bf7a3 100644
--- a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml
+++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml
@@ -20,6 +20,6 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M12,2C6.48,2,2,6.48,2,12c0,5.52,4.48,10,10,10s10-4.48,10-10C22,6.48,17.52,2,12,2z M13,17c0,0.55-0.45,1-1,1s-1-0.45-1-1 v-5c0-0.55,0.45-1,1-1s1,0.45,1,1V17z M12,9.25c-0.69,0-1.25-0.56-1.25-1.25S11.31,6.75,12,6.75S13.25,7.31,13.25,8 S12.69,9.25,12,9.25z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_wireless.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_wireless.xml
index 92dbd29cad7e..6e80d1349fc2 100644
--- a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_wireless.xml
+++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_wireless.xml
@@ -21,12 +21,12 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M11.29,19.29c0.39,0.39,1.03,0.4,1.42,0L14,18c0.47-0.47,0.38-1.28-0.22-1.58C13.25,16.15,12.64,16,12,16 c-0.64,0-1.24,0.15-1.77,0.41c-0.59,0.29-0.69,1.11-0.22,1.58L11.29,19.29z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M17.6,14.39l0.71-0.71c0.42-0.42,0.39-1.12-0.08-1.5C16.52,10.82,14.35,10,12,10c-2.34,0-4.5,0.81-6.21,2.17 c-0.47,0.37-0.51,1.07-0.09,1.49l0.71,0.71c0.35,0.36,0.92,0.39,1.32,0.08C8.91,13.54,10.39,13,12,13c1.61,0,3.1,0.55,4.29,1.47 C16.69,14.78,17.25,14.75,17.6,14.39z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M21.83,10.16l0.71-0.71c0.42-0.42,0.38-1.09-0.06-1.48C19.68,5.5,16.01,4,12,4C8.01,4,4.36,5.49,1.56,7.94 C1.12,8.33,1.08,9,1.49,9.41l0.71,0.71c0.37,0.37,0.96,0.4,1.35,0.06C5.81,8.2,8.77,7,12,7c3.25,0,6.22,1.22,8.49,3.22 C20.88,10.56,21.47,10.53,21.83,10.16z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_storage_white.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_storage_white.xml
index 03780db829d9..9eb336c44141 100644
--- a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_storage_white.xml
+++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_storage_white.xml
@@ -20,12 +20,12 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M19,10H5c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2C21,10.9,20.1,10,19,10z M6,13.1c-0.61,0-1.1-0.49-1.1-1.1 s0.49-1.1,1.1-1.1s1.1,0.49,1.1,1.1S6.61,13.1,6,13.1z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M21,18c0-1.1-0.9-2-2-2H5c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2h14C20.1,20,21,19.1,21,18z M6,19.1c-0.61,0-1.1-0.49-1.1-1.1 s0.49-1.1,1.1-1.1s1.1,0.49,1.1,1.1S6.61,19.1,6,19.1z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M19,4H5C3.9,4,3,4.9,3,6c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2C21,4.9,20.1,4,19,4z M6,7.1C5.39,7.1,4.9,6.61,4.9,6 S5.39,4.9,6,4.9S7.1,5.39,7.1,6S6.61,7.1,6,7.1z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_volume_up_24dp.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_volume_up_24dp.xml
index 863df7173c1f..b94035100e40 100644
--- a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_volume_up_24dp.xml
+++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_volume_up_24dp.xml
@@ -20,12 +20,12 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M20.4,8.78c-0.91-2.39-2.8-4.27-5.18-5.18C14.63,3.37,14,3.83,14,4.46v0.19c0,0.38,0.25,0.71,0.61,0.85 C17.18,6.54,19,9.06,19,12s-1.82,5.46-4.39,6.5C14.25,18.64,14,18.97,14,19.35v0.19c0,0.63,0.63,1.08,1.22,0.86 C19.86,18.62,22.18,13.42,20.4,8.78z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M16.5,12c0-1.71-0.97-3.27-2.5-4.03v8.05C15.48,15.29,16.5,13.77,16.5,12z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M10.29,5.7L7,9H4c-0.55,0-1,0.45-1,1v4c0,0.55,0.45,1,1,1h3l3.29,3.29c0.63,0.63,1.71,0.18,1.71-0.71V6.41 C12,5.52,10.92,5.07,10.29,5.7z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_apps.xml b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_apps.xml
index 58999d0925c2..5b1850f5f15d 100644
--- a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_apps.xml
+++ b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_apps.xml
@@ -14,13 +14,13 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M 6 4 C 7.10456949966 4 8 4.89543050034 8 6 C 8 7.10456949966 7.10456949966 8 6 8 C 4.89543050034 8 4 7.10456949966 4 6 C 4 4.89543050034 4.89543050034 4 6 4 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 12 4 C 13.1045694997 4 14 4.89543050034 14 6 C 14 7.10456949966 13.1045694997 8 12 8 C 10.8954305003 8 10 7.10456949966 10 6 C 10 4.89543050034 10.8954305003 4 12 4 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 18 4 C 19.1045694997 4 20 4.89543050034 20 6 C 20 7.10456949966 19.1045694997 8 18 8 C 16.8954305003 8 16 7.10456949966 16 6 C 16 4.89543050034 16.8954305003 4 18 4 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 6 10 C 7.10456949966 10 8 10.8954305003 8 12 C 8 13.1045694997 7.10456949966 14 6 14 C 4.89543050034 14 4 13.1045694997 4 12 C 4 10.8954305003 4.89543050034 10 6 10 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 12 10 C 13.1045694997 10 14 10.8954305003 14 12 C 14 13.1045694997 13.1045694997 14 12 14 C 10.8954305003 14 10 13.1045694997 10 12 C 10 10.8954305003 10.8954305003 10 12 10 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 18 10 C 19.1045694997 10 20 10.8954305003 20 12 C 20 13.1045694997 19.1045694997 14 18 14 C 16.8954305003 14 16 13.1045694997 16 12 C 16 10.8954305003 16.8954305003 10 18 10 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 6 16 C 7.10456949966 16 8 16.8954305003 8 18 C 8 19.1045694997 7.10456949966 20 6 20 C 4.89543050034 20 4 19.1045694997 4 18 C 4 16.8954305003 4.89543050034 16 6 16 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 12 16 C 13.1045694997 16 14 16.8954305003 14 18 C 14 19.1045694997 13.1045694997 20 12 20 C 10.8954305003 20 10 19.1045694997 10 18 C 10 16.8954305003 10.8954305003 16 12 16 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 18 16 C 19.1045694997 16 20 16.8954305003 20 18 C 20 19.1045694997 19.1045694997 20 18 20 C 16.8954305003 20 16 19.1045694997 16 18 C 16 16.8954305003 16.8954305003 16 18 16 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 6 4 C 7.10456949966 4 8 4.89543050034 8 6 C 8 7.10456949966 7.10456949966 8 6 8 C 4.89543050034 8 4 7.10456949966 4 6 C 4 4.89543050034 4.89543050034 4 6 4 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 4 C 13.1045694997 4 14 4.89543050034 14 6 C 14 7.10456949966 13.1045694997 8 12 8 C 10.8954305003 8 10 7.10456949966 10 6 C 10 4.89543050034 10.8954305003 4 12 4 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 18 4 C 19.1045694997 4 20 4.89543050034 20 6 C 20 7.10456949966 19.1045694997 8 18 8 C 16.8954305003 8 16 7.10456949966 16 6 C 16 4.89543050034 16.8954305003 4 18 4 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 6 10 C 7.10456949966 10 8 10.8954305003 8 12 C 8 13.1045694997 7.10456949966 14 6 14 C 4.89543050034 14 4 13.1045694997 4 12 C 4 10.8954305003 4.89543050034 10 6 10 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 10 C 13.1045694997 10 14 10.8954305003 14 12 C 14 13.1045694997 13.1045694997 14 12 14 C 10.8954305003 14 10 13.1045694997 10 12 C 10 10.8954305003 10.8954305003 10 12 10 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 18 10 C 19.1045694997 10 20 10.8954305003 20 12 C 20 13.1045694997 19.1045694997 14 18 14 C 16.8954305003 14 16 13.1045694997 16 12 C 16 10.8954305003 16.8954305003 10 18 10 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 6 16 C 7.10456949966 16 8 16.8954305003 8 18 C 8 19.1045694997 7.10456949966 20 6 20 C 4.89543050034 20 4 19.1045694997 4 18 C 4 16.8954305003 4.89543050034 16 6 16 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 16 C 13.1045694997 16 14 16.8954305003 14 18 C 14 19.1045694997 13.1045694997 20 12 20 C 10.8954305003 20 10 19.1045694997 10 18 C 10 16.8954305003 10.8954305003 16 12 16 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 18 16 C 19.1045694997 16 20 16.8954305003 20 18 C 20 19.1045694997 19.1045694997 20 18 20 C 16.8954305003 20 16 19.1045694997 16 18 C 16 16.8954305003 16.8954305003 16 18 16 Z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_devices_other.xml b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_devices_other.xml
index fa91107cfe7e..954ff328dbd2 100644
--- a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_devices_other.xml
+++ b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_devices_other.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
<vector android:autoMirrored="true" android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M2.51,17.74V6.27c0-0.41,0.34-0.75,0.75-0.75H21v-1.5H3.26c-1.24,0-2.25,1.01-2.25,2.25v11.46c0,1.24,1.01,2.25,2.25,2.25 H7v-1.5H3.26C2.85,18.49,2.51,18.15,2.51,17.74z"/>
- <path android:fillColor="@android:color/white" android:pathData="M20.75,8h-3.5C16.01,8,15,9.01,15,10.25v7.5c0,1.24,1.01,2.25,2.25,2.25h3.5c1.24,0,2.25-1.01,2.25-2.25v-7.5 C23,9.01,21.99,8,20.75,8z M21.5,17.75c0,0.41-0.34,0.75-0.75,0.75h-3.5c-0.41,0-0.75-0.34-0.75-0.75v-7.5 c0-0.41,0.34-0.75,0.75-0.75h3.5c0.41,0,0.75,0.34,0.75,0.75V17.75z"/>
- <path android:fillColor="@android:color/white" android:pathData="M13,13.84v-1.09c0-0.41-0.34-0.75-0.75-0.75h-2.5C9.34,12,9,12.34,9,12.75v1.09C8.54,14.25,8.18,14.92,8.18,16 c0,1.09,0.36,1.75,0.82,2.16v1.09C9,19.66,9.34,20,9.75,20h2.5c0.41,0,0.75-0.34,0.75-0.75v-1.09c0.46-0.41,0.82-1.08,0.82-2.16 C13.82,14.91,13.46,14.25,13,13.84z M11,17.25C10.03,17.26,9.75,16.9,9.75,16c0-0.9,0.3-1.25,1.25-1.25 c0.95-0.01,1.25,0.33,1.25,1.25C12.25,16.84,11.98,17.25,11,17.25z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M2.51,17.74V6.27c0-0.41,0.34-0.75,0.75-0.75H21v-1.5H3.26c-1.24,0-2.25,1.01-2.25,2.25v11.46c0,1.24,1.01,2.25,2.25,2.25 H7v-1.5H3.26C2.85,18.49,2.51,18.15,2.51,17.74z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M20.75,8h-3.5C16.01,8,15,9.01,15,10.25v7.5c0,1.24,1.01,2.25,2.25,2.25h3.5c1.24,0,2.25-1.01,2.25-2.25v-7.5 C23,9.01,21.99,8,20.75,8z M21.5,17.75c0,0.41-0.34,0.75-0.75,0.75h-3.5c-0.41,0-0.75-0.34-0.75-0.75v-7.5 c0-0.41,0.34-0.75,0.75-0.75h3.5c0.41,0,0.75,0.34,0.75,0.75V17.75z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M13,13.84v-1.09c0-0.41-0.34-0.75-0.75-0.75h-2.5C9.34,12,9,12.34,9,12.75v1.09C8.54,14.25,8.18,14.92,8.18,16 c0,1.09,0.36,1.75,0.82,2.16v1.09C9,19.66,9.34,20,9.75,20h2.5c0.41,0,0.75-0.34,0.75-0.75v-1.09c0.46-0.41,0.82-1.08,0.82-2.16 C13.82,14.91,13.46,14.25,13,13.84z M11,17.25C10.03,17.26,9.75,16.9,9.75,16c0-0.9,0.3-1.25,1.25-1.25 c0.95-0.01,1.25,0.33,1.25,1.25C12.25,16.84,11.98,17.25,11,17.25z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_help.xml b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_help.xml
index 8ab01a638545..89b80319a539 100644
--- a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_help.xml
+++ b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_help.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M13.73,6.41c-0.51-0.27-1.09-0.4-1.74-0.4c-0.87,0-1.58,0.24-2.13,0.73C9.31,7.22,8.91,7.78,8.67,8.42l1.29,0.54 c0.16-0.46,0.41-0.84,0.73-1.16c0.32-0.31,0.76-0.47,1.3-0.47c0.6,0,1.07,0.16,1.41,0.48c0.34,0.32,0.51,0.75,0.51,1.27 c0,0.39-0.1,0.73-0.29,1.01c-0.19,0.28-0.51,0.61-0.94,1.01c-0.54,0.5-0.91,0.93-1.11,1.29c-0.21,0.36-0.31,0.81-0.31,1.36v0.79 h1.43v-0.69c0-0.39,0.08-0.74,0.25-1.03c0.16-0.29,0.43-0.61,0.79-0.93c0.47-0.43,0.85-0.85,1.15-1.27 c0.3-0.42,0.45-0.93,0.45-1.53c0-0.58-0.14-1.1-0.42-1.57C14.63,7.05,14.24,6.68,13.73,6.41z"/>
- <path android:fillColor="@android:color/white" android:pathData="M11.98,15.96c-0.03,0-1-0.06-1,1c0,1.06,0.96,1,1,1c0.03,0,1,0.06,1-1C12.98,15.9,12.02,15.96,11.98,15.96z"/>
- <path android:fillColor="@android:color/white" android:pathData="M12,2C4.41,2,2,6.9,2,12c0,5.09,2.35,10,10,10c7.59,0,10-4.9,10-10C22,6.91,19.65,2,12,2z M12,20.5 c-2.64,0.05-8.5-0.59-8.5-8.5c0-7.91,5.88-8.55,8.5-8.5c2.64-0.05,8.5,0.59,8.5,8.5C20.5,19.91,14.62,20.55,12,20.5z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M13.73,6.41c-0.51-0.27-1.09-0.4-1.74-0.4c-0.87,0-1.58,0.24-2.13,0.73C9.31,7.22,8.91,7.78,8.67,8.42l1.29,0.54 c0.16-0.46,0.41-0.84,0.73-1.16c0.32-0.31,0.76-0.47,1.3-0.47c0.6,0,1.07,0.16,1.41,0.48c0.34,0.32,0.51,0.75,0.51,1.27 c0,0.39-0.1,0.73-0.29,1.01c-0.19,0.28-0.51,0.61-0.94,1.01c-0.54,0.5-0.91,0.93-1.11,1.29c-0.21,0.36-0.31,0.81-0.31,1.36v0.79 h1.43v-0.69c0-0.39,0.08-0.74,0.25-1.03c0.16-0.29,0.43-0.61,0.79-0.93c0.47-0.43,0.85-0.85,1.15-1.27 c0.3-0.42,0.45-0.93,0.45-1.53c0-0.58-0.14-1.1-0.42-1.57C14.63,7.05,14.24,6.68,13.73,6.41z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M11.98,15.96c-0.03,0-1-0.06-1,1c0,1.06,0.96,1,1,1c0.03,0,1,0.06,1-1C12.98,15.9,12.02,15.96,11.98,15.96z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,2C4.41,2,2,6.9,2,12c0,5.09,2.35,10,10,10c7.59,0,10-4.9,10-10C22,6.91,19.65,2,12,2z M12,20.5 c-2.64,0.05-8.5-0.59-8.5-8.5c0-7.91,5.88-8.55,8.5-8.5c2.64-0.05,8.5,0.59,8.5,8.5C20.5,19.91,14.62,20.55,12,20.5z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_phone_info.xml b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_phone_info.xml
index 2edda9c50a2c..e2246ce7fb16 100644
--- a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_phone_info.xml
+++ b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_phone_info.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M16.75,1h-9.5C6.01,1,5,2.01,5,3.25v17.5C5,21.99,6.01,23,7.25,23h9.5c1.24,0,2.25-1.01,2.25-2.25V3.25 C19,2.01,17.99,1,16.75,1z M7.25,2.5h9.5c0.41,0,0.75,0.34,0.75,0.75v1h-11v-1C6.5,2.84,6.84,2.5,7.25,2.5z M17.5,5.75v12.5h-11 V5.75H17.5z M16.75,21.5h-9.5c-0.41,0-0.75-0.34-0.75-0.75v-1h11v1C17.5,21.16,17.16,21.5,16.75,21.5z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 11.25 11 H 12.75 V 16 H 11.25 V 11 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 12 8 C 12.4142135624 8 12.75 8.33578643763 12.75 8.75 C 12.75 9.16421356237 12.4142135624 9.5 12 9.5 C 11.5857864376 9.5 11.25 9.16421356237 11.25 8.75 C 11.25 8.33578643763 11.5857864376 8 12 8 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M16.75,1h-9.5C6.01,1,5,2.01,5,3.25v17.5C5,21.99,6.01,23,7.25,23h9.5c1.24,0,2.25-1.01,2.25-2.25V3.25 C19,2.01,17.99,1,16.75,1z M7.25,2.5h9.5c0.41,0,0.75,0.34,0.75,0.75v1h-11v-1C6.5,2.84,6.84,2.5,7.25,2.5z M17.5,5.75v12.5h-11 V5.75H17.5z M16.75,21.5h-9.5c-0.41,0-0.75-0.34-0.75-0.75v-1h11v1C17.5,21.16,17.16,21.5,16.75,21.5z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 11.25 11 H 12.75 V 16 H 11.25 V 11 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 8 C 12.4142135624 8 12.75 8.33578643763 12.75 8.75 C 12.75 9.16421356237 12.4142135624 9.5 12 9.5 C 11.5857864376 9.5 11.25 9.16421356237 11.25 8.75 C 11.25 8.33578643763 11.5857864376 8 12 8 Z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_accessibility.xml b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_accessibility.xml
index 900a3a6cb082..a92595bea8ac 100644
--- a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_accessibility.xml
+++ b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_accessibility.xml
@@ -14,9 +14,9 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M 8 22 C 8.55228474983 22 9 22.4477152502 9 23 C 9 23.5522847498 8.55228474983 24 8 24 C 7.44771525017 24 7 23.5522847498 7 23 C 7 22.4477152502 7.44771525017 22 8 22 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 12 22 C 12.5522847498 22 13 22.4477152502 13 23 C 13 23.5522847498 12.5522847498 24 12 24 C 11.4477152502 24 11 23.5522847498 11 23 C 11 22.4477152502 11.4477152502 22 12 22 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 16 22 C 16.5522847498 22 17 22.4477152502 17 23 C 17 23.5522847498 16.5522847498 24 16 24 C 15.4477152502 24 15 23.5522847498 15 23 C 15 22.4477152502 15.4477152502 22 16 22 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 12 0 C 13.1045694997 0 14 0.895430500338 14 2 C 14 3.10456949966 13.1045694997 4 12 4 C 10.8954305003 4 10 3.10456949966 10 2 C 10 0.895430500338 10.8954305003 0 12 0 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M15,20V7c2-0.17,4.14-0.5,6-1l-0.5-2c-2.61,0.7-5.67,1-8.5,1S6.11,4.7,3.5,4L3,6c1.86,0.5,4,0.83,6,1v13h2v-6h2v6H15z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 8 22 C 8.55228474983 22 9 22.4477152502 9 23 C 9 23.5522847498 8.55228474983 24 8 24 C 7.44771525017 24 7 23.5522847498 7 23 C 7 22.4477152502 7.44771525017 22 8 22 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 22 C 12.5522847498 22 13 22.4477152502 13 23 C 13 23.5522847498 12.5522847498 24 12 24 C 11.4477152502 24 11 23.5522847498 11 23 C 11 22.4477152502 11.4477152502 22 12 22 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 16 22 C 16.5522847498 22 17 22.4477152502 17 23 C 17 23.5522847498 16.5522847498 24 16 24 C 15.4477152502 24 15 23.5522847498 15 23 C 15 22.4477152502 15.4477152502 22 16 22 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 0 C 13.1045694997 0 14 0.895430500338 14 2 C 14 3.10456949966 13.1045694997 4 12 4 C 10.8954305003 4 10 3.10456949966 10 2 C 10 0.895430500338 10.8954305003 0 12 0 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M15,20V7c2-0.17,4.14-0.5,6-1l-0.5-2c-2.61,0.7-5.67,1-8.5,1S6.11,4.7,3.5,4L3,6c1.86,0.5,4,0.83,6,1v13h2v-6h2v6H15z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_accounts.xml b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_accounts.xml
index 0a7ec3fa3daa..b17efd1fe3ca 100644
--- a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_accounts.xml
+++ b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_accounts.xml
@@ -14,6 +14,6 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M18.75,3H5.25C4.01,3,3,4.01,3,5.25v13.5C3,19.99,4.01,21,5.25,21h13.5c1.24,0,2.25-1.01,2.25-2.25V5.25 C21,4.01,19.99,3,18.75,3z M18.75,19.5H5.25c-0.35,0-0.64-0.25-0.72-0.58C4.9,18.6,7.23,16.4,12,16.51 c4.77-0.11,7.11,2.1,7.47,2.41C19.39,19.25,19.1,19.5,18.75,19.5z M19.5,17.03c-3.24-2.22-7.05-2.02-7.5-2.02 c-0.46,0-4.27-0.19-7.5,2.02V5.25c0-0.41,0.34-0.75,0.75-0.75h13.5c0.41,0,0.75,0.34,0.75,0.75V17.03z"/>
- <path android:fillColor="@android:color/white" android:pathData="M12,6C9.33,6,8.5,7.73,8.5,9.5c0,1.78,0.83,3.5,3.5,3.5c2.67,0,3.5-1.73,3.5-3.5C15.5,7.72,14.67,6,12,6z M12,11.5 c-0.43,0.01-2,0.13-2-2c0-2.14,1.58-2,2-2c0.43-0.01,2-0.13,2,2C14,11.64,12.42,11.5,12,11.5z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M18.75,3H5.25C4.01,3,3,4.01,3,5.25v13.5C3,19.99,4.01,21,5.25,21h13.5c1.24,0,2.25-1.01,2.25-2.25V5.25 C21,4.01,19.99,3,18.75,3z M18.75,19.5H5.25c-0.35,0-0.64-0.25-0.72-0.58C4.9,18.6,7.23,16.4,12,16.51 c4.77-0.11,7.11,2.1,7.47,2.41C19.39,19.25,19.1,19.5,18.75,19.5z M19.5,17.03c-3.24-2.22-7.05-2.02-7.5-2.02 c-0.46,0-4.27-0.19-7.5,2.02V5.25c0-0.41,0.34-0.75,0.75-0.75h13.5c0.41,0,0.75,0.34,0.75,0.75V17.03z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,6C9.33,6,8.5,7.73,8.5,9.5c0,1.78,0.83,3.5,3.5,3.5c2.67,0,3.5-1.73,3.5-3.5C15.5,7.72,14.67,6,12,6z M12,11.5 c-0.43,0.01-2,0.13-2-2c0-2.14,1.58-2,2-2c0.43-0.01,2-0.13,2,2C14,11.64,12.42,11.5,12,11.5z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_battery_white.xml b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_battery_white.xml
index cc80eb7e8b84..4af480634f85 100644
--- a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_battery_white.xml
+++ b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_battery_white.xml
@@ -14,5 +14,5 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M16,4h-1V2.5C15,2.22,14.78,2,14.5,2h-5C9.22,2,9,2.22,9,2.5V4H8C6.9,4,6,4.9,6,6v12c0,2.21,1.79,4,4,4h4 c2.21,0,4-1.79,4-4V6C18,4.9,17.1,4,16,4z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M16,4h-1V2.5C15,2.22,14.78,2,14.5,2h-5C9.22,2,9,2.22,9,2.5V4H8C6.9,4,6,4.9,6,6v12c0,2.21,1.79,4,4,4h4 c2.21,0,4-1.79,4-4V6C18,4.9,17.1,4,16,4z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_display_white.xml b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_display_white.xml
index d4d4174e0923..9eb8a0b0d2e8 100644
--- a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_display_white.xml
+++ b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_display_white.xml
@@ -14,6 +14,6 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M12,7V17c0.17,0,5,0.31,5-5C17,6.7,12.22,7,12,7z"/>
- <path android:fillColor="@android:color/white" android:pathData="M22.78,11.47L20,8.69V4.75C20,4.34,19.66,4,19.25,4h-3.94l-2.78-2.78c-0.29-0.29-0.77-0.29-1.06,0L8.69,4H4.75 C4.34,4,4,4.34,4,4.75v3.94l-2.78,2.78c-0.29,0.29-0.29,0.77,0,1.06L4,15.31v3.94C4,19.66,4.34,20,4.75,20h3.94l2.78,2.78 C11.62,22.93,11.81,23,12,23s0.38-0.07,0.53-0.22L15.31,20h3.94c0.41,0,0.75-0.34,0.75-0.75v-3.94l2.78-2.78 C23.07,12.24,23.07,11.76,22.78,11.47z M18.72,14.47C18.58,14.61,18.5,14.8,18.5,15v3.5H15c-0.2,0-0.39,0.08-0.53,0.22L12,21.19 l-2.47-2.47C9.39,18.58,9.2,18.5,9,18.5H5.5V15c0-0.2-0.08-0.39-0.22-0.53L2.81,12l2.47-2.47C5.42,9.39,5.5,9.2,5.5,9V5.5H9 c0.2,0,0.39-0.08,0.53-0.22L12,2.81l2.47,2.47C14.61,5.42,14.8,5.5,15,5.5h3.5V9c0,0.2,0.08,0.39,0.22,0.53L21.19,12L18.72,14.47z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,7V17c0.17,0,5,0.31,5-5C17,6.7,12.22,7,12,7z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M22.78,11.47L20,8.69V4.75C20,4.34,19.66,4,19.25,4h-3.94l-2.78-2.78c-0.29-0.29-0.77-0.29-1.06,0L8.69,4H4.75 C4.34,4,4,4.34,4,4.75v3.94l-2.78,2.78c-0.29,0.29-0.29,0.77,0,1.06L4,15.31v3.94C4,19.66,4.34,20,4.75,20h3.94l2.78,2.78 C11.62,22.93,11.81,23,12,23s0.38-0.07,0.53-0.22L15.31,20h3.94c0.41,0,0.75-0.34,0.75-0.75v-3.94l2.78-2.78 C23.07,12.24,23.07,11.76,22.78,11.47z M18.72,14.47C18.58,14.61,18.5,14.8,18.5,15v3.5H15c-0.2,0-0.39,0.08-0.53,0.22L12,21.19 l-2.47-2.47C9.39,18.58,9.2,18.5,9,18.5H5.5V15c0-0.2-0.08-0.39-0.22-0.53L2.81,12l2.47-2.47C5.42,9.39,5.5,9.2,5.5,9V5.5H9 c0.2,0,0.39-0.08,0.53-0.22L12,2.81l2.47,2.47C14.61,5.42,14.8,5.5,15,5.5h3.5V9c0,0.2,0.08,0.39,0.22,0.53L21.19,12L18.72,14.47z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_location.xml b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_location.xml
index 2d1de9490bba..d437035642bc 100644
--- a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_location.xml
+++ b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_location.xml
@@ -14,6 +14,6 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M12,6c-1.88,0-3,1.04-3,3c0,1.91,1.06,3,3,3c1.89,0,3-1.05,3-3C15,7.08,13.93,6,12,6z M12,10.5 c-1.15,0.01-1.5-0.47-1.5-1.5c0-1.06,0.37-1.5,1.5-1.5c1.15-0.01,1.5,0.47,1.5,1.5C13.5,10.09,13.1,10.5,12,10.5z"/>
- <path android:fillColor="@android:color/white" android:pathData="M11.99,2C9.69,1.94,5,2.93,5,9c0,6.88,6.23,12.56,6.5,12.8c0.14,0.13,0.32,0.2,0.5,0.2s0.36-0.06,0.5-0.2 C12.77,21.56,19,15.88,19,9C19,2.87,14.3,1.96,11.99,2z M12,20.2C10.55,18.7,6.5,14.12,6.5,9c0-4.91,3.63-5.55,5.44-5.5 C16.9,3.34,17.5,7.14,17.5,9C17.5,14.13,13.45,18.7,12,20.2z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,6c-1.88,0-3,1.04-3,3c0,1.91,1.06,3,3,3c1.89,0,3-1.05,3-3C15,7.08,13.93,6,12,6z M12,10.5 c-1.15,0.01-1.5-0.47-1.5-1.5c0-1.06,0.37-1.5,1.5-1.5c1.15-0.01,1.5,0.47,1.5,1.5C13.5,10.09,13.1,10.5,12,10.5z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M11.99,2C9.69,1.94,5,2.93,5,9c0,6.88,6.23,12.56,6.5,12.8c0.14,0.13,0.32,0.2,0.5,0.2s0.36-0.06,0.5-0.2 C12.77,21.56,19,15.88,19,9C19,2.87,14.3,1.96,11.99,2z M12,20.2C10.55,18.7,6.5,14.12,6.5,9c0-4.91,3.63-5.55,5.44-5.5 C16.9,3.34,17.5,7.14,17.5,9C17.5,14.13,13.45,18.7,12,20.2z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_privacy.xml b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_privacy.xml
index b8e5a7dd9957..28d111da49a9 100644
--- a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_privacy.xml
+++ b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_privacy.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M22.74,11.4C20.87,7.33,16.77,4.5,12,4.5S3.13,7.33,1.26,11.4c-0.17,0.38-0.17,0.83,0,1.21c1.87,4.07,5.97,6.9,10.74,6.9 c0.68,0,1.35-0.06,2-0.18v-1.55C13.35,17.91,12.68,18,12,18c-4.02,0-7.7-2.36-9.38-5.98C4.3,8.36,7.98,6,12,6s7.7,2.36,9.38,5.98 c-0.08,0.17-0.19,0.33-0.28,0.5c0.47,0.22,0.9,0.51,1.27,0.85c0.13-0.24,0.25-0.48,0.37-0.73C22.92,12.22,22.92,11.78,22.74,11.4 z"/>
- <path android:fillColor="@android:color/white" android:pathData="M16.5,12c0-2.3-1.06-4.5-4.5-4.5c-3.43,0-4.5,2.22-4.5,4.5c0,2.3,1.06,4.5,4.5,4.5c0.83,0,1.52-0.14,2.09-0.36 c0.26-1.46,1.14-2.69,2.37-3.42C16.48,12.48,16.5,12.24,16.5,12z M12,15c-3.05,0.04-3-2.35-3-3c0-0.63-0.04-3.06,3-3 c3.05-0.04,3,2.35,3,3C15,12.63,15.04,15.06,12,15z"/>
- <path android:fillColor="@android:color/white" android:pathData="M21,17v-1c0-1.1-0.9-2-2-2s-2,0.9-2,2v1c-0.55,0-1,0.45-1,1v3c0,0.55,0.45,1,1,1h4c0.55,0,1-0.45,1-1v-3 C22,17.45,21.55,17,21,17z M18.5,16c0-0.28,0.22-0.5,0.5-0.5s0.5,0.22,0.5,0.5v1h-1V16z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M22.74,11.4C20.87,7.33,16.77,4.5,12,4.5S3.13,7.33,1.26,11.4c-0.17,0.38-0.17,0.83,0,1.21c1.87,4.07,5.97,6.9,10.74,6.9 c0.68,0,1.35-0.06,2-0.18v-1.55C13.35,17.91,12.68,18,12,18c-4.02,0-7.7-2.36-9.38-5.98C4.3,8.36,7.98,6,12,6s7.7,2.36,9.38,5.98 c-0.08,0.17-0.19,0.33-0.28,0.5c0.47,0.22,0.9,0.51,1.27,0.85c0.13-0.24,0.25-0.48,0.37-0.73C22.92,12.22,22.92,11.78,22.74,11.4 z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M16.5,12c0-2.3-1.06-4.5-4.5-4.5c-3.43,0-4.5,2.22-4.5,4.5c0,2.3,1.06,4.5,4.5,4.5c0.83,0,1.52-0.14,2.09-0.36 c0.26-1.46,1.14-2.69,2.37-3.42C16.48,12.48,16.5,12.24,16.5,12z M12,15c-3.05,0.04-3-2.35-3-3c0-0.63-0.04-3.06,3-3 c3.05-0.04,3,2.35,3,3C15,12.63,15.04,15.06,12,15z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M21,17v-1c0-1.1-0.9-2-2-2s-2,0.9-2,2v1c-0.55,0-1,0.45-1,1v3c0,0.55,0.45,1,1,1h4c0.55,0,1-0.45,1-1v-3 C22,17.45,21.55,17,21,17z M18.5,16c0-0.28,0.22-0.5,0.5-0.5s0.5,0.22,0.5,0.5v1h-1V16z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_security_white.xml b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_security_white.xml
index 0475e33d5350..7f654c120583 100644
--- a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_security_white.xml
+++ b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_security_white.xml
@@ -14,6 +14,6 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M18.5,1C17.03,0.96,14,1.62,14,5.5v2.49H6.25C5.01,7.99,4,9,4,10.24v9.51C4,20.99,5.01,22,6.25,22h11.5 c1.24,0,2.25-1.01,2.25-2.25v-9.51c0-1.24-1.01-2.25-2.25-2.25H15.5V5.5c0-2.77,2-3.01,3.05-3c1.02-0.02,2.96,0.28,2.96,3V6H23 V5.5C23,1.6,19.97,0.96,18.5,1z M17.75,9.49c0.41,0,0.75,0.34,0.75,0.75v9.51c0,0.41-0.34,0.75-0.75,0.75H6.25 c-0.41,0-0.75-0.34-0.75-0.75v-9.51c0-0.41,0.34-0.75,0.75-0.75H17.75z"/>
- <path android:fillColor="@android:color/white" android:pathData="M12,17.74c0.13,0,2.75,0.06,2.75-2.75c0-2.34-1.85-2.77-2.74-2.75c-0.89-0.02-2.76,0.4-2.76,2.75 C9.25,17.41,11.19,17.74,12,17.74z M12.03,13.74c1.32-0.06,1.22,1.22,1.22,1.25c0,0.89-0.42,1.26-1.25,1.25 c-0.81,0.01-1.25-0.34-1.25-1.25C10.75,14.15,11.12,13.73,12.03,13.74z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M18.5,1C17.03,0.96,14,1.62,14,5.5v2.49H6.25C5.01,7.99,4,9,4,10.24v9.51C4,20.99,5.01,22,6.25,22h11.5 c1.24,0,2.25-1.01,2.25-2.25v-9.51c0-1.24-1.01-2.25-2.25-2.25H15.5V5.5c0-2.77,2-3.01,3.05-3c1.02-0.02,2.96,0.28,2.96,3V6H23 V5.5C23,1.6,19.97,0.96,18.5,1z M17.75,9.49c0.41,0,0.75,0.34,0.75,0.75v9.51c0,0.41-0.34,0.75-0.75,0.75H6.25 c-0.41,0-0.75-0.34-0.75-0.75v-9.51c0-0.41,0.34-0.75,0.75-0.75H17.75z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,17.74c0.13,0,2.75,0.06,2.75-2.75c0-2.34-1.85-2.77-2.74-2.75c-0.89-0.02-2.76,0.4-2.76,2.75 C9.25,17.41,11.19,17.74,12,17.74z M12.03,13.74c1.32-0.06,1.22,1.22,1.22,1.25c0,0.89-0.42,1.26-1.25,1.25 c-0.81,0.01-1.25-0.34-1.25-1.25C10.75,14.15,11.12,13.73,12.03,13.74z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml
index 0c0a6827f617..70d36280c8b1 100644
--- a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml
+++ b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M 11.25 10 H 12.75 V 17 H 11.25 V 10 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 12 7 C 12.4142135624 7 12.75 7.33578643763 12.75 7.75 C 12.75 8.16421356237 12.4142135624 8.5 12 8.5 C 11.5857864376 8.5 11.25 8.16421356237 11.25 7.75 C 11.25 7.33578643763 11.5857864376 7 12 7 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M12,2C4.41,2,2,6.9,2,12c0,5.09,2.35,10,10,10c7.59,0,10-4.9,10-10C22,6.91,19.65,2,12,2z M12,20.5 c-2.64,0.05-8.5-0.59-8.5-8.5c0-7.91,5.88-8.55,8.5-8.5c2.64-0.05,8.5,0.59,8.5,8.5C20.5,19.91,14.62,20.55,12,20.5z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 11.25 10 H 12.75 V 17 H 11.25 V 10 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 7 C 12.4142135624 7 12.75 7.33578643763 12.75 7.75 C 12.75 8.16421356237 12.4142135624 8.5 12 8.5 C 11.5857864376 8.5 11.25 8.16421356237 11.25 7.75 C 11.25 7.33578643763 11.5857864376 7 12 7 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,2C4.41,2,2,6.9,2,12c0,5.09,2.35,10,10,10c7.59,0,10-4.9,10-10C22,6.91,19.65,2,12,2z M12,20.5 c-2.64,0.05-8.5-0.59-8.5-8.5c0-7.91,5.88-8.55,8.5-8.5c2.64-0.05,8.5,0.59,8.5,8.5C20.5,19.91,14.62,20.55,12,20.5z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_wireless.xml b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_wireless.xml
index 2899c7f1a580..63346a485f0d 100644
--- a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_wireless.xml
+++ b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_wireless.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
<vector android:height="24dp" android:tint="?android:attr/colorControlNormal" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M12.14,6c4.29,0.06,7.79,2.34,9.81,4.05l1.07-1.07c-2.26-1.94-6.06-4.41-10.86-4.48C8.32,4.46,4.57,5.96,1,9l1.07,1.07 C5.33,7.32,8.72,5.95,12.14,6z"/>
- <path android:fillColor="@android:color/white" android:pathData="M11.97,11.5c0.04,0,0.08,0,0.12,0c2.54,0.04,4.67,1.25,6.07,2.34l1.08-1.08c-1.6-1.28-4.07-2.71-7.13-2.76 c-2.54-0.03-4.99,0.91-7.33,2.78l1.07,1.07C7.84,12.3,9.89,11.5,11.97,11.5z"/>
- <path android:fillColor="@android:color/white" android:pathData="M11.98,17.5c0.02,0,0.04,0,0.05,0c0.73,0.01,1.38,0.24,1.93,0.53l1.1-1.1c-0.79-0.49-1.81-0.92-3.01-0.93 c-1.07-0.01-2.12,0.31-3.12,0.94l1.1,1.1C10.68,17.69,11.33,17.5,11.98,17.5z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12.14,6c4.29,0.06,7.79,2.34,9.81,4.05l1.07-1.07c-2.26-1.94-6.06-4.41-10.86-4.48C8.32,4.46,4.57,5.96,1,9l1.07,1.07 C5.33,7.32,8.72,5.95,12.14,6z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M11.97,11.5c0.04,0,0.08,0,0.12,0c2.54,0.04,4.67,1.25,6.07,2.34l1.08-1.08c-1.6-1.28-4.07-2.71-7.13-2.76 c-2.54-0.03-4.99,0.91-7.33,2.78l1.07,1.07C7.84,12.3,9.89,11.5,11.97,11.5z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M11.98,17.5c0.02,0,0.04,0,0.05,0c0.73,0.01,1.38,0.24,1.93,0.53l1.1-1.1c-0.79-0.49-1.81-0.92-3.01-0.93 c-1.07-0.01-2.12,0.31-3.12,0.94l1.1,1.1C10.68,17.69,11.33,17.5,11.98,17.5z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_storage_white.xml b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_storage_white.xml
index 7b5d94696a52..0ef8a7cc9c6b 100644
--- a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_storage_white.xml
+++ b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_storage_white.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M20,4H4C3.45,4,3,4.45,3,5v2c0,0.55,0.45,1,1,1h16c0.55,0,1-0.45,1-1V5C21,4.45,20.55,4,20,4z M6,7C5.96,7,5,7.06,5,6 c0-1.06,0.97-1,1-1c0.04,0,1-0.06,1,1C7,7.06,6.03,7,6,7z"/>
- <path android:fillColor="@android:color/white" android:pathData="M20,10H4c-0.55,0-1,0.45-1,1v2c0,0.55,0.45,1,1,1h16c0.55,0,1-0.45,1-1v-2C21,10.45,20.55,10,20,10z M6,13 c-0.04,0-1,0.06-1-1c0-1.06,0.97-1,1-1c0.04,0,1-0.06,1,1C7,13.06,6.03,13,6,13z"/>
- <path android:fillColor="@android:color/white" android:pathData="M20,16H4c-0.55,0-1,0.45-1,1v2c0,0.55,0.45,1,1,1h16c0.55,0,1-0.45,1-1v-2C21,16.45,20.55,16,20,16z M6,19 c-0.04,0-1,0.06-1-1c0-1.06,0.97-1,1-1c0.04,0,1-0.06,1,1C7,19.06,6.03,19,6,19z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M20,4H4C3.45,4,3,4.45,3,5v2c0,0.55,0.45,1,1,1h16c0.55,0,1-0.45,1-1V5C21,4.45,20.55,4,20,4z M6,7C5.96,7,5,7.06,5,6 c0-1.06,0.97-1,1-1c0.04,0,1-0.06,1,1C7,7.06,6.03,7,6,7z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M20,10H4c-0.55,0-1,0.45-1,1v2c0,0.55,0.45,1,1,1h16c0.55,0,1-0.45,1-1v-2C21,10.45,20.55,10,20,10z M6,13 c-0.04,0-1,0.06-1-1c0-1.06,0.97-1,1-1c0.04,0,1-0.06,1,1C7,13.06,6.03,13,6,13z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M20,16H4c-0.55,0-1,0.45-1,1v2c0,0.55,0.45,1,1,1h16c0.55,0,1-0.45,1-1v-2C21,16.45,20.55,16,20,16z M6,19 c-0.04,0-1,0.06-1-1c0-1.06,0.97-1,1-1c0.04,0,1-0.06,1,1C7,19.06,6.03,19,6,19z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_volume_up_24dp.xml b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_volume_up_24dp.xml
index 019fed9f6470..b9753f5d8e9d 100644
--- a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_volume_up_24dp.xml
+++ b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_volume_up_24dp.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M14,3.3v1.58c2.35,0.89,5.55,3.95,5.5,7.18c-0.06,3.65-3.79,6.41-5.5,7.05v1.58c1.12-0.33,6.92-3.18,7-8.61 C21.07,7.39,16.32,3.99,14,3.3z"/>
- <path android:fillColor="@android:color/white" android:pathData="M16.5,12.05c0.01-0.53-0.02-2.55-2.5-4.54v9C15.72,15.12,16.48,13.52,16.5,12.05z"/>
- <path android:fillColor="@android:color/white" android:pathData="M9.86,6.08L6.95,9H6c-2.56,0-3.02,2.02-3,2.99C2.97,12.96,3.44,15,6,15h0.95l2.91,2.92C10.69,18.75,12,18.1,12,17.04V6.96 C12,5.85,10.65,5.29,9.86,6.08z M10.5,16.43l-2.7-2.71c-0.14-0.14-0.33-0.22-0.53-0.22H6c-1.42,0-1.51-0.99-1.5-1.54 C4.47,10.73,5.29,10.5,6,10.5h1.26c0.2,0,0.39-0.08,0.53-0.22l2.7-2.71V16.43z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M14,3.3v1.58c2.35,0.89,5.55,3.95,5.5,7.18c-0.06,3.65-3.79,6.41-5.5,7.05v1.58c1.12-0.33,6.92-3.18,7-8.61 C21.07,7.39,16.32,3.99,14,3.3z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M16.5,12.05c0.01-0.53-0.02-2.55-2.5-4.54v9C15.72,15.12,16.48,13.52,16.5,12.05z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M9.86,6.08L6.95,9H6c-2.56,0-3.02,2.02-3,2.99C2.97,12.96,3.44,15,6,15h0.95l2.91,2.92C10.69,18.75,12,18.1,12,17.04V6.96 C12,5.85,10.65,5.29,9.86,6.08z M10.5,16.43l-2.7-2.71c-0.14-0.14-0.33-0.22-0.53-0.22H6c-1.42,0-1.51-0.99-1.5-1.54 C4.47,10.73,5.29,10.5,6,10.5h1.26c0.2,0,0.39-0.08,0.53-0.22l2.7-2.71V16.43z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_apps.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_apps.xml
index fd71322eb8b0..8db613dfa5d2 100644
--- a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_apps.xml
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_apps.xml
@@ -20,30 +20,30 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M6.5,4h-1C4.67,4,4,4.67,4,5.5v1C4,7.33,4.67,8,5.5,8h1C7.33,8,8,7.33,8,6.5v-1C8,4.67,7.33,4,6.5,4z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M12.5,4h-1C10.67,4,10,4.67,10,5.5v1C10,7.33,10.67,8,11.5,8h1C13.33,8,14,7.33,14,6.5v-1C14,4.67,13.33,4,12.5,4z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M18.5,4h-1C16.67,4,16,4.67,16,5.5v1C16,7.33,16.67,8,17.5,8h1C19.33,8,20,7.33,20,6.5v-1C20,4.67,19.33,4,18.5,4z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M6.5,10h-1C4.67,10,4,10.67,4,11.5v1C4,13.33,4.67,14,5.5,14h1C7.33,14,8,13.33,8,12.5v-1C8,10.67,7.33,10,6.5,10z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M12.5,10h-1c-0.83,0-1.5,0.67-1.5,1.5v1c0,0.83,0.67,1.5,1.5,1.5h1c0.83,0,1.5-0.67,1.5-1.5v-1C14,10.67,13.33,10,12.5,10 z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M18.5,10h-1c-0.83,0-1.5,0.67-1.5,1.5v1c0,0.83,0.67,1.5,1.5,1.5h1c0.83,0,1.5-0.67,1.5-1.5v-1C20,10.67,19.33,10,18.5,10 z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M6.5,16h-1C4.67,16,4,16.67,4,17.5v1C4,19.33,4.67,20,5.5,20h1C7.33,20,8,19.33,8,18.5v-1C8,16.67,7.33,16,6.5,16z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M12.5,16h-1c-0.83,0-1.5,0.67-1.5,1.5v1c0,0.83,0.67,1.5,1.5,1.5h1c0.83,0,1.5-0.67,1.5-1.5v-1C14,16.67,13.33,16,12.5,16 z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M18.5,16h-1c-0.83,0-1.5,0.67-1.5,1.5v1c0,0.83,0.67,1.5,1.5,1.5h1c0.83,0,1.5-0.67,1.5-1.5v-1C20,16.67,19.33,16,18.5,16 z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_devices_other.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_devices_other.xml
index 463525d4bf26..1b4ec92e3734 100644
--- a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_devices_other.xml
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_devices_other.xml
@@ -21,12 +21,12 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M5.25,18H3.5V5.5h17.75C21.66,5.5,22,5.16,22,4.75S21.66,4,21.25,4H3.5C2.67,4,2,4.67,2,5.5V18c0,0.83,0.67,1.5,1.5,1.5 h1.75C5.66,19.5,6,19.16,6,18.75S5.66,18,5.25,18z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M10.5,14.5C9.12,14.5,8,15.62,8,17s1.12,2.5,2.5,2.5S13,18.38,13,17S11.88,14.5,10.5,14.5z M10.5,18c-0.55,0-1-0.45-1-1 s0.45-1,1-1s1,0.45,1,1S11.05,18,10.5,18z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M20.5,8.5h-4C15.67,8.5,15,9.17,15,10v8c0,0.83,0.67,1.5,1.5,1.5h4c0.83,0,1.5-0.67,1.5-1.5v-8 C22,9.17,21.33,8.5,20.5,8.5z M20.5,18h-4v-8h4V18z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_help.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_help.xml
index fce8140fa3b0..d062e65dedd5 100644
--- a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_help.xml
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_help.xml
@@ -20,12 +20,12 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M12,22c0,0,0.01,0,0.01,0c5.5,0,9.98-4.47,9.99-9.98V12c0-5.51-4.49-10-10-10S2,6.49,2,12S6.49,22,12,22z M12,3.5 c4.69,0,8.5,3.81,8.5,8.5v0.02c0,4.68-3.81,8.48-8.49,8.48c0,0-0.01,0-0.01,0c-4.69,0-8.5-3.81-8.5-8.5S7.31,3.5,12,3.5z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M8.67,9.98c0.4,0.1,0.81-0.15,0.9-0.56c0.11-0.47,0.33-0.86,0.65-1.19c0.94-0.94,2.59-0.94,3.54,0 c0.49,0.49,0.73,1.13,0.67,1.76c-0.06,0.57-0.36,1.06-0.84,1.38c-0.13,0.08-0.26,0.16-0.4,0.24c-0.7,0.4-1.67,0.94-1.93,2.51 c-0.07,0.41,0.21,0.8,0.61,0.86C11.92,15,11.96,15,12,15c0.36,0,0.68-0.26,0.74-0.62c0.15-0.87,0.58-1.12,1.19-1.46 c0.17-0.09,0.33-0.19,0.49-0.29c0.87-0.58,1.41-1.46,1.51-2.48c0.11-1.08-0.29-2.17-1.1-2.97c-1.51-1.51-4.15-1.51-5.66,0 c-0.52,0.51-0.88,1.17-1.05,1.9C8.02,9.48,8.27,9.88,8.67,9.98z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 12 16 C 12.5522847498 16 13 16.4477152502 13 17 C 13 17.5522847498 12.5522847498 18 12 18 C 11.4477152502 18 11 17.5522847498 11 17 C 11 16.4477152502 11.4477152502 16 12 16 Z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_phone_info.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_phone_info.xml
index 0983f9f4a8aa..f41f7a05e68f 100644
--- a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_phone_info.xml
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_phone_info.xml
@@ -20,12 +20,12 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M8,1C6.34,1,5,2.34,5,4v16c0,1.66,1.34,3,3,3h8c1.66,0,3-1.34,3-3V4c0-1.66-1.34-3-3-3H8z M16,21.5H8 c-0.83,0-1.5-0.67-1.5-1.5h11C17.5,20.83,16.83,21.5,16,21.5z M17.5,18.5h-11v-13h11V18.5z M17.5,4h-11c0-0.83,0.67-1.5,1.5-1.5h8 C16.83,2.5,17.5,3.17,17.5,4z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M12,10.5c-0.41,0-0.75,0.34-0.75,0.75v5c0,0.41,0.34,0.75,0.75,0.75s0.75-0.34,0.75-0.75v-5 C12.75,10.84,12.41,10.5,12,10.5z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 12 7 C 12.5522847498 7 13 7.44771525017 13 8 C 13 8.55228474983 12.5522847498 9 12 9 C 11.4477152502 9 11 8.55228474983 11 8 C 11 7.44771525017 11.4477152502 7 12 7 Z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_accessibility.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_accessibility.xml
index bfffc307c9b6..f17b5b93d84f 100644
--- a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_accessibility.xml
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_accessibility.xml
@@ -20,18 +20,18 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 12 0.5 C 13.1045694997 0.5 14 1.39543050034 14 2.5 C 14 3.60456949966 13.1045694997 4.5 12 4.5 C 10.8954305003 4.5 10 3.60456949966 10 2.5 C 10 1.39543050034 10.8954305003 0.5 12 0.5 Z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M20.72,5.28c-0.12-0.4-0.54-0.62-0.94-0.5C19.75,4.79,16.55,5.75,12,5.75c-4.53,0-7.75-0.96-7.78-0.97 c-0.39-0.12-0.81,0.1-0.94,0.5c-0.12,0.4,0.1,0.81,0.5,0.94C3.89,6.25,5.89,6.85,9,7.12v12.13C9,19.66,9.34,20,9.75,20 s0.75-0.34,0.75-0.75V14h3v5.25c0,0.41,0.34,0.75,0.75,0.75S15,19.66,15,19.25V7.12c3.11-0.27,5.11-0.87,5.22-0.9 C20.61,6.1,20.84,5.68,20.72,5.28z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 8 22 C 8.55228474983 22 9 22.4477152502 9 23 C 9 23.5522847498 8.55228474983 24 8 24 C 7.44771525017 24 7 23.5522847498 7 23 C 7 22.4477152502 7.44771525017 22 8 22 Z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 12 22 C 12.5522847498 22 13 22.4477152502 13 23 C 13 23.5522847498 12.5522847498 24 12 24 C 11.4477152502 24 11 23.5522847498 11 23 C 11 22.4477152502 11.4477152502 22 12 22 Z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 16 22 C 16.5522847498 22 17 22.4477152502 17 23 C 17 23.5522847498 16.5522847498 24 16 24 C 15.4477152502 24 15 23.5522847498 15 23 C 15 22.4477152502 15.4477152502 22 16 22 Z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_accounts.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_accounts.xml
index f213bc4ddc91..f02da58550ba 100644
--- a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_accounts.xml
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_accounts.xml
@@ -20,9 +20,9 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M12,6c-1.65,0-3,1.35-3,3v0.01C9,10.66,10.35,12,11.99,12c0,0,0,0,0.01,0c1.65,0,3-1.35,3-3S13.65,6,12,6z M12,10.5 C12,10.5,12,10.5,12,10.5c-0.83,0-1.5-0.67-1.5-1.49V9c0-0.83,0.67-1.5,1.5-1.5s1.5,0.67,1.5,1.5S12.83,10.5,12,10.5z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M19.25,3.25H4.75c-0.83,0-1.5,0.67-1.5,1.5v14.5c0,0.83,0.67,1.5,1.5,1.5h14.5c0.83,0,1.5-0.67,1.5-1.5V4.75 C20.75,3.92,20.08,3.25,19.25,3.25z M16.5,19.25h-9v-3.5C7.5,15.34,7.84,15,8.25,15h7.5c0.41,0,0.75,0.34,0.75,0.75V19.25z M19.25,19.25H18v-3.5c0-1.24-1.01-2.25-2.25-2.25h-7.5C7.01,13.5,6,14.51,6,15.75v3.5H4.75V4.75h14.5L19.25,19.25z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_battery_white.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_battery_white.xml
index 4ed698cfb164..e27cb8d54838 100644
--- a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_battery_white.xml
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_battery_white.xml
@@ -20,6 +20,6 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M13,2.49h-2c-0.55,0-1,0.45-1,1V4H7C6.45,4,6,4.45,6,5v16c0,0.55,0.45,1,1,1h10c0.55,0,1-0.45,1-1V5c0-0.55-0.45-1-1-1h-3 V3.49C14,2.94,13.55,2.49,13,2.49z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_display_white.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_display_white.xml
index 2e662684438f..19acd6ae8210 100644
--- a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_display_white.xml
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_display_white.xml
@@ -20,9 +20,9 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M21.25,11.25h-2.8A6.46,6.46,0,0,0,17.09,8l2-2A0.75 0.75 ,0,0,0,18,4.93l-2,2a6.46,6.46,0,0,0-3.28-1.36V2.75a0.75 0.75 ,0,0,0-1.5,0v2.8A6.46,6.46,0,0,0,8,6.91l-2-2A0.75 0.75 ,0,0,0,4.93,6l2,2a6.46,6.46,0,0,0-1.36,3.28H2.75a0.75 0.75 ,0,0,0,0,1.5h2.8A6.46,6.46,0,0,0,6.91,16l-2,2A0.75 0.75 ,0,0,0,6,19.07l2-2a6.46,6.46,0,0,0,3.28,1.36v2.8a0.75 0.75 ,0,0,0,1.5,0v-2.8A6.46,6.46,0,0,0,16,17.09l2,2A0.75 0.75 ,0,0,0,19.07,18l-2-2a6.46,6.46,0,0,0,1.36-3.28h2.8a0.75 0.75 ,0,0,0,0-1.5ZM12,17a5,5,0,1,1,5-5A5,5,0,0,1,12,17Z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M12,15.5a3.5,3.5,0,0,0,0-7Z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_location.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_location.xml
index a00c85f3d1f1..762d67d7fa87 100644
--- a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_location.xml
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_location.xml
@@ -20,9 +20,9 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M12,7c-1.66,0-3,1.34-3,3s1.34,3,3,3s3-1.34,3-3S13.66,7,12,7z M12,11.5c-0.83,0-1.5-0.67-1.5-1.5s0.67-1.5,1.5-1.5 s1.5,0.67,1.5,1.5S12.83,11.5,12,11.5z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M12,2.01c-4.5,0-8,3.49-8,8c0,5.49,5.48,10.24,7.37,11.76c0.19,0.15,0.41,0.22,0.63,0.22c0.22,0,0.44-0.07,0.62-0.22 C14.5,20.26,20,15.5,20,10C20,5.72,16.5,2.01,12,2.01z M12,20.34c-2.18-1.8-6.5-5.94-6.5-10.34c0-3.64,2.86-6.5,6.5-6.5 c3.58,0,6.5,2.91,6.5,6.49C18.5,14.4,14.19,18.53,12,20.34z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_privacy.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_privacy.xml
index 69cd1a4aa018..12a82f2c1f0b 100644
--- a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_privacy.xml
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_privacy.xml
@@ -20,12 +20,12 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M12,8c-2.21,0-4,1.79-4,4s1.79,4,4,4c0.76,0,1.46-0.22,2.06-0.59c0.16-1.38,0.88-2.59,1.94-3.39c0-0.01,0-0.02,0-0.02 C16,9.79,14.21,8,12,8z M12,14.5c-1.38,0-2.5-1.12-2.5-2.5s1.12-2.5,2.5-2.5c1.38,0,2.5,1.12,2.5,2.5S13.38,14.5,12,14.5z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M3.09,12C3.73,10.81,6.43,6.5,12,6.5c4.53,0,7.14,2.79,8.31,4.5h1.77C21.1,9.28,18.05,5,12,5c-7.4,0-10.32,6.42-10.44,6.7 c-0.09,0.19-0.09,0.41,0,0.61C1.68,12.58,4.61,19,12,19c0.71,0,1.37-0.07,2-0.18V17.3c-0.62,0.13-1.28,0.2-2,0.2 C6.39,17.5,3.73,13.21,3.09,12z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M21,16c0-0.35,0-0.72,0-1c0-1.1-0.9-2-2-2c-1.1,0-2,0.9-2,2c0,0.37,0,0.7,0,1c-0.55,0-1,0.45-1,1v3c0,0.55,0.45,1,1,1h4 c0.55,0,1-0.45,1-1v-3C22,16.45,21.55,16,21,16z M20,16h-2v-1c0-0.55,0.45-1,1-1s1,0.45,1,1V16z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_security_white.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_security_white.xml
index c49959628470..e93e63f5f603 100644
--- a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_security_white.xml
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_security_white.xml
@@ -20,9 +20,9 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 12 13.5 C 12.8284271247 13.5 13.5 14.1715728753 13.5 15 C 13.5 15.8284271247 12.8284271247 16.5 12 16.5 C 11.1715728753 16.5 10.5 15.8284271247 10.5 15 C 10.5 14.1715728753 11.1715728753 13.5 12 13.5 Z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M18.51,1.46c-2.19-0.06-3.98,1.61-4.01,3.68V8.5h-9C4.67,8.5,4,9.17,4,10v10c0,0.83,0.67,1.5,1.5,1.5h13 c0.83,0,1.5-0.67,1.5-1.5V10c0-0.83-0.67-1.5-1.5-1.5H16V5.15c0.02-1.23,1.14-2.23,2.51-2.19C19.9,2.93,20.98,3.92,21,5.15 c0.01,0.41,0.36,0.71,0.76,0.74c0.41-0.01,0.74-0.35,0.74-0.76C22.46,3.07,20.7,1.39,18.51,1.46z M18.5,10v10h-13V10H18.5z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml
index fe9c57870035..9ec3ffcc47fa 100644
--- a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml
@@ -20,12 +20,12 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M4.92,4.94c-3.9,3.91-3.9,10.24,0.01,14.14s10.24,3.9,14.14-0.01C20.95,17.2,22,14.65,22,12c0-2.65-1.06-5.19-2.93-7.07 C15.16,1.03,8.83,1.03,4.92,4.94z M18,18c-1.6,1.59-3.76,2.48-6.02,2.48c-4.69-0.01-8.49-3.83-8.48-8.52 c0.01-4.69,3.83-8.49,8.52-8.48c4.69,0.01,8.49,3.83,8.48,8.52C20.49,14.25,19.6,16.41,18,18z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 12 7 C 12.5522847498 7 13 7.44771525017 13 8 C 13 8.55228474983 12.5522847498 9 12 9 C 11.4477152502 9 11 8.55228474983 11 8 C 11 7.44771525017 11.4477152502 7 12 7 Z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M12,10.5c-0.41,0-0.75,0.34-0.75,0.75v5c0,0.41,0.34,0.75,0.75,0.75s0.75-0.34,0.75-0.75v-5 C12.75,10.84,12.41,10.5,12,10.5z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_wireless.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_wireless.xml
index be66878909a2..c8c8abc0b6ac 100644
--- a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_wireless.xml
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_wireless.xml
@@ -21,15 +21,15 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M12,2.75C7.95,2.69,4.05,4.3,1.22,7.2C0.96,7.5,0.97,7.95,1.24,8.23C1.53,8.53,2,8.54,2.3,8.25c2.55-2.61,6.05-4.06,9.7-4 c3.65-0.06,7.17,1.4,9.72,4.02c0.28,0.27,0.73,0.28,1.03,0.01c0.31-0.28,0.33-0.75,0.05-1.06C19.96,4.32,16.06,2.69,12,2.75z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M15.78,14.82c0.05,0.06,0.1,0.11,0.17,0.15c0.34,0.23,0.81,0.14,1.04-0.21s0.14-0.81-0.21-1.04 c-2.64-2.64-6.91-2.64-9.55,0c-0.27,0.29-0.27,0.73,0,1.02c0.28,0.3,0.76,0.32,1.06,0.04h0.03c0,0,0,0,0.01-0.01 c2.05-2.05,5.37-2.04,7.42,0.01C15.75,14.8,15.76,14.81,15.78,14.82z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 12 17 C 12.8284271247 17 13.5 17.6715728753 13.5 18.5 C 13.5 19.3284271247 12.8284271247 20 12 20 C 11.1715728753 20 10.5 19.3284271247 10.5 18.5 C 10.5 17.6715728753 11.1715728753 17 12 17 Z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M20.03,11.79c0.3-0.29,0.3-0.77,0.01-1.06h-0.01c-2.12-2.18-5.01-3.44-8.04-3.5c-3.04,0.06-5.93,1.32-8.05,3.5 c-0.29,0.3-0.28,0.77,0.01,1.06c0.3,0.29,0.77,0.28,1.06-0.01c1.85-1.88,4.36-2.96,7-3c2.62,0.05,5.11,1.13,6.95,3 C19.25,12.07,19.73,12.08,20.03,11.79z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_storage_white.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_storage_white.xml
index e80df4f20c82..355ee3b920ee 100644
--- a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_storage_white.xml
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_storage_white.xml
@@ -20,21 +20,21 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M20.25,2H3.75C3.34,2,3,2.34,3,2.75v4.5C3,7.66,3.34,8,3.75,8h16.5C20.66,8,21,7.66,21,7.25v-4.5C21,2.34,20.66,2,20.25,2 z M19.5,6.5h-15v-3h15V6.5z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 5.25 4.25 H 6.75 V 5.75 H 5.25 V 4.25 Z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M20.25,9H3.75C3.34,9,3,9.34,3,9.75v4.5C3,14.66,3.34,15,3.75,15h16.5c0.41,0,0.75-0.34,0.75-0.75v-4.5 C21,9.34,20.66,9,20.25,9z M19.5,13.5h-15v-3h15V13.5z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 5.25 11.25 H 6.75 V 12.75 H 5.25 V 11.25 Z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M20.25,16H3.75C3.34,16,3,16.34,3,16.75v4.5C3,21.66,3.34,22,3.75,22h16.5c0.41,0,0.75-0.34,0.75-0.75v-4.5 C21,16.34,20.66,16,20.25,16z M19.5,20.5h-15v-3h15V20.5z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M 5.25 18.25 H 6.75 V 19.75 H 5.25 V 18.25 Z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_volume_up_24dp.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_volume_up_24dp.xml
index 9370151c4255..25f81b71256a 100644
--- a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_volume_up_24dp.xml
+++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_volume_up_24dp.xml
@@ -20,12 +20,12 @@
android:viewportWidth="24"
android:width="24dp" >
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M5.69,16l5.03,5.03c0.14,0.14,0.34,0.22,0.53,0.22c0.1,0,0.19-0.02,0.29-0.06C11.82,21.08,12,20.8,12,20.5v-17 c0-0.3-0.18-0.58-0.46-0.69c-0.28-0.11-0.6-0.05-0.82,0.16L5.69,8H3.49C2.68,8.01,2.01,8.68,2,9.5v5C2,15.33,2.67,16,3.5,16H5.69z M3.5,9.5H6c0.2,0,0.39-0.08,0.53-0.22l3.97-3.97v13.38l-3.97-3.97C6.39,14.58,6.2,14.5,6,14.5H3.5V9.5z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M13.52,20.64c0.09,0.34,0.39,0.56,0.72,0.56c0.06,0,0.13-0.01,0.19-0.02c3.29-0.87,5.88-3.46,6.75-6.75 c1.34-5.06-1.69-10.26-6.75-11.59c-0.4-0.11-0.81,0.13-0.92,0.53c-0.11,0.4,0.13,0.81,0.53,0.92c4.26,1.13,6.8,5.5,5.68,9.76 c-0.73,2.77-2.91,4.95-5.68,5.68C13.66,19.83,13.42,20.24,13.52,20.64z" />
<path
- android:fillColor="@android:color/white"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M13.85,14.96c-0.35,0.22-0.46,0.68-0.24,1.03c0.14,0.23,0.39,0.35,0.64,0.35c0.14,0,0.27-0.04,0.4-0.11 c1.13-0.7,1.92-1.81,2.22-3.1c0.3-1.3,0.08-2.64-0.62-3.77c-0.4-0.65-0.96-1.2-1.6-1.6C14.29,7.54,13.83,7.65,13.61,8 c-0.22,0.35-0.11,0.81,0.24,1.03c0.45,0.28,0.84,0.67,1.12,1.12c0.49,0.79,0.65,1.73,0.44,2.64C15.2,13.7,14.65,14.47,13.85,14.96z" />
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_apps.xml b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_apps.xml
index ff34995e9e8b..69835c0b867a 100644
--- a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_apps.xml
+++ b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_apps.xml
@@ -14,13 +14,13 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M 6 10 C 7.10456949966 10 8 10.8954305003 8 12 C 8 13.1045694997 7.10456949966 14 6 14 C 4.89543050034 14 4 13.1045694997 4 12 C 4 10.8954305003 4.89543050034 10 6 10 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 6 4 C 7.10456949966 4 8 4.89543050034 8 6 C 8 7.10456949966 7.10456949966 8 6 8 C 4.89543050034 8 4 7.10456949966 4 6 C 4 4.89543050034 4.89543050034 4 6 4 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 18 4 C 19.1045694997 4 20 4.89543050034 20 6 C 20 7.10456949966 19.1045694997 8 18 8 C 16.8954305003 8 16 7.10456949966 16 6 C 16 4.89543050034 16.8954305003 4 18 4 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 6 16 C 7.10456949966 16 8 16.8954305003 8 18 C 8 19.1045694997 7.10456949966 20 6 20 C 4.89543050034 20 4 19.1045694997 4 18 C 4 16.8954305003 4.89543050034 16 6 16 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 12 16 C 13.1045694997 16 14 16.8954305003 14 18 C 14 19.1045694997 13.1045694997 20 12 20 C 10.8954305003 20 10 19.1045694997 10 18 C 10 16.8954305003 10.8954305003 16 12 16 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 18 10 C 19.1045694997 10 20 10.8954305003 20 12 C 20 13.1045694997 19.1045694997 14 18 14 C 16.8954305003 14 16 13.1045694997 16 12 C 16 10.8954305003 16.8954305003 10 18 10 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 18 16 C 19.1045694997 16 20 16.8954305003 20 18 C 20 19.1045694997 19.1045694997 20 18 20 C 16.8954305003 20 16 19.1045694997 16 18 C 16 16.8954305003 16.8954305003 16 18 16 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 12 10 C 13.1045694997 10 14 10.8954305003 14 12 C 14 13.1045694997 13.1045694997 14 12 14 C 10.8954305003 14 10 13.1045694997 10 12 C 10 10.8954305003 10.8954305003 10 12 10 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 12 4 C 13.1045694997 4 14 4.89543050034 14 6 C 14 7.10456949966 13.1045694997 8 12 8 C 10.8954305003 8 10 7.10456949966 10 6 C 10 4.89543050034 10.8954305003 4 12 4 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 6 10 C 7.10456949966 10 8 10.8954305003 8 12 C 8 13.1045694997 7.10456949966 14 6 14 C 4.89543050034 14 4 13.1045694997 4 12 C 4 10.8954305003 4.89543050034 10 6 10 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 6 4 C 7.10456949966 4 8 4.89543050034 8 6 C 8 7.10456949966 7.10456949966 8 6 8 C 4.89543050034 8 4 7.10456949966 4 6 C 4 4.89543050034 4.89543050034 4 6 4 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 18 4 C 19.1045694997 4 20 4.89543050034 20 6 C 20 7.10456949966 19.1045694997 8 18 8 C 16.8954305003 8 16 7.10456949966 16 6 C 16 4.89543050034 16.8954305003 4 18 4 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 6 16 C 7.10456949966 16 8 16.8954305003 8 18 C 8 19.1045694997 7.10456949966 20 6 20 C 4.89543050034 20 4 19.1045694997 4 18 C 4 16.8954305003 4.89543050034 16 6 16 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 16 C 13.1045694997 16 14 16.8954305003 14 18 C 14 19.1045694997 13.1045694997 20 12 20 C 10.8954305003 20 10 19.1045694997 10 18 C 10 16.8954305003 10.8954305003 16 12 16 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 18 10 C 19.1045694997 10 20 10.8954305003 20 12 C 20 13.1045694997 19.1045694997 14 18 14 C 16.8954305003 14 16 13.1045694997 16 12 C 16 10.8954305003 16.8954305003 10 18 10 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 18 16 C 19.1045694997 16 20 16.8954305003 20 18 C 20 19.1045694997 19.1045694997 20 18 20 C 16.8954305003 20 16 19.1045694997 16 18 C 16 16.8954305003 16.8954305003 16 18 16 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 10 C 13.1045694997 10 14 10.8954305003 14 12 C 14 13.1045694997 13.1045694997 14 12 14 C 10.8954305003 14 10 13.1045694997 10 12 C 10 10.8954305003 10.8954305003 10 12 10 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 4 C 13.1045694997 4 14 4.89543050034 14 6 C 14 7.10456949966 13.1045694997 8 12 8 C 10.8954305003 8 10 7.10456949966 10 6 C 10 4.89543050034 10.8954305003 4 12 4 Z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_devices_other.xml b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_devices_other.xml
index 733fe7ffaa98..c692eebb1adf 100644
--- a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_devices_other.xml
+++ b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_devices_other.xml
@@ -14,5 +14,5 @@
limitations under the License.
-->
<vector android:autoMirrored="true" android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M3,7c0-0.55,0.45-1,1-1h16c0.55,0,1-0.45,1-1v0c0-0.55-0.45-1-1-1H4C2.35,4,1,5.35,1,7v10c0,1.65,1.35,3,3,3h2 c0.55,0,1-0.45,1-1v0c0-0.55-0.45-1-1-1H4c-0.55,0-1-0.45-1-1V7z M12,12h-2c-0.55,0-1,0.45-1,1v0.78C8.39,14.33,8,15.11,8,16 c0,0.89,0.39,1.67,1,2.22V19c0,0.55,0.45,1,1,1h2c0.55,0,1-0.45,1-1v-0.78c0.61-0.55,1-1.34,1-2.22c0-0.88-0.39-1.67-1-2.22V13 C13,12.45,12.55,12,12,12z M11,17.5c-0.83,0-1.5-0.67-1.5-1.5s0.67-1.5,1.5-1.5s1.5,0.67,1.5,1.5S11.83,17.5,11,17.5 M17,8 c-1.1,0-2,0.9-2,2v8c0,1.1,0.9,2,2,2h4c1.1,0,2-0.9,2-2v-8c0-1.1-0.9-2-2-2H17z M21,18h-4v-8h4V18z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M3,7c0-0.55,0.45-1,1-1h16c0.55,0,1-0.45,1-1v0c0-0.55-0.45-1-1-1H4C2.35,4,1,5.35,1,7v10c0,1.65,1.35,3,3,3h2 c0.55,0,1-0.45,1-1v0c0-0.55-0.45-1-1-1H4c-0.55,0-1-0.45-1-1V7z M12,12h-2c-0.55,0-1,0.45-1,1v0.78C8.39,14.33,8,15.11,8,16 c0,0.89,0.39,1.67,1,2.22V19c0,0.55,0.45,1,1,1h2c0.55,0,1-0.45,1-1v-0.78c0.61-0.55,1-1.34,1-2.22c0-0.88-0.39-1.67-1-2.22V13 C13,12.45,12.55,12,12,12z M11,17.5c-0.83,0-1.5-0.67-1.5-1.5s0.67-1.5,1.5-1.5s1.5,0.67,1.5,1.5S11.83,17.5,11,17.5 M17,8 c-1.1,0-2,0.9-2,2v8c0,1.1,0.9,2,2,2h4c1.1,0,2-0.9,2-2v-8c0-1.1-0.9-2-2-2H17z M21,18h-4v-8h4V18z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_help.xml b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_help.xml
index 4650a72605ea..5a8185a3b173 100644
--- a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_help.xml
+++ b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_help.xml
@@ -14,5 +14,5 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M12,2C6.48,2,2,6.48,2,12c0,5.52,4.48,10,10,10s10-4.48,10-10C22,6.48,17.52,2,12,2z M12,18c-0.55,0-1-0.45-1-1 c0-0.55,0.45-1,1-1s1,0.45,1,1C13,17.55,12.55,18,12,18z M13.1,14.32C12.98,14.71,12.65,15,12.24,15h-0.15 c-0.62,0-1.11-0.6-0.93-1.2c0.61-1.97,2.71-2.08,2.83-3.66c0.07-0.97-0.62-1.9-1.57-2.1c-1.04-0.22-1.98,0.39-2.3,1.28 C9.98,9.71,9.65,10,9.24,10H9.07C8.45,10,8,9.4,8.18,8.81C8.68,7.18,10.2,6,12,6c2.21,0,4,1.79,4,4 C16,12.22,13.63,12.67,13.1,14.32z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,2C6.48,2,2,6.48,2,12c0,5.52,4.48,10,10,10s10-4.48,10-10C22,6.48,17.52,2,12,2z M12,18c-0.55,0-1-0.45-1-1 c0-0.55,0.45-1,1-1s1,0.45,1,1C13,17.55,12.55,18,12,18z M13.1,14.32C12.98,14.71,12.65,15,12.24,15h-0.15 c-0.62,0-1.11-0.6-0.93-1.2c0.61-1.97,2.71-2.08,2.83-3.66c0.07-0.97-0.62-1.9-1.57-2.1c-1.04-0.22-1.98,0.39-2.3,1.28 C9.98,9.71,9.65,10,9.24,10H9.07C8.45,10,8,9.4,8.18,8.81C8.68,7.18,10.2,6,12,6c2.21,0,4,1.79,4,4 C16,12.22,13.63,12.67,13.1,14.32z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_phone_info.xml b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_phone_info.xml
index dac1deef3e20..54336d0ca1ff 100644
--- a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_phone_info.xml
+++ b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_phone_info.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M12,11L12,11c0.55,0,1,0.45,1,1v4c0,0.55-0.45,1-1,1c-0.55,0-1-0.45-1-1v-4C11,11.45,11.45,11,12,11"/>
- <path android:fillColor="@android:color/white" android:pathData="M16,1H8C6.34,1,5,2.34,5,4v16c0,1.65,1.35,3,3,3h8c1.65,0,3-1.35,3-3V4C19,2.34,17.66,1,16,1 M17,18H7V6h10V18z"/>
- <path android:fillColor="@android:color/white" android:pathData="M13,8c0,0.55-0.45,1-1,1c-0.55,0-1-0.45-1-1c0-0.55,0.45-1,1-1C12.55,7,13,7.45,13,8"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,11L12,11c0.55,0,1,0.45,1,1v4c0,0.55-0.45,1-1,1c-0.55,0-1-0.45-1-1v-4C11,11.45,11.45,11,12,11"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M16,1H8C6.34,1,5,2.34,5,4v16c0,1.65,1.35,3,3,3h8c1.65,0,3-1.35,3-3V4C19,2.34,17.66,1,16,1 M17,18H7V6h10V18z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M13,8c0,0.55-0.45,1-1,1c-0.55,0-1-0.45-1-1c0-0.55,0.45-1,1-1C12.55,7,13,7.45,13,8"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_accessibility.xml b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_accessibility.xml
index 3be42b3c88ab..4a4baf9121bb 100644
--- a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_accessibility.xml
+++ b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_accessibility.xml
@@ -14,9 +14,9 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M20.75,4.99c-0.14-0.55-0.69-0.87-1.24-0.75C17.13,4.77,14.48,5,12,5S6.87,4.77,4.49,4.24c-0.55-0.12-1.1,0.2-1.24,0.75 c-0.14,0.56,0.2,1.13,0.75,1.26C5.61,6.61,7.35,6.86,9,7v12c0,0.55,0.45,1,1,1s1-0.45,1-1v-4c0-0.55,0.45-1,1-1s1,0.45,1,1v4 c0,0.55,0.45,1,1,1s1-0.45,1-1V7c1.65-0.14,3.39-0.39,4.99-0.75C20.55,6.12,20.89,5.55,20.75,4.99z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 12 0 C 13.1045694997 0 14 0.895430500338 14 2 C 14 3.10456949966 13.1045694997 4 12 4 C 10.8954305003 4 10 3.10456949966 10 2 C 10 0.895430500338 10.8954305003 0 12 0 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 8 22 C 8.55228474983 22 9 22.4477152502 9 23 C 9 23.5522847498 8.55228474983 24 8 24 C 7.44771525017 24 7 23.5522847498 7 23 C 7 22.4477152502 7.44771525017 22 8 22 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 12 22 C 12.5522847498 22 13 22.4477152502 13 23 C 13 23.5522847498 12.5522847498 24 12 24 C 11.4477152502 24 11 23.5522847498 11 23 C 11 22.4477152502 11.4477152502 22 12 22 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 16 22 C 16.5522847498 22 17 22.4477152502 17 23 C 17 23.5522847498 16.5522847498 24 16 24 C 15.4477152502 24 15 23.5522847498 15 23 C 15 22.4477152502 15.4477152502 22 16 22 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M20.75,4.99c-0.14-0.55-0.69-0.87-1.24-0.75C17.13,4.77,14.48,5,12,5S6.87,4.77,4.49,4.24c-0.55-0.12-1.1,0.2-1.24,0.75 c-0.14,0.56,0.2,1.13,0.75,1.26C5.61,6.61,7.35,6.86,9,7v12c0,0.55,0.45,1,1,1s1-0.45,1-1v-4c0-0.55,0.45-1,1-1s1,0.45,1,1v4 c0,0.55,0.45,1,1,1s1-0.45,1-1V7c1.65-0.14,3.39-0.39,4.99-0.75C20.55,6.12,20.89,5.55,20.75,4.99z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 0 C 13.1045694997 0 14 0.895430500338 14 2 C 14 3.10456949966 13.1045694997 4 12 4 C 10.8954305003 4 10 3.10456949966 10 2 C 10 0.895430500338 10.8954305003 0 12 0 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 8 22 C 8.55228474983 22 9 22.4477152502 9 23 C 9 23.5522847498 8.55228474983 24 8 24 C 7.44771525017 24 7 23.5522847498 7 23 C 7 22.4477152502 7.44771525017 22 8 22 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 22 C 12.5522847498 22 13 22.4477152502 13 23 C 13 23.5522847498 12.5522847498 24 12 24 C 11.4477152502 24 11 23.5522847498 11 23 C 11 22.4477152502 11.4477152502 22 12 22 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 16 22 C 16.5522847498 22 17 22.4477152502 17 23 C 17 23.5522847498 16.5522847498 24 16 24 C 15.4477152502 24 15 23.5522847498 15 23 C 15 22.4477152502 15.4477152502 22 16 22 Z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_accounts.xml b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_accounts.xml
index a9bf036b284f..334b2b7bde48 100644
--- a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_accounts.xml
+++ b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_accounts.xml
@@ -14,6 +14,6 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M6,3C4.34,3,3,4.34,3,6v12c0,1.66,1.34,3,3,3h12c1.65,0,3-1.35,3-3V6c0-1.66-1.34-3-3-3H6z M19,6v9.79 C16.52,14.37,13.23,14,12,14s-4.52,0.37-7,1.79V6c0-0.55,0.45-1,1-1h12C18.55,5,19,5.45,19,6z"/>
- <path android:fillColor="@android:color/white" android:pathData="M12,13c1.94,0,3.5-1.56,3.5-3.5S13.94,6,12,6S8.5,7.56,8.5,9.5S10.06,13,12,13"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M6,3C4.34,3,3,4.34,3,6v12c0,1.66,1.34,3,3,3h12c1.65,0,3-1.35,3-3V6c0-1.66-1.34-3-3-3H6z M19,6v9.79 C16.52,14.37,13.23,14,12,14s-4.52,0.37-7,1.79V6c0-0.55,0.45-1,1-1h12C18.55,5,19,5.45,19,6z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,13c1.94,0,3.5-1.56,3.5-3.5S13.94,6,12,6S8.5,7.56,8.5,9.5S10.06,13,12,13"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_battery_white.xml b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_battery_white.xml
index d616ad6d0886..fff56cede23f 100644
--- a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_battery_white.xml
+++ b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_battery_white.xml
@@ -14,5 +14,5 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M15,4h-1c0-1.1-0.9-2-2-2s-2,0.9-2,2H9C7.35,4,6,5.35,6,7v12c0,1.65,1.35,3,3,3h6c1.65,0,3-1.35,3-3V7 C18,5.35,16.65,4,15,4z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M15,4h-1c0-1.1-0.9-2-2-2s-2,0.9-2,2H9C7.35,4,6,5.35,6,7v12c0,1.65,1.35,3,3,3h6c1.65,0,3-1.35,3-3V7 C18,5.35,16.65,4,15,4z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_display_white.xml b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_display_white.xml
index cf4af6f1e1fd..723c36c669ea 100644
--- a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_display_white.xml
+++ b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_display_white.xml
@@ -14,6 +14,6 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M13.25,7.16C12.62,6.99,12,7.48,12,8.13v7.74c0,0.65,0.62,1.14,1.25,0.97C15.41,16.29,17,14.33,17,12 S15.41,7.71,13.25,7.16z"/>
- <path android:fillColor="@android:color/white" android:pathData="M21.19,9.88l-0.9-0.9C20.1,8.8,20,8.54,20,8.28V7c0-1.66-1.34-3-3-3h-1.28c-0.27,0-0.52-0.11-0.71-0.29l-0.9-0.9 c-1.17-1.17-3.07-1.17-4.24,0l-0.9,0.9C8.79,3.89,8.54,4,8.28,4H7C5.34,4,4,5.34,4,7v1.28C4,8.54,3.89,8.8,3.71,8.98l-0.9,0.9 c-1.17,1.17-1.17,3.07,0,4.24l0.9,0.9C3.89,15.2,4,15.46,4,15.72V17c0,1.66,1.34,3,3,3h1.28c0.27,0,0.52,0.11,0.71,0.29l0.9,0.9 c1.17,1.17,3.07,1.17,4.24,0l0.9-0.9C15.2,20.11,15.46,20,15.72,20H17c1.66,0,3-1.34,3-3v-1.28c0-0.27,0.11-0.52,0.29-0.71 l0.9-0.9C22.36,12.95,22.36,11.05,21.19,9.88z M19.77,12.71l-1.48,1.48C18.11,14.37,18,14.63,18,14.89V17c0,0.55-0.45,1-1,1h-2.11 c-0.27,0-0.52,0.11-0.71,0.29l-1.48,1.48c-0.39,0.39-1.02,0.39-1.41,0l-1.48-1.48C9.63,18.1,9.37,18,9.11,18H7c-0.55,0-1-0.45-1-1 v-2.1c0-0.27-0.11-0.52-0.29-0.71l-1.48-1.48c-0.39-0.39-0.39-1.02,0-1.41l1.48-1.48C5.9,9.63,6,9.37,6,9.11V7c0-0.55,0.45-1,1-1 h2.11c0.27,0,0.52-0.11,0.71-0.29l1.48-1.48c0.39-0.39,1.02-0.39,1.41,0l1.48,1.48C14.38,5.89,14.63,6,14.89,6H17 c0.55,0,1,0.45,1,1v2.11c0,0.27,0.11,0.52,0.29,0.71l1.48,1.48C20.16,11.68,20.16,12.32,19.77,12.71z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M13.25,7.16C12.62,6.99,12,7.48,12,8.13v7.74c0,0.65,0.62,1.14,1.25,0.97C15.41,16.29,17,14.33,17,12 S15.41,7.71,13.25,7.16z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M21.19,9.88l-0.9-0.9C20.1,8.8,20,8.54,20,8.28V7c0-1.66-1.34-3-3-3h-1.28c-0.27,0-0.52-0.11-0.71-0.29l-0.9-0.9 c-1.17-1.17-3.07-1.17-4.24,0l-0.9,0.9C8.79,3.89,8.54,4,8.28,4H7C5.34,4,4,5.34,4,7v1.28C4,8.54,3.89,8.8,3.71,8.98l-0.9,0.9 c-1.17,1.17-1.17,3.07,0,4.24l0.9,0.9C3.89,15.2,4,15.46,4,15.72V17c0,1.66,1.34,3,3,3h1.28c0.27,0,0.52,0.11,0.71,0.29l0.9,0.9 c1.17,1.17,3.07,1.17,4.24,0l0.9-0.9C15.2,20.11,15.46,20,15.72,20H17c1.66,0,3-1.34,3-3v-1.28c0-0.27,0.11-0.52,0.29-0.71 l0.9-0.9C22.36,12.95,22.36,11.05,21.19,9.88z M19.77,12.71l-1.48,1.48C18.11,14.37,18,14.63,18,14.89V17c0,0.55-0.45,1-1,1h-2.11 c-0.27,0-0.52,0.11-0.71,0.29l-1.48,1.48c-0.39,0.39-1.02,0.39-1.41,0l-1.48-1.48C9.63,18.1,9.37,18,9.11,18H7c-0.55,0-1-0.45-1-1 v-2.1c0-0.27-0.11-0.52-0.29-0.71l-1.48-1.48c-0.39-0.39-0.39-1.02,0-1.41l1.48-1.48C5.9,9.63,6,9.37,6,9.11V7c0-0.55,0.45-1,1-1 h2.11c0.27,0,0.52-0.11,0.71-0.29l1.48-1.48c0.39-0.39,1.02-0.39,1.41,0l1.48,1.48C14.38,5.89,14.63,6,14.89,6H17 c0.55,0,1,0.45,1,1v2.11c0,0.27,0.11,0.52,0.29,0.71l1.48,1.48C20.16,11.68,20.16,12.32,19.77,12.71z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_location.xml b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_location.xml
index a802bee9af5f..9230e3eb8a08 100644
--- a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_location.xml
+++ b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_location.xml
@@ -14,5 +14,5 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M12,3c-3.87,0-7,3.13-7,7c0,3.18,2.56,7.05,4.59,9.78c1.2,1.62,3.62,1.62,4.82,0C16.44,17.05,19,13.18,19,10 C19,6.13,15.87,3,12,3 M12,12.5c-1.38,0-2.5-1.12-2.5-2.5s1.12-2.5,2.5-2.5s2.5,1.12,2.5,2.5S13.38,12.5,12,12.5"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,3c-3.87,0-7,3.13-7,7c0,3.18,2.56,7.05,4.59,9.78c1.2,1.62,3.62,1.62,4.82,0C16.44,17.05,19,13.18,19,10 C19,6.13,15.87,3,12,3 M12,12.5c-1.38,0-2.5-1.12-2.5-2.5s1.12-2.5,2.5-2.5s2.5,1.12,2.5,2.5S13.38,12.5,12,12.5"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_privacy.xml b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_privacy.xml
index 10c059f9f592..4392c121c4ff 100644
--- a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_privacy.xml
+++ b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_privacy.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M14.14,15.43C13.5,15.78,12.78,16,12,16c-2.48,0-4.5-2.02-4.5-4.5C7.5,9.02,9.52,7,12,7c2.48,0,4.5,2.02,4.5,4.5 c0,0.37-0.06,0.72-0.14,1.07C17,12.22,17.72,12,18.5,12c1.38,0,2.59,0.63,3.42,1.6c0.15-0.23,0.29-0.46,0.42-0.69 c0.48-0.87,0.48-1.95,0-2.81C20.32,6.46,16.45,4,12,4C7.55,4,3.68,6.46,1.66,10.09c-0.48,0.87-0.48,1.95,0,2.81 C3.68,16.54,7.55,19,12,19c0.68,0,1.35-0.06,2-0.18V16.5C14,16.13,14.06,15.78,14.14,15.43z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 12 8.8 C 13.4911688245 8.8 14.7 10.0088311755 14.7 11.5 C 14.7 12.9911688245 13.4911688245 14.2 12 14.2 C 10.5088311755 14.2 9.3 12.9911688245 9.3 11.5 C 9.3 10.0088311755 10.5088311755 8.8 12 8.8 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M21,17v-1c0-1.1-0.9-2-2-2s-2,0.9-2,2v1c-0.55,0-1,0.45-1,1v3c0,0.55,0.45,1,1,1h4c0.55,0,1-0.45,1-1v-3 C22,17.45,21.55,17,21,17z M18.5,16c0-0.28,0.22-0.5,0.5-0.5s0.5,0.22,0.5,0.5v1h-1V16z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M14.14,15.43C13.5,15.78,12.78,16,12,16c-2.48,0-4.5-2.02-4.5-4.5C7.5,9.02,9.52,7,12,7c2.48,0,4.5,2.02,4.5,4.5 c0,0.37-0.06,0.72-0.14,1.07C17,12.22,17.72,12,18.5,12c1.38,0,2.59,0.63,3.42,1.6c0.15-0.23,0.29-0.46,0.42-0.69 c0.48-0.87,0.48-1.95,0-2.81C20.32,6.46,16.45,4,12,4C7.55,4,3.68,6.46,1.66,10.09c-0.48,0.87-0.48,1.95,0,2.81 C3.68,16.54,7.55,19,12,19c0.68,0,1.35-0.06,2-0.18V16.5C14,16.13,14.06,15.78,14.14,15.43z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 8.8 C 13.4911688245 8.8 14.7 10.0088311755 14.7 11.5 C 14.7 12.9911688245 13.4911688245 14.2 12 14.2 C 10.5088311755 14.2 9.3 12.9911688245 9.3 11.5 C 9.3 10.0088311755 10.5088311755 8.8 12 8.8 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M21,17v-1c0-1.1-0.9-2-2-2s-2,0.9-2,2v1c-0.55,0-1,0.45-1,1v3c0,0.55,0.45,1,1,1h4c0.55,0,1-0.45,1-1v-3 C22,17.45,21.55,17,21,17z M18.5,16c0-0.28,0.22-0.5,0.5-0.5s0.5,0.22,0.5,0.5v1h-1V16z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_security_white.xml b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_security_white.xml
index 8ca2dd6d2589..baa6a0a11700 100644
--- a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_security_white.xml
+++ b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_security_white.xml
@@ -14,5 +14,5 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M18.5,1C16.01,1,14,3.01,14,5.5V8H7c-1.65,0-3,1.35-3,3v8c0,1.65,1.35,3,3,3h10c1.65,0,3-1.35,3-3v-8c0-1.65-1.35-3-3-3h-1 V5.64c0-1.31,0.94-2.5,2.24-2.63c0.52-0.05,2.3,0.02,2.69,2.12C21.02,5.63,21.42,6,21.92,6c0.6,0,1.09-0.53,0.99-1.13 C22.47,2.3,20.55,1,18.5,1z M12,17c-1.1,0-2-0.9-2-2s0.9-2,2-2s2,0.9,2,2S13.1,17,12,17z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M18.5,1C16.01,1,14,3.01,14,5.5V8H7c-1.65,0-3,1.35-3,3v8c0,1.65,1.35,3,3,3h10c1.65,0,3-1.35,3-3v-8c0-1.65-1.35-3-3-3h-1 V5.64c0-1.31,0.94-2.5,2.24-2.63c0.52-0.05,2.3,0.02,2.69,2.12C21.02,5.63,21.42,6,21.92,6c0.6,0,1.09-0.53,0.99-1.13 C22.47,2.3,20.55,1,18.5,1z M12,17c-1.1,0-2-0.9-2-2s0.9-2,2-2s2,0.9,2,2S13.1,17,12,17z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml
index 437afcce6add..54aa6ce5c9ca 100644
--- a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml
+++ b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml
@@ -14,5 +14,5 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M12,2C6.48,2,2,6.48,2,12c0,5.52,4.48,10,10,10s10-4.48,10-10C22,6.48,17.52,2,12,2z M13,16c0,0.55-0.45,1-1,1s-1-0.45-1-1 v-4c0-0.55,0.45-1,1-1s1,0.45,1,1V16z M12,9c-0.55,0-1-0.45-1-1c0-0.55,0.45-1,1-1s1,0.45,1,1C13,8.55,12.55,9,12,9z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,2C6.48,2,2,6.48,2,12c0,5.52,4.48,10,10,10s10-4.48,10-10C22,6.48,17.52,2,12,2z M13,16c0,0.55-0.45,1-1,1s-1-0.45-1-1 v-4c0-0.55,0.45-1,1-1s1,0.45,1,1V16z M12,9c-0.55,0-1-0.45-1-1c0-0.55,0.45-1,1-1s1,0.45,1,1C13,8.55,12.55,9,12,9z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_wireless.xml b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_wireless.xml
index 145886a49ce2..c40f314e8c78 100644
--- a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_wireless.xml
+++ b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_wireless.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
<vector android:height="24dp" android:tint="?android:attr/colorControlNormal" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M17.79,11.97c-3.7-2.67-8.4-2.29-11.58,0c-0.69,0.5-0.73,1.51-0.13,2.11l0.01,0.01c0.49,0.49,1.26,0.54,1.83,0.13 c2.6-1.84,5.88-1.61,8.16,0c0.57,0.4,1.34,0.36,1.83-0.13l0.01-0.01C18.52,13.48,18.48,12.47,17.79,11.97z"/>
- <path android:fillColor="@android:color/white" android:pathData="M21.84,7.95c-5.71-4.68-13.97-4.67-19.69,0c-0.65,0.53-0.69,1.51-0.1,2.1c0.51,0.51,1.33,0.55,1.89,0.09 c3.45-2.83,10.36-4.72,16.11,0c0.56,0.46,1.38,0.42,1.89-0.09C22.54,9.46,22.49,8.48,21.84,7.95z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 12 15 C 13.1045694997 15 14 15.8954305003 14 17 C 14 18.1045694997 13.1045694997 19 12 19 C 10.8954305003 19 10 18.1045694997 10 17 C 10 15.8954305003 10.8954305003 15 12 15 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M17.79,11.97c-3.7-2.67-8.4-2.29-11.58,0c-0.69,0.5-0.73,1.51-0.13,2.11l0.01,0.01c0.49,0.49,1.26,0.54,1.83,0.13 c2.6-1.84,5.88-1.61,8.16,0c0.57,0.4,1.34,0.36,1.83-0.13l0.01-0.01C18.52,13.48,18.48,12.47,17.79,11.97z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M21.84,7.95c-5.71-4.68-13.97-4.67-19.69,0c-0.65,0.53-0.69,1.51-0.1,2.1c0.51,0.51,1.33,0.55,1.89,0.09 c3.45-2.83,10.36-4.72,16.11,0c0.56,0.46,1.38,0.42,1.89-0.09C22.54,9.46,22.49,8.48,21.84,7.95z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 15 C 13.1045694997 15 14 15.8954305003 14 17 C 14 18.1045694997 13.1045694997 19 12 19 C 10.8954305003 19 10 18.1045694997 10 17 C 10 15.8954305003 10.8954305003 15 12 15 Z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_storage_white.xml b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_storage_white.xml
index 3ed51506752d..2ae828d8be55 100644
--- a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_storage_white.xml
+++ b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_storage_white.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M19,16H5c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2C21,16.9,20.1,16,19,16z M6,19c-0.55,0-1-0.45-1-1 c0-0.55,0.45-1,1-1s1,0.45,1,1C7,18.55,6.55,19,6,19z"/>
- <path android:fillColor="@android:color/white" android:pathData="M5,8h14c1.1,0,2-0.9,2-2c0-1.1-0.9-2-2-2H5C3.9,4,3,4.9,3,6C3,7.1,3.9,8,5,8z M6,5c0.55,0,1,0.45,1,1c0,0.55-0.45,1-1,1 S5,6.55,5,6C5,5.45,5.45,5,6,5z"/>
- <path android:fillColor="@android:color/white" android:pathData="M19,10H5c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2C21,10.9,20.1,10,19,10z M6,13c-0.55,0-1-0.45-1-1 c0-0.55,0.45-1,1-1s1,0.45,1,1C7,12.55,6.55,13,6,13z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M19,16H5c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2C21,16.9,20.1,16,19,16z M6,19c-0.55,0-1-0.45-1-1 c0-0.55,0.45-1,1-1s1,0.45,1,1C7,18.55,6.55,19,6,19z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M5,8h14c1.1,0,2-0.9,2-2c0-1.1-0.9-2-2-2H5C3.9,4,3,4.9,3,6C3,7.1,3.9,8,5,8z M6,5c0.55,0,1,0.45,1,1c0,0.55-0.45,1-1,1 S5,6.55,5,6C5,5.45,5.45,5,6,5z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M19,10H5c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2C21,10.9,20.1,10,19,10z M6,13c-0.55,0-1-0.45-1-1 c0-0.55,0.45-1,1-1s1,0.45,1,1C7,12.55,6.55,13,6,13z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_volume_up_24dp.xml b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_volume_up_24dp.xml
index 0a9a4c79bb72..4fc2489d6541 100644
--- a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_volume_up_24dp.xml
+++ b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_volume_up_24dp.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M10.29,5.71L7,9H6c-1.66,0-3,1.34-3,3s1.34,3,3,3h1l3.29,3.29c0.63,0.63,1.71,0.18,1.71-0.71V6.41 C12,5.52,10.92,5.08,10.29,5.71z"/>
- <path android:fillColor="@android:color/white" android:pathData="M14.79,15.52c1.04-0.82,1.71-2.09,1.71-3.52c0-1.43-0.67-2.7-1.71-3.52C14.47,8.22,14,8.47,14,8.88v6.23 C14,15.52,14.47,15.77,14.79,15.52z"/>
- <path android:fillColor="@android:color/white" android:pathData="M15.36,3.65C14.71,3.39,14,3.89,14,4.59v0c0,0.41,0.25,0.77,0.62,0.92C17.19,6.55,19,9.06,19,12 c0,2.94-1.81,5.45-4.38,6.49C14.25,18.64,14,19.01,14,19.41v0c0,0.7,0.71,1.19,1.36,0.93C18.67,19.02,21,15.78,21,12 C21,8.22,18.67,4.98,15.36,3.65z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M10.29,5.71L7,9H6c-1.66,0-3,1.34-3,3s1.34,3,3,3h1l3.29,3.29c0.63,0.63,1.71,0.18,1.71-0.71V6.41 C12,5.52,10.92,5.08,10.29,5.71z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M14.79,15.52c1.04-0.82,1.71-2.09,1.71-3.52c0-1.43-0.67-2.7-1.71-3.52C14.47,8.22,14,8.47,14,8.88v6.23 C14,15.52,14.47,15.77,14.79,15.52z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M15.36,3.65C14.71,3.39,14,3.89,14,4.59v0c0,0.41,0.25,0.77,0.62,0.92C17.19,6.55,19,9.06,19,12 c0,2.94-1.81,5.45-4.38,6.49C14.25,18.64,14,19.01,14,19.41v0c0,0.7,0.71,1.19,1.36,0.93C18.67,19.02,21,15.78,21,12 C21,8.22,18.67,4.98,15.36,3.65z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_apps.xml b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_apps.xml
index ab58f2045f1a..d62b777592b7 100644
--- a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_apps.xml
+++ b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_apps.xml
@@ -14,13 +14,13 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M 4 4 H 8 V 8 H 4 V 4 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 4 10 H 8 V 14 H 4 V 10 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 4 16 H 8 V 20 H 4 V 16 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 10 4 H 14 V 8 H 10 V 4 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 10 10 H 14 V 14 H 10 V 10 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 10 16 H 14 V 20 H 10 V 16 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 16 4 H 20 V 8 H 16 V 4 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 16 10 H 20 V 14 H 16 V 10 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 16 16 H 20 V 20 H 16 V 16 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 4 4 H 8 V 8 H 4 V 4 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 4 10 H 8 V 14 H 4 V 10 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 4 16 H 8 V 20 H 4 V 16 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 10 4 H 14 V 8 H 10 V 4 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 10 10 H 14 V 14 H 10 V 10 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 10 16 H 14 V 20 H 10 V 16 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 16 4 H 20 V 8 H 16 V 4 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 16 10 H 20 V 14 H 16 V 10 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 16 16 H 20 V 20 H 16 V 16 Z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_devices_other.xml b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_devices_other.xml
index 7548dfac18e0..71fb7a3d5096 100644
--- a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_devices_other.xml
+++ b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_devices_other.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
<vector android:autoMirrored="true" android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M21.5,8h-5L15,9.5v9l1.5,1.5h5l1.5-1.5v-9L21.5,8z M21.5,18.5h-5v-9h5V18.5z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 2.5 5.5 L 21 5.5 L 21 4 L 2.5 4 L 1 5.5 L 1 18.5 L 2.5 20 L 7 20 L 7 18.5 L 2.5 18.5 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M13,12H9v1.78C8.39,14.33,8,15.11,8,16s0.39,1.67,1,2.22V20h4v-1.78c0.61-0.55,1-1.34,1-2.22s-0.39-1.67-1-2.22V12z M11,14.5c0.83,0,1.5,0.67,1.5,1.5s-0.67,1.5-1.5,1.5S9.5,16.83,9.5,16S10.17,14.5,11,14.5z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M21.5,8h-5L15,9.5v9l1.5,1.5h5l1.5-1.5v-9L21.5,8z M21.5,18.5h-5v-9h5V18.5z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 2.5 5.5 L 21 5.5 L 21 4 L 2.5 4 L 1 5.5 L 1 18.5 L 2.5 20 L 7 20 L 7 18.5 L 2.5 18.5 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M13,12H9v1.78C8.39,14.33,8,15.11,8,16s0.39,1.67,1,2.22V20h4v-1.78c0.61-0.55,1-1.34,1-2.22s-0.39-1.67-1-2.22V12z M11,14.5c0.83,0,1.5,0.67,1.5,1.5s-0.67,1.5-1.5,1.5S9.5,16.83,9.5,16S10.17,14.5,11,14.5z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_help.xml b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_help.xml
index eb2e966d2bcb..32c603dc7a26 100644
--- a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_help.xml
+++ b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_help.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M12,2C6.48,2,2,6.48,2,12s4.48,10,10,10s10-4.48,10-10S17.52,2,12,2z M12,20.5c-4.69,0-8.5-3.81-8.5-8.5S7.31,3.5,12,3.5 s8.5,3.81,8.5,8.5S16.69,20.5,12,20.5z"/>
- <path android:fillColor="@android:color/white" android:pathData="M12.48,6c-1.71,0-3.53,0.96-3.53,3.17v0.37c0,0.11,0.06,0.17,0.17,0.17l1.29,0.07c0.11,0,0.17-0.06,0.17-0.17V9.17 c0-1.29,1.07-1.7,1.85-1.7c0.74,0,1.81,0.37,1.81,1.66c0,1.48-1.57,1.85-2.54,3.09c-0.48,0.6-0.39,1.28-0.39,2.1 c0,0.09,0.08,0.17,0.17,0.17l1.3,0c0.11,0,0.17-0.06,0.17-0.17c0-0.72-0.07-1.13,0.28-1.54c0.91-1.05,2.65-1.46,2.65-3.7 C15.89,6.99,14.27,6,12.48,6z"/>
- <path android:fillColor="@android:color/white" android:pathData="M12.83,16h-1.66C11.08,16,11,16.08,11,16.17v1.66c0,0.09,0.08,0.17,0.17,0.17h1.66c0.09,0,0.17-0.08,0.17-0.17v-1.66 C13,16.08,12.92,16,12.83,16z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,2C6.48,2,2,6.48,2,12s4.48,10,10,10s10-4.48,10-10S17.52,2,12,2z M12,20.5c-4.69,0-8.5-3.81-8.5-8.5S7.31,3.5,12,3.5 s8.5,3.81,8.5,8.5S16.69,20.5,12,20.5z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12.48,6c-1.71,0-3.53,0.96-3.53,3.17v0.37c0,0.11,0.06,0.17,0.17,0.17l1.29,0.07c0.11,0,0.17-0.06,0.17-0.17V9.17 c0-1.29,1.07-1.7,1.85-1.7c0.74,0,1.81,0.37,1.81,1.66c0,1.48-1.57,1.85-2.54,3.09c-0.48,0.6-0.39,1.28-0.39,2.1 c0,0.09,0.08,0.17,0.17,0.17l1.3,0c0.11,0,0.17-0.06,0.17-0.17c0-0.72-0.07-1.13,0.28-1.54c0.91-1.05,2.65-1.46,2.65-3.7 C15.89,6.99,14.27,6,12.48,6z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12.83,16h-1.66C11.08,16,11,16.08,11,16.17v1.66c0,0.09,0.08,0.17,0.17,0.17h1.66c0.09,0,0.17-0.08,0.17-0.17v-1.66 C13,16.08,12.92,16,12.83,16z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_phone_info.xml b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_phone_info.xml
index 0484309adedb..1307f38494f3 100644
--- a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_phone_info.xml
+++ b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_phone_info.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M 11.25 11 H 12.75 V 16 H 11.25 V 11 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 11.25 8 H 12.75 V 9.5 H 11.25 V 8 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M17.59,1H6.41L5,2.41v19.17L6.41,23h11.17L19,21.59V2.41L17.59,1z M17.5,2.5v1.75h-11V2.5H17.5z M17.5,5.75v12.5h-11V5.75 H17.5z M6.5,21.5v-1.75h11v1.75H6.5z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 11.25 11 H 12.75 V 16 H 11.25 V 11 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 11.25 8 H 12.75 V 9.5 H 11.25 V 8 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M17.59,1H6.41L5,2.41v19.17L6.41,23h11.17L19,21.59V2.41L17.59,1z M17.5,2.5v1.75h-11V2.5H17.5z M17.5,5.75v12.5h-11V5.75 H17.5z M6.5,21.5v-1.75h11v1.75H6.5z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_accessibility.xml b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_accessibility.xml
index 7f80c7d18df8..5983b89e4dc8 100644
--- a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_accessibility.xml
+++ b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_accessibility.xml
@@ -14,6 +14,6 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M20.5,4c-2.61,0.7-5.67,1-8.5,1S6.11,4.7,3.5,4L3,6c1.86,0.5,4,0.83,6,1v13h2v-6h2v6h2V7c2-0.17,4.14-0.5,6-1L20.5,4z M12,4c1.1,0,2-0.9,2-2c0-1.1-0.9-2-2-2s-2,0.9-2,2C10,3.1,10.9,4,12,4"/>
- <path android:fillColor="@android:color/white" android:pathData="M7,24h2v-2H7V24z M11,24h2v-2h-2V24z M15,24h2v-2h-2V24z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M20.5,4c-2.61,0.7-5.67,1-8.5,1S6.11,4.7,3.5,4L3,6c1.86,0.5,4,0.83,6,1v13h2v-6h2v6h2V7c2-0.17,4.14-0.5,6-1L20.5,4z M12,4c1.1,0,2-0.9,2-2c0-1.1-0.9-2-2-2s-2,0.9-2,2C10,3.1,10.9,4,12,4"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M7,24h2v-2H7V24z M11,24h2v-2h-2V24z M15,24h2v-2h-2V24z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_accounts.xml b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_accounts.xml
index 758e63f7087a..3a997b085d40 100644
--- a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_accounts.xml
+++ b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_accounts.xml
@@ -14,6 +14,6 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M12,13c1.93,0,3.5-1.57,3.5-3.5S13.93,6,12,6S8.5,7.57,8.5,9.5S10.07,13,12,13z M12,7.5c1.1,0,2,0.9,2,2s-0.9,2-2,2 s-2-0.9-2-2S10.9,7.5,12,7.5z"/>
- <path android:fillColor="@android:color/white" android:pathData="M19.5,3h-15L3,4.5v15L4.5,21h15l1.5-1.5v-15L19.5,3z M19.5,19.5h-15v-1.65c1.76-1.53,5.37-2.35,7.5-2.35 c1.45,0,3.76,0.38,5.67,1.24c0.62,0.28,1.29,0.65,1.83,1.12V19.5z M19.5,16.02C17.26,14.64,14.04,14,12,14s-5.26,0.64-7.5,2.02 V4.5h15V16.02z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,13c1.93,0,3.5-1.57,3.5-3.5S13.93,6,12,6S8.5,7.57,8.5,9.5S10.07,13,12,13z M12,7.5c1.1,0,2,0.9,2,2s-0.9,2-2,2 s-2-0.9-2-2S10.9,7.5,12,7.5z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M19.5,3h-15L3,4.5v15L4.5,21h15l1.5-1.5v-15L19.5,3z M19.5,19.5h-15v-1.65c1.76-1.53,5.37-2.35,7.5-2.35 c1.45,0,3.76,0.38,5.67,1.24c0.62,0.28,1.29,0.65,1.83,1.12V19.5z M19.5,16.02C17.26,14.64,14.04,14,12,14s-5.26,0.64-7.5,2.02 V4.5h15V16.02z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_battery_white.xml b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_battery_white.xml
index e1b7945aec2f..ca9bfc9ad8da 100644
--- a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_battery_white.xml
+++ b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_battery_white.xml
@@ -14,5 +14,5 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M 16.59 4 L 15 4 L 14 2 L 10 2 L 9 4 L 7.41 4 L 6 5.41 L 6 20.59 L 7.41 22 L 16.59 22 L 18 20.59 L 18 5.41 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 16.59 4 L 15 4 L 14 2 L 10 2 L 9 4 L 7.41 4 L 6 5.41 L 6 20.59 L 7.41 22 L 16.59 22 L 18 20.59 L 18 5.41 Z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_display_white.xml b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_display_white.xml
index c7d8a617bb70..adf1c82b729d 100644
--- a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_display_white.xml
+++ b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_display_white.xml
@@ -14,6 +14,6 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M12,7v10c2.76,0,5-2.24,5-5S14.76,7,12,7z"/>
- <path android:fillColor="@android:color/white" android:pathData="M22.81,12.5v-1L20,8.69V4.71l0,0L19.29,4l0,0h-3.98L12.5,1.19h-1v0L8.69,4H4.71l0,0L4,4.71l0,0v3.98L1.19,11.5v1h0 L4,15.31v3.98l0,0L4.71,20l0,0h3.98l2.81,2.81v0h1L15.31,20h3.98l0,0L20,19.29l0,0v-3.98L22.81,12.5L22.81,12.5z M18.5,14.69v3.81 h-3.81L12,21.19L9.31,18.5H5.5v-3.81L2.81,12L5.5,9.31V5.5h3.81L12,2.81l2.69,2.69h3.81v3.81L21.19,12L18.5,14.69z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,7v10c2.76,0,5-2.24,5-5S14.76,7,12,7z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M22.81,12.5v-1L20,8.69V4.71l0,0L19.29,4l0,0h-3.98L12.5,1.19h-1v0L8.69,4H4.71l0,0L4,4.71l0,0v3.98L1.19,11.5v1h0 L4,15.31v3.98l0,0L4.71,20l0,0h3.98l2.81,2.81v0h1L15.31,20h3.98l0,0L20,19.29l0,0v-3.98L22.81,12.5L22.81,12.5z M18.5,14.69v3.81 h-3.81L12,21.19L9.31,18.5H5.5v-3.81L2.81,12L5.5,9.31V5.5h3.81L12,2.81l2.69,2.69h3.81v3.81L21.19,12L18.5,14.69z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_location.xml b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_location.xml
index 7975db696d2d..90ad165e8fda 100644
--- a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_location.xml
+++ b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_location.xml
@@ -14,6 +14,6 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M12,2c-4.2,0-8,3.22-8,8.2c0,3.11,2.33,6.62,7,10.8h2c4.67-4.18,7-7.7,7-10.8C20,5.22,16.2,2,12,2z M12.42,19.5h-0.84 c-4.04-3.7-6.08-6.74-6.08-9.3c0-4.35,3.35-6.7,6.5-6.7s6.5,2.35,6.5,6.7C18.5,12.76,16.46,15.8,12.42,19.5z"/>
- <path android:fillColor="@android:color/white" android:pathData="M12,7c-1.66,0-3,1.34-3,3c0,1.66,1.34,3,3,3s3-1.34,3-3C15,8.34,13.66,7,12,7z M12,11.5c-0.83,0-1.5-0.67-1.5-1.5 c0-0.83,0.67-1.5,1.5-1.5s1.5,0.67,1.5,1.5C13.5,10.83,12.83,11.5,12,11.5z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,2c-4.2,0-8,3.22-8,8.2c0,3.11,2.33,6.62,7,10.8h2c4.67-4.18,7-7.7,7-10.8C20,5.22,16.2,2,12,2z M12.42,19.5h-0.84 c-4.04-3.7-6.08-6.74-6.08-9.3c0-4.35,3.35-6.7,6.5-6.7s6.5,2.35,6.5,6.7C18.5,12.76,16.46,15.8,12.42,19.5z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,7c-1.66,0-3,1.34-3,3c0,1.66,1.34,3,3,3s3-1.34,3-3C15,8.34,13.66,7,12,7z M12,11.5c-0.83,0-1.5-0.67-1.5-1.5 c0-0.83,0.67-1.5,1.5-1.5s1.5,0.67,1.5,1.5C13.5,10.83,12.83,11.5,12,11.5z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_privacy.xml b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_privacy.xml
index 7da20c14749c..f499227f9d80 100644
--- a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_privacy.xml
+++ b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_privacy.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M12,8.5c1.48,0,2.71,1.08,2.95,2.5h1.5C16.2,8.76,14.31,7,12,7c-2.48,0-4.5,2.02-4.5,4.5c0,2.48,2.02,4.5,4.5,4.5 c0.72,0,1.39-0.19,2-0.49v-1.79c-0.53,0.48-1.23,0.78-2,0.78c-1.65,0-3-1.35-3-3S10.35,8.5,12,8.5z"/>
- <path android:fillColor="@android:color/white" android:pathData="M12,4C7.38,4,3.4,6.65,1.45,10.51v1.97C3.4,16.35,7.38,19,12,19c0.68,0,1.35-0.06,2-0.18v-1.54 c-0.65,0.13-1.32,0.22-2,0.22c-4.07,0-7.68-2.34-9.37-6c1.69-3.66,5.3-6,9.37-6c3.87,0,7.32,2.13,9.09,5.5h1.46v-0.49 C20.6,6.65,16.62,4,12,4z"/>
- <path android:fillColor="@android:color/white" android:pathData="M21,14l-1-1h-2l-1,1v2h-1v5h6v-5h-1V14z M19.5,16h-1v-1.5h1V16z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,8.5c1.48,0,2.71,1.08,2.95,2.5h1.5C16.2,8.76,14.31,7,12,7c-2.48,0-4.5,2.02-4.5,4.5c0,2.48,2.02,4.5,4.5,4.5 c0.72,0,1.39-0.19,2-0.49v-1.79c-0.53,0.48-1.23,0.78-2,0.78c-1.65,0-3-1.35-3-3S10.35,8.5,12,8.5z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,4C7.38,4,3.4,6.65,1.45,10.51v1.97C3.4,16.35,7.38,19,12,19c0.68,0,1.35-0.06,2-0.18v-1.54 c-0.65,0.13-1.32,0.22-2,0.22c-4.07,0-7.68-2.34-9.37-6c1.69-3.66,5.3-6,9.37-6c3.87,0,7.32,2.13,9.09,5.5h1.46v-0.49 C20.6,6.65,16.62,4,12,4z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M21,14l-1-1h-2l-1,1v2h-1v5h6v-5h-1V14z M19.5,16h-1v-1.5h1V16z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_security_white.xml b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_security_white.xml
index fbf6ae13903b..49d31f73317b 100644
--- a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_security_white.xml
+++ b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_security_white.xml
@@ -14,6 +14,6 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M21,2h-5l-1.5,1.5l0,4.5h-9L4,9.5v11L5.5,22h13l1.5-1.5v-11L18.5,8H16V3.5h5v2.62h1.5V3.5L21,2z M18.5,20.5h-13v-11h13 V20.5z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 12 13 C 13.1045694997 13 14 13.8954305003 14 15 C 14 16.1045694997 13.1045694997 17 12 17 C 10.8954305003 17 10 16.1045694997 10 15 C 10 13.8954305003 10.8954305003 13 12 13 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M21,2h-5l-1.5,1.5l0,4.5h-9L4,9.5v11L5.5,22h13l1.5-1.5v-11L18.5,8H16V3.5h5v2.62h1.5V3.5L21,2z M18.5,20.5h-13v-11h13 V20.5z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 13 C 13.1045694997 13 14 13.8954305003 14 15 C 14 16.1045694997 13.1045694997 17 12 17 C 10.8954305003 17 10 16.1045694997 10 15 C 10 13.8954305003 10.8954305003 13 12 13 Z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml
index 0594b9abd1bf..b668eb20c28a 100644
--- a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml
+++ b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M12,2C6.48,2,2,6.48,2,12s4.48,10,10,10s10-4.48,10-10S17.52,2,12,2z M12,20.5c-4.69,0-8.5-3.81-8.5-8.5S7.31,3.5,12,3.5 s8.5,3.81,8.5,8.5S16.69,20.5,12,20.5z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 11.25 10 H 12.75 V 17 H 11.25 V 10 Z"/>
- <path android:fillColor="@android:color/white" android:pathData="M 11.25 7 H 12.75 V 8.5 H 11.25 V 7 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,2C6.48,2,2,6.48,2,12s4.48,10,10,10s10-4.48,10-10S17.52,2,12,2z M12,20.5c-4.69,0-8.5-3.81-8.5-8.5S7.31,3.5,12,3.5 s8.5,3.81,8.5,8.5S16.69,20.5,12,20.5z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 11.25 10 H 12.75 V 17 H 11.25 V 10 Z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 11.25 7 H 12.75 V 8.5 H 11.25 V 7 Z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_wireless.xml b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_wireless.xml
index 802a041edfb3..9223902d5a23 100644
--- a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_wireless.xml
+++ b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_wireless.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
<vector android:height="24dp" android:tint="?android:attr/colorControlNormal" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M12,4.5c-4.29,0-8.17,1.72-11.01,4.49l1.42,1.42C4.89,8,8.27,6.5,12,6.5c3.73,0,7.11,1.5,9.59,3.91l1.42-1.42 C20.17,6.22,16.29,4.5,12,4.5z"/>
- <path android:fillColor="@android:color/white" android:pathData="M4.93,12.93l1.42,1.42C7.79,12.9,9.79,12,12,12s4.21,0.9,5.65,2.35l1.42-1.42C17.26,11.12,14.76,10,12,10 S6.74,11.12,4.93,12.93z"/>
- <path android:fillColor="@android:color/white" android:pathData="M9.06,17.06L12,20l2.94-2.94c-0.73-0.8-1.77-1.31-2.94-1.31S9.79,16.26,9.06,17.06z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,4.5c-4.29,0-8.17,1.72-11.01,4.49l1.42,1.42C4.89,8,8.27,6.5,12,6.5c3.73,0,7.11,1.5,9.59,3.91l1.42-1.42 C20.17,6.22,16.29,4.5,12,4.5z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M4.93,12.93l1.42,1.42C7.79,12.9,9.79,12,12,12s4.21,0.9,5.65,2.35l1.42-1.42C17.26,11.12,14.76,10,12,10 S6.74,11.12,4.93,12.93z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M9.06,17.06L12,20l2.94-2.94c-0.73-0.8-1.77-1.31-2.94-1.31S9.79,16.26,9.06,17.06z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_storage_white.xml b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_storage_white.xml
index 45288f9df04f..22f6092a35ed 100644
--- a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_storage_white.xml
+++ b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_storage_white.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M19.5,4h-15L3,5.5v1L4.5,8h15L21,6.5v-1L19.5,4z M6.5,6.75H5v-1.5h1.5V6.75z"/>
- <path android:fillColor="@android:color/white" android:pathData="M4.5,10L3,11.5v1L4.5,14h15l1.5-1.5v-1L19.5,10H4.5z M6.5,12.75H5v-1.5h1.5V12.75z"/>
- <path android:fillColor="@android:color/white" android:pathData="M4.5,16L3,17.5v1L4.5,20h15l1.5-1.5v-1L19.5,16H4.5z M6.5,18.75H5v-1.5h1.5V18.75z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M19.5,4h-15L3,5.5v1L4.5,8h15L21,6.5v-1L19.5,4z M6.5,6.75H5v-1.5h1.5V6.75z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M4.5,10L3,11.5v1L4.5,14h15l1.5-1.5v-1L19.5,10H4.5z M6.5,12.75H5v-1.5h1.5V12.75z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M4.5,16L3,17.5v1L4.5,20h15l1.5-1.5v-1L19.5,16H4.5z M6.5,18.75H5v-1.5h1.5V18.75z"/>
</vector> \ No newline at end of file
diff --git a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_volume_up_24dp.xml b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_volume_up_24dp.xml
index 781ed94cfb58..964f66897822 100644
--- a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_volume_up_24dp.xml
+++ b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_volume_up_24dp.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@android:color/white" android:pathData="M7,9H4.5L3,10.5v3L4.5,15H7l4,4h1V5h-1L7,9z M10.5,16.38L7.62,13.5H4.5v-3h3.12l2.88-2.88V16.38z"/>
- <path android:fillColor="@android:color/white" android:pathData="M14,3.23v1.55c3.17,0.88,5.5,3.78,5.5,7.22s-2.33,6.34-5.5,7.22v1.55c4.01-0.91,7-4.49,7-8.77S18.01,4.14,14,3.23z"/>
- <path android:fillColor="@android:color/white" android:pathData="M16.5,12c0-1.76-1.02-3.27-2.5-4.01v8.02C15.48,15.27,16.5,13.76,16.5,12z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M7,9H4.5L3,10.5v3L4.5,15H7l4,4h1V5h-1L7,9z M10.5,16.38L7.62,13.5H4.5v-3h3.12l2.88-2.88V16.38z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M14,3.23v1.55c3.17,0.88,5.5,3.78,5.5,7.22s-2.33,6.34-5.5,7.22v1.55c4.01-0.91,7-4.49,7-8.77S18.01,4.14,14,3.23z"/>
+ <path android:fillColor="?android:attr/colorPrimary" android:pathData="M16.5,12c0-1.76-1.02-3.27-2.5-4.01v8.02C15.48,15.27,16.5,13.76,16.5,12z"/>
</vector> \ No newline at end of file
diff --git a/services/Android.bp b/services/Android.bp
index b51e4b014b27..da24719c0f68 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -30,6 +30,7 @@ filegroup {
":services.searchui-sources",
":services.startop.iorap-sources",
":services.systemcaptions-sources",
+ ":services.translation-sources",
":services.usage-sources",
":services.usb-sources",
":services.voiceinteraction-sources",
@@ -76,6 +77,7 @@ java_library {
"services.searchui",
"services.startop",
"services.systemcaptions",
+ "services.translation",
"services.usage",
"services.usb",
"services.voiceinteraction",
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 9d028358561f..04e08aeaf78e 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -27,12 +27,14 @@ import static com.android.internal.util.Preconditions.checkState;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
+import static java.util.Objects.requireNonNull;
import static java.util.concurrent.TimeUnit.MINUTES;
import android.annotation.CheckResult;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
+import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.app.role.RoleManager;
@@ -53,6 +55,7 @@ import android.content.pm.FeatureInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.UserInfo;
import android.net.NetworkPolicyManager;
import android.os.Binder;
@@ -70,6 +73,7 @@ import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.UserHandle;
import android.os.UserManager;
+import android.permission.PermissionControllerManager;
import android.provider.Settings;
import android.provider.SettingsStringUtil.ComponentNameSet;
import android.text.BidiFormatter;
@@ -111,7 +115,6 @@ import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -158,6 +161,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
private AssociationRequest mRequest;
private String mCallingPackage;
private AndroidFuture<Association> mOngoingDeviceDiscovery;
+ private PermissionControllerManager mPermissionControllerManager;
private BluetoothDeviceConnectedListener mBluetoothDeviceConnectedListener =
new BluetoothDeviceConnectedListener();
@@ -169,6 +173,10 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
@GuardedBy("mLock")
private @Nullable SparseArray<Set<Association>> mCachedAssociations = new SparseArray<>();
+ ActivityTaskManagerInternal mAtmInternal;
+ ActivityManagerInternal mAmInternal;
+ PackageManagerInternal mPackageManagerInternal;
+
public CompanionDeviceManagerService(Context context) {
super(context);
mImpl = new CompanionDeviceManagerImpl();
@@ -176,6 +184,11 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
mRoleManager = context.getSystemService(RoleManager.class);
mAppOpsManager = IAppOpsService.Stub.asInterface(
ServiceManager.getService(Context.APP_OPS_SERVICE));
+ mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
+ mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
+ mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+ mPermissionControllerManager = requireNonNull(
+ context.getSystemService(PermissionControllerManager.class));
Intent serviceIntent = new Intent().setComponent(SERVICE_TO_BIND_TO);
mServiceConnectors = new PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>>() {
@@ -236,15 +249,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
if (associations == null || associations.isEmpty()) {
return;
}
- Set<String> companionAppPackages = new HashSet<>();
- for (Association association : associations) {
- companionAppPackages.add(association.getPackageName());
- }
- ActivityTaskManagerInternal atmInternal = LocalServices.getService(
- ActivityTaskManagerInternal.class);
- if (atmInternal != null) {
- atmInternal.setCompanionAppPackages(userHandle, companionAppPackages);
- }
+ updateAtm(userHandle, associations);
BackgroundThread.getHandler().sendMessageDelayed(
obtainMessage(CompanionDeviceManagerService::maybeGrantAutoRevokeExemptions, this),
@@ -344,13 +349,25 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
request.setCallingPackage(callingPackage);
callback.asBinder().linkToDeath(CompanionDeviceManagerService.this /* recipient */, 0);
- final long callingIdentity = Binder.clearCallingIdentity();
- try {
- mOngoingDeviceDiscovery = mServiceConnectors.forUser(userId).postAsync(service -> {
+ AndroidFuture<String> fetchProfileDescription =
+ request.getDeviceProfile() == null
+ ? AndroidFuture.completedFuture(null)
+ : getDeviceProfilePermissionDescription(
+ request.getDeviceProfile());
+
+ mOngoingDeviceDiscovery = fetchProfileDescription.thenComposeAsync(description -> {
+ request.setDeviceProfilePrivilegesDescription(description);
+
+ return mServiceConnectors.forUser(userId).postAsync(service -> {
AndroidFuture<Association> future = new AndroidFuture<>();
service.startDiscovery(request, callingPackage, callback, future);
return future;
- }).cancelTimeout().whenComplete(uncheckExceptions((association, err) -> {
+ }).cancelTimeout();
+
+ }, FgThread.getExecutor()).whenComplete(uncheckExceptions((association, err) -> {
+
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
if (err == null) {
addAssociation(association);
} else {
@@ -358,10 +375,10 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
callback.onFailure("No devices found: " + err.getMessage());
}
cleanup();
- }));
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
- }
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }));
}
@Override
@@ -727,12 +744,6 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
final Set<Association> old = getAllAssociations(userId);
Set<Association> associations = new ArraySet<>(old);
associations = update.apply(associations);
-
- Set<String> companionAppPackages = new HashSet<>();
- for (Association association : associations) {
- companionAppPackages.add(association.getPackageName());
- }
-
if (DEBUG) {
Slog.i(LOG_TAG, "Updating associations: " + old + " --> " + associations);
}
@@ -741,9 +752,25 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
CompanionDeviceManagerService::persistAssociations,
this, associations, userId));
- ActivityTaskManagerInternal atmInternal = LocalServices.getService(
- ActivityTaskManagerInternal.class);
- atmInternal.setCompanionAppPackages(userId, companionAppPackages);
+ updateAtm(userId, associations);
+ }
+ }
+
+ private void updateAtm(int userId, Set<Association> associations) {
+ final Set<Integer> companionAppUids = new ArraySet<>();
+ for (Association association : associations) {
+ final int uid = mPackageManagerInternal.getPackageUid(association.getPackageName(),
+ 0, userId);
+ if (uid >= 0) {
+ companionAppUids.add(uid);
+ }
+ }
+ if (mAtmInternal != null) {
+ mAtmInternal.setCompanionAppUids(userId, companionAppUids);
+ }
+ if (mAmInternal != null) {
+ // Make a copy of companionAppUids and send it to ActivityManager.
+ mAmInternal.setCompanionAppUids(userId, new ArraySet<>(companionAppUids));
}
}
@@ -894,6 +921,19 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
mCurrentlyConnectedDevices.remove(address);
}
+ private AndroidFuture<String> getDeviceProfilePermissionDescription(String deviceProfile) {
+ AndroidFuture<String> result = new AndroidFuture<>();
+ mPermissionControllerManager.getPrivilegesDescriptionStringForProfile(
+ deviceProfile, FgThread.getExecutor(), desc -> {
+ try {
+ result.complete(requireNonNull(desc));
+ } catch (Exception e) {
+ result.completeExceptionally(e);
+ }
+ });
+ return result;
+ }
+
private class ShellCmd extends ShellCommand {
public static final String USAGE = "help\n"
+ "list USER_ID\n"
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 019d8c558aef..3750f148f45c 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -212,15 +212,11 @@ filegroup {
"java/com/android/server/connectivity/AutodestructReference.java",
"java/com/android/server/connectivity/ConnectivityConstants.java",
"java/com/android/server/connectivity/DataConnectionStats.java",
- "java/com/android/server/connectivity/DefaultNetworkMetrics.java",
"java/com/android/server/connectivity/DnsManager.java",
- "java/com/android/server/connectivity/IpConnectivityEventBuilder.java",
- "java/com/android/server/connectivity/IpConnectivityMetrics.java",
"java/com/android/server/connectivity/KeepaliveTracker.java",
"java/com/android/server/connectivity/LingerMonitor.java",
"java/com/android/server/connectivity/MockableSystemProperties.java",
"java/com/android/server/connectivity/Nat464Xlat.java",
- "java/com/android/server/connectivity/NetdEventListenerService.java",
"java/com/android/server/connectivity/NetworkAgentInfo.java",
"java/com/android/server/connectivity/NetworkDiagnostics.java",
"java/com/android/server/connectivity/NetworkNotificationManager.java",
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 020c17a77084..71821b33f47d 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -56,12 +56,14 @@ import static android.net.NetworkPolicyManager.RULE_NONE;
import static android.net.NetworkPolicyManager.uidRulesToString;
import static android.net.shared.NetworkMonitorUtils.isPrivateDnsValidationRequired;
import static android.os.Process.INVALID_UID;
+import static android.os.Process.VPN_UID;
import static android.system.OsConstants.IPPROTO_TCP;
import static android.system.OsConstants.IPPROTO_UDP;
import static java.util.Map.Entry;
import android.Manifest;
+import android.annotation.BoolRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
@@ -87,7 +89,6 @@ import android.net.ICaptivePortal;
import android.net.IConnectivityDiagnosticsCallback;
import android.net.IConnectivityManager;
import android.net.IDnsResolver;
-import android.net.IIpConnectivityMetrics;
import android.net.INetd;
import android.net.INetworkManagementEventObserver;
import android.net.INetworkMonitor;
@@ -154,7 +155,6 @@ import android.os.PersistableBundle;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.os.SystemClock;
import android.os.SystemProperties;
@@ -927,14 +927,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
"no IpConnectivityMetrics service");
}
- /**
- * @see IpConnectivityMetrics
- */
- public IIpConnectivityMetrics getIpConnectivityMetrics() {
- return IIpConnectivityMetrics.Stub.asInterface(
- ServiceManager.getService(IpConnectivityLog.SERVICE_NAME));
- }
-
public IBatteryStats getBatteryStatsService() {
return BatteryStatsService.getService();
}
@@ -973,6 +965,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
mDefaultWifiRequest = createDefaultInternetRequestForTransport(
NetworkCapabilities.TRANSPORT_WIFI, NetworkRequest.Type.BACKGROUND_REQUEST);
+ mDefaultVehicleRequest = createAlwaysOnRequestForCapability(
+ NetworkCapabilities.NET_CAPABILITY_VEHICLE_INTERNAL,
+ NetworkRequest.Type.BACKGROUND_REQUEST);
+
mHandlerThread = mDeps.makeHandlerThread();
mHandlerThread.start();
mHandler = new InternalHandler(mHandlerThread.getLooper());
@@ -1172,6 +1168,15 @@ public class ConnectivityService extends IConnectivityManager.Stub
return new NetworkRequest(netCap, TYPE_NONE, nextNetworkRequestId(), type);
}
+ private NetworkRequest createAlwaysOnRequestForCapability(int capability,
+ NetworkRequest.Type type) {
+ final NetworkCapabilities netCap = new NetworkCapabilities();
+ netCap.clearAll();
+ netCap.addCapability(capability);
+ netCap.setRequestorUidAndPackageName(Process.myUid(), mContext.getPackageName());
+ return new NetworkRequest(netCap, TYPE_NONE, nextNetworkRequestId(), type);
+ }
+
// Used only for testing.
// TODO: Delete this and either:
// 1. Give FakeSettingsProvider the ability to send settings change notifications (requires
@@ -1189,10 +1194,19 @@ public class ConnectivityService extends IConnectivityManager.Stub
mHandler.sendEmptyMessage(EVENT_PRIVATE_DNS_SETTINGS_CHANGED);
}
+ private void handleAlwaysOnNetworkRequest(NetworkRequest networkRequest, @BoolRes int id) {
+ final boolean enable = mContext.getResources().getBoolean(id);
+ handleAlwaysOnNetworkRequest(networkRequest, enable);
+ }
+
private void handleAlwaysOnNetworkRequest(
NetworkRequest networkRequest, String settingName, boolean defaultValue) {
final boolean enable = toBool(Settings.Global.getInt(
mContext.getContentResolver(), settingName, encodeBool(defaultValue)));
+ handleAlwaysOnNetworkRequest(networkRequest, enable);
+ }
+
+ private void handleAlwaysOnNetworkRequest(NetworkRequest networkRequest, boolean enable) {
final boolean isEnabled = (mNetworkRequests.get(networkRequest) != null);
if (enable == isEnabled) {
return; // Nothing to do.
@@ -1209,9 +1223,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
private void handleConfigureAlwaysOnNetworks() {
handleAlwaysOnNetworkRequest(
- mDefaultMobileDataRequest,Settings.Global.MOBILE_DATA_ALWAYS_ON, true);
+ mDefaultMobileDataRequest, Settings.Global.MOBILE_DATA_ALWAYS_ON, true);
handleAlwaysOnNetworkRequest(mDefaultWifiRequest, Settings.Global.WIFI_ALWAYS_REQUESTED,
false);
+ handleAlwaysOnNetworkRequest(mDefaultVehicleRequest,
+ com.android.internal.R.bool.config_vehicleInternalNetworkAlwaysRequested);
}
private void registerSettingsCallbacks() {
@@ -1565,7 +1581,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (nc != null) {
result.put(
nai.network,
- maybeSanitizeLocationInfoForCaller(
+ createWithLocationInfoSanitizedIfNecessaryWhenParceled(
nc, mDeps.getCallingUid(), callingPackageName));
}
@@ -1575,7 +1591,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
for (Network network : networks) {
nc = getNetworkCapabilitiesInternal(network);
if (nc != null) {
- result.put(network, maybeSanitizeLocationInfoForCaller(
+ result.put(
+ network,
+ createWithLocationInfoSanitizedIfNecessaryWhenParceled(
nc, mDeps.getCallingUid(), callingPackageName));
}
}
@@ -1657,7 +1675,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
public NetworkCapabilities getNetworkCapabilities(Network network, String callingPackageName) {
mAppOpsManager.checkPackage(mDeps.getCallingUid(), callingPackageName);
enforceAccessPermission();
- return maybeSanitizeLocationInfoForCaller(
+ return createWithLocationInfoSanitizedIfNecessaryWhenParceled(
getNetworkCapabilitiesInternal(network),
mDeps.getCallingUid(), callingPackageName);
}
@@ -1678,37 +1696,51 @@ public class ConnectivityService extends IConnectivityManager.Stub
return newNc;
}
+ private boolean hasLocationPermission(int callerUid, @NonNull String callerPkgName) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return mLocationPermissionChecker.checkLocationPermission(
+ callerPkgName, null /* featureId */, callerUid, null /* message */);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
@VisibleForTesting
@Nullable
- NetworkCapabilities maybeSanitizeLocationInfoForCaller(
+ NetworkCapabilities createWithLocationInfoSanitizedIfNecessaryWhenParceled(
@Nullable NetworkCapabilities nc, int callerUid, @NonNull String callerPkgName) {
if (nc == null) {
return null;
}
- final NetworkCapabilities newNc = new NetworkCapabilities(nc);
- if (callerUid != newNc.getOwnerUid()) {
+ Boolean hasLocationPermission = null;
+ final NetworkCapabilities newNc;
+ // Avoid doing location permission check if the transport info has no location sensitive
+ // data.
+ if (nc.getTransportInfo() != null && nc.getTransportInfo().hasLocationSensitiveFields()) {
+ hasLocationPermission = hasLocationPermission(callerUid, callerPkgName);
+ newNc = new NetworkCapabilities(nc, hasLocationPermission);
+ } else {
+ newNc = new NetworkCapabilities(nc, false /* parcelLocationSensitiveFields */);
+ }
+ // Reset owner uid if not destined for the owner app.
+ if (callerUid != nc.getOwnerUid()) {
newNc.setOwnerUid(INVALID_UID);
return newNc;
}
-
// Allow VPNs to see ownership of their own VPN networks - not location sensitive.
if (nc.hasTransport(TRANSPORT_VPN)) {
// Owner UIDs already checked above. No need to re-check.
return newNc;
}
-
- final long token = Binder.clearCallingIdentity();
- try {
- if (!mLocationPermissionChecker.checkLocationPermission(
- callerPkgName, null /* featureId */, callerUid, null /* message */)) {
- // Caller does not have the requisite location permissions. Reset the
- // owner's UID in the NetworkCapabilities.
- newNc.setOwnerUid(INVALID_UID);
- }
- } finally {
- Binder.restoreCallingIdentity(token);
+ if (hasLocationPermission == null) {
+ // Location permission not checked yet, check now for masking owner UID.
+ hasLocationPermission = hasLocationPermission(callerUid, callerPkgName);
+ }
+ // Reset owner uid if the app has no location permission.
+ if (!hasLocationPermission) {
+ newNc.setOwnerUid(INVALID_UID);
}
-
return newNc;
}
@@ -1787,12 +1819,28 @@ public class ConnectivityService extends IConnectivityManager.Stub
private INetworkManagementEventObserver mDataActivityObserver = new BaseNetworkObserver() {
@Override
- public void interfaceClassDataActivityChanged(int networkType, boolean active, long tsNanos,
- int uid) {
- sendDataActivityBroadcast(networkType, active, tsNanos);
+ public void interfaceClassDataActivityChanged(int transportType, boolean active,
+ long tsNanos, int uid) {
+ sendDataActivityBroadcast(transportTypeToLegacyType(transportType), active, tsNanos);
}
};
+ // This is deprecated and only to support legacy use cases.
+ private int transportTypeToLegacyType(int type) {
+ switch (type) {
+ case NetworkCapabilities.TRANSPORT_CELLULAR:
+ return ConnectivityManager.TYPE_MOBILE;
+ case NetworkCapabilities.TRANSPORT_WIFI:
+ return ConnectivityManager.TYPE_WIFI;
+ case NetworkCapabilities.TRANSPORT_BLUETOOTH:
+ return ConnectivityManager.TYPE_BLUETOOTH;
+ case NetworkCapabilities.TRANSPORT_ETHERNET:
+ return ConnectivityManager.TYPE_ETHERNET;
+ default:
+ loge("Unexpected transport in transportTypeToLegacyType: " + type);
+ }
+ return ConnectivityManager.TYPE_NONE;
+ }
/**
* Ensures that the system cannot call a particular method.
*/
@@ -2376,13 +2424,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
timeout = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.DATA_ACTIVITY_TIMEOUT_MOBILE,
10);
- type = ConnectivityManager.TYPE_MOBILE;
+ type = NetworkCapabilities.TRANSPORT_CELLULAR;
} else if (networkAgent.networkCapabilities.hasTransport(
NetworkCapabilities.TRANSPORT_WIFI)) {
timeout = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.DATA_ACTIVITY_TIMEOUT_WIFI,
15);
- type = ConnectivityManager.TYPE_WIFI;
+ type = NetworkCapabilities.TRANSPORT_WIFI;
} else {
return; // do not track any other networks
}
@@ -2957,7 +3005,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
case EVENT_CAPPORT_DATA_CHANGED: {
final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
if (nai == null) break;
- handleCaptivePortalDataUpdate(nai, (CaptivePortalData) msg.obj);
+ handleCapportApiDataUpdate(nai, (CaptivePortalData) msg.obj);
break;
}
}
@@ -2983,9 +3031,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
if (valid != nai.lastValidated) {
if (wasDefault) {
- mDeps.getMetricsLogger()
- .defaultNetworkMetrics().logDefaultNetworkValidity(
- SystemClock.elapsedRealtime(), valid);
+ mMetricsLog.logDefaultNetworkValidity(valid);
}
final int oldScore = nai.getCurrentScore();
nai.lastValidated = valid;
@@ -3297,9 +3343,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
}
- private void handleCaptivePortalDataUpdate(@NonNull final NetworkAgentInfo nai,
+ private void handleCapportApiDataUpdate(@NonNull final NetworkAgentInfo nai,
@Nullable final CaptivePortalData data) {
- nai.captivePortalData = data;
+ nai.capportApiData = data;
// CaptivePortalData will be merged into LinkProperties from NetworkAgentInfo
handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
}
@@ -3413,7 +3459,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
// if there is a fallback. Taken together, the two form a X -> 0, 0 -> Y sequence
// whose timestamps tell how long it takes to recover a default network.
long now = SystemClock.elapsedRealtime();
- mDeps.getMetricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(now, null, nai);
+ mMetricsLog.logDefaultNetworkEvent(null, 0, false,
+ null /* lp */, null /* nc */, nai.network, nai.getCurrentScore(),
+ nai.linkProperties, nai.networkCapabilities);
}
notifyIfacesChangedForNetworkStats();
// TODO - we shouldn't send CALLBACK_LOST to requests that can be satisfied
@@ -5965,6 +6013,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
// priority networks like ethernet are active.
private final NetworkRequest mDefaultWifiRequest;
+ // Request used to optionally keep vehicle internal network always active
+ private final NetworkRequest mDefaultVehicleRequest;
+
private NetworkAgentInfo getDefaultNetwork() {
return mDefaultNetworkNai;
}
@@ -6104,6 +6155,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
private void processLinkPropertiesFromAgent(NetworkAgentInfo nai, LinkProperties lp) {
lp.ensureDirectlyConnectedRoutes();
nai.clatd.setNat64PrefixFromRa(lp.getNat64Prefix());
+ nai.networkAgentPortalData = lp.getCaptivePortalData();
}
private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties newLp,
@@ -6147,9 +6199,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
updateWakeOnLan(newLp);
- // Captive portal data is obtained from NetworkMonitor and stored in NetworkAgentInfo,
- // it is not contained in LinkProperties sent from NetworkAgents so needs to be merged here.
- newLp.setCaptivePortalData(networkAgent.captivePortalData);
+ // Captive portal data is obtained from NetworkMonitor and stored in NetworkAgentInfo.
+ // It is not always contained in the LinkProperties sent from NetworkAgents, and if it
+ // does, it needs to be merged here.
+ newLp.setCaptivePortalData(mergeCaptivePortalData(networkAgent.networkAgentPortalData,
+ networkAgent.capportApiData));
// TODO - move this check to cover the whole function
if (!Objects.equals(newLp, oldLp)) {
@@ -6169,6 +6223,57 @@ public class ConnectivityService extends IConnectivityManager.Stub
mKeepaliveTracker.handleCheckKeepalivesStillValid(networkAgent);
}
+ /**
+ * @param naData captive portal data from NetworkAgent
+ * @param apiData captive portal data from capport API
+ */
+ @Nullable
+ private CaptivePortalData mergeCaptivePortalData(CaptivePortalData naData,
+ CaptivePortalData apiData) {
+ if (naData == null || apiData == null) {
+ return naData == null ? apiData : naData;
+ }
+ final CaptivePortalData.Builder captivePortalBuilder =
+ new CaptivePortalData.Builder(naData);
+
+ if (apiData.isCaptive()) {
+ captivePortalBuilder.setCaptive(true);
+ }
+ if (apiData.isSessionExtendable()) {
+ captivePortalBuilder.setSessionExtendable(true);
+ }
+ if (apiData.getExpiryTimeMillis() >= 0 || apiData.getByteLimit() >= 0) {
+ // Expiry time, bytes remaining, refresh time all need to come from the same source,
+ // otherwise data would be inconsistent. Prefer the capport API info if present,
+ // as it can generally be refreshed more often.
+ captivePortalBuilder.setExpiryTime(apiData.getExpiryTimeMillis());
+ captivePortalBuilder.setBytesRemaining(apiData.getByteLimit());
+ captivePortalBuilder.setRefreshTime(apiData.getRefreshTimeMillis());
+ } else if (naData.getExpiryTimeMillis() < 0 && naData.getByteLimit() < 0) {
+ // No source has time / bytes remaining information: surface the newest refresh time
+ // for other fields
+ captivePortalBuilder.setRefreshTime(
+ Math.max(naData.getRefreshTimeMillis(), apiData.getRefreshTimeMillis()));
+ }
+
+ // Prioritize the user portal URL from the network agent.
+ if (apiData.getUserPortalUrl() != null && (naData.getUserPortalUrl() == null
+ || TextUtils.isEmpty(naData.getUserPortalUrl().toSafeString()))) {
+ captivePortalBuilder.setUserPortalUrl(apiData.getUserPortalUrl());
+ }
+ // Prioritize the venue information URL from the network agent.
+ if (apiData.getVenueInfoUrl() != null && (naData.getVenueInfoUrl() == null
+ || TextUtils.isEmpty(naData.getVenueInfoUrl().toSafeString()))) {
+ captivePortalBuilder.setVenueInfoUrl(apiData.getVenueInfoUrl());
+
+ // Note that venue friendly name can only come from the network agent because it is not
+ // in use in RFC8908. However, if using the Capport venue URL, make sure that the
+ // friendly name is not set from the network agent.
+ captivePortalBuilder.setVenueFriendlyName(null);
+ }
+ return captivePortalBuilder.build();
+ }
+
private void wakeupModifyInterface(String iface, NetworkCapabilities caps, boolean add) {
// Marks are only available on WiFi interfaces. Checking for
// marks on unsupported interfaces is harmless.
@@ -6675,6 +6780,39 @@ public class ConnectivityService extends IConnectivityManager.Stub
return stableRanges;
}
+ private void maybeCloseSockets(NetworkAgentInfo nai, UidRangeParcel[] ranges,
+ int[] exemptUids) {
+ if (nai.isVPN() && !nai.networkAgentConfig.allowBypass) {
+ try {
+ mNetd.socketDestroy(ranges, exemptUids);
+ } catch (Exception e) {
+ loge("Exception in socket destroy: ", e);
+ }
+ }
+ }
+
+ private void updateUidRanges(boolean add, NetworkAgentInfo nai, Set<UidRange> uidRanges) {
+ int[] exemptUids = new int[2];
+ // TODO: Excluding VPN_UID is necessary in order to not to kill the TCP connection used
+ // by PPTP. Fix this by making Vpn set the owner UID to VPN_UID instead of system when
+ // starting a legacy VPN, and remove VPN_UID here. (b/176542831)
+ exemptUids[0] = VPN_UID;
+ exemptUids[1] = nai.networkCapabilities.getOwnerUid();
+ UidRangeParcel[] ranges = toUidRangeStableParcels(uidRanges);
+
+ maybeCloseSockets(nai, ranges, exemptUids);
+ try {
+ if (add) {
+ mNetd.networkAddUidRanges(nai.network.netId, ranges);
+ } else {
+ mNetd.networkRemoveUidRanges(nai.network.netId, ranges);
+ }
+ } catch (Exception e) {
+ loge("Exception while " + (add ? "adding" : "removing") + " uid ranges " + uidRanges +
+ " on netId " + nai.network.netId + ". " + e);
+ }
+ maybeCloseSockets(nai, ranges, exemptUids);
+ }
private void updateUids(NetworkAgentInfo nai, NetworkCapabilities prevNc,
NetworkCapabilities newNc) {
@@ -6694,12 +6832,21 @@ public class ConnectivityService extends IConnectivityManager.Stub
// in both ranges are not subject to any VPN routing rules. Adding new range before
// removing old range works because, unlike the filtering rules below, it's possible to
// add duplicate UID routing rules.
+ // TODO: calculate the intersection of add & remove. Imagining that we are trying to
+ // remove uid 3 from a set containing 1-5. Intersection of the prev and new sets is:
+ // [1-5] & [1-2],[4-5] == [3]
+ // Then we can do:
+ // maybeCloseSockets([3])
+ // mNetd.networkAddUidRanges([1-2],[4-5])
+ // mNetd.networkRemoveUidRanges([1-5])
+ // maybeCloseSockets([3])
+ // This can prevent the sockets of uid 1-2, 4-5 from being closed. It also reduce the
+ // number of binder calls from 6 to 4.
if (!newRanges.isEmpty()) {
- mNetd.networkAddUidRanges(nai.network.netId, toUidRangeStableParcels(newRanges));
+ updateUidRanges(true, nai, newRanges);
}
if (!prevRanges.isEmpty()) {
- mNetd.networkRemoveUidRanges(
- nai.network.netId, toUidRangeStableParcels(prevRanges));
+ updateUidRanges(false, nai, prevRanges);
}
final boolean wasFiltering = requiresVpnIsolation(nai, prevNc, nai.linkProperties);
final boolean shouldFilter = requiresVpnIsolation(nai, newNc, nai.linkProperties);
@@ -6848,7 +6995,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
networkAgent.networkCapabilities, nri.mPid, nri.mUid);
putParcelable(
bundle,
- maybeSanitizeLocationInfoForCaller(
+ createWithLocationInfoSanitizedIfNecessaryWhenParceled(
nc, nri.mUid, nri.request.getRequestorPackageName()));
putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions(
networkAgent.linkProperties, nri.mPid, nri.mUid));
@@ -6867,7 +7014,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
networkAgent.networkCapabilities, nri.mPid, nri.mUid);
putParcelable(
bundle,
- maybeSanitizeLocationInfoForCaller(
+ createWithLocationInfoSanitizedIfNecessaryWhenParceled(
netCap, nri.mUid, nri.request.getRequestorPackageName()));
break;
}
@@ -7167,9 +7314,28 @@ public class ConnectivityService extends IConnectivityManager.Stub
updateDataActivityTracking(newDefaultNetwork, oldDefaultNetwork);
// Notify system services of the new default.
makeDefault(newDefaultNetwork);
+
// Log 0 -> X and Y -> X default network transitions, where X is the new default.
- mDeps.getMetricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(
- now, newDefaultNetwork, oldDefaultNetwork);
+ final Network network = (newDefaultNetwork != null) ? newDefaultNetwork.network : null;
+ final int score = (newDefaultNetwork != null) ? newDefaultNetwork.getCurrentScore() : 0;
+ final boolean validated = newDefaultNetwork != null && newDefaultNetwork.lastValidated;
+ final LinkProperties lp = (newDefaultNetwork != null)
+ ? newDefaultNetwork.linkProperties : null;
+ final NetworkCapabilities nc = (newDefaultNetwork != null)
+ ? newDefaultNetwork.networkCapabilities : null;
+
+ final Network prevNetwork = (oldDefaultNetwork != null)
+ ? oldDefaultNetwork.network : null;
+ final int prevScore = (oldDefaultNetwork != null)
+ ? oldDefaultNetwork.getCurrentScore() : 0;
+ final LinkProperties prevLp = (oldDefaultNetwork != null)
+ ? oldDefaultNetwork.linkProperties : null;
+ final NetworkCapabilities prevNc = (oldDefaultNetwork != null)
+ ? oldDefaultNetwork.networkCapabilities : null;
+
+ mMetricsLog.logDefaultNetworkEvent(network, score, validated, lp, nc,
+ prevNetwork, prevScore, prevLp, prevNc);
+
// Have a new default network, release the transition wakelock in
scheduleReleaseNetworkTransitionWakelock();
}
diff --git a/services/core/java/com/android/server/DropBoxManagerInternal.java b/services/core/java/com/android/server/DropBoxManagerInternal.java
new file mode 100644
index 000000000000..3785a9c011ce
--- /dev/null
+++ b/services/core/java/com/android/server/DropBoxManagerInternal.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.annotation.BytesLong;
+import android.annotation.NonNull;
+import android.os.DropBoxManager;
+
+import java.io.Closeable;
+import java.io.FileDescriptor;
+import java.io.IOException;
+
+public abstract class DropBoxManagerInternal {
+ public abstract void addEntry(@NonNull String tag, @NonNull EntrySource source,
+ @DropBoxManager.Flags int flags);
+
+ /**
+ * Interface which describes a pending entry which knows how to write itself
+ * to the given FD. This abstraction supports implementations which may want
+ * to dynamically generate the entry contents.
+ */
+ public interface EntrySource extends Closeable {
+ public @BytesLong long length();
+ public void writeTo(@NonNull FileDescriptor fd) throws IOException;
+ }
+}
diff --git a/services/core/java/com/android/server/DropBoxManagerService.java b/services/core/java/com/android/server/DropBoxManagerService.java
index 30fc3364f6b7..a6d9bf8bc55b 100644
--- a/services/core/java/com/android/server/DropBoxManagerService.java
+++ b/services/core/java/com/android/server/DropBoxManagerService.java
@@ -35,6 +35,7 @@ import android.os.FileUtils;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.ParcelFileDescriptor;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.ShellCommand;
@@ -43,6 +44,10 @@ import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.dropbox.DropBoxManagerServiceDumpProto;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+import android.system.StructStat;
import android.text.TextUtils;
import android.text.format.TimeMigrationUtils;
import android.util.ArrayMap;
@@ -56,18 +61,19 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.IDropBoxManagerService;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.ObjectUtils;
+import com.android.server.DropBoxManagerInternal.EntrySource;
import libcore.io.IoUtils;
-import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
-import java.io.OutputStream;
import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.SortedSet;
@@ -93,6 +99,9 @@ public final class DropBoxManagerService extends SystemService {
// Max number of bytes of a dropbox entry to write into protobuf.
private static final int PROTO_MAX_DATA_BYTES = 256 * 1024;
+ // Size beyond which to force-compress newly added entries.
+ private static final long COMPRESS_THRESHOLD_BYTES = 16_384;
+
// TODO: This implementation currently uses one file per entry, which is
// inefficient for smallish entries -- consider using a single queue file
// per tag (or even globally) instead.
@@ -149,8 +158,13 @@ public final class DropBoxManagerService extends SystemService {
private final IDropBoxManagerService.Stub mStub = new IDropBoxManagerService.Stub() {
@Override
- public void add(DropBoxManager.Entry entry) {
- DropBoxManagerService.this.add(entry);
+ public void addData(String tag, byte[] data, int flags) {
+ DropBoxManagerService.this.addData(tag, data, flags);
+ }
+
+ @Override
+ public void addFile(String tag, ParcelFileDescriptor fd, int flags) {
+ DropBoxManagerService.this.addFile(tag, fd, flags);
}
@Override
@@ -333,6 +347,7 @@ public final class DropBoxManagerService extends SystemService {
mDropBoxDir = path;
mContentResolver = getContext().getContentResolver();
mHandler = new DropBoxManagerBroadcastHandler(looper);
+ LocalServices.addService(DropBoxManagerInternal.class, new DropBoxManagerInternalImpl());
}
@Override
@@ -374,77 +389,101 @@ public final class DropBoxManagerService extends SystemService {
return mStub;
}
- public void add(DropBoxManager.Entry entry) {
- File temp = null;
- InputStream input = null;
- OutputStream output = null;
- final String tag = entry.getTag();
+ public void addData(String tag, byte[] data, int flags) {
+ addEntry(tag, new ByteArrayInputStream(data), data.length, flags);
+ }
+
+ public void addFile(String tag, ParcelFileDescriptor fd, int flags) {
+ final StructStat stat;
try {
- int flags = entry.getFlags();
- Slog.i(TAG, "add tag=" + tag + " isTagEnabled=" + isTagEnabled(tag)
- + " flags=0x" + Integer.toHexString(flags));
- if ((flags & DropBoxManager.IS_EMPTY) != 0) throw new IllegalArgumentException();
+ stat = Os.fstat(fd.getFileDescriptor());
- init();
- if (!isTagEnabled(tag)) return;
- long max = trimToFit();
- long lastTrim = System.currentTimeMillis();
+ // Verify caller isn't playing games with pipes or sockets
+ if (!OsConstants.S_ISREG(stat.st_mode)) {
+ throw new IllegalArgumentException(tag + " entry must be real file");
+ }
+ } catch (ErrnoException e) {
+ throw new IllegalArgumentException(e);
+ }
- byte[] buffer = new byte[mBlockSize];
- input = entry.getInputStream();
+ addEntry(tag, new ParcelFileDescriptor.AutoCloseInputStream(fd), stat.st_size, flags);
+ }
- // First, accumulate up to one block worth of data in memory before
- // deciding whether to compress the data or not.
+ public void addEntry(String tag, InputStream in, long length, int flags) {
+ // If entry being added is large, and if it's not already compressed,
+ // then we'll force compress it during write
+ boolean forceCompress = false;
+ if ((flags & DropBoxManager.IS_GZIPPED) == 0
+ && length > COMPRESS_THRESHOLD_BYTES) {
+ forceCompress = true;
+ flags |= DropBoxManager.IS_GZIPPED;
+ }
- int read = 0;
- while (read < buffer.length) {
- int n = input.read(buffer, read, buffer.length - read);
- if (n <= 0) break;
- read += n;
- }
+ addEntry(tag, new SimpleEntrySource(in, length, forceCompress), flags);
+ }
+
+ /**
+ * Simple entry which contains data ready to be written.
+ */
+ public static class SimpleEntrySource implements EntrySource {
+ private final InputStream in;
+ private final long length;
+ private final boolean forceCompress;
+
+ public SimpleEntrySource(InputStream in, long length, boolean forceCompress) {
+ this.in = in;
+ this.length = length;
+ this.forceCompress = forceCompress;
+ }
+
+ public long length() {
+ return length;
+ }
- // If we have at least one block, compress it -- otherwise, just write
- // the data in uncompressed form.
-
- temp = new File(mDropBoxDir, "drop" + Thread.currentThread().getId() + ".tmp");
- int bufferSize = mBlockSize;
- if (bufferSize > 4096) bufferSize = 4096;
- if (bufferSize < 512) bufferSize = 512;
- FileOutputStream foutput = new FileOutputStream(temp);
- output = new BufferedOutputStream(foutput, bufferSize);
- if (read == buffer.length && ((flags & DropBoxManager.IS_GZIPPED) == 0)) {
- output = new GZIPOutputStream(output);
- flags = flags | DropBoxManager.IS_GZIPPED;
+ @Override
+ public void writeTo(FileDescriptor fd) throws IOException {
+ // No need to buffer the output here, since data is either coming
+ // from an in-memory buffer, or another file on disk; if we buffered
+ // we'd lose out on sendfile() optimizations
+ if (forceCompress) {
+ FileUtils.copy(in, new GZIPOutputStream(new FileOutputStream(fd)));
+ } else {
+ FileUtils.copy(in, new FileOutputStream(fd));
}
+ }
- do {
- output.write(buffer, 0, read);
+ @Override
+ public void close() throws IOException {
+ FileUtils.closeQuietly(in);
+ }
+ }
- long now = System.currentTimeMillis();
- if (now - lastTrim > 30 * 1000) {
- max = trimToFit(); // In case data dribbles in slowly
- lastTrim = now;
- }
+ public void addEntry(String tag, EntrySource entry, int flags) {
+ File temp = null;
+ try {
+ Slog.i(TAG, "add tag=" + tag + " isTagEnabled=" + isTagEnabled(tag)
+ + " flags=0x" + Integer.toHexString(flags));
+ if ((flags & DropBoxManager.IS_EMPTY) != 0) throw new IllegalArgumentException();
- read = input.read(buffer);
- if (read <= 0) {
- FileUtils.sync(foutput);
- output.close(); // Get a final size measurement
- output = null;
- } else {
- output.flush(); // So the size measurement is pseudo-reasonable
- }
+ init();
- long len = temp.length();
- if (len > max) {
- Slog.w(TAG, "Dropping: " + tag + " (" + temp.length() + " > "
- + max + " bytes)");
- temp.delete();
- temp = null; // Pass temp = null to createEntry() to leave a tombstone
- break;
+ // Bail early if we know tag is disabled
+ if (!isTagEnabled(tag)) return;
+
+ // Drop entries which are too large for our quota
+ final long length = entry.length();
+ final long max = trimToFit();
+ if (length > max) {
+ // Log and fall through to create empty tombstone below
+ Slog.w(TAG, "Dropping: " + tag + " (" + length + " > " + max + " bytes)");
+ } else {
+ temp = new File(mDropBoxDir, "drop" + Thread.currentThread().getId() + ".tmp");
+ try (FileOutputStream out = new FileOutputStream(temp)) {
+ entry.writeTo(out.getFD());
}
- } while (read > 0);
+ }
+ // Writing above succeeded, so create the finalized entry
long time = createEntry(temp, tag, flags);
temp = null;
@@ -461,9 +500,7 @@ public final class DropBoxManagerService extends SystemService {
} catch (IOException e) {
Slog.e(TAG, "Can't write: " + tag, e);
} finally {
- IoUtils.closeQuietly(output);
- IoUtils.closeQuietly(input);
- entry.close();
+ IoUtils.closeQuietly(entry);
if (temp != null) temp.delete();
}
}
@@ -1187,4 +1224,11 @@ public final class DropBoxManagerService extends SystemService {
mLowPriorityTags.add(lowPrioritytags[i]);
}
}
+
+ private final class DropBoxManagerInternalImpl extends DropBoxManagerInternal {
+ @Override
+ public void addEntry(String tag, EntrySource entry, int flags) {
+ DropBoxManagerService.this.addEntry(tag, entry, flags);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index e8687e57a07b..a08d066513c7 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -242,6 +242,7 @@ class TestNetworkService extends ITestNetworkManager.Stub {
nc.addTransportType(NetworkCapabilities.TRANSPORT_TEST);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
+ nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
nc.setNetworkSpecifier(new StringNetworkSpecifier(iface));
nc.setAdministratorUids(administratorUids);
if (!isMetered) {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index bb07ee6c37a9..5ce630e3daed 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -170,6 +170,8 @@ public final class ActiveServices {
public static final int FGS_FEATURE_ALLOWED_BY_PROCESS_RECORD = 19;
public static final int FGS_FEATURE_ALLOWED_BY_EXEMPTED_PACKAGES = 20;
public static final int FGS_FEATURE_ALLOWED_BY_ACTIVITY_STARTER = 21;
+ public static final int FGS_FEATURE_ALLOWED_BY_COMPANION_APP = 22;
+ public static final int FGS_FEATURE_ALLOWED_BY_PROFILE_OWNER = 23;
@IntDef(flag = true, prefix = { "FGS_FEATURE_" }, value = {
FGS_FEATURE_DENIED,
@@ -192,7 +194,9 @@ public final class ActiveServices {
FGS_FEATURE_ALLOWED_BY_DEVICE_DEMO_MODE,
FGS_FEATURE_ALLOWED_BY_PROCESS_RECORD,
FGS_FEATURE_ALLOWED_BY_EXEMPTED_PACKAGES,
- FGS_FEATURE_ALLOWED_BY_ACTIVITY_STARTER
+ FGS_FEATURE_ALLOWED_BY_ACTIVITY_STARTER,
+ FGS_FEATURE_ALLOWED_BY_COMPANION_APP,
+ FGS_FEATURE_ALLOWED_BY_PROFILE_OWNER
})
@Retention(RetentionPolicy.SOURCE)
public @interface FgsFeatureRetCode {}
@@ -5371,6 +5375,14 @@ public final class ActiveServices {
}
}
+ if (ret == FGS_FEATURE_DENIED) {
+ // Is the calling UID a profile owner app?
+ final boolean isProfileOwner = mAm.mInternal.isProfileOwner(callingUid);
+ if (isProfileOwner) {
+ ret = FGS_FEATURE_ALLOWED_BY_PROFILE_OWNER;
+ }
+ }
+
// NOTE this should always be the last check.
if (ret == FGS_FEATURE_DENIED) {
if (isPackageExemptedFromFgsRestriction(r.appInfo.packageName, r.appInfo.uid)
@@ -5379,6 +5391,14 @@ public final class ActiveServices {
}
}
+ if (ret == FGS_FEATURE_DENIED) {
+ final boolean isCompanionApp = mAm.mInternal.isAssociatedCompanionApp(
+ UserHandle.getUserId(callingUid), callingUid);
+ if (isCompanionApp) {
+ ret = FGS_FEATURE_ALLOWED_BY_COMPANION_APP;
+ }
+ }
+
final String debugInfo =
"[callingPackage: " + callingPackage
+ "; callingUid: " + callingUid
@@ -5462,6 +5482,10 @@ public final class ActiveServices {
return "FGS_FEATURE_ALLOWED_BY_EXEMPTED_PACKAGES";
case FGS_FEATURE_ALLOWED_BY_ACTIVITY_STARTER:
return "ALLOWED_BY_ACTIVITY_STARTER";
+ case FGS_FEATURE_ALLOWED_BY_COMPANION_APP:
+ return "ALLOWED_BY_COMPANION_APP";
+ case FGS_FEATURE_ALLOWED_BY_PROFILE_OWNER:
+ return "ALLOWED_BY_PROFILE_OWNER";
default:
return "";
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c1ab5b6a315e..be1d1bcbdfff 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -174,7 +174,6 @@ import android.app.ProfilerInfo;
import android.app.WaitResult;
import android.app.backup.BackupManager.OperationType;
import android.app.backup.IBackupManager;
-import android.app.compat.CompatChanges;
import android.app.usage.UsageEvents;
import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStatsManager;
@@ -573,6 +572,16 @@ public class ActivityManagerService extends IActivityManager.Stub
private int mDeviceOwnerUid = Process.INVALID_UID;
+ /**
+ * Map userId to its companion app uids.
+ */
+ private final Map<Integer, Set<Integer>> mCompanionAppUidsMap = new ArrayMap<>();
+
+ /**
+ * The profile owner UIDs.
+ */
+ private ArraySet<Integer> mProfileOwnerUids = null;
+
final UserController mUserController;
@VisibleForTesting
public final PendingIntentController mPendingIntentController;
@@ -13707,34 +13716,10 @@ public class ActivityManagerService extends IActivityManager.Stub
false, false, userId, "package unstartable");
break;
case Intent.ACTION_CLOSE_SYSTEM_DIALOGS:
- if (!canCloseSystemDialogs(callingPid, callingUid, callerApp)) {
- // The app can't close system dialogs, throw only if it targets S+
- if (CompatChanges.isChangeEnabled(
- ActivityManager.LOCK_DOWN_CLOSE_SYSTEM_DIALOGS, callingUid)) {
- throw new SecurityException(
- "Permission Denial: " + Intent.ACTION_CLOSE_SYSTEM_DIALOGS
- + " broadcast from " + callerPackage + " (pid="
- + callingPid + ", uid=" + callingUid + ")"
- + " requires "
- + permission.BROADCAST_CLOSE_SYSTEM_DIALOGS + ".");
- } else if (CompatChanges.isChangeEnabled(
- ActivityManager.DROP_CLOSE_SYSTEM_DIALOGS, callingUid)) {
- Slog.w(TAG, "Permission Denial: " + intent.getAction()
- + " broadcast from " + callerPackage + " (pid=" + callingPid
- + ", uid=" + callingUid + ")"
- + " requires "
- + permission.BROADCAST_CLOSE_SYSTEM_DIALOGS
- + ", dropping broadcast.");
- // Returning success seems to be the pattern here
- return ActivityManager.BROADCAST_SUCCESS;
- } else {
- Slog.w(TAG, intent.getAction()
- + " broadcast from " + callerPackage + " (pid=" + callingPid
- + ", uid=" + callingUid + ")"
- + " will require "
- + permission.BROADCAST_CLOSE_SYSTEM_DIALOGS
- + " in future builds.");
- }
+ if (!mAtmInternal.checkCanCloseSystemDialogs(callingPid, callingUid,
+ callerPackage)) {
+ // Returning success seems to be the pattern here
+ return ActivityManager.BROADCAST_SUCCESS;
}
break;
}
@@ -14029,39 +14014,6 @@ public class ActivityManagerService extends IActivityManager.Stub
return ActivityManager.BROADCAST_SUCCESS;
}
- private boolean canCloseSystemDialogs(int pid, int uid, @Nullable ProcessRecord callerApp) {
- if (checkPermission(permission.BROADCAST_CLOSE_SYSTEM_DIALOGS, pid, uid)
- == PERMISSION_GRANTED) {
- return true;
- }
- if (callerApp == null) {
- synchronized (mPidsSelfLocked) {
- callerApp = mPidsSelfLocked.get(pid);
- }
- }
-
- if (callerApp != null) {
- // Check if the instrumentation of the process has the permission. This covers the usual
- // test started from the shell (which has the permission) case. This is needed for apps
- // targeting SDK level < S but we are also allowing for targetSdk S+ as a convenience to
- // avoid breaking a bunch of existing tests and asking them to adopt shell permissions
- // to do this.
- ActiveInstrumentation instrumentation = callerApp.getActiveInstrumentation();
- if (instrumentation != null && checkPermission(
- permission.BROADCAST_CLOSE_SYSTEM_DIALOGS, -1, instrumentation.mSourceUid)
- == PERMISSION_GRANTED) {
- return true;
- }
- // This is the notification trampoline use-case for example, where apps use Intent.ACSD
- // to close the shade prior to starting an activity.
- WindowProcessController wmApp = callerApp.getWindowProcessController();
- if (wmApp.canCloseSystemDialogsByToken()) {
- return true;
- }
- }
- return false;
- }
-
/**
* @return uid from the extra field {@link Intent#EXTRA_UID} if present, Otherwise -1
*/
@@ -16783,6 +16735,37 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
+
+ @Override
+ public void setProfileOwnerUid(ArraySet<Integer> profileOwnerUids) {
+ synchronized (ActivityManagerService.this) {
+ mProfileOwnerUids = profileOwnerUids;
+ }
+ }
+
+ @Override
+ public boolean isProfileOwner(int uid) {
+ synchronized (ActivityManagerService.this) {
+ return mProfileOwnerUids != null && mProfileOwnerUids.indexOf(uid) >= 0;
+ }
+ }
+
+ @Override
+ public void setCompanionAppUids(int userId, Set<Integer> companionAppUids) {
+ synchronized (ActivityManagerService.this) {
+ mCompanionAppUidsMap.put(userId, companionAppUids);
+ }
+ }
+
+ @Override
+ public boolean isAssociatedCompanionApp(int userId, int uid) {
+ final Set<Integer> allUids = mCompanionAppUidsMap.get(userId);
+ if (allUids == null) {
+ return false;
+ }
+ return allUids.contains(uid);
+ }
+
@Override
public void addPendingTopUid(int uid, int pid) {
mPendingStartActivityUids.add(uid, pid);
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 36d4a38c1624..9eb7c07baaed 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -1101,15 +1101,26 @@ public final class CachedAppOptimizer {
}
private void freezeProcess(ProcessRecord proc) {
- final int pid;
- final String name;
+ final int pid = proc.pid;
+ final String name = proc.processName;
final long unfrozenDuration;
final boolean frozen;
- synchronized (mAm) {
- pid = proc.pid;
- name = proc.processName;
+ try {
+ // pre-check for locks to avoid unnecessary freeze/unfreeze operations
+ if (Process.hasFileLocks(pid)) {
+ if (DEBUG_FREEZER) {
+ Slog.d(TAG_AM, name + " (" + pid + ") holds file locks, not freezing");
+ }
+ return;
+ }
+ } catch (IOException e) {
+ Slog.e(TAG_AM, "Not freezing. Unable to check file locks for " + name + "(" + pid
+ + "): " + e);
+ return;
+ }
+ synchronized (mAm) {
if (proc.curAdj < ProcessList.CACHED_APP_MIN_ADJ
|| proc.shouldNotFreeze) {
if (DEBUG_FREEZER) {
@@ -1141,29 +1152,50 @@ public final class CachedAppOptimizer {
frozen = proc.frozen;
}
- if (frozen) {
- if (DEBUG_FREEZER) {
- Slog.d(TAG_AM, "froze " + pid + " " + name);
- }
+ if (!frozen) {
+ return;
+ }
- EventLog.writeEvent(EventLogTags.AM_FREEZE, pid, name);
- try {
- freezeBinder(pid, true);
- } catch (RuntimeException e) {
- Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name);
- proc.kill("Unable to freeze binder interface",
- ApplicationExitInfo.REASON_OTHER,
- ApplicationExitInfo.SUBREASON_INVALID_STATE, true);
- }
+ if (DEBUG_FREEZER) {
+ Slog.d(TAG_AM, "froze " + pid + " " + name);
+ }
+
+ EventLog.writeEvent(EventLogTags.AM_FREEZE, pid, name);
+
+ try {
+ freezeBinder(pid, true);
+ } catch (RuntimeException e) {
+ Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name);
+ proc.kill("Unable to freeze binder interface",
+ ApplicationExitInfo.REASON_OTHER,
+ ApplicationExitInfo.SUBREASON_INVALID_STATE, true);
+ }
+
+ // See above for why we're not taking mPhenotypeFlagLock here
+ if (mRandom.nextFloat() < mFreezerStatsdSampleRate) {
+ FrameworkStatsLog.write(FrameworkStatsLog.APP_FREEZE_CHANGED,
+ FrameworkStatsLog.APP_FREEZE_CHANGED__ACTION__FREEZE_APP,
+ pid,
+ name,
+ unfrozenDuration);
+ }
+
+ try {
+ // post-check to prevent races
+ if (Process.hasFileLocks(pid)) {
+ if (DEBUG_FREEZER) {
+ Slog.d(TAG_AM, name + " (" + pid + ") holds file locks, reverting freeze");
+ }
- // See above for why we're not taking mPhenotypeFlagLock here
- if (mRandom.nextFloat() < mFreezerStatsdSampleRate) {
- FrameworkStatsLog.write(FrameworkStatsLog.APP_FREEZE_CHANGED,
- FrameworkStatsLog.APP_FREEZE_CHANGED__ACTION__FREEZE_APP,
- pid,
- name,
- unfrozenDuration);
+ synchronized (mAm) {
+ unfreezeAppLocked(proc);
+ }
+ }
+ } catch (IOException e) {
+ Slog.e(TAG_AM, "Unable to check file locks for " + name + "(" + pid + "): " + e);
+ synchronized (mAm) {
+ unfreezeAppLocked(proc);
}
}
}
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index e90423c2566a..d4d01652e338 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -1431,7 +1431,9 @@ class ProcessRecord implements WindowProcessListener {
void setActiveInstrumentation(ActiveInstrumentation instr) {
mInstr = instr;
boolean isInstrumenting = instr != null;
- mWindowProcessController.setInstrumenting(isInstrumenting,
+ mWindowProcessController.setInstrumenting(
+ isInstrumenting,
+ isInstrumenting ? instr.mSourceUid : -1,
isInstrumenting && instr.mHasBackgroundActivityStartsPermission);
}
@@ -2069,6 +2071,20 @@ class ProcessRecord implements WindowProcessListener {
}
if (!mAllowStartFgs) {
+ if (mService.mInternal != null) {
+ mAllowStartFgs = mService.mInternal.isAssociatedCompanionApp(
+ UserHandle.getUserId(info.uid), info.uid);
+ }
+ }
+
+ if (!mAllowStartFgs) {
+ // Is the calling UID a profile owner app?
+ if (mService.mInternal != null) {
+ mAllowStartFgs = mService.mInternal.isProfileOwner(info.uid);
+ }
+ }
+
+ if (!mAllowStartFgs) {
// uid is on DeviceIdleController's user/system allowlist
// or AMS's FgsStartTempAllowList.
mAllowStartFgs = mService.isWhitelistedForFgsStartLocked(info.uid);
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index cf8bfbc547f8..b15a8869b22a 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -38,6 +38,7 @@ import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceReceiver;
+import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.PromptInfo;
import android.hardware.biometrics.SensorPropertiesInternal;
@@ -296,6 +297,19 @@ public class AuthService extends SystemService {
}
@Override
+ public void invalidateAuthenticatorIds(int userId, int fromSensorId,
+ IInvalidationCallback callback) throws RemoteException {
+ checkInternalPermission();
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mBiometricService.invalidateAuthenticatorIds(userId, fromSensorId, callback);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public long[] getAuthenticatorIds() throws RemoteException {
// In this method, we're not checking whether the caller is permitted to use face
// API because current authenticator ID is leaked (in a more contrived way) via Android
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index a81abcd3b510..fd5ada0ff9be 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -42,6 +42,7 @@ import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceReceiver;
import android.hardware.biometrics.IBiometricSysuiReceiver;
+import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.PromptInfo;
import android.hardware.biometrics.SensorPropertiesInternal;
@@ -60,6 +61,7 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.security.KeyStore;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.Pair;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
@@ -78,6 +80,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
+import java.util.Set;
/**
* System service that arbitrates the modality for BiometricPrompt to use.
@@ -240,6 +243,72 @@ public class BiometricService extends SystemService {
}
};
+ /**
+ * Tracks authenticatorId invalidation. For more details, see
+ * {@link com.android.server.biometrics.sensors.InvalidationRequesterClient}.
+ */
+ @VisibleForTesting
+ static class InvalidationTracker {
+ @NonNull private final IInvalidationCallback mClientCallback;
+ @NonNull private final Set<Integer> mSensorsPendingInvalidation;
+
+ public static InvalidationTracker start(@NonNull ArrayList<BiometricSensor> sensors,
+ int userId, int fromSensorId, @NonNull IInvalidationCallback clientCallback) {
+ return new InvalidationTracker(sensors, userId, fromSensorId, clientCallback);
+ }
+
+ private InvalidationTracker(@NonNull ArrayList<BiometricSensor> sensors, int userId,
+ int fromSensorId, @NonNull IInvalidationCallback clientCallback) {
+ mClientCallback = clientCallback;
+ mSensorsPendingInvalidation = new ArraySet<>();
+
+ for (BiometricSensor sensor : sensors) {
+ if (sensor.id == fromSensorId) {
+ continue;
+ }
+
+ if (!Utils.isAtLeastStrength(sensor.oemStrength, Authenticators.BIOMETRIC_STRONG)) {
+ continue;
+ }
+
+ Slog.d(TAG, "Requesting authenticatorId invalidation for sensor: " + sensor.id);
+
+ synchronized (this) {
+ mSensorsPendingInvalidation.add(sensor.id);
+ }
+
+ try {
+ sensor.impl.invalidateAuthenticatorId(userId, new IInvalidationCallback.Stub() {
+ @Override
+ public void onCompleted() {
+ onInvalidated(sensor.id);
+ }
+ });
+ } catch (RemoteException e) {
+ Slog.d(TAG, "RemoteException", e);
+ }
+ }
+ }
+
+ @VisibleForTesting
+ void onInvalidated(int sensorId) {
+ synchronized (this) {
+ mSensorsPendingInvalidation.remove(sensorId);
+
+ Slog.d(TAG, "Sensor " + sensorId + " invalidated, remaining size: "
+ + mSensorsPendingInvalidation.size());
+
+ if (mSensorsPendingInvalidation.isEmpty()) {
+ try {
+ mClientCallback.onCompleted();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote Exception", e);
+ }
+ }
+ }
+ }
+ }
+
@VisibleForTesting
public static class SettingObserver extends ContentObserver {
@@ -668,6 +737,14 @@ public class BiometricService extends SystemService {
}
}
+ @Override
+ public void invalidateAuthenticatorIds(int userId, int fromSensorId,
+ IInvalidationCallback callback) {
+ checkInternalPermission();
+
+ InvalidationTracker.start(mSensors, userId, fromSensorId, callback);
+ }
+
@Override // Binder call
public long[] getAuthenticatorIds(int callingUserId) {
checkInternalPermission();
diff --git a/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java b/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java
index ca34eee17f7f..d95fa236fcef 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java
@@ -18,8 +18,10 @@ package com.android.server.biometrics.sensors;
import android.annotation.NonNull;
import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.IInvalidationCallback;
/**
* ClientMonitor subclass responsible for coordination of authenticatorId invalidation of other
@@ -53,25 +55,39 @@ import android.hardware.biometrics.BiometricsProtoEnums;
* switches, the framework can check if any sensor has the "invalidationInProgress" flag set. If so,
* the framework should re-start the invalidation process described above.
*/
-public abstract class InvalidationRequesterClient<T> extends ClientMonitor<T> {
+public abstract class InvalidationRequesterClient<S extends BiometricAuthenticator.Identifier, T>
+ extends ClientMonitor<T> {
private final BiometricManager mBiometricManager;
+ @NonNull private final BiometricUtils<S> mUtils;
+
+ @NonNull private final IInvalidationCallback mInvalidationCallback =
+ new IInvalidationCallback.Stub() {
+ @Override
+ public void onCompleted() {
+ mUtils.setInvalidationInProgress(getContext(), getTargetUserId(),
+ false /* inProgress */);
+ mCallback.onClientFinished(InvalidationRequesterClient.this, true /* success */);
+ }
+ };
public InvalidationRequesterClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
- int userId, int sensorId) {
+ int userId, int sensorId, @NonNull BiometricUtils<S> utils) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId,
context.getOpPackageName(), 0 /* cookie */, sensorId,
BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN);
mBiometricManager = context.getSystemService(BiometricManager.class);
+ mUtils = utils;
}
@Override
public void start(@NonNull Callback callback) {
super.start(callback);
- // TODO(b/159667191): Request BiometricManager/BiometricService to invalidate
- // authenticatorIds. Be sure to invoke BiometricUtils#setInvalidationInProgress(true)
+ mUtils.setInvalidationInProgress(getContext(), getTargetUserId(), true /* inProgress */);
+ mBiometricManager.invalidateAuthenticatorIds(getTargetUserId(), getSensorId(),
+ mInvalidationCallback);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
index f07bf1e236b8..54ab2e564676 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
@@ -19,13 +19,13 @@ package com.android.server.biometrics.sensors.face;
import android.annotation.NonNull;
import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IBiometricSensorReceiver;
+import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.SensorPropertiesInternal;
import android.hardware.face.IFaceService;
import android.os.IBinder;
import android.os.RemoteException;
-import com.android.server.biometrics.SensorConfig;
import com.android.server.biometrics.sensors.LockoutTracker;
/**
@@ -87,6 +87,12 @@ public final class FaceAuthenticator extends IBiometricAuthenticator.Stub {
}
@Override
+ public void invalidateAuthenticatorId(int userId, IInvalidationCallback callback)
+ throws RemoteException {
+ mFaceService.invalidateAuthenticatorId(mSensorId, userId, callback);
+ }
+
+ @Override
public @LockoutTracker.LockoutMode int getLockoutModeForUser(int userId)
throws RemoteException {
return mFaceService.getLockoutModeForUser(mSensorId, userId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index cb56e8cd4b7f..f055d559cc83 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -29,6 +29,7 @@ import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.SensorProps;
@@ -54,10 +55,10 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.server.ServiceThread;
import com.android.server.SystemService;
import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.sensors.BiometricServiceCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
-import com.android.server.biometrics.sensors.BiometricServiceCallback;
import com.android.server.biometrics.sensors.face.aidl.FaceProvider;
import com.android.server.biometrics.sensors.face.hidl.Face10;
@@ -503,6 +504,19 @@ public class FaceService extends SystemService implements BiometricServiceCallba
return provider.getLockoutModeForUser(sensorId, userId);
}
+ @Override
+ public void invalidateAuthenticatorId(int sensorId, int userId,
+ IInvalidationCallback callback) {
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+
+ final ServiceProvider provider = getProviderForSensor(sensorId);
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for invalidateAuthenticatorId");
+ return;
+ }
+ provider.scheduleInvalidateAuthenticatorId(sensorId, userId, callback);
+ }
+
@Override // Binder call
public long getAuthenticatorId(int sensorId, int userId) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
index be4e2482acc9..51b427d772a1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
@@ -18,6 +18,7 @@ package com.android.server.biometrics.sensors.face;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.face.Face;
import android.hardware.face.FaceManager;
@@ -71,6 +72,16 @@ public interface ServiceProvider {
@LockoutTracker.LockoutMode
int getLockoutModeForUser(int sensorId, int userId);
+ /**
+ * Requests for the authenticatorId (whose source of truth is in the TEE or equivalent) to
+ * be invalidated. See {@link com.android.server.biometrics.sensors.InvalidationRequesterClient}
+ */
+ default void scheduleInvalidateAuthenticatorId(int sensorId, int userId,
+ @NonNull IInvalidationCallback callback) {
+ throw new IllegalStateException("Providers that support invalidation must override"
+ + " this method");
+ }
+
long getAuthenticatorId(int sensorId, int userId);
boolean isHardwareDetected(int sensorId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
index d9d473722fd5..f9e31063e595 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
@@ -375,7 +375,7 @@ public class Sensor implements IBinder.DeathRecipient {
}
@Override
- public void onAuthenticatorIdInvalidated() {
+ public void onAuthenticatorIdInvalidated(long newAuthenticatorId) {
// TODO(b/159667191)
}
@@ -460,6 +460,7 @@ public class Sensor implements IBinder.DeathRecipient {
final long sensorToken = proto.start(SensorServiceStateProto.SENSOR_STATES);
proto.write(SensorStateProto.SENSOR_ID, mSensorProperties.sensorId);
+ proto.write(SensorStateProto.MODALITY, SensorStateProto.FACE);
proto.write(SensorStateProto.IS_BUSY, mScheduler.getCurrentClient() != null);
for (UserInfo user : UserManager.get(mContext).getUsers()) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index c4e4d1fe0f82..10b12cb22e85 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -770,6 +770,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final long sensorToken = proto.start(SensorServiceStateProto.SENSOR_STATES);
proto.write(SensorStateProto.SENSOR_ID, mSensorProperties.sensorId);
+ proto.write(SensorStateProto.MODALITY, SensorStateProto.FACE);
proto.write(SensorStateProto.IS_BUSY, mScheduler.getCurrentClient() != null);
for (UserInfo user : UserManager.get(mContext).getUsers()) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
index d4cdc8b18898..312a3ba80d69 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
@@ -19,13 +19,13 @@ package com.android.server.biometrics.sensors.fingerprint;
import android.annotation.NonNull;
import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IBiometricSensorReceiver;
+import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.SensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintService;
import android.os.IBinder;
import android.os.RemoteException;
-import com.android.server.biometrics.SensorConfig;
import com.android.server.biometrics.sensors.LockoutTracker;
/**
@@ -94,6 +94,12 @@ public final class FingerprintAuthenticator extends IBiometricAuthenticator.Stub
}
@Override
+ public void invalidateAuthenticatorId(int userId, IInvalidationCallback callback)
+ throws RemoteException {
+ mFingerprintService.invalidateAuthenticatorId(mSensorId, userId, callback);
+ }
+
+ @Override
public long getAuthenticatorId(int callingUserId) throws RemoteException {
return mFingerprintService.getAuthenticatorId(mSensorId, callingUserId);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index e6cdbd28f9d1..d541eb3bd176 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -36,6 +36,7 @@ import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.SensorProps;
@@ -571,6 +572,19 @@ public class FingerprintService extends SystemService implements BiometricServic
return provider.getLockoutModeForUser(sensorId, userId);
}
+ @Override
+ public void invalidateAuthenticatorId(int sensorId, int userId,
+ IInvalidationCallback callback) {
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+
+ final ServiceProvider provider = getProviderForSensor(sensorId);
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for invalidateAuthenticatorId");
+ return;
+ }
+ provider.scheduleInvalidateAuthenticatorId(sensorId, userId, callback);
+ }
+
@Override // Binder call
public long getAuthenticatorId(int sensorId, int userId) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
index d94c98481eeb..272e2b277941 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -18,6 +18,7 @@ package com.android.server.biometrics.sensors.fingerprint;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
@@ -109,6 +110,16 @@ public interface ServiceProvider {
@LockoutTracker.LockoutMode
int getLockoutModeForUser(int sensorId, int userId);
+ /**
+ * Requests for the authenticatorId (whose source of truth is in the TEE or equivalent) to
+ * be invalidated. See {@link com.android.server.biometrics.sensors.InvalidationRequesterClient}
+ */
+ default void scheduleInvalidateAuthenticatorId(int sensorId, int userId,
+ @NonNull IInvalidationCallback callback) {
+ throw new IllegalStateException("Providers that support invalidation must override"
+ + " this method");
+ }
+
long getAuthenticatorId(int sensorId, int userId);
void onPointerDown(int sensorId, int x, int y, float minor, float major);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
index ecb998594d44..bb0f9830603c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
@@ -388,7 +388,7 @@ class Sensor implements IBinder.DeathRecipient {
}
@Override
- public void onAuthenticatorIdInvalidated() {
+ public void onAuthenticatorIdInvalidated(long newAuthenticatorId) {
// TODO(159667191)
}
}
@@ -473,6 +473,7 @@ class Sensor implements IBinder.DeathRecipient {
final long sensorToken = proto.start(SensorServiceStateProto.SENSOR_STATES);
proto.write(SensorStateProto.SENSOR_ID, mSensorProperties.sensorId);
+ proto.write(SensorStateProto.MODALITY, SensorStateProto.FINGERPRINT);
proto.write(SensorStateProto.IS_BUSY, mScheduler.getCurrentClient() != null);
for (UserInfo user : UserManager.get(mContext).getUsers()) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index f5ce8943c188..7989e6e43d7e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -716,6 +716,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
final long sensorToken = proto.start(SensorServiceStateProto.SENSOR_STATES);
proto.write(SensorStateProto.SENSOR_ID, mSensorProperties.sensorId);
+ proto.write(SensorStateProto.MODALITY, SensorStateProto.FINGERPRINT);
proto.write(SensorStateProto.IS_BUSY, mScheduler.getCurrentClient() != null);
for (UserInfo user : UserManager.get(mContext).getUsers()) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java
index 6f437a7c31d2..3e5b88cca1e4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java
@@ -19,13 +19,13 @@ package com.android.server.biometrics.sensors.iris;
import android.annotation.NonNull;
import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IBiometricSensorReceiver;
+import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.SensorPropertiesInternal;
import android.hardware.iris.IIrisService;
import android.os.IBinder;
import android.os.RemoteException;
-import com.android.server.biometrics.SensorConfig;
import com.android.server.biometrics.sensors.LockoutTracker;
/**
@@ -86,6 +86,10 @@ public final class IrisAuthenticator extends IBiometricAuthenticator.Stub {
}
@Override
+ public void invalidateAuthenticatorId(int userId, IInvalidationCallback callback) {
+ }
+
+ @Override
public long getAuthenticatorId(int callingUserId) throws RemoteException {
return 0;
}
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index 18907a19f96d..9ba957ef27ae 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -64,7 +64,7 @@ public final class CompatChange extends CompatibilityChangeInfo {
private Map<String, Boolean> mDeferredOverrides;
public CompatChange(long changeId) {
- this(changeId, null, -1, -1, false, false, null);
+ this(changeId, null, -1, -1, false, false, null, false);
}
/**
@@ -77,9 +77,10 @@ public final class CompatChange extends CompatibilityChangeInfo {
* @param disabled If {@code true}, overrides any {@code enableAfterTargetSdk} set.
*/
public CompatChange(long changeId, @Nullable String name, int enableAfterTargetSdk,
- int enableSinceTargetSdk, boolean disabled, boolean loggingOnly, String description) {
+ int enableSinceTargetSdk, boolean disabled, boolean loggingOnly, String description,
+ boolean overridable) {
super(changeId, name, enableAfterTargetSdk, enableSinceTargetSdk, disabled, loggingOnly,
- description);
+ description, overridable);
}
/**
@@ -88,7 +89,7 @@ public final class CompatChange extends CompatibilityChangeInfo {
public CompatChange(Change change) {
super(change.getId(), change.getName(), change.getEnableAfterTargetSdk(),
change.getEnableSinceTargetSdk(), change.getDisabled(), change.getLoggingOnly(),
- change.getDescription());
+ change.getDescription(), change.getOverridable());
}
void registerListener(ChangeListener listener) {
@@ -274,6 +275,9 @@ public final class CompatChange extends CompatibilityChangeInfo {
if (mDeferredOverrides != null && mDeferredOverrides.size() > 0) {
sb.append("; deferredOverrides=").append(mDeferredOverrides);
}
+ if (getOverridable()) {
+ sb.append("; overridable");
+ }
return sb.append(")").toString();
}
diff --git a/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java b/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java
index 995bb2422de2..8cd1fd6f2b64 100644
--- a/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java
+++ b/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java
@@ -17,6 +17,8 @@
package com.android.server.connectivity;
import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
import android.net.metrics.DefaultNetworkEvent;
import android.os.SystemClock;
@@ -61,7 +63,7 @@ public class DefaultNetworkMetrics {
private int mLastTransports;
public DefaultNetworkMetrics() {
- newDefaultNetwork(creationTimeMs, null);
+ newDefaultNetwork(creationTimeMs, null, 0, false, null, null);
}
public synchronized void listEvents(PrintWriter pw) {
@@ -117,13 +119,21 @@ public class DefaultNetworkMetrics {
mCurrentDefaultNetwork.validatedMs += timeMs - mLastValidationTimeMs;
}
- public synchronized void logDefaultNetworkEvent(
- long timeMs, NetworkAgentInfo newNai, NetworkAgentInfo oldNai) {
- logCurrentDefaultNetwork(timeMs, oldNai);
- newDefaultNetwork(timeMs, newNai);
+ /**
+ * Logs a default network event.
+ * @see {IpConnectivityLog#logDefaultNetworkEvent}.
+ */
+ public synchronized void logDefaultNetworkEvent(long timeMs, Network defaultNetwork, int score,
+ boolean validated, LinkProperties lp, NetworkCapabilities nc,
+ Network previousDefaultNetwork, int previousScore, LinkProperties previousLp,
+ NetworkCapabilities previousNc) {
+ logCurrentDefaultNetwork(timeMs, previousDefaultNetwork, previousScore, previousLp,
+ previousNc);
+ newDefaultNetwork(timeMs, defaultNetwork, score, validated, lp, nc);
}
- private void logCurrentDefaultNetwork(long timeMs, NetworkAgentInfo oldNai) {
+ private void logCurrentDefaultNetwork(long timeMs, Network network, int score,
+ LinkProperties lp, NetworkCapabilities nc) {
if (mIsCurrentlyValid) {
updateValidationTime(timeMs);
}
@@ -131,10 +141,10 @@ public class DefaultNetworkMetrics {
ev.updateDuration(timeMs);
ev.previousTransports = mLastTransports;
// oldNai is null if the system had no default network before the transition.
- if (oldNai != null) {
+ if (network != null) {
// The system acquired a new default network.
- fillLinkInfo(ev, oldNai);
- ev.finalScore = oldNai.getCurrentScore();
+ fillLinkInfo(ev, network, lp, nc);
+ ev.finalScore = score;
}
// Only change transport of the previous default network if the event currently logged
// corresponds to an existing default network, and not to the absence of a default network.
@@ -147,14 +157,15 @@ public class DefaultNetworkMetrics {
mEventsLog.append(ev);
}
- private void newDefaultNetwork(long timeMs, NetworkAgentInfo newNai) {
+ private void newDefaultNetwork(long timeMs, Network network, int score, boolean validated,
+ LinkProperties lp, NetworkCapabilities nc) {
DefaultNetworkEvent ev = new DefaultNetworkEvent(timeMs);
ev.durationMs = timeMs;
// newNai is null if the system has no default network after the transition.
- if (newNai != null) {
- fillLinkInfo(ev, newNai);
- ev.initialScore = newNai.getCurrentScore();
- if (newNai.lastValidated) {
+ if (network != null) {
+ fillLinkInfo(ev, network, lp, nc);
+ ev.initialScore = score;
+ if (validated) {
mIsCurrentlyValid = true;
mLastValidationTimeMs = timeMs;
}
@@ -164,10 +175,10 @@ public class DefaultNetworkMetrics {
mCurrentDefaultNetwork = ev;
}
- private static void fillLinkInfo(DefaultNetworkEvent ev, NetworkAgentInfo nai) {
- LinkProperties lp = nai.linkProperties;
- ev.netId = nai.network().getNetId();
- ev.transports |= BitUtils.packBits(nai.networkCapabilities.getTransportTypes());
+ private static void fillLinkInfo(DefaultNetworkEvent ev, Network network, LinkProperties lp,
+ NetworkCapabilities nc) {
+ ev.netId = network.getNetId();
+ ev.transports |= BitUtils.packBits(nc.getTransportTypes());
ev.ipv4 |= lp.hasIpv4Address() && lp.hasIpv4DefaultRoute();
ev.ipv6 |= lp.hasGlobalIpv6Address() && lp.hasIpv6DefaultRoute();
}
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
index b5d875d5c162..1024556c17eb 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
@@ -20,11 +20,15 @@ import android.content.Context;
import android.net.ConnectivityMetricsEvent;
import android.net.IIpConnectivityMetrics;
import android.net.INetdEventCallback;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
import android.net.NetworkStack;
import android.net.metrics.ApfProgramEvent;
import android.net.metrics.IpConnectivityLog;
import android.os.Binder;
import android.os.Process;
+import android.os.SystemClock;
import android.provider.Settings;
import android.text.TextUtils;
import android.text.format.DateUtils;
@@ -122,8 +126,6 @@ final public class IpConnectivityMetrics extends SystemService {
public IpConnectivityMetrics(Context ctx, ToIntFunction<Context> capacityGetter) {
super(ctx);
- // Load JNI libraries used by the IpConnectivityMetrics service and its dependencies
- System.loadLibrary("service-connectivity");
mCapacityGetter = capacityGetter;
initBuffer();
}
@@ -363,6 +365,21 @@ final public class IpConnectivityMetrics extends SystemService {
}
return mNetdListener.removeNetdEventCallback(callerType);
}
+
+ @Override
+ public void logDefaultNetworkValidity(boolean valid) {
+ mDefaultNetworkMetrics.logDefaultNetworkValidity(SystemClock.elapsedRealtime(), valid);
+ }
+
+ @Override
+ public void logDefaultNetworkEvent(Network defaultNetwork, int score, boolean validated,
+ LinkProperties lp, NetworkCapabilities nc, Network previousDefaultNetwork,
+ int previousScore, LinkProperties previousLp, NetworkCapabilities previousNc) {
+ final long timeMs = SystemClock.elapsedRealtime();
+ mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs, defaultNetwork, score, validated,
+ lp, nc, previousDefaultNetwork, previousScore, previousLp, previousNc);
+ }
+
};
private static final ToIntFunction<Context> READ_BUFFER_SIZE = (ctx) -> {
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 55d8279a92d0..b0a73f105725 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -189,13 +189,18 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
// Set to true when partial connectivity was detected.
public boolean partialConnectivity;
- // Captive portal info of the network, if any.
+ // Captive portal info of the network from RFC8908, if any.
// Obtained by ConnectivityService and merged into NetworkAgent-provided information.
- public CaptivePortalData captivePortalData;
+ public CaptivePortalData capportApiData;
// The UID of the remote entity that created this Network.
public final int creatorUid;
+ // Network agent portal info of the network, if any. This information is provided from
+ // non-RFC8908 sources, such as Wi-Fi Passpoint, which can provide information such as Venue
+ // URL, Terms & Conditions URL, and network friendly name.
+ public CaptivePortalData networkAgentPortalData;
+
// Networks are lingered when they become unneeded as a result of their NetworkRequests being
// satisfied by a higher-scoring network. so as to allow communication to wrap up before the
// network is taken down. This usually only happens to the default network. Lingering ends with
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 95918945eecd..006f87571f82 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -52,9 +52,12 @@ import com.android.server.display.utils.AmbientFilterFactory;
import com.android.server.utils.DeviceConfigInterface;
import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Date;
import java.util.List;
+import java.util.Locale;
import java.util.Objects;
@@ -1299,7 +1302,8 @@ public class DisplayModeDirector {
// mShouldObserveAmbientHighChange is true, screen is on, peak refresh rate
// changeable and low power mode off. After initialization, these states will
// be updated from the same handler thread.
- private boolean mDefaultDisplayOn = false;
+ private int mDefaultDisplayState = Display.STATE_UNKNOWN;
+ private boolean mIsDeviceActive = false;
private boolean mRefreshRateChangeable = false;
private boolean mLowPowerModeEnabled = false;
@@ -1480,7 +1484,8 @@ public class DisplayModeDirector {
pw.println(" BrightnessObserver");
pw.println(" mAmbientLux: " + mAmbientLux);
pw.println(" mBrightness: " + mBrightness);
- pw.println(" mDefaultDisplayOn: " + mDefaultDisplayOn);
+ pw.println(" mDefaultDisplayState: " + mDefaultDisplayState);
+ pw.println(" mIsDeviceActive: " + mIsDeviceActive);
pw.println(" mLowPowerModeEnabled: " + mLowPowerModeEnabled);
pw.println(" mRefreshRateChangeable: " + mRefreshRateChangeable);
pw.println(" mShouldObserveDisplayLowChange: " + mShouldObserveDisplayLowChange);
@@ -1706,14 +1711,17 @@ public class DisplayModeDirector {
private void updateDefaultDisplayState() {
Display display = mContext.getSystemService(DisplayManager.class)
.getDisplay(Display.DEFAULT_DISPLAY);
- boolean defaultDisplayOn = display != null && display.getState() != Display.STATE_OFF;
- setDefaultDisplayState(defaultDisplayOn);
+ if (display == null) {
+ return;
+ }
+
+ setDefaultDisplayState(display.getState());
}
@VisibleForTesting
- public void setDefaultDisplayState(boolean on) {
- if (mDefaultDisplayOn != on) {
- mDefaultDisplayOn = on;
+ public void setDefaultDisplayState(int state) {
+ if (mDefaultDisplayState != state) {
+ mDefaultDisplayState = state;
updateSensorStatus();
}
}
@@ -1734,15 +1742,19 @@ public class DisplayModeDirector {
}
private boolean isDeviceActive() {
- return mDefaultDisplayOn && mInjector.isDeviceInteractive(mContext);
+ mIsDeviceActive = mInjector.isDeviceInteractive(mContext);
+ return (mDefaultDisplayState == Display.STATE_ON)
+ && mIsDeviceActive;
}
private final class LightSensorEventListener implements SensorEventListener {
final private static int INJECT_EVENTS_INTERVAL_MS = LIGHT_SENSOR_RATE_MS;
private float mLastSensorData;
+ private long mTimestamp;
public void dumpLocked(PrintWriter pw) {
pw.println(" mLastSensorData: " + mLastSensorData);
+ pw.println(" mTimestamp: " + formatTimestamp(mTimestamp));
}
@Override
@@ -1766,6 +1778,7 @@ public class DisplayModeDirector {
}
long now = SystemClock.uptimeMillis();
+ mTimestamp = System.currentTimeMillis();
if (mAmbientFilter != null) {
mAmbientFilter.addValue(now, mLastSensorData);
}
@@ -1792,6 +1805,12 @@ public class DisplayModeDirector {
mHandler.removeCallbacks(mInjectSensorEventRunnable);
}
+ private String formatTimestamp(long time) {
+ SimpleDateFormat dateFormat =
+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US);
+ return dateFormat.format(new Date(time));
+ }
+
private void processSensorData(long now) {
if (mAmbientFilter != null) {
mAmbientLux = mAmbientFilter.getEstimate(now);
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 7ec4a5adb33e..e8052289cca4 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -1179,7 +1179,7 @@ public class LocationManagerService extends ILocationManager.Stub {
@Override
public int handleShellCommand(ParcelFileDescriptor in, ParcelFileDescriptor out,
ParcelFileDescriptor err, String[] args) {
- return new LocationShellCommand(this).exec(
+ return new LocationShellCommand(mContext, this).exec(
this, in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(),
args);
}
diff --git a/services/core/java/com/android/server/location/LocationShellCommand.java b/services/core/java/com/android/server/location/LocationShellCommand.java
index 7d3ccbf5a1b4..0fe66e0b4ea1 100644
--- a/services/core/java/com/android/server/location/LocationShellCommand.java
+++ b/services/core/java/com/android/server/location/LocationShellCommand.java
@@ -16,21 +16,29 @@
package com.android.server.location;
+import android.content.Context;
+import android.location.Criteria;
+import android.location.Location;
+import android.location.provider.ProviderProperties;
import android.os.UserHandle;
import com.android.modules.utils.BasicShellCommandHandler;
import java.io.PrintWriter;
+import java.util.Arrays;
import java.util.Objects;
/**
* Interprets and executes 'adb shell cmd location [args]'.
*/
class LocationShellCommand extends BasicShellCommandHandler {
+ private static final float DEFAULT_TEST_LOCATION_ACCURACY = 100.0f;
+ private final Context mContext;
private final LocationManagerService mService;
- LocationShellCommand(LocationManagerService service) {
+ LocationShellCommand(Context context, LocationManagerService service) {
+ mContext = context;
mService = Objects.requireNonNull(service);
}
@@ -47,6 +55,44 @@ class LocationShellCommand extends BasicShellCommandHandler {
mService.setLocationEnabledForUser(enabled, userId);
return 0;
}
+ case "providers": {
+ String command = getNextArgRequired();
+ return parseProvidersCommand(command);
+ }
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ }
+
+ private int parseProvidersCommand(String cmd) {
+ switch (cmd) {
+ case "add-test-provider": {
+ String provider = getNextArgRequired();
+ ProviderProperties properties = parseTestProviderProviderProperties();
+ mService.addTestProvider(provider, properties, mContext.getOpPackageName(),
+ mContext.getFeatureId());
+ return 0;
+ }
+ case "remove-test-provider": {
+ String provider = getNextArgRequired();
+ mService.removeTestProvider(provider, mContext.getOpPackageName(),
+ mContext.getFeatureId());
+ return 0;
+ }
+ case "set-test-provider-enabled": {
+ String provider = getNextArgRequired();
+ boolean enabled = Boolean.parseBoolean(getNextArgRequired());
+ mService.setTestProviderEnabled(provider, enabled, mContext.getOpPackageName(),
+ mContext.getFeatureId());
+ return 0;
+ }
+ case "set-test-provider-location": {
+ String provider = getNextArgRequired();
+ Location location = parseTestProviderLocation(provider);
+ mService.setTestProviderLocation(provider, location, mContext.getOpPackageName(),
+ mContext.getFeatureId());
+ return 0;
+ }
case "send-extra-command": {
String provider = getNextArgRequired();
String command = getNextArgRequired();
@@ -72,6 +118,120 @@ class LocationShellCommand extends BasicShellCommandHandler {
return UserHandle.USER_CURRENT_OR_SELF;
}
+ private ProviderProperties parseTestProviderProviderProperties() {
+ boolean requiresNetwork = false;
+ boolean requiresSatellite = false;
+ boolean requiresCell = false;
+ boolean hasMonetaryCost = false;
+ boolean supportsAltitude = false;
+ boolean supportsSpeed = false;
+ boolean supportsBearing = false;
+ int powerRequirement = Criteria.POWER_LOW;
+ int accuracy = Criteria.ACCURACY_FINE;
+
+ String option = getNextOption();
+ while (option != null) {
+ switch (option) {
+ case "--requiresNetwork": {
+ requiresNetwork = true;
+ break;
+ }
+ case "--requiresSatellite": {
+ requiresSatellite = true;
+ break;
+ }
+ case "--requiresCell": {
+ requiresCell = true;
+ break;
+ }
+ case "--hasMonetaryCost": {
+ hasMonetaryCost = true;
+ break;
+ }
+ case "--supportsAltitude": {
+ supportsAltitude = true;
+ break;
+ }
+ case "--supportsSpeed": {
+ supportsSpeed = true;
+ break;
+ }
+ case "--supportsBearing": {
+ supportsBearing = true;
+ break;
+ }
+ case "--powerRequirement": {
+ powerRequirement = Integer.parseInt(getNextArgRequired());
+ break;
+ }
+ case "--accuracy": {
+ accuracy = Integer.parseInt(getNextArgRequired());
+ break;
+ }
+ default:
+ throw new IllegalArgumentException(
+ "Received unexpected option: " + option);
+ }
+ option = getNextOption();
+ }
+
+ ProviderProperties properties = new ProviderProperties.Builder()
+ .setHasNetworkRequirement(requiresNetwork)
+ .setHasSatelliteRequirement(requiresSatellite)
+ .setHasCellRequirement(requiresCell)
+ .setHasMonetaryCost(hasMonetaryCost)
+ .setHasAltitudeSupport(supportsAltitude)
+ .setHasSpeedSupport(supportsSpeed)
+ .setHasBearingSupport(supportsBearing)
+ .setPowerUsage(powerRequirement)
+ .setAccuracy(accuracy)
+ .build();
+
+ return properties;
+ }
+
+ private Location parseTestProviderLocation(String provider) {
+ boolean hasLatitude = false;
+ boolean hasLongitude = false;
+
+ Location location = new Location(provider);
+ location.setAccuracy(DEFAULT_TEST_LOCATION_ACCURACY);
+ location.setTime(System.currentTimeMillis());
+
+ String option = getNextOption();
+ while (option != null) {
+ switch (option) {
+ case "--location": {
+ String[] locationInput = getNextArgRequired().split(",");
+ if (locationInput.length != 2) {
+ throw new IllegalArgumentException(
+ "Unexpected location format: " + Arrays.toString(locationInput));
+ }
+
+ location.setLatitude(Double.parseDouble(locationInput[0]));
+ location.setLongitude(Double.parseDouble(locationInput[1]));
+ break;
+ }
+ case "--accuracy": {
+ location.setAccuracy(Float.parseFloat(getNextArgRequired()));
+ break;
+ }
+ case "--time": {
+ location.setTime(Long.parseLong(getNextArgRequired()));
+ break;
+ }
+ default:
+ throw new IllegalArgumentException(
+ "Received unexpected option: " + option);
+ }
+ option = getNextOption();
+ }
+
+ location.setElapsedRealtimeNanos(System.nanoTime());
+
+ return location;
+ }
+
@Override
public void onHelp() {
PrintWriter pw = getOutPrintWriter();
@@ -80,15 +240,32 @@ class LocationShellCommand extends BasicShellCommandHandler {
pw.println(" Print this help text.");
pw.println(" set-location-enabled [--user <USER_ID>] true|false");
pw.println(" Sets the master location switch enabled state.");
- pw.println(" send-extra-command <PROVIDER> <COMMAND>");
- pw.println(" Sends the given extra command to the given provider.");
+ pw.println(" providers");
+ pw.println(" add-test-provider <PROVIDER> [--requiresNetwork] [--requiresSatellite]");
+ pw.println(" [--requiresCell] [--hasMonetaryCost] [--supportsAltitude]");
+ pw.println(" [--supportsSpeed] [--supportsBearing]");
+ pw.println(" [--powerRequirement <POWER_REQUIREMENT>]");
+ pw.println(" Add the given test provider. Requires MOCK_LOCATION permissions which");
+ pw.println(" can be enabled by running \"adb shell appops set <uid>");
+ pw.println(" android:mock_location allow\". There are optional flags that can be");
+ pw.println(" used to configure the provider properties. If no flags are included,");
+ pw.println(" then default values will be used.");
+ pw.println(" remove-test-provider <PROVIDER>");
+ pw.println(" Remove the given test provider.");
+ pw.println(" set-test-provider-enabled <PROVIDER> true|false");
+ pw.println(" Sets the given test provider enabled state.");
+ pw.println(" set-test-provider-location <PROVIDER> [--location <LATITUDE>,<LONGITUDE>]");
+ pw.println(" [--accuracy <ACCURACY>] [--time <TIME>]");
+ pw.println(" Set location for given test provider. Accuracy and time are optional.");
+ pw.println(" send-extra-command <PROVIDER> <COMMAND>");
+ pw.println(" Sends the given extra command to the given provider.");
pw.println();
- pw.println(" Common commands that may be supported by the gps provider, depending on");
- pw.println(" hardware and software configurations:");
- pw.println(" delete_aiding_data - requests deletion of any predictive aiding data");
- pw.println(" force_time_injection - requests NTP time injection to chipset");
- pw.println(" force_psds_injection - "
+ pw.println(" Common commands that may be supported by the gps provider, depending on");
+ pw.println(" hardware and software configurations:");
+ pw.println(" delete_aiding_data - requests deletion of any predictive aiding data");
+ pw.println(" force_time_injection - requests NTP time injection to chipset");
+ pw.println(" force_psds_injection - "
+ "requests predictive aiding data injection to chipset");
- pw.println(" request_power_stats - requests GNSS power stats update from chipset");
+ pw.println(" request_power_stats - requests GNSS power stats update from chipset");
}
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 7c17356fa3cc..5ed7a9650f6c 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -3862,6 +3862,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
}
+ @VisibleForTesting
+ boolean isRestrictedModeEnabled() {
+ synchronized (mUidRulesFirstLock) {
+ return mRestrictedNetworkingMode;
+ }
+ }
+
/**
* updates restricted mode state / access for all apps
* Called on initialization and when restricted mode is enabled / disabled.
@@ -4489,26 +4496,26 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final boolean isDenied = (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
final boolean isAllowed = (uidPolicy & POLICY_ALLOW_METERED_BACKGROUND) != 0;
- final int oldRule = oldUidRules & MASK_METERED_NETWORKS;
- int newRule = RULE_NONE;
+
+ // copy oldUidRules and clear out METERED_NETWORKS rules.
+ int newUidRules = oldUidRules & (~MASK_METERED_NETWORKS);
// First step: define the new rule based on user restrictions and foreground state.
if (isRestrictedByAdmin) {
- newRule = RULE_REJECT_METERED;
+ newUidRules |= RULE_REJECT_METERED;
} else if (isForeground) {
if (isDenied || (mRestrictBackground && !isAllowed)) {
- newRule = RULE_TEMPORARY_ALLOW_METERED;
+ newUidRules |= RULE_TEMPORARY_ALLOW_METERED;
} else if (isAllowed) {
- newRule = RULE_ALLOW_METERED;
+ newUidRules |= RULE_ALLOW_METERED;
}
} else {
if (isDenied) {
- newRule = RULE_REJECT_METERED;
+ newUidRules |= RULE_REJECT_METERED;
} else if (mRestrictBackground && isAllowed) {
- newRule = RULE_ALLOW_METERED;
+ newUidRules |= RULE_ALLOW_METERED;
}
}
- final int newUidRules = newRule | (oldUidRules & MASK_ALL_NETWORKS);
if (LOGV) {
Log.v(TAG, "updateRuleForRestrictBackgroundUL(" + uid + ")"
@@ -4516,8 +4523,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
+ ", isDenied=" + isDenied
+ ", isAllowed=" + isAllowed
+ ", isRestrictedByAdmin=" + isRestrictedByAdmin
- + ", oldRule=" + uidRulesToString(oldRule)
- + ", newRule=" + uidRulesToString(newRule)
+ + ", oldRule=" + uidRulesToString(oldUidRules & MASK_METERED_NETWORKS)
+ + ", newRule=" + uidRulesToString(newUidRules & MASK_METERED_NETWORKS)
+ ", newUidRules=" + uidRulesToString(newUidRules)
+ ", oldUidRules=" + uidRulesToString(oldUidRules));
}
@@ -4529,8 +4536,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
// Second step: apply bw changes based on change of state.
- if (newRule != oldRule) {
- if (hasRule(newRule, RULE_TEMPORARY_ALLOW_METERED)) {
+ if (newUidRules != oldUidRules) {
+ if (hasRule(newUidRules, RULE_TEMPORARY_ALLOW_METERED)) {
// Temporarily allow foreground app, removing from denylist if necessary
// (since bw_penalty_box prevails over bw_happy_box).
@@ -4541,7 +4548,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
if (isDenied) {
setMeteredNetworkDenylist(uid, false);
}
- } else if (hasRule(oldRule, RULE_TEMPORARY_ALLOW_METERED)) {
+ } else if (hasRule(oldUidRules, RULE_TEMPORARY_ALLOW_METERED)) {
// Remove temporary exemption from app that is not on foreground anymore.
// TODO: if statements below are used to avoid unnecessary calls to netd / iptables,
@@ -4554,18 +4561,18 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
if (isDenied || isRestrictedByAdmin) {
setMeteredNetworkDenylist(uid, true);
}
- } else if (hasRule(newRule, RULE_REJECT_METERED)
- || hasRule(oldRule, RULE_REJECT_METERED)) {
+ } else if (hasRule(newUidRules, RULE_REJECT_METERED)
+ || hasRule(oldUidRules, RULE_REJECT_METERED)) {
// Flip state because app was explicitly added or removed to denylist.
setMeteredNetworkDenylist(uid, (isDenied || isRestrictedByAdmin));
- if (hasRule(oldRule, RULE_REJECT_METERED) && isAllowed) {
+ if (hasRule(oldUidRules, RULE_REJECT_METERED) && isAllowed) {
// Since denial prevails over allowance, we need to handle the special case
// where app is allowed and denied at the same time (although such
// scenario should be blocked by the UI), then it is removed from the denylist.
setMeteredNetworkAllowlist(uid, isAllowed);
}
- } else if (hasRule(newRule, RULE_ALLOW_METERED)
- || hasRule(oldRule, RULE_ALLOW_METERED)) {
+ } else if (hasRule(newUidRules, RULE_ALLOW_METERED)
+ || hasRule(oldUidRules, RULE_ALLOW_METERED)) {
// Flip state because app was explicitly added or removed to allowlist.
setMeteredNetworkAllowlist(uid, isAllowed);
} else {
@@ -4651,8 +4658,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final boolean isForeground = isUidForegroundOnRestrictPowerUL(uid);
final boolean isWhitelisted = isWhitelistedFromPowerSaveUL(uid, mDeviceIdleMode);
- final int oldRule = oldUidRules & MASK_ALL_NETWORKS;
- int newRule = RULE_NONE;
+
+ // Copy existing uid rules and clear ALL_NETWORK rules.
+ int newUidRules = oldUidRules & (~MASK_ALL_NETWORKS);
// First step: define the new rule based on user restrictions and foreground state.
@@ -4660,14 +4668,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// by considering the foreground and non-foreground states.
if (isForeground) {
if (restrictMode) {
- newRule = RULE_ALLOW_ALL;
+ newUidRules |= RULE_ALLOW_ALL;
}
} else if (restrictMode) {
- newRule = isWhitelisted ? RULE_ALLOW_ALL : RULE_REJECT_ALL;
+ newUidRules |= isWhitelisted ? RULE_ALLOW_ALL : RULE_REJECT_ALL;
}
- final int newUidRules = (oldUidRules & MASK_METERED_NETWORKS) | newRule;
-
if (LOGV) {
Log.v(TAG, "updateRulesForPowerRestrictionsUL(" + uid + ")"
+ ", isIdle: " + isUidIdle
@@ -4675,17 +4681,18 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
+ ", mDeviceIdleMode: " + mDeviceIdleMode
+ ", isForeground=" + isForeground
+ ", isWhitelisted=" + isWhitelisted
- + ", oldRule=" + uidRulesToString(oldRule)
- + ", newRule=" + uidRulesToString(newRule)
+ + ", oldRule=" + uidRulesToString(oldUidRules & MASK_ALL_NETWORKS)
+ + ", newRule=" + uidRulesToString(newUidRules & MASK_ALL_NETWORKS)
+ ", newUidRules=" + uidRulesToString(newUidRules)
+ ", oldUidRules=" + uidRulesToString(oldUidRules));
}
// Second step: notify listeners if state changed.
- if (newRule != oldRule) {
- if (newRule == RULE_NONE || hasRule(newRule, RULE_ALLOW_ALL)) {
+ if (newUidRules != oldUidRules) {
+ if ((newUidRules & MASK_ALL_NETWORKS) == RULE_NONE || hasRule(newUidRules,
+ RULE_ALLOW_ALL)) {
if (LOGV) Log.v(TAG, "Allowing non-metered access for UID " + uid);
- } else if (hasRule(newRule, RULE_REJECT_ALL)) {
+ } else if (hasRule(newUidRules, RULE_REJECT_ALL)) {
if (LOGV) Log.v(TAG, "Rejecting non-metered access for UID " + uid);
} else {
// All scenarios should have been covered above
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
index 7bcf3183bf69..47bb8f009920 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
@@ -119,6 +119,8 @@ class NetworkPolicyManagerShellCommand extends ShellCommand {
switch(type) {
case "restrict-background":
return getRestrictBackground();
+ case "restricted-mode":
+ return getRestrictedModeState();
}
pw.println("Error: unknown get type '" + type + "'");
return -1;
@@ -255,6 +257,13 @@ class NetworkPolicyManagerShellCommand extends ShellCommand {
return listUidList("App Idle whitelisted UIDs", uids);
}
+ private int getRestrictedModeState() {
+ final PrintWriter pw = getOutPrintWriter();
+ pw.print("Restricted mode status: ");
+ pw.println(mInterface.isRestrictedModeEnabled() ? "enabled" : "disabled");
+ return 0;
+ }
+
private int getRestrictBackground() throws RemoteException {
final PrintWriter pw = getOutPrintWriter();
pw.print("Restrict background status: ");
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 4ab827908e34..ab5238f1ddbf 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -154,6 +154,7 @@ import android.app.usage.UsageStatsManagerInternal;
import android.companion.ICompanionDeviceManager;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
+import android.compat.annotation.LoggingOnly;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentProvider;
@@ -250,6 +251,7 @@ import android.widget.Toast;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.compat.IPlatformCompat;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.InstanceIdSequence;
@@ -446,6 +448,17 @@ public class NotificationManagerService extends SystemService {
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
private static final long NOTIFICATION_TRAMPOLINE_BLOCK = 167676448L;
+ /**
+ * Rate limit showing toasts, on a per package basis.
+ *
+ * It limits the number of {@link android.widget.Toast#show()} calls to prevent overburdening
+ * the user with too many toasts in a limited time. Any attempt to show more toasts than allowed
+ * in a certain time frame will result in the toast being discarded.
+ */
+ @ChangeId
+ @LoggingOnly
+ private static final long RATE_LIMIT_TOASTS = 174840628L;
+
private IActivityManager mAm;
private ActivityTaskManagerInternal mAtm;
private ActivityManager mActivityManager;
@@ -466,6 +479,7 @@ public class NotificationManagerService extends SystemService {
private UriGrantsManagerInternal mUgmInternal;
private RoleObserver mRoleObserver;
private UserManager mUm;
+ private IPlatformCompat mPlatformCompat;
private ShortcutHelper mShortcutHelper;
final IBinder mForegroundToken = new Binder();
@@ -1988,6 +2002,8 @@ public class NotificationManagerService extends SystemService {
mDeviceIdleManager = getContext().getSystemService(DeviceIdleManager.class);
mDpm = dpm;
mUm = userManager;
+ mPlatformCompat = IPlatformCompat.Stub.asInterface(
+ ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
mUiHandler = new Handler(UiThread.get().getLooper());
String[] extractorNames;
@@ -7412,6 +7428,7 @@ public class NotificationManagerService extends SystemService {
private boolean tryShowToast(ToastRecord record, boolean rateLimitingEnabled,
boolean isWithinQuota) {
if (rateLimitingEnabled && !isWithinQuota) {
+ reportCompatRateLimitingToastsChange(record.uid);
Slog.w(TAG, "Package " + record.pkg + " is above allowed toast quota, the "
+ "following toast was blocked and discarded: " + record);
return false;
@@ -7424,6 +7441,19 @@ public class NotificationManagerService extends SystemService {
return record.show();
}
+ /** Reports rate limiting toasts compat change (used when the toast was blocked). */
+ private void reportCompatRateLimitingToastsChange(int uid) {
+ final long id = Binder.clearCallingIdentity();
+ try {
+ mPlatformCompat.reportChangeByUid(RATE_LIMIT_TOASTS, uid);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unexpected exception while reporting toast was blocked due to rate"
+ + " limiting", e);
+ } finally {
+ Binder.restoreCallingIdentity(id);
+ }
+ }
+
@GuardedBy("mToastQueue")
void cancelToastLocked(int index) {
ToastRecord record = mToastQueue.get(index);
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 4d4a6c19c7cf..2a9322d2444f 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -24,8 +24,6 @@ import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static android.content.Intent.ACTION_USER_ADDED;
import static android.content.Intent.ACTION_USER_REMOVED;
import static android.content.Intent.EXTRA_REASON;
-import static android.content.om.OverlayManagerTransaction.Request.TYPE_SET_DISABLED;
-import static android.content.om.OverlayManagerTransaction.Request.TYPE_SET_ENABLED;
import static android.content.pm.PackageManager.SIGNATURE_MATCH;
import static android.os.Trace.TRACE_TAG_RRO;
import static android.os.Trace.traceBegin;
@@ -44,7 +42,6 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.om.IOverlayManager;
import android.content.om.OverlayInfo;
-import android.content.om.OverlayManagerTransaction;
import android.content.om.OverlayableInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
@@ -89,14 +86,11 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
import java.util.function.Consumer;
/**
@@ -246,13 +240,8 @@ public final class OverlayManagerService extends SystemService {
private final OverlayActorEnforcer mActorEnforcer;
- private final Consumer<PackageAndUser> mPropagateOverlayChange = (pair) -> {
- persistSettings();
- FgThread.getHandler().post(() -> {
- List<String> affectedTargets = updatePackageManager(pair.packageName, pair.userId);
- updateActivityManager(affectedTargets, pair.userId);
- broadcastActionOverlayChanged(affectedTargets, pair.userId);
- });
+ private final Consumer<PackageAndUser> mOnOverlaysChanged = (pair) -> {
+ onOverlaysChanged(pair.packageName, pair.userId);
};
public OverlayManagerService(@NonNull final Context context) {
@@ -311,11 +300,11 @@ public final class OverlayManagerService extends SystemService {
for (int i = 0; i < userCount; i++) {
final UserInfo userInfo = users.get(i);
if (!userInfo.supportsSwitchTo() && userInfo.id != UserHandle.USER_SYSTEM) {
- // Initialize any users that can't be switched to, as their state would
+ // Initialize any users that can't be switched to, as there state would
// never be setup in onSwitchUser(). We will switch to the system user right
// after this, and its state will be setup there.
final List<String> targets = mImpl.updateOverlaysForUser(users.get(i).id);
- updatePackageManager(targets, users.get(i).id);
+ updateOverlayPaths(users.get(i).id, targets);
}
}
}
@@ -333,8 +322,7 @@ public final class OverlayManagerService extends SystemService {
// any asset changes to the rest of the system
synchronized (mLock) {
final List<String> targets = mImpl.updateOverlaysForUser(newUserId);
- final List<String> affectedTargets = updatePackageManager(targets, newUserId);
- updateActivityManager(affectedTargets, newUserId);
+ updateAssets(newUserId, targets);
}
persistSettings();
} finally {
@@ -424,10 +412,10 @@ public final class OverlayManagerService extends SystemService {
try {
if (pi.isOverlayPackage()) {
mImpl.onOverlayPackageAdded(packageName, userId)
- .ifPresent(mPropagateOverlayChange);
+ .ifPresent(mOnOverlaysChanged);
} else {
mImpl.onTargetPackageAdded(packageName, userId)
- .ifPresent(mPropagateOverlayChange);
+ .ifPresent(mOnOverlaysChanged);
}
} catch (OperationFailedException e) {
Slog.e(TAG, "onPackageAdded internal error", e);
@@ -454,10 +442,10 @@ public final class OverlayManagerService extends SystemService {
try {
if (pi.isOverlayPackage()) {
mImpl.onOverlayPackageChanged(packageName, userId)
- .ifPresent(mPropagateOverlayChange);
+ .ifPresent(mOnOverlaysChanged);
} else {
mImpl.onTargetPackageChanged(packageName, userId)
- .ifPresent(mPropagateOverlayChange);
+ .ifPresent(mOnOverlaysChanged);
}
} catch (OperationFailedException e) {
Slog.e(TAG, "onPackageChanged internal error", e);
@@ -481,7 +469,7 @@ public final class OverlayManagerService extends SystemService {
if (oi != null) {
try {
mImpl.onOverlayPackageReplacing(packageName, userId)
- .ifPresent(mPropagateOverlayChange);
+ .ifPresent(mOnOverlaysChanged);
} catch (OperationFailedException e) {
Slog.e(TAG, "onPackageReplacing internal error", e);
}
@@ -506,10 +494,10 @@ public final class OverlayManagerService extends SystemService {
try {
if (pi.isOverlayPackage()) {
mImpl.onOverlayPackageReplaced(packageName, userId)
- .ifPresent(mPropagateOverlayChange);
+ .ifPresent(mOnOverlaysChanged);
} else {
mImpl.onTargetPackageReplaced(packageName, userId)
- .ifPresent(mPropagateOverlayChange);
+ .ifPresent(mOnOverlaysChanged);
}
} catch (OperationFailedException e) {
Slog.e(TAG, "onPackageReplaced internal error", e);
@@ -534,10 +522,10 @@ public final class OverlayManagerService extends SystemService {
try {
if (oi != null) {
mImpl.onOverlayPackageRemoved(packageName, userId)
- .ifPresent(mPropagateOverlayChange);
+ .ifPresent(mOnOverlaysChanged);
} else {
mImpl.onTargetPackageRemoved(packageName, userId)
- .ifPresent(mPropagateOverlayChange);
+ .ifPresent(mOnOverlaysChanged);
}
} catch (OperationFailedException e) {
Slog.e(TAG, "onPackageRemoved internal error", e);
@@ -563,7 +551,7 @@ public final class OverlayManagerService extends SystemService {
synchronized (mLock) {
targets = mImpl.updateOverlaysForUser(userId);
}
- updatePackageManager(targets, userId);
+ updateOverlayPaths(userId, targets);
} finally {
traceEnd(TRACE_TAG_RRO);
}
@@ -660,7 +648,7 @@ public final class OverlayManagerService extends SystemService {
synchronized (mLock) {
try {
mImpl.setEnabled(packageName, enable, realUserId)
- .ifPresent(mPropagateOverlayChange);
+ .ifPresent(mOnOverlaysChanged);
return true;
} catch (OperationFailedException e) {
return false;
@@ -692,7 +680,7 @@ public final class OverlayManagerService extends SystemService {
try {
mImpl.setEnabledExclusive(packageName,
false /* withinCategory */, realUserId)
- .ifPresent(mPropagateOverlayChange);
+ .ifPresent(mOnOverlaysChanged);
return true;
} catch (OperationFailedException e) {
return false;
@@ -725,7 +713,7 @@ public final class OverlayManagerService extends SystemService {
try {
mImpl.setEnabledExclusive(packageName,
true /* withinCategory */, realUserId)
- .ifPresent(mPropagateOverlayChange);
+ .ifPresent(mOnOverlaysChanged);
return true;
} catch (OperationFailedException e) {
return false;
@@ -757,7 +745,7 @@ public final class OverlayManagerService extends SystemService {
synchronized (mLock) {
try {
mImpl.setPriority(packageName, parentPackageName, realUserId)
- .ifPresent(mPropagateOverlayChange);
+ .ifPresent(mOnOverlaysChanged);
return true;
} catch (OperationFailedException e) {
return false;
@@ -787,7 +775,7 @@ public final class OverlayManagerService extends SystemService {
synchronized (mLock) {
try {
mImpl.setHighestPriority(packageName, realUserId)
- .ifPresent(mPropagateOverlayChange);
+ .ifPresent(mOnOverlaysChanged);
return true;
} catch (OperationFailedException e) {
return false;
@@ -817,7 +805,7 @@ public final class OverlayManagerService extends SystemService {
synchronized (mLock) {
try {
mImpl.setLowestPriority(packageName, realUserId)
- .ifPresent(mPropagateOverlayChange);
+ .ifPresent(mOnOverlaysChanged);
return true;
} catch (OperationFailedException e) {
return false;
@@ -870,120 +858,6 @@ public final class OverlayManagerService extends SystemService {
}
@Override
- public void commit(@NonNull final OverlayManagerTransaction transaction)
- throws RemoteException {
- try {
- traceBegin(TRACE_TAG_RRO, "OMS#commit " + transaction);
- try {
- executeAllRequests(transaction);
- } catch (Exception e) {
- final long ident = Binder.clearCallingIdentity();
- try {
- restoreSettings();
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- Slog.d(TAG, "commit failed: " + e.getMessage(), e);
- throw new SecurityException("commit failed"
- + (DEBUG ? ": " + e.getMessage() : ""));
- }
- } finally {
- traceEnd(TRACE_TAG_RRO);
- }
- }
-
- private Optional<PackageAndUser> executeRequest(
- @NonNull final OverlayManagerTransaction.Request request) throws Exception {
- final int realUserId = handleIncomingUser(request.userId, request.typeToString());
- enforceActor(request.packageName, request.typeToString(), realUserId);
-
- final long ident = Binder.clearCallingIdentity();
- try {
- switch (request.type) {
- case TYPE_SET_ENABLED:
- Optional<PackageAndUser> opt1 =
- mImpl.setEnabled(request.packageName, true, request.userId);
- Optional<PackageAndUser> opt2 =
- mImpl.setHighestPriority(request.packageName, request.userId);
- // Both setEnabled and setHighestPriority affected the same
- // target package and user: if both return non-empty
- // Optionals, they are identical
- return opt1.isPresent() ? opt1 : opt2;
- case TYPE_SET_DISABLED:
- return mImpl.setEnabled(request.packageName, false, request.userId);
- default:
- throw new IllegalArgumentException("unsupported request: " + request);
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- private void executeAllRequests(@NonNull final OverlayManagerTransaction transaction)
- throws Exception {
- if (DEBUG) {
- Slog.d(TAG, "commit " + transaction);
- }
- if (transaction == null) {
- throw new IllegalArgumentException("null transaction");
- }
-
- // map: userId -> list<targetPackageName>
- SparseArray<List<String>> affectedTargetsToUpdate = new SparseArray<>();
-
- synchronized (mLock) {
- // map: userId -> set<targetPackageName>
- SparseArray<Set<String>> targetsToUpdate = new SparseArray<>();
-
- // execute the requests (as calling user)
- for (final OverlayManagerTransaction.Request request : transaction) {
- executeRequest(request).ifPresent(target -> {
- Set<String> userTargets = targetsToUpdate.get(target.userId);
- if (userTargets == null) {
- userTargets = new ArraySet<String>();
- targetsToUpdate.put(target.userId, userTargets);
- }
- userTargets.add(target.packageName);
- });
- }
-
- // past the point of no return: the entire transaction has been
- // processed successfully, we can no longer fail: continue as
- // system_server
- final long ident = Binder.clearCallingIdentity();
- try {
- persistSettings();
-
- // inform the package manager about the new paths
- for (int index = 0; index < targetsToUpdate.size(); index++) {
- final int userId = targetsToUpdate.keyAt(index);
- final List<String> affectedTargets =
- updatePackageManager(targetsToUpdate.valueAt(index), userId);
- affectedTargetsToUpdate.put(userId, affectedTargets);
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- } // synchronized (mLock)
-
- FgThread.getHandler().post(() -> {
- final long ident = Binder.clearCallingIdentity();
- try {
- // schedule apps to refresh + broadcast the ACTION_OVERLAY_CHANGED intents
- for (int index = 0; index < affectedTargetsToUpdate.size(); index++) {
- final int userId = affectedTargetsToUpdate.keyAt(index);
- final List<String> packageNames = affectedTargetsToUpdate.valueAt(index);
-
- updateActivityManager(packageNames, userId);
- broadcastActionOverlayChanged(packageNames, userId);
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- });
- }
-
- @Override
public void onShellCommand(@NonNull final FileDescriptor in,
@NonNull final FileDescriptor out, @NonNull final FileDescriptor err,
@NonNull final String[] args, @NonNull final ShellCallback callback,
@@ -1104,7 +978,152 @@ public final class OverlayManagerService extends SystemService {
}
};
- private static final class PackageManagerHelperImpl implements PackageManagerHelper {
+ private void onOverlaysChanged(@NonNull final String targetPackageName, final int userId) {
+ persistSettings();
+ FgThread.getHandler().post(() -> {
+ updateAssets(userId, targetPackageName);
+
+ final Intent intent = new Intent(ACTION_OVERLAY_CHANGED,
+ Uri.fromParts("package", targetPackageName, null));
+ intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+
+ if (DEBUG) {
+ Slog.d(TAG, "send broadcast " + intent);
+ }
+
+ try {
+ ActivityManager.getService().broadcastIntent(null, intent, null, null, 0,
+ null, null, null, android.app.AppOpsManager.OP_NONE, null, false, false,
+ userId);
+ } catch (RemoteException e) {
+ // Intentionally left empty.
+ }
+ });
+ }
+
+ /**
+ * Updates the target packages' set of enabled overlays in PackageManager.
+ */
+ private ArrayList<String> updateOverlayPaths(int userId, List<String> targetPackageNames) {
+ try {
+ traceBegin(TRACE_TAG_RRO, "OMS#updateOverlayPaths " + targetPackageNames);
+ if (DEBUG) {
+ Slog.d(TAG, "Updating overlay assets");
+ }
+ final PackageManagerInternal pm =
+ LocalServices.getService(PackageManagerInternal.class);
+ final boolean updateFrameworkRes = targetPackageNames.contains("android");
+ if (updateFrameworkRes) {
+ targetPackageNames = pm.getTargetPackageNames(userId);
+ }
+
+ final Map<String, List<String>> pendingChanges =
+ new ArrayMap<>(targetPackageNames.size());
+ synchronized (mLock) {
+ final List<String> frameworkOverlays =
+ mImpl.getEnabledOverlayPackageNames("android", userId);
+ final int n = targetPackageNames.size();
+ for (int i = 0; i < n; i++) {
+ final String targetPackageName = targetPackageNames.get(i);
+ List<String> list = new ArrayList<>();
+ if (!"android".equals(targetPackageName)) {
+ list.addAll(frameworkOverlays);
+ }
+ list.addAll(mImpl.getEnabledOverlayPackageNames(targetPackageName, userId));
+ pendingChanges.put(targetPackageName, list);
+ }
+ }
+
+ final HashSet<String> updatedPackages = new HashSet<>();
+ final int n = targetPackageNames.size();
+ for (int i = 0; i < n; i++) {
+ final String targetPackageName = targetPackageNames.get(i);
+ if (DEBUG) {
+ Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=["
+ + TextUtils.join(",", pendingChanges.get(targetPackageName))
+ + "] userId=" + userId);
+ }
+
+ if (!pm.setEnabledOverlayPackages(
+ userId, targetPackageName, pendingChanges.get(targetPackageName),
+ updatedPackages)) {
+ Slog.e(TAG, String.format("Failed to change enabled overlays for %s user %d",
+ targetPackageName, userId));
+ }
+ }
+ return new ArrayList<>(updatedPackages);
+ } finally {
+ traceEnd(TRACE_TAG_RRO);
+ }
+ }
+
+ private void updateAssets(final int userId, final String targetPackageName) {
+ updateAssets(userId, Collections.singletonList(targetPackageName));
+ }
+
+ private void updateAssets(final int userId, List<String> targetPackageNames) {
+ final IActivityManager am = ActivityManager.getService();
+ try {
+ final ArrayList<String> updatedPaths = updateOverlayPaths(userId, targetPackageNames);
+ am.scheduleApplicationInfoChanged(updatedPaths, userId);
+ } catch (RemoteException e) {
+ // Intentionally left empty.
+ }
+ }
+
+ private void persistSettings() {
+ if (DEBUG) {
+ Slog.d(TAG, "Writing overlay settings");
+ }
+ synchronized (mLock) {
+ FileOutputStream stream = null;
+ try {
+ stream = mSettingsFile.startWrite();
+ mSettings.persist(stream);
+ mSettingsFile.finishWrite(stream);
+ } catch (IOException | XmlPullParserException e) {
+ mSettingsFile.failWrite(stream);
+ Slog.e(TAG, "failed to persist overlay state", e);
+ }
+ }
+ }
+
+ private void restoreSettings() {
+ try {
+ traceBegin(TRACE_TAG_RRO, "OMS#restoreSettings");
+ synchronized (mLock) {
+ if (!mSettingsFile.getBaseFile().exists()) {
+ return;
+ }
+ try (FileInputStream stream = mSettingsFile.openRead()) {
+ mSettings.restore(stream);
+
+ // We might have data for dying users if the device was
+ // restarted before we received USER_REMOVED. Remove data for
+ // users that will not exist after the system is ready.
+
+ final List<UserInfo> liveUsers = mUserManager.getUsers(true /*excludeDying*/);
+ final int[] liveUserIds = new int[liveUsers.size()];
+ for (int i = 0; i < liveUsers.size(); i++) {
+ liveUserIds[i] = liveUsers.get(i).getUserHandle().getIdentifier();
+ }
+ Arrays.sort(liveUserIds);
+
+ for (int userId : mSettings.getUsers()) {
+ if (Arrays.binarySearch(liveUserIds, userId) < 0) {
+ mSettings.removeUser(userId);
+ }
+ }
+ } catch (IOException | XmlPullParserException e) {
+ Slog.e(TAG, "failed to restore overlay state", e);
+ }
+ }
+ } finally {
+ traceEnd(TRACE_TAG_RRO);
+ }
+ }
+
+ private static final class PackageManagerHelperImpl implements PackageManagerHelper {
private final Context mContext;
private final IPackageManager mPackageManager;
@@ -1314,151 +1333,4 @@ public final class OverlayManagerService extends SystemService {
}
}
}
-
- // Helper methods to update other parts of the system or read/write
- // settings: these methods should never call into each other!
-
- private void broadcastActionOverlayChanged(@NonNull final Collection<String> packageNames,
- final int userId) {
- for (final String packageName : packageNames) {
- broadcastActionOverlayChanged(packageName, userId);
- }
- }
-
- private void broadcastActionOverlayChanged(@NonNull final String targetPackageName,
- final int userId) {
- final Intent intent = new Intent(ACTION_OVERLAY_CHANGED,
- Uri.fromParts("package", targetPackageName, null));
- intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- try {
- ActivityManager.getService().broadcastIntent(null, intent, null, null, 0, null, null,
- null, android.app.AppOpsManager.OP_NONE, null, false, false, userId);
- } catch (RemoteException e) {
- // Intentionally left empty.
- }
- }
-
- /**
- * Tell the activity manager to tell a set of packages to reload their
- * resources.
- */
- private void updateActivityManager(List<String> targetPackageNames, final int userId) {
- final IActivityManager am = ActivityManager.getService();
- try {
- am.scheduleApplicationInfoChanged(targetPackageNames, userId);
- } catch (RemoteException e) {
- // Intentionally left empty.
- }
- }
-
- private ArrayList<String> updatePackageManager(String targetPackageNames, final int userId) {
- return updatePackageManager(Collections.singletonList(targetPackageNames), userId);
- }
-
- /**
- * Updates the target packages' set of enabled overlays in PackageManager.
- * @return the package names of affected targets (a superset of
- * targetPackageNames: the target themserlves and shared libraries)
- */
- private ArrayList<String> updatePackageManager(@NonNull Collection<String> targetPackageNames,
- final int userId) {
- try {
- traceBegin(TRACE_TAG_RRO, "OMS#updatePackageManager " + targetPackageNames);
- if (DEBUG) {
- Slog.d(TAG, "Update package manager about changed overlays");
- }
- final PackageManagerInternal pm =
- LocalServices.getService(PackageManagerInternal.class);
- final boolean updateFrameworkRes = targetPackageNames.contains("android");
- if (updateFrameworkRes) {
- targetPackageNames = pm.getTargetPackageNames(userId);
- }
-
- final Map<String, List<String>> pendingChanges =
- new ArrayMap<>(targetPackageNames.size());
- synchronized (mLock) {
- final List<String> frameworkOverlays =
- mImpl.getEnabledOverlayPackageNames("android", userId);
- for (final String targetPackageName : targetPackageNames) {
- List<String> list = new ArrayList<>();
- if (!"android".equals(targetPackageName)) {
- list.addAll(frameworkOverlays);
- }
- list.addAll(mImpl.getEnabledOverlayPackageNames(targetPackageName, userId));
- pendingChanges.put(targetPackageName, list);
- }
- }
-
- final HashSet<String> updatedPackages = new HashSet<>();
- for (final String targetPackageName : targetPackageNames) {
- if (DEBUG) {
- Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=["
- + TextUtils.join(",", pendingChanges.get(targetPackageName))
- + "] userId=" + userId);
- }
-
- if (!pm.setEnabledOverlayPackages(
- userId, targetPackageName, pendingChanges.get(targetPackageName),
- updatedPackages)) {
- Slog.e(TAG, String.format("Failed to change enabled overlays for %s user %d",
- targetPackageName, userId));
- }
- }
- return new ArrayList<>(updatedPackages);
- } finally {
- traceEnd(TRACE_TAG_RRO);
- }
- }
-
- private void persistSettings() {
- if (DEBUG) {
- Slog.d(TAG, "Writing overlay settings");
- }
- synchronized (mLock) {
- FileOutputStream stream = null;
- try {
- stream = mSettingsFile.startWrite();
- mSettings.persist(stream);
- mSettingsFile.finishWrite(stream);
- } catch (IOException | XmlPullParserException e) {
- mSettingsFile.failWrite(stream);
- Slog.e(TAG, "failed to persist overlay state", e);
- }
- }
- }
-
- private void restoreSettings() {
- try {
- traceBegin(TRACE_TAG_RRO, "OMS#restoreSettings");
- synchronized (mLock) {
- if (!mSettingsFile.getBaseFile().exists()) {
- return;
- }
- try (FileInputStream stream = mSettingsFile.openRead()) {
- mSettings.restore(stream);
-
- // We might have data for dying users if the device was
- // restarted before we received USER_REMOVED. Remove data for
- // users that will not exist after the system is ready.
-
- final List<UserInfo> liveUsers = mUserManager.getUsers(true /*excludeDying*/);
- final int[] liveUserIds = new int[liveUsers.size()];
- for (int i = 0; i < liveUsers.size(); i++) {
- liveUserIds[i] = liveUsers.get(i).getUserHandle().getIdentifier();
- }
- Arrays.sort(liveUserIds);
-
- for (int userId : mSettings.getUsers()) {
- if (Arrays.binarySearch(liveUserIds, userId) < 0) {
- mSettings.removeUser(userId);
- }
- }
- } catch (IOException | XmlPullParserException e) {
- Slog.e(TAG, "failed to restore overlay state", e);
- }
- }
- } finally {
- traceEnd(TRACE_TAG_RRO);
- }
- }
}
diff --git a/services/core/java/com/android/server/pm/DumpState.java b/services/core/java/com/android/server/pm/DumpState.java
index 520871ff40c8..4f986bd5276b 100644
--- a/services/core/java/com/android/server/pm/DumpState.java
+++ b/services/core/java/com/android/server/pm/DumpState.java
@@ -44,6 +44,7 @@ public final class DumpState {
public static final int DUMP_APEX = 1 << 25;
public static final int DUMP_QUERIES = 1 << 26;
public static final int DUMP_KNOWN_PACKAGES = 1 << 27;
+ public static final int DUMP_PER_UID_READ_TIMEOUTS = 1 << 28;
public static final int OPTION_SHOW_FILTERS = 1 << 0;
public static final int OPTION_DUMP_ALL_COMPONENTS = 1 << 1;
diff --git a/services/core/java/com/android/server/pm/MULTIUSER_AND_ENTERPRISE_OWNERS b/services/core/java/com/android/server/pm/MULTIUSER_AND_ENTERPRISE_OWNERS
index 48f6bf84c28b..a52e9cf2f4c3 100644
--- a/services/core/java/com/android/server/pm/MULTIUSER_AND_ENTERPRISE_OWNERS
+++ b/services/core/java/com/android/server/pm/MULTIUSER_AND_ENTERPRISE_OWNERS
@@ -1,6 +1,6 @@
# OWNERS of Multiuser related files related to Enterprise
-# TODO: include /MULTIUSER_OWNERS
+include /MULTIUSER_OWNERS
# Enterprise owners
rubinxu@google.com
diff --git a/services/core/java/com/android/server/pm/OWNERS b/services/core/java/com/android/server/pm/OWNERS
index 004259b7478c..43c5d5e4015e 100644
--- a/services/core/java/com/android/server/pm/OWNERS
+++ b/services/core/java/com/android/server/pm/OWNERS
@@ -30,13 +30,12 @@ per-file CrossProfileAppsServiceImpl.java = omakoto@google.com, yamasani@google.
per-file CrossProfileAppsService.java = omakoto@google.com, yamasani@google.com
per-file CrossProfileIntentFilter.java = omakoto@google.com, yamasani@google.com
per-file CrossProfileIntentResolver.java = omakoto@google.com, yamasani@google.com
-per-file RestrictionsSet.java = bookatz@google.com, omakoto@google.com, yamasani@google.com, rubinxu@google.com, sandness@google.com
-per-file UserManagerInternal.java = bookatz@google.com, omakoto@google.com, yamasani@google.com
-per-file UserManagerService.java = bookatz@google.com, omakoto@google.com, yamasani@google.com
-per-file UserRestrictionsUtils.java = omakoto@google.com, rubinxu@google.com, sandness@google.com, yamasani@google.com
-per-file UserSystemPackageInstaller.java = bookatz@google.com, omakoto@google.com, yamasani@google.com
-per-file UserTypeDetails.java = bookatz@google.com, omakoto@google.com, yamasani@google.com
-per-file UserTypeFactory.java = bookatz@google.com, omakoto@google.com, yamasani@google.com
+per-file RestrictionsSet.java = file:MULTIUSER_AND_ENTERPRISE_OWNERS
+per-file UserManager* = file:/MULTIUSER_OWNERS
+per-file UserRestriction* = file:MULTIUSER_AND_ENTERPRISE_OWNERS
+per-file UserSystemPackageInstaller* = file:/MULTIUSER_OWNERS
+per-file UserTypeDetails.java = file:/MULTIUSER_OWNERS
+per-file UserTypeFactory.java = file:/MULTIUSER_OWNERS
# security
per-file KeySetHandle.java = cbrubaker@google.com, nnk@google.com
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index f97a5ee45346..3d04b5607922 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -111,6 +111,7 @@ import android.os.UserHandle;
import android.os.incremental.IStorageHealthListener;
import android.os.incremental.IncrementalFileStorages;
import android.os.incremental.IncrementalManager;
+import android.os.incremental.PerUidReadTimeouts;
import android.os.incremental.StorageHealthCheckParams;
import android.os.storage.StorageManager;
import android.provider.Settings.Secure;
@@ -3006,7 +3007,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final boolean isInstallerShell = (mInstallerUid == Process.SHELL_UID);
if (isInstallerShell && isIncrementalInstallation() && mIncrementalFileStorages != null) {
if (!packageLite.debuggable && !packageLite.profilableByShell) {
- mIncrementalFileStorages.disableReadLogs();
+ mIncrementalFileStorages.disallowReadLogs();
}
}
}
@@ -3720,12 +3721,16 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
};
if (!manualStartAndDestroy) {
+ final PerUidReadTimeouts[] perUidReadTimeouts = mPm.getPerUidReadTimeouts();
+
final StorageHealthCheckParams healthCheckParams = new StorageHealthCheckParams();
healthCheckParams.blockedTimeoutMs = INCREMENTAL_STORAGE_BLOCKED_TIMEOUT_MS;
healthCheckParams.unhealthyTimeoutMs = INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS;
healthCheckParams.unhealthyMonitoringMs = INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS;
+
final boolean systemDataLoader =
params.getComponentName().getPackageName() == SYSTEM_DATA_LOADER_PACKAGE;
+
final IStorageHealthListener healthListener = new IStorageHealthListener.Stub() {
@Override
public void onHealthStatus(int storageId, int status) {
@@ -3760,7 +3765,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
try {
mIncrementalFileStorages = IncrementalFileStorages.initialize(mContext, stageDir,
- params, statusListener, healthCheckParams, healthListener, addedFiles);
+ params, statusListener, healthCheckParams, healthListener, addedFiles,
+ perUidReadTimeouts);
return false;
} catch (IOException e) {
throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, e.getMessage(),
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 27008d824dd2..4467b5110d08 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -107,6 +107,7 @@ import static android.os.incremental.IncrementalManager.isIncrementalPath;
import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
+import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE;
import static com.android.internal.annotations.VisibleForTesting.Visibility;
import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
@@ -287,6 +288,7 @@ import android.os.UserManager;
import android.os.incremental.IStorageHealthListener;
import android.os.incremental.IncrementalManager;
import android.os.incremental.IncrementalStorage;
+import android.os.incremental.PerUidReadTimeouts;
import android.os.incremental.StorageHealthCheckParams;
import android.os.storage.DiskInfo;
import android.os.storage.IStorageManager;
@@ -512,6 +514,7 @@ public class PackageManagerService extends IPackageManager.Stub
public static final boolean DEBUG_COMPRESSION = Build.IS_DEBUGGABLE;
public static final boolean DEBUG_CACHES = false;
public static final boolean TRACE_CACHES = false;
+ private static final boolean DEBUG_PER_UID_READ_TIMEOUTS = false;
// Debug output for dexopting. This is shared between PackageManagerService, OtaDexoptService
// and PackageDexOptimizer. All these classes have their own flag to allow switching a single
@@ -647,6 +650,24 @@ public class PackageManagerService extends IPackageManager.Stub
private static final long DEFAULT_ENABLE_ROLLBACK_TIMEOUT_MILLIS = 10 * 1000;
/**
+ * Default IncFs timeouts. Maximum values in IncFs is 1hr.
+ *
+ * <p>If flag value is empty, the default value will be assigned.
+ *
+ * Flag type: {@code String}
+ * Namespace: NAMESPACE_PACKAGE_MANAGER_SERVICE
+ */
+ private static final String PROPERTY_INCFS_DEFAULT_TIMEOUTS = "incfs_default_timeouts";
+
+ /**
+ * Known digesters with optional timeouts.
+ *
+ * Flag type: {@code String}
+ * Namespace: NAMESPACE_PACKAGE_MANAGER_SERVICE
+ */
+ private static final String PROPERTY_KNOWN_DIGESTERS_LIST = "known_digesters_list";
+
+ /**
* The default response for package verification timeout.
*
* This can be either PackageManager.VERIFICATION_ALLOW or
@@ -909,6 +930,11 @@ public class PackageManagerService extends IPackageManager.Stub
final private ArrayList<IPackageChangeObserver> mPackageChangeObservers =
new ArrayList<>();
+ // Cached parsed flag value. Invalidated on each flag change.
+ private PerUidReadTimeouts[] mPerUidReadTimeoutsCache;
+
+ private static final PerUidReadTimeouts[] EMPTY_PER_UID_READ_TIMEOUTS_ARRAY = {};
+
/**
* Unit tests will instantiate, extend and/or mock to mock dependencies / behaviors.
*
@@ -23738,6 +23764,17 @@ public class PackageManagerService extends IPackageManager.Stub
mInstallerService.restoreAndApplyStagedSessionIfNeeded();
mExistingPackages = null;
+
+ // Clear cache on flags changes.
+ DeviceConfig.addOnPropertiesChangedListener(
+ NAMESPACE_PACKAGE_MANAGER_SERVICE, FgThread.getExecutor(),
+ properties -> {
+ final Set<String> keyset = properties.getKeyset();
+ if (keyset.contains(PROPERTY_INCFS_DEFAULT_TIMEOUTS) || keyset.contains(
+ PROPERTY_KNOWN_DIGESTERS_LIST)) {
+ mPerUidReadTimeoutsCache = null;
+ }
+ });
}
public void waitForAppDataPrepared() {
@@ -23828,6 +23865,7 @@ public class PackageManagerService extends IPackageManager.Stub
pw.println(" v[erifiers]: print package verifier info");
pw.println(" d[omain-preferred-apps]: print domains preferred apps");
pw.println(" i[ntent-filter-verifiers]|ifv: print intent filter verifier info");
+ pw.println(" t[imeouts]: print read timeouts for known digesters");
pw.println(" version: print database version info");
pw.println(" write: write current settings now");
pw.println(" installs: details about install sessions");
@@ -23982,6 +24020,8 @@ public class PackageManagerService extends IPackageManager.Stub
dumpState.setDump(DumpState.DUMP_SERVICE_PERMISSIONS);
} else if ("known-packages".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_KNOWN_PACKAGES);
+ } else if ("t".equals(cmd) || "timeouts".equals(cmd)) {
+ dumpState.setDump(DumpState.DUMP_PER_UID_READ_TIMEOUTS);
} else if ("write".equals(cmd)) {
synchronized (mLock) {
writeSettingsLPrTEMP();
@@ -24380,6 +24420,25 @@ public class PackageManagerService extends IPackageManager.Stub
if (!checkin && dumpState.isDumping(DumpState.DUMP_APEX)) {
mApexManager.dump(pw, packageName);
}
+
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_PER_UID_READ_TIMEOUTS)
+ && packageName == null) {
+ pw.println();
+ pw.println("Per UID read timeouts:");
+ pw.println(" Default timeouts flag: " + getDefaultTimeouts());
+ pw.println(" Known digesters list flag: " + getKnownDigestersList());
+
+ PerUidReadTimeouts[] items = getPerUidReadTimeouts();
+ pw.println(" Timeouts (" + items.length + "):");
+ for (PerUidReadTimeouts item : items) {
+ pw.print(" (");
+ pw.print("uid=" + item.uid + ", ");
+ pw.print("minTimeUs=" + item.minTimeUs + ", ");
+ pw.print("minPendingTimeUs=" + item.minPendingTimeUs + ", ");
+ pw.print("maxPendingTimeUs=" + item.maxPendingTimeUs);
+ pw.println(")");
+ }
+ }
}
//TODO: b/111402650
@@ -27967,6 +28026,88 @@ public class PackageManagerService extends IPackageManager.Stub
SystemClock.sleep(durationMs);
}
}
+
+ private static String getDefaultTimeouts() {
+ return DeviceConfig.getString(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE,
+ PROPERTY_INCFS_DEFAULT_TIMEOUTS, "");
+ }
+
+ private static String getKnownDigestersList() {
+ return DeviceConfig.getString(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE,
+ PROPERTY_KNOWN_DIGESTERS_LIST, "");
+ }
+
+ /**
+ * Returns the array containing per-uid timeout configuration.
+ * This is derived from DeviceConfig flags.
+ */
+ public @NonNull PerUidReadTimeouts[] getPerUidReadTimeouts() {
+ PerUidReadTimeouts[] result = mPerUidReadTimeoutsCache;
+ if (result == null) {
+ result = parsePerUidReadTimeouts();
+ mPerUidReadTimeoutsCache = result;
+ }
+ return result;
+ }
+
+ private @NonNull PerUidReadTimeouts[] parsePerUidReadTimeouts() {
+ final String defaultTimeouts = getDefaultTimeouts();
+ final String knownDigestersList = getKnownDigestersList();
+ final List<PerPackageReadTimeouts> perPackageReadTimeouts =
+ PerPackageReadTimeouts.parseDigestersList(defaultTimeouts, knownDigestersList);
+
+ if (perPackageReadTimeouts.size() == 0) {
+ return EMPTY_PER_UID_READ_TIMEOUTS_ARRAY;
+ }
+
+ final int[] allUsers = mInjector.getUserManagerService().getUserIds();
+
+ List<PerUidReadTimeouts> result = new ArrayList<>(perPackageReadTimeouts.size());
+ synchronized (mLock) {
+ for (int i = 0, size = perPackageReadTimeouts.size(); i < size; ++i) {
+ final PerPackageReadTimeouts perPackage = perPackageReadTimeouts.get(i);
+ final PackageSetting ps = mSettings.mPackages.get(perPackage.packageName);
+ if (ps == null) {
+ if (DEBUG_PER_UID_READ_TIMEOUTS) {
+ Slog.i(TAG, "PerUidReadTimeouts: package not found = "
+ + perPackage.packageName);
+ }
+ continue;
+ }
+ final AndroidPackage pkg = ps.getPkg();
+ if (pkg.getLongVersionCode() < perPackage.versionCodes.minVersionCode
+ || pkg.getLongVersionCode() > perPackage.versionCodes.maxVersionCode) {
+ if (DEBUG_PER_UID_READ_TIMEOUTS) {
+ Slog.i(TAG, "PerUidReadTimeouts: version code is not in range = "
+ + perPackage.packageName + ":" + pkg.getLongVersionCode());
+ }
+ continue;
+ }
+ if (perPackage.sha256certificate != null
+ && !pkg.getSigningDetails().hasSha256Certificate(
+ perPackage.sha256certificate)) {
+ if (DEBUG_PER_UID_READ_TIMEOUTS) {
+ Slog.i(TAG, "PerUidReadTimeouts: invalid certificate = "
+ + perPackage.packageName + ":" + pkg.getLongVersionCode());
+ }
+ continue;
+ }
+ for (int userId : allUsers) {
+ if (!ps.getInstalled(userId)) {
+ continue;
+ }
+ final int uid = UserHandle.getUid(userId, ps.appId);
+ final PerUidReadTimeouts perUid = new PerUidReadTimeouts();
+ perUid.uid = uid;
+ perUid.minTimeUs = perPackage.timeouts.minTimeUs;
+ perUid.minPendingTimeUs = perPackage.timeouts.minPendingTimeUs;
+ perUid.maxPendingTimeUs = perPackage.timeouts.maxPendingTimeUs;
+ result.add(perUid);
+ }
+ }
+ }
+ return result.toArray(new PerUidReadTimeouts[result.size()]);
+ }
}
interface PackageSender {
diff --git a/services/core/java/com/android/server/pm/PerPackageReadTimeouts.java b/services/core/java/com/android/server/pm/PerPackageReadTimeouts.java
new file mode 100644
index 000000000000..3b306a850b64
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PerPackageReadTimeouts.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.annotation.NonNull;;
+import android.text.TextUtils;
+
+import com.android.internal.util.HexDump;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+class PerPackageReadTimeouts {
+ static long tryParseLong(String str, long defaultValue) {
+ try {
+ return Long.parseLong(str);
+ } catch (NumberFormatException nfe) {
+ return defaultValue;
+ }
+ }
+
+ static byte[] tryParseSha256(String str) {
+ if (TextUtils.isEmpty(str)) {
+ return null;
+ }
+ try {
+ return HexDump.hexStringToByteArray(str);
+ } catch (RuntimeException e) {
+ return null;
+ }
+ }
+
+ static class Timeouts {
+ public final long minTimeUs;
+ public final long minPendingTimeUs;
+ public final long maxPendingTimeUs;
+
+ // 3600000000us == 1hr
+ public static final Timeouts DEFAULT = new Timeouts(3600000000L, 3600000000L, 3600000000L);
+
+ private Timeouts(long minTimeUs, long minPendingTimeUs, long maxPendingTimeUs) {
+ this.minTimeUs = minTimeUs;
+ this.minPendingTimeUs = minPendingTimeUs;
+ this.maxPendingTimeUs = maxPendingTimeUs;
+ }
+
+ static Timeouts parse(String timeouts) {
+ String[] splits = timeouts.split(":", 3);
+ if (splits.length != 3) {
+ return DEFAULT;
+ }
+ final long minTimeUs = tryParseLong(splits[0], DEFAULT.minTimeUs);
+ final long minPendingTimeUs = tryParseLong(splits[1], DEFAULT.minPendingTimeUs);
+ final long maxPendingTimeUs = tryParseLong(splits[2], DEFAULT.maxPendingTimeUs);
+ if (0 <= minTimeUs && minTimeUs <= minPendingTimeUs
+ && minPendingTimeUs <= maxPendingTimeUs) {
+ // validity check
+ return new Timeouts(minTimeUs, minPendingTimeUs, maxPendingTimeUs);
+ }
+ return DEFAULT;
+ }
+ }
+
+ static class VersionCodes {
+ public final long minVersionCode;
+ public final long maxVersionCode;
+
+ public static final VersionCodes ALL_VERSION_CODES = new VersionCodes(Long.MIN_VALUE,
+ Long.MAX_VALUE);
+
+ private VersionCodes(long minVersionCode, long maxVersionCode) {
+ this.minVersionCode = minVersionCode;
+ this.maxVersionCode = maxVersionCode;
+ }
+
+ static VersionCodes parse(String codes) {
+ if (TextUtils.isEmpty(codes)) {
+ return ALL_VERSION_CODES;
+ }
+ String[] splits = codes.split("-", 2);
+ switch (splits.length) {
+ case 1: {
+ // single version code
+ try {
+ final long versionCode = Long.parseLong(splits[0]);
+ return new VersionCodes(versionCode, versionCode);
+ } catch (NumberFormatException nfe) {
+ return ALL_VERSION_CODES;
+ }
+ }
+ case 2: {
+ final long minVersionCode = tryParseLong(splits[0],
+ ALL_VERSION_CODES.minVersionCode);
+ final long maxVersionCode = tryParseLong(splits[1],
+ ALL_VERSION_CODES.maxVersionCode);
+ if (minVersionCode <= maxVersionCode) {
+ return new VersionCodes(minVersionCode, maxVersionCode);
+ }
+ break;
+ }
+ }
+ return ALL_VERSION_CODES;
+ }
+ }
+
+ public final String packageName;
+ public final byte[] sha256certificate;
+ public final VersionCodes versionCodes;
+ public final Timeouts timeouts;
+
+ private PerPackageReadTimeouts(String packageName, byte[] sha256certificate,
+ VersionCodes versionCodes, Timeouts timeouts) {
+ this.packageName = packageName;
+ this.sha256certificate = sha256certificate;
+ this.versionCodes = versionCodes;
+ this.timeouts = timeouts;
+ }
+
+ @SuppressWarnings("fallthrough")
+ static PerPackageReadTimeouts parse(String timeoutsStr, VersionCodes defaultVersionCodes,
+ Timeouts defaultTimeouts) {
+ String packageName = null;
+ byte[] sha256certificate = null;
+ VersionCodes versionCodes = defaultVersionCodes;
+ Timeouts timeouts = defaultTimeouts;
+
+ final String[] splits = timeoutsStr.split(":", 4);
+ switch (splits.length) {
+ case 4:
+ timeouts = Timeouts.parse(splits[3]);
+ // fall through
+ case 3:
+ versionCodes = VersionCodes.parse(splits[2]);
+ // fall through
+ case 2:
+ sha256certificate = tryParseSha256(splits[1]);
+ // fall through
+ case 1:
+ packageName = splits[0];
+ break;
+ default:
+ return null;
+ }
+ if (TextUtils.isEmpty(packageName)) {
+ return null;
+ }
+
+ return new PerPackageReadTimeouts(packageName, sha256certificate, versionCodes,
+ timeouts);
+ }
+
+ static @NonNull List<PerPackageReadTimeouts> parseDigestersList(String defaultTimeoutsStr,
+ String knownDigestersList) {
+ if (TextUtils.isEmpty(knownDigestersList)) {
+ return Collections.emptyList();
+ }
+
+ final VersionCodes defaultVersionCodes = VersionCodes.ALL_VERSION_CODES;
+ final Timeouts defaultTimeouts = Timeouts.parse(defaultTimeoutsStr);
+
+ String[] packages = knownDigestersList.split(",");
+ List<PerPackageReadTimeouts> result = new ArrayList<>(packages.length);
+ for (int i = 0, size = packages.length; i < size; ++i) {
+ PerPackageReadTimeouts timeouts = PerPackageReadTimeouts.parse(packages[i],
+ defaultVersionCodes, defaultTimeouts);
+ if (timeouts != null) {
+ result.add(timeouts);
+ }
+ }
+ return result;
+ }
+}
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 2314afc787c3..a036bd196b23 100755
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -380,8 +380,9 @@ class TvInputHardwareManager implements TvInputHal.Callback {
return null;
}
- ResourceClientProfile profile =
- new ResourceClientProfile(tvInputSessionId, priorityHint);
+ ResourceClientProfile profile = new ResourceClientProfile();
+ profile.tvInputSessionId = tvInputSessionId;
+ profile.useCase = priorityHint;
ResourceClientProfile holderProfile = connection.getResourceClientProfileLocked();
if (holderProfile != null && trm != null
&& !trm.isHigherPriority(profile, holderProfile)) {
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java
index 54ad1d268e56..4a81c95f0b8f 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java
@@ -25,7 +25,7 @@ import java.util.Set;
*
* @hide
*/
-public final class CasResource {
+public class CasResource {
private final int mSystemId;
@@ -38,7 +38,7 @@ public final class CasResource {
*/
private Map<Integer, Integer> mOwnerClientIdsToSessionNum = new HashMap<>();
- private CasResource(Builder builder) {
+ CasResource(Builder builder) {
this.mSystemId = builder.mSystemId;
this.mMaxSessionNum = builder.mMaxSessionNum;
this.mAvailableSessionNum = builder.mMaxSessionNum;
@@ -111,7 +111,7 @@ public final class CasResource {
public static class Builder {
private int mSystemId;
- private int mMaxSessionNum;
+ protected int mMaxSessionNum;
Builder(int systemId) {
this.mSystemId = systemId;
@@ -138,7 +138,7 @@ public final class CasResource {
}
}
- private String ownersMapToString() {
+ protected String ownersMapToString() {
StringBuilder string = new StringBuilder("{");
for (int clienId : mOwnerClientIdsToSessionNum.keySet()) {
string.append(" clientId=")
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/CiCamResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/CiCamResource.java
new file mode 100644
index 000000000000..31149f3590b8
--- /dev/null
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/CiCamResource.java
@@ -0,0 +1,70 @@
+/*
+ * 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 com.android.server.tv.tunerresourcemanager;
+
+/**
+ * A CiCam resource object used by the Tuner Resource Manager to record the CiCam
+ * information.
+ *
+ * @hide
+ */
+public final class CiCamResource extends CasResource {
+ private CiCamResource(Builder builder) {
+ super(builder);
+ }
+
+ @Override
+ public String toString() {
+ return "CiCamResource[systemId=" + this.getSystemId()
+ + ", isFullyUsed=" + (this.isFullyUsed())
+ + ", maxSessionNum=" + this.getMaxSessionNum()
+ + ", ownerClients=" + ownersMapToString() + "]";
+ }
+
+ public int getCiCamId() {
+ return this.getSystemId();
+ }
+
+ /**
+ * Builder class for {@link CiCamResource}.
+ */
+ public static class Builder extends CasResource.Builder {
+ Builder(int systemId) {
+ super(systemId);
+ }
+
+ /**
+ * Builder for {@link CasResource}.
+ *
+ * @param maxSessionNum the max session num the current Cas has.
+ */
+ public Builder maxSessionNum(int maxSessionNum) {
+ super.mMaxSessionNum = maxSessionNum;
+ return this;
+ }
+
+ /**
+ * Build a {@link CiCamResource}.
+ *
+ * @return {@link CiCamResource}.
+ */
+ @Override
+ public CiCamResource build() {
+ CiCamResource ciCam = new CiCamResource(this);
+ return ciCam;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
index edf007d428c1..5723e1dcceb5 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
@@ -50,6 +50,8 @@ public final class ClientProfile {
*/
private final int mProcessId;
+ private boolean mIsForeground;
+
/**
* All the clients that share the same resource would be under the same group id.
*
@@ -83,6 +85,11 @@ public final class ClientProfile {
private int mUsingCasSystemId = INVALID_RESOURCE_ID;
/**
+ * CiCam id that is used by the client.
+ */
+ private int mUsingCiCamId = INVALID_RESOURCE_ID;
+
+ /**
* Optional arbitrary priority value given by the client.
*
* <p>This value can override the default priorotiy calculated from
@@ -113,6 +120,20 @@ public final class ClientProfile {
return mProcessId;
}
+ /**
+ * Set the current isForeground status.
+ */
+ public void setForeground(boolean isForeground) {
+ mIsForeground = isForeground;
+ }
+
+ /**
+ * Get the previous recorded isForeground status.
+ */
+ public boolean isForeground() {
+ return mIsForeground;
+ }
+
public int getGroupId() {
return mGroupId;
}
@@ -222,6 +243,26 @@ public final class ClientProfile {
}
/**
+ * Set when the client starts to connect to a CiCam.
+ *
+ * @param ciCamId ciCam being used.
+ */
+ public void useCiCam(int ciCamId) {
+ mUsingCiCamId = ciCamId;
+ }
+
+ public int getInUseCiCamId() {
+ return mUsingCiCamId;
+ }
+
+ /**
+ * Called when the client disconnect to a CiCam.
+ */
+ public void releaseCiCam() {
+ mUsingCiCamId = INVALID_RESOURCE_ID;
+ }
+
+ /**
* Called to reclaim all the resources being used by the current client.
*/
public void reclaimAllResources() {
@@ -229,6 +270,7 @@ public final class ClientProfile {
mShareFeClientIds.clear();
mUsingLnbHandles.clear();
mUsingCasSystemId = INVALID_RESOURCE_ID;
+ mUsingCiCamId = INVALID_RESOURCE_ID;
}
@Override
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index 8c6e690afe5b..072bdd2d1506 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -27,6 +27,7 @@ import android.media.tv.tunerresourcemanager.CasSessionRequest;
import android.media.tv.tunerresourcemanager.IResourcesReclaimListener;
import android.media.tv.tunerresourcemanager.ITunerResourceManager;
import android.media.tv.tunerresourcemanager.ResourceClientProfile;
+import android.media.tv.tunerresourcemanager.TunerCiCamRequest;
import android.media.tv.tunerresourcemanager.TunerDemuxRequest;
import android.media.tv.tunerresourcemanager.TunerDescramblerRequest;
import android.media.tv.tunerresourcemanager.TunerFrontendInfo;
@@ -71,6 +72,8 @@ public class TunerResourceManagerService extends SystemService implements IBinde
private Map<Integer, LnbResource> mLnbResources = new HashMap<>();
// Map of the current available Cas resources
private Map<Integer, CasResource> mCasResources = new HashMap<>();
+ // Map of the current available CiCam resources
+ private Map<Integer, CiCamResource> mCiCamResources = new HashMap<>();
@GuardedBy("mLock")
private Map<Integer, ResourcesReclaimListenerRecord> mListeners = new HashMap<>();
@@ -141,8 +144,8 @@ public class TunerResourceManagerService extends SystemService implements IBinde
throw new RemoteException("IResourcesReclaimListener can't be null!");
}
- if (!mPriorityCongfig.isDefinedUseCase(profile.getUseCase())) {
- throw new RemoteException("Use undefined client use case:" + profile.getUseCase());
+ if (!mPriorityCongfig.isDefinedUseCase(profile.useCase)) {
+ throw new RemoteException("Use undefined client use case:" + profile.useCase);
}
synchronized (mLock) {
@@ -209,14 +212,14 @@ public class TunerResourceManagerService extends SystemService implements IBinde
throw new RemoteException("frontendHandle can't be null");
}
synchronized (mLock) {
- if (!checkClientExists(request.getClientId())) {
+ if (!checkClientExists(request.clientId)) {
throw new RemoteException("Request frontend from unregistered client: "
- + request.getClientId());
+ + request.clientId);
}
// If the request client is holding or sharing a frontend, throw an exception.
- if (!getClientProfile(request.getClientId()).getInUseFrontendHandles().isEmpty()) {
+ if (!getClientProfile(request.clientId).getInUseFrontendHandles().isEmpty()) {
throw new RemoteException("Release frontend before requesting another one. "
- + "Client id: " + request.getClientId());
+ + "Client id: " + request.clientId);
}
return requestFrontendInternal(request, frontendHandle);
}
@@ -252,9 +255,9 @@ public class TunerResourceManagerService extends SystemService implements IBinde
throw new RemoteException("demuxHandle can't be null");
}
synchronized (mLock) {
- if (!checkClientExists(request.getClientId())) {
+ if (!checkClientExists(request.clientId)) {
throw new RemoteException("Request demux from unregistered client:"
- + request.getClientId());
+ + request.clientId);
}
return requestDemuxInternal(request, demuxHandle);
}
@@ -269,9 +272,9 @@ public class TunerResourceManagerService extends SystemService implements IBinde
throw new RemoteException("descramblerHandle can't be null");
}
synchronized (mLock) {
- if (!checkClientExists(request.getClientId())) {
+ if (!checkClientExists(request.clientId)) {
throw new RemoteException("Request descrambler from unregistered client:"
- + request.getClientId());
+ + request.clientId);
}
return requestDescramblerInternal(request, descramblerHandle);
}
@@ -285,15 +288,31 @@ public class TunerResourceManagerService extends SystemService implements IBinde
throw new RemoteException("casSessionHandle can't be null");
}
synchronized (mLock) {
- if (!checkClientExists(request.getClientId())) {
+ if (!checkClientExists(request.clientId)) {
throw new RemoteException("Request cas from unregistered client:"
- + request.getClientId());
+ + request.clientId);
}
return requestCasSessionInternal(request, casSessionHandle);
}
}
@Override
+ public boolean requestCiCam(@NonNull TunerCiCamRequest request,
+ @NonNull int[] ciCamHandle) throws RemoteException {
+ enforceTrmAccessPermission("requestCiCam");
+ if (ciCamHandle == null) {
+ throw new RemoteException("ciCamHandle can't be null");
+ }
+ synchronized (mLock) {
+ if (!checkClientExists(request.clientId)) {
+ throw new RemoteException("Request ciCam from unregistered client:"
+ + request.clientId);
+ }
+ return requestCiCamInternal(request, ciCamHandle);
+ }
+ }
+
+ @Override
public boolean requestLnb(@NonNull TunerLnbRequest request, @NonNull int[] lnbHandle)
throws RemoteException {
enforceTunerAccessPermission("requestLnb");
@@ -302,9 +321,9 @@ public class TunerResourceManagerService extends SystemService implements IBinde
throw new RemoteException("lnbHandle can't be null");
}
synchronized (mLock) {
- if (!checkClientExists(request.getClientId())) {
+ if (!checkClientExists(request.clientId)) {
throw new RemoteException("Request lnb from unregistered client:"
- + request.getClientId());
+ + request.clientId);
}
return requestLnbInternal(request, lnbHandle);
}
@@ -378,6 +397,34 @@ public class TunerResourceManagerService extends SystemService implements IBinde
}
@Override
+ public void releaseCiCam(int ciCamHandle, int clientId) throws RemoteException {
+ enforceTrmAccessPermission("releaseCiCam");
+ if (!validateResourceHandle(
+ TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, ciCamHandle)) {
+ throw new RemoteException("ciCamHandle can't be invalid");
+ }
+ synchronized (mLock) {
+ if (!checkClientExists(clientId)) {
+ throw new RemoteException("Release ciCam from unregistered client:" + clientId);
+ }
+ int ciCamId = getClientProfile(clientId).getInUseCiCamId();
+ if (ciCamId != getResourceIdFromHandle(ciCamHandle)) {
+ throw new RemoteException("The client " + clientId + " is not the owner of "
+ + "the releasing ciCam.");
+ }
+ CiCamResource ciCam = getCiCamResource(ciCamId);
+ if (ciCam == null) {
+ throw new RemoteException("Releasing ciCam does not exist.");
+ }
+ if (!ciCam.getOwnerClientIds().contains(clientId)) {
+ throw new RemoteException(
+ "Client is not the current owner of the releasing ciCam.");
+ }
+ releaseCiCamInternal(ciCam, clientId);
+ }
+ }
+
+ @Override
public void releaseLnb(int lnbHandle, int clientId) throws RemoteException {
enforceTunerAccessPermission("releaseLnb");
enforceTrmAccessPermission("releaseLnb");
@@ -441,12 +488,12 @@ public class TunerResourceManagerService extends SystemService implements IBinde
// TODO tell if the client already exists
clientId[0] = mNextUnusedClientId++;
- int pid = profile.getTvInputSessionId() == null
+ int pid = profile.tvInputSessionId == null
? Binder.getCallingPid() /*callingPid*/
- : mTvInputManager.getClientPid(profile.getTvInputSessionId()); /*tvAppId*/
+ : mTvInputManager.getClientPid(profile.tvInputSessionId); /*tvAppId*/
// Update Media Resource Manager with the tvAppId
- if (profile.getTvInputSessionId() != null && mMediaResourceManager != null) {
+ if (profile.tvInputSessionId != null && mMediaResourceManager != null) {
try {
mMediaResourceManager.overridePid(Binder.getCallingPid(), pid);
} catch (RemoteException e) {
@@ -456,11 +503,13 @@ public class TunerResourceManagerService extends SystemService implements IBinde
}
ClientProfile clientProfile = new ClientProfile.Builder(clientId[0])
- .tvInputSessionId(profile.getTvInputSessionId())
- .useCase(profile.getUseCase())
+ .tvInputSessionId(profile.tvInputSessionId)
+ .useCase(profile.useCase)
.processId(pid)
.build();
- clientProfile.setPriority(getClientPriority(profile.getUseCase(), pid));
+ clientProfile.setForeground(checkIsForeground(pid));
+ clientProfile.setPriority(
+ getClientPriority(profile.useCase, clientProfile.isForeground()));
addClientProfile(clientId[0], clientProfile, listener);
}
@@ -498,6 +547,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde
return false;
}
+ profile.setForeground(checkIsForeground(profile.getProcessId()));
profile.setPriority(priority);
profile.setNiceValue(niceValue);
@@ -520,16 +570,16 @@ public class TunerResourceManagerService extends SystemService implements IBinde
// Update frontendResources map and other mappings accordingly
for (int i = 0; i < infos.length; i++) {
- if (getFrontendResource(infos[i].getHandle()) != null) {
+ if (getFrontendResource(infos[i].handle) != null) {
if (DEBUG) {
- Slog.d(TAG, "Frontend handle=" + infos[i].getHandle() + "exists.");
+ Slog.d(TAG, "Frontend handle=" + infos[i].handle + "exists.");
}
- updatingFrontendHandles.remove(infos[i].getHandle());
+ updatingFrontendHandles.remove(infos[i].handle);
} else {
// Add a new fe resource
- FrontendResource newFe = new FrontendResource.Builder(infos[i].getHandle())
- .type(infos[i].getFrontendType())
- .exclusiveGroupId(infos[i].getExclusiveGroupId())
+ FrontendResource newFe = new FrontendResource.Builder(infos[i].handle)
+ .type(infos[i].frontendType)
+ .exclusiveGroupId(infos[i].exclusiveGroupId)
.build();
addFrontendResource(newFe);
}
@@ -583,24 +633,33 @@ public class TunerResourceManagerService extends SystemService implements IBinde
// If maxSessionNum is 0, removing the Cas Resource.
if (maxSessionNum == 0) {
removeCasResource(casSystemId);
+ removeCiCamResource(casSystemId);
return;
}
// If the Cas exists, updates the Cas Resource accordingly.
CasResource cas = getCasResource(casSystemId);
+ CiCamResource ciCam = getCiCamResource(casSystemId);
if (cas != null) {
if (cas.getUsedSessionNum() > maxSessionNum) {
// Sort and release the short number of Cas resources.
int releasingCasResourceNum = cas.getUsedSessionNum() - maxSessionNum;
- releaseLowerPriorityClientCasResources(releasingCasResourceNum);
+ // TODO: handle CiCam session update.
}
cas.updateMaxSessionNum(maxSessionNum);
+ if (ciCam != null) {
+ ciCam.updateMaxSessionNum(maxSessionNum);
+ }
return;
}
// Add the new Cas Resource.
cas = new CasResource.Builder(casSystemId)
.maxSessionNum(maxSessionNum)
.build();
+ ciCam = new CiCamResource.Builder(casSystemId)
+ .maxSessionNum(maxSessionNum)
+ .build();
addCasResource(cas);
+ addCiCamResource(ciCam);
}
@VisibleForTesting
@@ -610,13 +669,17 @@ public class TunerResourceManagerService extends SystemService implements IBinde
}
frontendHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
- ClientProfile requestClient = getClientProfile(request.getClientId());
+ ClientProfile requestClient = getClientProfile(request.clientId);
+ if (requestClient == null) {
+ return false;
+ }
+ clientPriorityUpdateOnRequest(requestClient);
int grantingFrontendHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
int inUseLowestPriorityFrHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
// Priority max value is 1000
int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
for (FrontendResource fr : getFrontendResources().values()) {
- if (fr.getType() == request.getFrontendType()) {
+ if (fr.getType() == request.frontendType) {
if (!fr.isInUse()) {
// Grant unused frontend with no exclusive group members first.
if (fr.getExclusiveGroupMemberFeHandles().isEmpty()) {
@@ -643,7 +706,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde
// Grant frontend when there is unused resource.
if (grantingFrontendHandle != TunerResourceManager.INVALID_RESOURCE_HANDLE) {
frontendHandle[0] = grantingFrontendHandle;
- updateFrontendClientMappingOnNewGrant(grantingFrontendHandle, request.getClientId());
+ updateFrontendClientMappingOnNewGrant(grantingFrontendHandle, request.clientId);
return true;
}
@@ -658,7 +721,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde
}
frontendHandle[0] = inUseLowestPriorityFrHandle;
updateFrontendClientMappingOnNewGrant(
- inUseLowestPriorityFrHandle, request.getClientId());
+ inUseLowestPriorityFrHandle, request.clientId);
return true;
}
@@ -683,7 +746,8 @@ public class TunerResourceManagerService extends SystemService implements IBinde
}
lnbHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
- ClientProfile requestClient = getClientProfile(request.getClientId());
+ ClientProfile requestClient = getClientProfile(request.clientId);
+ clientPriorityUpdateOnRequest(requestClient);
int grantingLnbHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
int inUseLowestPriorityLnbHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
// Priority max value is 1000
@@ -707,7 +771,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde
// Grant Lnb when there is unused resource.
if (grantingLnbHandle > -1) {
lnbHandle[0] = grantingLnbHandle;
- updateLnbClientMappingOnNewGrant(grantingLnbHandle, request.getClientId());
+ updateLnbClientMappingOnNewGrant(grantingLnbHandle, request.clientId);
return true;
}
@@ -720,7 +784,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde
return false;
}
lnbHandle[0] = inUseLowestPriorityLnbHandle;
- updateLnbClientMappingOnNewGrant(inUseLowestPriorityLnbHandle, request.getClientId());
+ updateLnbClientMappingOnNewGrant(inUseLowestPriorityLnbHandle, request.clientId);
return true;
}
@@ -732,23 +796,24 @@ public class TunerResourceManagerService extends SystemService implements IBinde
if (DEBUG) {
Slog.d(TAG, "requestCasSession(request=" + request + ")");
}
- CasResource cas = getCasResource(request.getCasSystemId());
+ CasResource cas = getCasResource(request.casSystemId);
// Unregistered Cas System is treated as having unlimited sessions.
if (cas == null) {
- cas = new CasResource.Builder(request.getCasSystemId())
+ cas = new CasResource.Builder(request.casSystemId)
.maxSessionNum(Integer.MAX_VALUE)
.build();
addCasResource(cas);
}
casSessionHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
- ClientProfile requestClient = getClientProfile(request.getClientId());
+ ClientProfile requestClient = getClientProfile(request.clientId);
+ clientPriorityUpdateOnRequest(requestClient);
int lowestPriorityOwnerId = -1;
// Priority max value is 1000
int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
if (!cas.isFullyUsed()) {
casSessionHandle[0] = generateResourceHandle(
TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, cas.getSystemId());
- updateCasClientMappingOnNewGrant(request.getCasSystemId(), request.getClientId());
+ updateCasClientMappingOnNewGrant(request.casSystemId, request.clientId);
return true;
}
for (int ownerId : cas.getOwnerClientIds()) {
@@ -769,7 +834,56 @@ public class TunerResourceManagerService extends SystemService implements IBinde
}
casSessionHandle[0] = generateResourceHandle(
TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, cas.getSystemId());
- updateCasClientMappingOnNewGrant(request.getCasSystemId(), request.getClientId());
+ updateCasClientMappingOnNewGrant(request.casSystemId, request.clientId);
+ return true;
+ }
+ return false;
+ }
+
+ @VisibleForTesting
+ protected boolean requestCiCamInternal(TunerCiCamRequest request, int[] ciCamHandle) {
+ if (DEBUG) {
+ Slog.d(TAG, "requestCiCamInternal(TunerCiCamRequest=" + request + ")");
+ }
+ CiCamResource ciCam = getCiCamResource(request.ciCamId);
+ // Unregistered Cas System is treated as having unlimited sessions.
+ if (ciCam == null) {
+ ciCam = new CiCamResource.Builder(request.ciCamId)
+ .maxSessionNum(Integer.MAX_VALUE)
+ .build();
+ addCiCamResource(ciCam);
+ }
+ ciCamHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
+ ClientProfile requestClient = getClientProfile(request.clientId);
+ clientPriorityUpdateOnRequest(requestClient);
+ int lowestPriorityOwnerId = -1;
+ // Priority max value is 1000
+ int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
+ if (!ciCam.isFullyUsed()) {
+ ciCamHandle[0] = generateResourceHandle(
+ TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, ciCam.getCiCamId());
+ updateCiCamClientMappingOnNewGrant(request.ciCamId, request.clientId);
+ return true;
+ }
+ for (int ownerId : ciCam.getOwnerClientIds()) {
+ // Record the client id with lowest priority that is using the current Cas system.
+ int priority = getOwnerClientPriority(ownerId);
+ if (currentLowestPriority > priority) {
+ lowestPriorityOwnerId = ownerId;
+ currentLowestPriority = priority;
+ }
+ }
+
+ // When all the CiCam sessions are occupied, reclaim the lowest priority client if the
+ // request client has higher priority.
+ if (lowestPriorityOwnerId > -1 && (requestClient.getPriority() > currentLowestPriority)) {
+ if (!reclaimResource(lowestPriorityOwnerId,
+ TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM)) {
+ return false;
+ }
+ ciCamHandle[0] = generateResourceHandle(
+ TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, ciCam.getCiCamId());
+ updateCiCamClientMappingOnNewGrant(request.ciCamId, request.clientId);
return true;
}
return false;
@@ -790,15 +904,16 @@ public class TunerResourceManagerService extends SystemService implements IBinde
return true;
}
- int challengerPid = challengerProfile.getTvInputSessionId() == null
+ int challengerPid = challengerProfile.tvInputSessionId == null
? Binder.getCallingPid() /*callingPid*/
- : mTvInputManager.getClientPid(challengerProfile.getTvInputSessionId()); /*tvAppId*/
- int holderPid = holderProfile.getTvInputSessionId() == null
+ : mTvInputManager.getClientPid(challengerProfile.tvInputSessionId); /*tvAppId*/
+ int holderPid = holderProfile.tvInputSessionId == null
? Binder.getCallingPid() /*callingPid*/
- : mTvInputManager.getClientPid(holderProfile.getTvInputSessionId()); /*tvAppId*/
+ : mTvInputManager.getClientPid(holderProfile.tvInputSessionId); /*tvAppId*/
- int challengerPriority = getClientPriority(challengerProfile.getUseCase(), challengerPid);
- int holderPriority = getClientPriority(holderProfile.getUseCase(), holderPid);
+ int challengerPriority = getClientPriority(
+ challengerProfile.useCase, checkIsForeground(challengerPid));
+ int holderPriority = getClientPriority(holderProfile.useCase, checkIsForeground(holderPid));
return challengerPriority > holderPriority;
}
@@ -833,6 +948,14 @@ public class TunerResourceManagerService extends SystemService implements IBinde
}
@VisibleForTesting
+ protected void releaseCiCamInternal(CiCamResource ciCam, int ownerClientId) {
+ if (DEBUG) {
+ Slog.d(TAG, "releaseCiCamInternal(ciCamId=" + ciCam.getCiCamId() + ")");
+ }
+ updateCiCamClientMappingOnRelease(ciCam, ownerClientId);
+ }
+
+ @VisibleForTesting
protected boolean requestDemuxInternal(TunerDemuxRequest request, int[] demuxHandle) {
if (DEBUG) {
Slog.d(TAG, "requestDemux(request=" + request + ")");
@@ -843,6 +966,21 @@ public class TunerResourceManagerService extends SystemService implements IBinde
}
@VisibleForTesting
+ // This mothod is to sync up the request client's foreground/background status and update
+ // the client priority accordingly whenever new resource request comes in.
+ protected void clientPriorityUpdateOnRequest(ClientProfile requestProfile) {
+ int pid = requestProfile.getProcessId();
+ boolean currentIsForeground = checkIsForeground(pid);
+ if (requestProfile.isForeground() == currentIsForeground) {
+ // To avoid overriding the priority set through updateClientPriority API.
+ return;
+ }
+ requestProfile.setForeground(currentIsForeground);
+ requestProfile.setPriority(
+ getClientPriority(requestProfile.getUseCase(), currentIsForeground));
+ }
+
+ @VisibleForTesting
protected boolean requestDescramblerInternal(
TunerDescramblerRequest request, int[] descramblerHandle) {
if (DEBUG) {
@@ -933,20 +1071,20 @@ public class TunerResourceManagerService extends SystemService implements IBinde
}
@VisibleForTesting
- protected int getClientPriority(int useCase, int pid) {
+ protected int getClientPriority(int useCase, boolean isForeground) {
if (DEBUG) {
Slog.d(TAG, "getClientPriority useCase=" + useCase
- + ", pid=" + pid + ")");
+ + ", isForeground=" + isForeground + ")");
}
- if (isForeground(pid)) {
+ if (isForeground) {
return mPriorityCongfig.getForegroundPriority(useCase);
}
return mPriorityCongfig.getBackgroundPriority(useCase);
}
@VisibleForTesting
- protected boolean isForeground(int pid) {
+ protected boolean checkIsForeground(int pid) {
if (mActivityManager == null) {
return false;
}
@@ -994,6 +1132,13 @@ public class TunerResourceManagerService extends SystemService implements IBinde
ownerProfile.useCas(grantingId);
}
+ private void updateCiCamClientMappingOnNewGrant(int grantingId, int ownerClientId) {
+ CiCamResource grantingCiCam = getCiCamResource(grantingId);
+ ClientProfile ownerProfile = getClientProfile(ownerClientId);
+ grantingCiCam.setOwner(ownerClientId);
+ ownerProfile.useCiCam(grantingId);
+ }
+
private void updateCasClientMappingOnRelease(
@NonNull CasResource releasingCas, int ownerClientId) {
ClientProfile ownerProfile = getClientProfile(ownerClientId);
@@ -1001,6 +1146,13 @@ public class TunerResourceManagerService extends SystemService implements IBinde
ownerProfile.releaseCas();
}
+ private void updateCiCamClientMappingOnRelease(
+ @NonNull CiCamResource releasingCiCam, int ownerClientId) {
+ ClientProfile ownerProfile = getClientProfile(ownerClientId);
+ releasingCiCam.removeOwner(ownerClientId);
+ ownerProfile.releaseCiCam();
+ }
+
/**
* Get the owner client's priority.
*
@@ -1093,15 +1245,31 @@ public class TunerResourceManagerService extends SystemService implements IBinde
}
@VisibleForTesting
+ @Nullable
+ protected CiCamResource getCiCamResource(int ciCamId) {
+ return mCiCamResources.get(ciCamId);
+ }
+
+ @VisibleForTesting
protected Map<Integer, CasResource> getCasResources() {
return mCasResources;
}
+ @VisibleForTesting
+ protected Map<Integer, CiCamResource> getCiCamResources() {
+ return mCiCamResources;
+ }
+
private void addCasResource(CasResource newCas) {
// Update resource list and available id list
mCasResources.put(newCas.getSystemId(), newCas);
}
+ private void addCiCamResource(CiCamResource newCiCam) {
+ // Update resource list and available id list
+ mCiCamResources.put(newCiCam.getCiCamId(), newCiCam);
+ }
+
private void removeCasResource(int removingId) {
CasResource cas = getCasResource(removingId);
if (cas == null) {
@@ -1113,6 +1281,17 @@ public class TunerResourceManagerService extends SystemService implements IBinde
mCasResources.remove(removingId);
}
+ private void removeCiCamResource(int removingId) {
+ CiCamResource ciCam = getCiCamResource(removingId);
+ if (ciCam == null) {
+ return;
+ }
+ for (int ownerId : ciCam.getOwnerClientIds()) {
+ getClientProfile(ownerId).releaseCiCam();
+ }
+ mCiCamResources.remove(removingId);
+ }
+
private void releaseLowerPriorityClientCasResources(int releasingCasResourceNum) {
// TODO: Sort with a treemap
@@ -1161,6 +1340,10 @@ public class TunerResourceManagerService extends SystemService implements IBinde
if (profile.getInUseCasSystemId() != ClientProfile.INVALID_RESOURCE_ID) {
getCasResource(profile.getInUseCasSystemId()).removeOwner(profile.getId());
}
+ // Clear CiCam
+ if (profile.getInUseCiCamId() != ClientProfile.INVALID_RESOURCE_ID) {
+ getCiCamResource(profile.getInUseCiCamId()).removeOwner(profile.getId());
+ }
// Clear Frontend
clearFrontendAndClientMapping(profile);
profile.reclaimAllResources();
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index c6cc83b4c3bb..7a4bcb1b8c37 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1646,7 +1646,7 @@ class ActivityStarter {
if (startedActivityStack != null && startedActivityStack.isAttached()
&& !startedActivityStack.hasActivity()
&& !startedActivityStack.isActivityTypeHome()) {
- startedActivityStack.removeIfPossible();
+ startedActivityStack.removeIfPossible("handleStartResult");
startedActivityStack = null;
}
return startedActivityStack;
@@ -1834,7 +1834,7 @@ class ActivityStarter {
return top.getTask();
} else {
// Remove the stack if no activity in the stack.
- stack.removeIfPossible();
+ stack.removeIfPossible("computeTargetTask");
}
}
return null;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 9ffedde8e616..109ea0954be6 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -285,6 +285,14 @@ public abstract class ActivityTaskManagerInternal {
public abstract void enforceCallerIsRecentsOrHasPermission(String permission, String func);
/**
+ * Returns true if the app can close system dialogs. Otherwise it either throws a {@link
+ * SecurityException} or returns false with a logcat message depending on whether the app
+ * targets SDK level {@link android.os.Build.VERSION_CODES#S} or not.
+ */
+ public abstract boolean checkCanCloseSystemDialogs(int pid, int uid,
+ @Nullable String packageName);
+
+ /**
* Called after the voice interaction service has changed.
*/
public abstract void notifyActiveVoiceInteractionServiceChanged(ComponentName component);
@@ -563,8 +571,13 @@ public abstract class ActivityTaskManagerInternal {
*/
public abstract void setDeviceOwnerUid(int uid);
- /** Set all associated companion app that belongs to an userId. */
- public abstract void setCompanionAppPackages(int userId, Set<String> companionAppPackages);
+ /**
+ * Set all associated companion app that belongs to a userId.
+ * @param userId
+ * @param companionAppUids ActivityTaskManager will take ownership of this Set, the caller
+ * shouldn't touch the Set after calling this interface.
+ */
+ public abstract void setCompanionAppUids(int userId, Set<Integer> companionAppUids);
/**
* @param packageName The package to check
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 461bbfb978e4..039f22da86d8 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -147,6 +147,7 @@ import android.app.WindowConfiguration;
import android.app.admin.DevicePolicyCache;
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
+import android.app.compat.CompatChanges;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
@@ -2900,6 +2901,86 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
}
+ /**
+ * Returns true if the app can close system dialogs. Otherwise it either throws a {@link
+ * SecurityException} or returns false with a logcat message depending on whether the app
+ * targets SDK level {@link android.os.Build.VERSION_CODES#S} or not.
+ */
+ private boolean checkCanCloseSystemDialogs(int pid, int uid, @Nullable String packageName) {
+ final WindowProcessController process;
+ synchronized (mGlobalLock) {
+ process = mProcessMap.getProcess(pid);
+ }
+ if (packageName == null && process != null) {
+ // WindowProcessController.mInfo is final, so after the synchronized memory barrier
+ // above, process.mInfo can't change. As for reading mInfo.packageName,
+ // WindowProcessController doesn't own the ApplicationInfo object referenced by mInfo.
+ // ProcessRecord for example also holds a reference to that object, so protecting access
+ // to packageName with the WM lock would not be enough as we'd also need to synchronize
+ // on the AM lock if we are worried about races, but we can't synchronize on AM lock
+ // here. Hence, since this is only used for logging, we don't synchronize here.
+ packageName = process.mInfo.packageName;
+ }
+ String caller = "(pid=" + pid + ", uid=" + uid + ")";
+ if (packageName != null) {
+ caller = packageName + " " + caller;
+ }
+ if (!canCloseSystemDialogs(pid, uid, process)) {
+ // The app can't close system dialogs, throw only if it targets S+
+ if (CompatChanges.isChangeEnabled(
+ ActivityManager.LOCK_DOWN_CLOSE_SYSTEM_DIALOGS, uid)) {
+ throw new SecurityException(
+ "Permission Denial: " + Intent.ACTION_CLOSE_SYSTEM_DIALOGS
+ + " broadcast from " + caller + " requires "
+ + Manifest.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS + ".");
+ } else if (CompatChanges.isChangeEnabled(
+ ActivityManager.DROP_CLOSE_SYSTEM_DIALOGS, uid)) {
+ Slog.e(TAG,
+ "Permission Denial: " + Intent.ACTION_CLOSE_SYSTEM_DIALOGS
+ + " broadcast from " + caller + " requires "
+ + Manifest.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS
+ + ", dropping broadcast.");
+ return false;
+ } else {
+ Slog.w(TAG, Intent.ACTION_CLOSE_SYSTEM_DIALOGS
+ + " broadcast from " + caller + " will require "
+ + Manifest.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS
+ + " in future builds.");
+ return true;
+ }
+ }
+ return true;
+ }
+
+ private boolean canCloseSystemDialogs(int pid, int uid,
+ @Nullable WindowProcessController process) {
+ if (checkPermission(Manifest.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS, pid, uid)
+ == PERMISSION_GRANTED) {
+ return true;
+ }
+ if (process != null) {
+ // Check if the instrumentation of the process has the permission. This covers the
+ // usual test started from the shell (which has the permission) case. This is needed
+ // for apps targeting SDK level < S but we are also allowing for targetSdk S+ as a
+ // convenience to avoid breaking a bunch of existing tests and asking them to adopt
+ // shell permissions to do this.
+ // Note that these getters all read from volatile fields in WindowProcessController, so
+ // no need to lock.
+ int sourceUid = process.getInstrumentationSourceUid();
+ if (process.isInstrumenting() && sourceUid != -1 && checkPermission(
+ Manifest.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS, -1, sourceUid)
+ == PERMISSION_GRANTED) {
+ return true;
+ }
+ // This is the notification trampoline use-case for example, where apps use Intent.ACSD
+ // to close the shade prior to starting an activity.
+ if (process.canCloseSystemDialogsByToken()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
static void enforceTaskPermission(String func) {
if (checkCallingPermission(MANAGE_ACTIVITY_TASKS) == PackageManager.PERMISSION_GRANTED) {
return;
@@ -5150,6 +5231,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
+ public boolean checkCanCloseSystemDialogs(int pid, int uid, @Nullable String packageName) {
+ return ActivityTaskManagerService.this.checkCanCloseSystemDialogs(pid, uid,
+ packageName);
+ }
+
+ @Override
public void notifyActiveVoiceInteractionServiceChanged(ComponentName component) {
synchronized (mGlobalLock) {
mActiveVoiceInteractionServiceComponent = component;
@@ -5649,9 +5736,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
@Override
public void closeSystemDialogs(String reason) {
enforceNotIsolatedCaller("closeSystemDialogs");
-
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
+ if (!checkCanCloseSystemDialogs(pid, uid, null)) {
+ return;
+ }
+
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -6272,17 +6362,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
- public void setCompanionAppPackages(int userId, Set<String> companionAppPackages) {
- // Translate package names into UIDs
- final Set<Integer> result = new HashSet<>();
- for (String pkg : companionAppPackages) {
- final int uid = getPackageManagerInternalLocked().getPackageUid(pkg, 0, userId);
- if (uid >= 0) {
- result.add(uid);
- }
- }
+ public void setCompanionAppUids(int userId, Set<Integer> companionAppUids) {
synchronized (mGlobalLock) {
- mCompanionAppUidsMap.put(userId, result);
+ mCompanionAppUidsMap.put(userId, companionAppUids);
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index c3a754279f80..5d79eb8d7a61 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3024,7 +3024,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
public void dump(PrintWriter pw, String prefix, boolean dumpAll) {
super.dump(pw, prefix, dumpAll);
pw.print(prefix);
- pw.println("Display: mDisplayId=" + mDisplayId + " stacks=" + getRootTaskCount());
+ pw.println("Display: mDisplayId=" + mDisplayId + " rootTasks=" + getRootTaskCount());
final String subPrefix = " " + prefix;
pw.print(subPrefix); pw.print("init="); pw.print(mInitialDisplayWidth); pw.print("x");
pw.print(mInitialDisplayHeight); pw.print(" "); pw.print(mInitialDisplayDensity);
@@ -3107,30 +3107,31 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
pw.println();
- // Dump stack references
- final Task homeStack = getDefaultTaskDisplayArea().getRootHomeTask();
- if (homeStack != null) {
- pw.println(prefix + "homeStack=" + homeStack.getName());
+ // Dump root task references
+ final Task rootHomeTask = getDefaultTaskDisplayArea().getRootHomeTask();
+ if (rootHomeTask != null) {
+ pw.println(prefix + "rootHomeTask=" + rootHomeTask.getName());
}
- final Task pinnedStack = getDefaultTaskDisplayArea().getRootPinnedTask();
- if (pinnedStack != null) {
- pw.println(prefix + "pinnedStack=" + pinnedStack.getName());
+ final Task rootPinnedTask = getDefaultTaskDisplayArea().getRootPinnedTask();
+ if (rootPinnedTask != null) {
+ pw.println(prefix + "rootPinnedTask=" + rootPinnedTask.getName());
}
- final Task splitScreenPrimaryStack = getDefaultTaskDisplayArea()
+ final Task rootSplitScreenPrimaryTask = getDefaultTaskDisplayArea()
.getRootSplitScreenPrimaryTask();
- if (splitScreenPrimaryStack != null) {
- pw.println(prefix + "splitScreenPrimaryStack=" + splitScreenPrimaryStack.getName());
+ if (rootSplitScreenPrimaryTask != null) {
+ pw.println(
+ prefix + "rootSplitScreenPrimaryTask=" + rootSplitScreenPrimaryTask.getName());
}
// TODO: Support recents on non-default task containers
- final Task recentsStack = getDefaultTaskDisplayArea().getRootTask(
+ final Task rootRecentsTask = getDefaultTaskDisplayArea().getRootTask(
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS);
- if (recentsStack != null) {
- pw.println(prefix + "recentsStack=" + recentsStack.getName());
+ if (rootRecentsTask != null) {
+ pw.println(prefix + "rootRecentsTask=" + rootRecentsTask.getName());
}
- final Task dreamStack =
+ final Task rootDreamTask =
getRootTask(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_DREAM);
- if (dreamStack != null) {
- pw.println(prefix + "dreamStack=" + dreamStack.getName());
+ if (rootDreamTask != null) {
+ pw.println(prefix + "rootDreamTask=" + rootDreamTask.getName());
}
pw.println();
@@ -3150,7 +3151,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
@Override
public String toString() {
- return "Display " + mDisplayId + " info=" + mDisplayInfo + " stacks=" + mChildren;
+ return "Display " + mDisplayId + " info=" + mDisplayInfo + " rootTasks=" + mChildren;
}
String getName() {
@@ -5490,7 +5491,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
if (!hasNonEmptyHomeRootTask && getRootTaskCount() > 0) {
// Release this display if only empty home root task(s) are left. This display will be
// released along with the root task(s) removal.
- forAllRootTasks(Task::removeIfPossible);
+ forAllRootTasks(t -> {t.removeIfPossible("releaseSelfIfNeeded");});
} else if (getTopRootTask() == null) {
removeIfPossible();
mRootWindowContainer.mTaskSupervisor
diff --git a/services/core/java/com/android/server/wm/EventLogTags.logtags b/services/core/java/com/android/server/wm/EventLogTags.logtags
index 6c346090578f..40b80f75c814 100644
--- a/services/core/java/com/android/server/wm/EventLogTags.logtags
+++ b/services/core/java/com/android/server/wm/EventLogTags.logtags
@@ -35,22 +35,18 @@ option java_package com.android.server.wm
30019 wm_relaunch_resume_activity (User|1|5),(Token|1|5),(Task ID|1|5),(Component Name|3)
# An activity has been relaunched:
30020 wm_relaunch_activity (User|1|5),(Token|1|5),(Task ID|1|5),(Component Name|3)
-# The activity's onPause has been called.
-30021 wm_on_paused_called (Token|1|5),(Component Name|3),(Reason|3)
-# The activity's onResume has been called.
-30022 wm_on_resume_called (Token|1|5),(Component Name|3),(Reason|3)
# Activity set to resumed
30043 wm_set_resumed_activity (User|1|5),(Component Name|3),(Reason|3)
-# Stack focus
-30044 wm_focused_stack (User|1|5),(Display Id|1|5),(Focused Stack Id|1|5),(Last Focused Stack Id|1|5),(Reason|3)
+# Root task focus
+30044 wm_focused_root_task (User|1|5),(Display Id|1|5),(Focused Root Task Id|1|5),(Last Focused Root Task Id|1|5),(Reason|3)
# Attempting to stop an activity
30048 wm_stop_activity (User|1|5),(Token|1|5),(Component Name|3)
-# The task is being removed from its parent stack
-30061 wm_remove_task (Task ID|1|5), (Stack ID|1|5)
+# The task is being removed from its parent task
+30061 wm_remove_task (Task ID|1|5), (Root Task ID|1|5)
# An activity been add into stopping list
30066 wm_add_to_stopping (User|1|5),(Token|1|5),(Component Name|3),(Reason|3)
@@ -61,17 +57,11 @@ option java_package com.android.server.wm
# Out of memory for surfaces.
31000 wm_no_surface_memory (Window|3),(PID|1|5),(Operation|3)
# Task created.
-31001 wm_task_created (TaskId|1|5),(StackId|1|5)
+31001 wm_task_created (TaskId|1|5),(RootTaskId|1|5)
# Task moved to top (1) or bottom (0).
31002 wm_task_moved (TaskId|1|5),(ToTop|1),(Index|1)
# Task removed with source explanation.
31003 wm_task_removed (TaskId|1|5),(Reason|3)
-# Stack created.
-31004 wm_stack_created (StackId|1|5)
-# Home stack moved to top (1) or bottom (0).
-31005 wm_home_stack_moved (ToTop|1)
-# Stack removed.
-31006 wm_stack_removed (StackId|1|5)
# bootanim finished:
31007 wm_boot_animation_done (time|2|3)
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 54996a66ecd7..0a95f8f852a9 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -861,6 +861,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
"<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
}
}
+
+ // Send any pending task-info changes that were queued-up during a layout deferment
+ mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
mWmService.mAnimator.executeAfterPrepareSurfacesRunnables();
checkAppTransitionReady(surfacePlacer);
@@ -1013,9 +1016,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
mWmService.scheduleAnimationLocked();
- // Send any pending task-info changes that were queued-up during a layout deferment
- mWmService.mAtmService.mTaskOrganizerController.dispatchPendingTaskInfoChanges();
-
if (DEBUG_WINDOW_TRACE) Slog.e(TAG, "performSurfacePlacementInner exit");
}
@@ -3572,7 +3572,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
public void dump(PrintWriter pw, String prefix, boolean dumpAll) {
super.dump(pw, prefix, dumpAll);
pw.print(prefix);
- pw.println("topDisplayFocusedStack=" + getTopDisplayFocusedRootTask());
+ pw.println("topDisplayFocusedRootTask=" + getTopDisplayFocusedRootTask());
for (int i = getChildCount() - 1; i >= 0; --i) {
final DisplayContent display = getChildAt(i);
display.dump(pw, prefix, dumpAll);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index a88e4537d1a2..260b6c55b4db 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -916,21 +916,26 @@ class Task extends WindowContainer<WindowContainer> {
mTaskSupervisor.mRecentTasks.remove(this);
}
- removeIfPossible();
+ removeIfPossible("cleanUpResourcesForDestroy");
}
@VisibleForTesting
@Override
void removeIfPossible() {
+ removeIfPossible("removeTaskIfPossible");
+ }
+
+ void removeIfPossible(String reason) {
final boolean isRootTask = isRootTask();
if (!isRootTask) {
mAtmService.getLockTaskController().clearLockedTask(this);
}
if (shouldDeferRemoval()) {
- if (DEBUG_ROOT_TASK) Slog.i(TAG, "removeTask: deferring removing taskId=" + mTaskId);
+ if (DEBUG_ROOT_TASK) Slog.i(TAG,
+ "removeTask:" + reason + " deferring removing taskId=" + mTaskId);
return;
}
- removeImmediately();
+ removeImmediately(reason);
if (isLeafTask()) {
mAtmService.getTaskChangeNotificationController().notifyTaskRemoved(mTaskId);
@@ -1402,7 +1407,6 @@ class Task extends WindowContainer<WindowContainer> {
// from the display, so we should probably consolidate it there instead.
if (getParent() == null && mDisplayContent != null) {
- EventLogTags.writeWmStackRemoved(getRootTaskId());
mDisplayContent = null;
mWmService.mWindowPlacerLocked.requestTraversal();
}
@@ -1774,8 +1778,8 @@ class Task extends WindowContainer<WindowContainer> {
getRootTask().removeChild(this, reason);
}
EventLogTags.writeWmTaskRemoved(mTaskId,
- "removeChild: last r=" + r + " in t=" + this);
- removeIfPossible();
+ "removeChild:" + reason + " last r=" + r + " in t=" + this);
+ removeIfPossible(reason);
}
}
@@ -1818,7 +1822,7 @@ class Task extends WindowContainer<WindowContainer> {
if (r.finishing) return;
// Task was restored from persistent storage.
r.takeFromHistory();
- removeChild(r);
+ removeChild(r, reason);
});
} else {
forAllActivities((r) -> {
@@ -3214,8 +3218,12 @@ class Task extends WindowContainer<WindowContainer> {
@Override
void removeImmediately() {
- if (DEBUG_ROOT_TASK) Slog.i(TAG, "removeTask: removing taskId=" + mTaskId);
- EventLogTags.writeWmTaskRemoved(mTaskId, "removeTask");
+ removeImmediately("removeTask");
+ }
+
+ void removeImmediately(String reason) {
+ if (DEBUG_ROOT_TASK) Slog.i(TAG, "removeTask:" + reason + " removing taskId=" + mTaskId);
+ EventLogTags.writeWmTaskRemoved(mTaskId, reason);
// If applicable let the TaskOrganizer know the Task is vanishing.
setTaskOrganizer(null);
@@ -4404,7 +4412,8 @@ class Task extends WindowContainer<WindowContainer> {
if (mRootProcess != null) {
pw.print(prefix); pw.print("mRootProcess="); pw.println(mRootProcess);
}
- pw.print(prefix); pw.print("taskId=" + mTaskId); pw.println(" stackId=" + getRootTaskId());
+ pw.print(prefix); pw.print("taskId=" + mTaskId);
+ pw.println(" rootTaskId=" + getRootTaskId());
pw.print(prefix); pw.print("mHasBeenVisible="); pw.println(getHasBeenVisible());
pw.print(prefix); pw.print("mResizeMode=");
pw.print(ActivityInfo.resizeModeToString(mResizeMode));
@@ -4983,10 +4992,9 @@ class Task extends WindowContainer<WindowContainer> {
}
} else {
// No longer managed by any organizer.
- mTaskAppearedSent = false;
setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, false /* set */);
if (mCreatedByOrganizer) {
- removeImmediately();
+ removeImmediately("setTaskOrganizer");
}
}
@@ -5008,11 +5016,6 @@ class Task extends WindowContainer<WindowContainer> {
* @return {@code true} if task organizer changed.
*/
boolean updateTaskOrganizerState(boolean forceUpdate, boolean skipTaskAppeared) {
- if (getSurfaceControl() == null) {
- // Can't call onTaskAppeared without a surfacecontrol, so defer this until after one
- // is created.
- return false;
- }
if (!canBeOrganized()) {
return setTaskOrganizer(null);
}
@@ -5022,6 +5025,10 @@ class Task extends WindowContainer<WindowContainer> {
final ITaskOrganizer organizer = controller.getTaskOrganizer(windowingMode);
if (!forceUpdate && mTaskOrganizer == organizer) {
return false;
+ } else if (organizer != null && getSurfaceControl() == null) {
+ // Can't call onTaskAppeared without a surfacecontrol, so defer this until after one
+ // is created.
+ return false;
}
return setTaskOrganizer(organizer, skipTaskAppeared);
}
@@ -6708,7 +6715,7 @@ class Task extends WindowContainer<WindowContainer> {
/** Finish all activities in the stack without waiting. */
void finishAllActivitiesImmediately() {
if (!hasChild()) {
- removeIfPossible();
+ removeIfPossible("finishAllActivitiesImmediately");
return;
}
forAllActivities((r) -> {
@@ -7152,7 +7159,7 @@ class Task extends WindowContainer<WindowContainer> {
if (needSep) {
pw.println();
}
- pw.println(" Stack #" + getRootTaskId()
+ pw.println(" RootTask #" + getRootTaskId()
+ ": type=" + activityTypeToString(getActivityType())
+ " mode=" + windowingModeToString(getWindowingMode()));
pw.println(" isSleeping=" + shouldSleepActivities());
@@ -7662,7 +7669,7 @@ class Task extends WindowContainer<WindowContainer> {
void dispatchTaskInfoChangedIfNeeded(boolean force) {
if (isOrganized()) {
- mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(this, force);
+ mAtmService.mTaskOrganizerController.onTaskInfoChanged(this, force);
}
}
@@ -8002,7 +8009,7 @@ class Task extends WindowContainer<WindowContainer> {
// Task created by organizer are added as root.
final Task launchRootTask = mCreatedByOrganizer
- ? null : tda.updateLaunchRootTask(mWindowingMode);
+ ? null : tda.getLaunchRootTask(mWindowingMode, mActivityType);
if (launchRootTask != null) {
// Since this task will be put into a root task, its windowingMode will be
// inherited.
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 3f4150b21178..832fc688d6eb 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -28,7 +28,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.app.WindowConfiguration.isSplitScreenWindowingMode;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -54,6 +53,7 @@ import android.window.WindowContainerTransaction;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ToBooleanFunction;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.internal.util.function.pooled.PooledPredicate;
@@ -117,8 +117,18 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
private RootWindowContainer mRootWindowContainer;
- // When non-null, new tasks get put into this root task.
- Task mLaunchRootTask = null;
+ // Launch root tasks by activityType then by windowingMode.
+ static private class LaunchRootTaskDef {
+ Task task;
+ int[] windowingModes;
+ int[] activityTypes;
+
+ boolean contains(int windowingMode, int activityType) {
+ return ArrayUtils.contains(windowingModes, windowingMode)
+ && ArrayUtils.contains(activityTypes, activityType);
+ }
+ }
+ private final ArrayList<LaunchRootTaskDef> mLaunchRootTasks = new ArrayList<>();
/**
* A focusable stack that is purposely to be positioned at the top. Although the stack may not
@@ -1017,7 +1027,7 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
} else if (candidateTask != null) {
final Task stack = candidateTask;
final int position = onTop ? POSITION_TOP : POSITION_BOTTOM;
- Task launchRootTask = updateLaunchRootTask(windowingMode);
+ final Task launchRootTask = getLaunchRootTask(windowingMode, activityType);
if (launchRootTask != null) {
if (stack.getParent() == null) {
@@ -1096,40 +1106,41 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
.build();
}
- /** @return the root task to create the next task in. */
- Task updateLaunchRootTask(int windowingMode) {
- if (!isSplitScreenWindowingMode(windowingMode)) {
- // Only split-screen windowing modes can do this currently...
- return null;
+ // TODO: Also clear when task is removed from system?
+ void setLaunchRootTask(Task rootTask, int[] windowingModes, int[] activityTypes) {
+ if (!rootTask.mCreatedByOrganizer) {
+ throw new IllegalArgumentException(
+ "Can't set not mCreatedByOrganizer as launch root tr=" + rootTask);
}
- for (int i = mChildren.size() - 1; i >= 0; --i) {
- final WindowContainer child = mChildren.get(i);
- if (child.asTaskDisplayArea() != null) {
- final Task t = child.asTaskDisplayArea().updateLaunchRootTask(windowingMode);
- if (t != null) {
- return t;
- }
- continue;
- }
- final Task t = mChildren.get(i).asTask();
- if (t == null || !t.mCreatedByOrganizer
- || t.getRequestedOverrideWindowingMode() != windowingMode) {
- continue;
- }
- // If not already set, pick a launch root which is not the one we are launching into.
- if (mLaunchRootTask == null) {
- for (int j = 0, n = mChildren.size(); j < n; ++j) {
- final Task tt = mChildren.get(j).asTask();
- if (tt != null && tt.mCreatedByOrganizer && tt != t) {
- mLaunchRootTask = tt;
- break;
- }
- }
+ LaunchRootTaskDef def = null;
+ for (int i = mLaunchRootTasks.size() - 1; i >= 0; --i) {
+ if (mLaunchRootTasks.get(i).task.mTaskId != rootTask.mTaskId) continue;
+ def = mLaunchRootTasks.get(i);
+ }
+
+ if (def != null) {
+ // Remove so we add to the end of the list.
+ mLaunchRootTasks.remove(def);
+ } else {
+ def = new LaunchRootTaskDef();
+ def.task = rootTask;
+ }
+
+ def.activityTypes = activityTypes;
+ def.windowingModes = windowingModes;
+ if (!ArrayUtils.isEmpty(windowingModes) || !ArrayUtils.isEmpty(activityTypes)) {
+ mLaunchRootTasks.add(def);
+ }
+ }
+
+ Task getLaunchRootTask(int windowingMode, int activityType) {
+ for (int i = mLaunchRootTasks.size() - 1; i >= 0; --i) {
+ if (mLaunchRootTasks.get(i).contains(windowingMode, activityType)) {
+ return mLaunchRootTasks.get(i).task;
}
- return t;
}
- return mLaunchRootTask;
+ return null;
}
/**
@@ -1248,7 +1259,7 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
}
mLastFocusedRootTask = prevFocusedTask;
- EventLogTags.writeWmFocusedStack(mRootWindowContainer.mCurrentUser,
+ EventLogTags.writeWmFocusedRootTask(mRootWindowContainer.mCurrentUser,
mDisplayContent.mDisplayId,
currentFocusedTask == null ? -1 : currentFocusedTask.getRootTaskId(),
mLastFocusedRootTask == null ? -1 : mLastFocusedRootTask.getRootTaskId(),
@@ -1321,7 +1332,6 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
void onSplitScreenModeDismissed(Task toTop) {
mAtmService.deferWindowLayout();
try {
- mLaunchRootTask = null;
moveSplitScreenTasksToFullScreen();
} finally {
final Task topFullscreenStack = toTop != null
@@ -1919,7 +1929,20 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
if (mLastFocusedRootTask != null) {
pw.println(doublePrefix + "mLastFocusedRootTask=" + mLastFocusedRootTask);
}
+
final String triplePrefix = doublePrefix + " ";
+
+ if (mLaunchRootTasks.size() > 0) {
+ pw.println(doublePrefix + "mLaunchRootTasks:");
+ for (int i = mLaunchRootTasks.size() - 1; i >= 0; --i) {
+ final LaunchRootTaskDef def = mLaunchRootTasks.get(i);
+ pw.println(triplePrefix
+ + def.activityTypes + " "
+ + def.windowingModes + " "
+ + " task=" + def.task);
+ }
+ }
+
pw.println(doublePrefix + "Application tokens in top down Z order:");
for (int index = getChildCount() - 1; index >= 0; --index) {
final WindowContainer child = getChildAt(index);
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 089071f5de54..383dfd48a07a 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -151,27 +151,23 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task appeared taskId=%d", task.mTaskId);
final boolean visible = task.isVisible();
final RunningTaskInfo taskInfo = task.getTaskInfo();
- mDeferTaskOrgCallbacksConsumer.accept(() -> {
- try {
- mTaskOrganizer.onTaskAppeared(taskInfo, prepareLeash(task, visible,
- "TaskOrganizerController.onTaskAppeared"));
- } catch (RemoteException e) {
- Slog.e(TAG, "Exception sending onTaskAppeared callback", e);
- }
- });
+ try {
+ mTaskOrganizer.onTaskAppeared(taskInfo, prepareLeash(task, visible,
+ "TaskOrganizerController.onTaskAppeared"));
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception sending onTaskAppeared callback", e);
+ }
}
void onTaskVanished(Task task) {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task vanished taskId=%d", task.mTaskId);
final RunningTaskInfo taskInfo = task.getTaskInfo();
- mDeferTaskOrgCallbacksConsumer.accept(() -> {
- try {
- mTaskOrganizer.onTaskVanished(taskInfo);
- } catch (RemoteException e) {
- Slog.e(TAG, "Exception sending onTaskVanished callback", e);
- }
- });
+ try {
+ mTaskOrganizer.onTaskVanished(taskInfo);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception sending onTaskVanished callback", e);
+ }
}
void onTaskInfoChanged(Task task, ActivityManager.RunningTaskInfo taskInfo) {
@@ -180,20 +176,18 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
return;
}
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task info changed taskId=%d", task.mTaskId);
- mDeferTaskOrgCallbacksConsumer.accept(() -> {
- if (!task.isOrganized()) {
- // This is safe to ignore if the task is no longer organized
- return;
- }
- try {
- // Purposely notify of task info change immediately instead of deferring (like
- // appear and vanish) to allow info changes (such as new PIP params) to flow
- // without waiting.
- mTaskOrganizer.onTaskInfoChanged(taskInfo);
- } catch (RemoteException e) {
- Slog.e(TAG, "Exception sending onTaskInfoChanged callback", e);
- }
- });
+ if (!task.isOrganized()) {
+ // This is safe to ignore if the task is no longer organized
+ return;
+ }
+ try {
+ // Purposely notify of task info change immediately instead of deferring (like
+ // appear and vanish) to allow info changes (such as new PIP params) to flow
+ // without waiting.
+ mTaskOrganizer.onTaskInfoChanged(taskInfo);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception sending onTaskInfoChanged callback", e);
+ }
}
void onBackPressedOnTaskRoot(Task task) {
@@ -203,17 +197,15 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
// Skip if the task has not yet received taskAppeared().
return;
}
- mDeferTaskOrgCallbacksConsumer.accept(() -> {
- if (!task.isOrganized()) {
- // This is safe to ignore if the task is no longer organized
- return;
- }
- try {
- mTaskOrganizer.onBackPressedOnTaskRoot(task.getTaskInfo());
- } catch (Exception e) {
- Slog.e(TAG, "Exception sending onBackPressedOnTaskRoot callback", e);
- }
- });
+ if (!task.isOrganized()) {
+ // This is safe to ignore if the task is no longer organized
+ return;
+ }
+ try {
+ mTaskOrganizer.onBackPressedOnTaskRoot(task.getTaskInfo());
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception sending onBackPressedOnTaskRoot callback", e);
+ }
}
}
@@ -258,28 +250,34 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
return mOrganizer.prepareLeash(t, t.isVisible(), reason);
}
- void addTask(Task t) {
- if (t.mTaskAppearedSent) return;
+ private boolean addTask(Task t) {
+ if (t.mTaskAppearedSent) {
+ return false;
+ }
if (!mOrganizedTasks.contains(t)) {
mOrganizedTasks.add(t);
}
+
if (t.taskAppearedReady()) {
t.mTaskAppearedSent = true;
- mOrganizer.onTaskAppeared(t);
+ return true;
}
+ return false;
}
- void removeTask(Task t) {
+ private boolean removeTask(Task t) {
+ mOrganizedTasks.remove(t);
+ mInterceptBackPressedOnRootTasks.remove(t.mTaskId);
+
if (t.mTaskAppearedSent) {
if (t.getSurfaceControl() != null) {
t.migrateToNewSurfaceControl();
}
t.mTaskAppearedSent = false;
- mOrganizer.onTaskVanished(t);
+ return true;
}
- mOrganizedTasks.remove(t);
- mInterceptBackPressedOnRootTasks.remove(t.mTaskId);
+ return false;
}
void dispose() {
@@ -291,7 +289,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
while (!mOrganizedTasks.isEmpty()) {
final Task t = mOrganizedTasks.get(0);
if (!t.updateTaskOrganizerState(true /* forceUpdate */)) {
- removeTask(t);
+ TaskOrganizerController.this.onTaskVanished(mOrganizer.mTaskOrganizer, t);
}
}
@@ -305,6 +303,33 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
}
}
+ static class PendingTaskEvent {
+ static final int EVENT_APPEARED = 0;
+ static final int EVENT_VANISHED = 1;
+ static final int EVENT_INFO_CHANGED = 2;
+ static final int EVENT_ROOT_BACK_PRESSED = 3;
+
+ final int mEventType;
+ final Task mTask;
+ final ITaskOrganizer mTaskOrg;
+ boolean mForce;
+
+ PendingTaskEvent(Task task, int event) {
+ this(task, task.mTaskOrganizer, event);
+ }
+
+ PendingTaskEvent(Task task, ITaskOrganizer taskOrg, int eventType) {
+ mTask = task;
+ mTaskOrg = taskOrg;
+ mEventType = eventType;
+ }
+
+ boolean isLifecycleEvent() {
+ return mEventType == EVENT_APPEARED || mEventType == EVENT_VANISHED
+ || mEventType == EVENT_INFO_CHANGED;
+ }
+ }
+
private final ActivityTaskManagerService mService;
private final WindowManagerGlobalLock mGlobalLock;
@@ -312,7 +337,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
private final LinkedList<ITaskOrganizer> mTaskOrganizers = new LinkedList<>();
private final HashMap<IBinder, TaskOrganizerState> mTaskOrganizerStates = new HashMap<>();
private final WeakHashMap<Task, RunningTaskInfo> mLastSentTaskInfos = new WeakHashMap<>();
- private final ArrayList<Task> mPendingTaskInfoChanges = new ArrayList<>();
+ // Pending task events due to layout deferred.
+ private final ArrayList<PendingTaskEvent> mPendingTaskEvents = new ArrayList<>();
// Set of organized tasks (by taskId) that dispatch back pressed to their organizers
private final HashSet<Integer> mInterceptBackPressedOnRootTasks = new HashSet();
@@ -337,6 +363,12 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
public void setDeferTaskOrgCallbacksConsumer(Consumer<Runnable> consumer) {
mDeferTaskOrgCallbacksConsumer = consumer;
}
+
+ @VisibleForTesting
+ ArrayList<PendingTaskEvent> getPendingEventList() {
+ return mPendingTaskEvents;
+ }
+
/**
* Register a TaskOrganizer to manage tasks as they enter the a supported windowing mode.
*/
@@ -442,13 +474,33 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
void onTaskAppeared(ITaskOrganizer organizer, Task task) {
final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder());
- state.addTask(task);
+ if (state != null && state.addTask(task)) {
+ PendingTaskEvent pending = getPendingTaskEvent(task, PendingTaskEvent.EVENT_APPEARED);
+ if (pending == null) {
+ pending = new PendingTaskEvent(task, PendingTaskEvent.EVENT_APPEARED);
+ mPendingTaskEvents.add(pending);
+ }
+ }
}
void onTaskVanished(ITaskOrganizer organizer, Task task) {
final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder());
- if (state != null) {
- state.removeTask(task);
+ if (state != null && state.removeTask(task)) {
+ for (int i = mPendingTaskEvents.size() - 1; i >= 0; i--) {
+ PendingTaskEvent entry = mPendingTaskEvents.get(i);
+ if (task.mTaskId == entry.mTask.mTaskId) {
+ // This task will vanished so remove all pending event of it.
+ mPendingTaskEvents.remove(i);
+ if (entry.mEventType == PendingTaskEvent.EVENT_APPEARED) {
+ // If task still not appeared, ignore this callback.
+ return;
+ }
+ }
+ }
+
+ PendingTaskEvent pending =
+ new PendingTaskEvent(task, organizer, PendingTaskEvent.EVENT_VANISHED);
+ mPendingTaskEvents.add(pending);
}
}
@@ -510,7 +562,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Delete root task display=%d winMode=%d",
task.getDisplayId(), task.getWindowingMode());
- task.removeImmediately();
+ task.removeImmediately("deleteRootTask");
return true;
}
} finally {
@@ -518,30 +570,76 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
}
}
- void dispatchPendingTaskInfoChanges() {
- if (mService.mWindowManager.mWindowPlacerLocked.isLayoutDeferred()) {
+ void dispatchPendingEvents() {
+ if (mService.mWindowManager.mWindowPlacerLocked.isLayoutDeferred()
+ || mPendingTaskEvents.isEmpty()) {
return;
}
- for (int i = 0, n = mPendingTaskInfoChanges.size(); i < n; ++i) {
- dispatchTaskInfoChanged(mPendingTaskInfoChanges.get(i), false /* force */);
+
+ for (int i = 0, n = mPendingTaskEvents.size(); i < n; i++) {
+ PendingTaskEvent event = mPendingTaskEvents.get(i);
+ final Task task = event.mTask;
+ final TaskOrganizerState state;
+ switch (event.mEventType) {
+ case PendingTaskEvent.EVENT_APPEARED:
+ state = mTaskOrganizerStates.get(event.mTaskOrg.asBinder());
+ if (state != null && task.taskAppearedReady()) {
+ state.mOrganizer.onTaskAppeared(task);
+ }
+ break;
+ case PendingTaskEvent.EVENT_VANISHED:
+ state = mTaskOrganizerStates.get(event.mTaskOrg.asBinder());
+ if (state != null) {
+ state.mOrganizer.onTaskVanished(task);
+ }
+ break;
+ case PendingTaskEvent.EVENT_INFO_CHANGED:
+ dispatchTaskInfoChanged(event.mTask, event.mForce);
+ break;
+ case PendingTaskEvent.EVENT_ROOT_BACK_PRESSED:
+ state = mTaskOrganizerStates.get(event.mTaskOrg.asBinder());
+ if (state != null) {
+ state.mOrganizer.onBackPressedOnTaskRoot(task);
+ }
+ break;
+ }
}
- mPendingTaskInfoChanges.clear();
+ mPendingTaskEvents.clear();
}
- void dispatchTaskInfoChanged(Task task, boolean force) {
- if (!force && mService.mWindowManager.mWindowPlacerLocked.isLayoutDeferred()) {
- // Defer task info reporting while layout is deferred. This is because layout defer
- // blocks tend to do lots of re-ordering which can mess up animations in receivers.
- mPendingTaskInfoChanges.remove(task);
- mPendingTaskInfoChanges.add(task);
+ void onTaskInfoChanged(Task task, boolean force) {
+ if (!task.mTaskAppearedSent) {
+ // Skip if task still not appeared.
return;
}
+
+ // Defer task info reporting while layout is deferred. This is because layout defer
+ // blocks tend to do lots of re-ordering which can mess up animations in receivers.
+ PendingTaskEvent pending = getPendingLifecycleTaskEvent(task);
+ if (pending == null) {
+ pending = new PendingTaskEvent(task, PendingTaskEvent.EVENT_INFO_CHANGED);
+ } else {
+ if (pending.mEventType != PendingTaskEvent.EVENT_INFO_CHANGED) {
+ // If queued event is appeared, it means task still not appeared so ignore
+ // this info changed. If queued event is vanished, it means task should
+ // will vanished early so do not need this info changed.
+ return;
+ }
+ // Remove and add for re-ordering.
+ mPendingTaskEvents.remove(pending);
+ }
+ pending.mForce = force;
+ mPendingTaskEvents.add(pending);
+ }
+
+ private void dispatchTaskInfoChanged(Task task, boolean force) {
RunningTaskInfo lastInfo = mLastSentTaskInfos.get(task);
if (mTmpTaskInfo == null) {
mTmpTaskInfo = new RunningTaskInfo();
}
mTmpTaskInfo.configuration.unset();
task.fillTaskInfo(mTmpTaskInfo);
+
boolean changed = !mTmpTaskInfo.equalsForTaskOrganizer(lastInfo);
if (!changed) {
int cfgChanges = mTmpTaskInfo.configuration.diff(lastInfo.configuration);
@@ -601,45 +699,6 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
}
@Override
- public void setLaunchRoot(int displayId, @Nullable WindowContainerToken token) {
- enforceTaskPermission("setLaunchRoot()");
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- TaskDisplayArea defaultTaskDisplayArea = mService.mRootWindowContainer
- .getDisplayContent(displayId).getDefaultTaskDisplayArea();
- if (defaultTaskDisplayArea == null) {
- return;
- }
- WindowContainer wc = null;
- if (token != null) {
- wc = WindowContainer.fromBinder(token.asBinder());
- if (wc == null) {
- throw new IllegalArgumentException("Can't resolve window from token");
- }
- }
- final Task task = wc == null ? null : wc.asTask();
- if (task == null) {
- defaultTaskDisplayArea.mLaunchRootTask = null;
- return;
- }
- if (!task.mCreatedByOrganizer) {
- throw new IllegalArgumentException("Attempt to set task not created by "
- + "organizer as launch root task=" + task);
- }
- if (task.getDisplayArea() == null
- || task.getDisplayArea().getDisplayId() != displayId) {
- throw new RuntimeException("Can't set launch root for display " + displayId
- + " to task on display " + task.getDisplayContent().getDisplayId());
- }
- task.getDisplayArea().mLaunchRootTask = task;
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
-
- @Override
public List<RunningTaskInfo> getChildTasks(WindowContainerToken parent,
@Nullable int[] activityTypes) {
enforceTaskPermission("getChildTasks()");
@@ -743,11 +802,48 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
return false;
}
- final TaskOrganizerState state = mTaskOrganizerStates.get(task.mTaskOrganizer.asBinder());
- state.mOrganizer.onBackPressedOnTaskRoot(task);
+ PendingTaskEvent pendingVanished =
+ getPendingTaskEvent(task, PendingTaskEvent.EVENT_VANISHED);
+ if (pendingVanished != null) {
+ // This task will vanish before this callback so just ignore.
+ return false;
+ }
+
+ PendingTaskEvent pending = getPendingTaskEvent(
+ task, PendingTaskEvent.EVENT_ROOT_BACK_PRESSED);
+ if (pending == null) {
+ pending = new PendingTaskEvent(task, PendingTaskEvent.EVENT_ROOT_BACK_PRESSED);
+ } else {
+ // Pending already exist, remove and add for re-ordering.
+ mPendingTaskEvents.remove(pending);
+ }
+ mPendingTaskEvents.add(pending);
return true;
}
+ @Nullable
+ private PendingTaskEvent getPendingTaskEvent(Task task, int type) {
+ for (int i = mPendingTaskEvents.size() - 1; i >= 0; i--) {
+ PendingTaskEvent entry = mPendingTaskEvents.get(i);
+ if (task.mTaskId == entry.mTask.mTaskId && type == entry.mEventType) {
+ return entry;
+ }
+ }
+ return null;
+ }
+
+ @VisibleForTesting
+ @Nullable
+ PendingTaskEvent getPendingLifecycleTaskEvent(Task task) {
+ for (int i = mPendingTaskEvents.size() - 1; i >= 0; i--) {
+ PendingTaskEvent entry = mPendingTaskEvents.get(i);
+ if (task.mTaskId == entry.mTask.mTaskId && entry.isLifecycleEvent()) {
+ return entry;
+ }
+ }
+ return null;
+ }
+
public void dump(PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
pw.print(prefix); pw.println("TaskOrganizerController:");
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index f627ca620bc3..83ad437eba29 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -224,6 +224,7 @@ public class WindowAnimator {
mService.destroyPreservedSurfaceLocked();
+ mService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
executeAfterPrepareSurfacesRunnables();
if (DEBUG_WINDOW_TRACE) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 4eeae6c0710c..a034bac993e0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3238,6 +3238,11 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public void closeSystemDialogs(String reason) {
+ int callingPid = Binder.getCallingPid();
+ int callingUid = Binder.getCallingUid();
+ if (!mAtmInternal.checkCanCloseSystemDialogs(callingPid, callingUid, null)) {
+ return;
+ }
synchronized (mGlobalLock) {
mRoot.closeSystemDialogs(reason);
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index b0e67ce17711..be1f7e16cd3c 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -17,6 +17,10 @@
package com.android.server.wm;
import static android.Manifest.permission.READ_FRAME_BUFFER;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
import static com.android.server.wm.ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED;
@@ -49,13 +53,16 @@ import android.window.WindowContainerTransaction;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.function.pooled.PooledConsumer;
import com.android.internal.util.function.pooled.PooledLambda;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.function.Consumer;
/**
* Server side implementation for the interface for organizing windows
@@ -256,34 +263,46 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
for (int i = 0, n = hops.size(); i < n; ++i) {
final WindowContainerTransaction.HierarchyOp hop = hops.get(i);
- final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
- if (wc == null || !wc.isAttached()) {
- Slog.e(TAG, "Attempt to operate on detached container: " + wc);
- continue;
- }
- if (syncId >= 0) {
- addToSyncSet(syncId, wc);
- }
- if (transition != null) {
- transition.collect(wc);
- if (hop.isReparent()) {
- if (wc.getParent() != null) {
- // Collect the current parent. It's visibility may change as a result
- // of this reparenting.
- transition.collect(wc.getParent());
+ switch (hop.getType()) {
+ case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT:
+ final Task task = WindowContainer.fromBinder(hop.getContainer()).asTask();
+ task.getDisplayArea().setLaunchRootTask(task,
+ hop.getWindowingModes(), hop.getActivityTypes());
+ break;
+ case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT:
+ effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId);
+ break;
+ case HIERARCHY_OP_TYPE_REORDER:
+ case HIERARCHY_OP_TYPE_REPARENT:
+ final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
+ if (wc == null || !wc.isAttached()) {
+ Slog.e(TAG, "Attempt to operate on detached container: " + wc);
+ continue;
}
- if (hop.getNewParent() != null) {
- final WindowContainer parentWc =
- WindowContainer.fromBinder(hop.getNewParent());
- if (parentWc == null) {
- Slog.e(TAG, "Can't resolve parent window from token");
- continue;
+ if (syncId >= 0) {
+ addToSyncSet(syncId, wc);
+ }
+ if (transition != null) {
+ transition.collect(wc);
+ if (hop.isReparent()) {
+ if (wc.getParent() != null) {
+ // Collect the current parent. It's visibility may change as
+ // a result of this reparenting.
+ transition.collect(wc.getParent());
+ }
+ if (hop.getNewParent() != null) {
+ final WindowContainer parentWc =
+ WindowContainer.fromBinder(hop.getNewParent());
+ if (parentWc == null) {
+ Slog.e(TAG, "Can't resolve parent window from token");
+ continue;
+ }
+ transition.collect(parentWc);
+ }
}
- transition.collect(parentWc);
}
- }
+ effects |= sanitizeAndApplyHierarchyOp(wc, hop);
}
- effects |= sanitizeAndApplyHierarchyOp(wc, hop);
}
// Queue-up bounds-change transactions for tasks which are now organized. Do
// this after hierarchy ops so we have the final organized state.
@@ -492,6 +511,85 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
return TRANSACT_EFFECTS_LIFECYCLE;
}
+ private int reparentChildrenTasksHierarchyOp(WindowContainerTransaction.HierarchyOp hop,
+ @Nullable Transition transition, int syncId) {
+ WindowContainer currentParent = hop.getContainer() != null
+ ? WindowContainer.fromBinder(hop.getContainer()) : null;
+ WindowContainer newParent = hop.getNewParent() != null
+ ? WindowContainer.fromBinder(hop.getNewParent()) : null;
+ if (currentParent == null && newParent == null) {
+ throw new IllegalArgumentException("reparentChildrenTasksHierarchyOp: " + hop);
+ } else if (currentParent == null) {
+ currentParent = newParent.asTask().getDisplayContent().getDefaultTaskDisplayArea();
+ } else if (newParent == null) {
+ newParent = currentParent.asTask().getDisplayContent().getDefaultTaskDisplayArea();
+ }
+
+ if (currentParent == newParent) {
+ Slog.e(TAG, "reparentChildrenTasksHierarchyOp parent not changing: " + hop);
+ return 0;
+ }
+ if (!currentParent.isAttached()) {
+ Slog.e(TAG, "reparentChildrenTasksHierarchyOp currentParent detached="
+ + currentParent + " hop=" + hop);
+ return 0;
+ }
+ if (!newParent.isAttached()) {
+ Slog.e(TAG, "reparentChildrenTasksHierarchyOp newParent detached="
+ + newParent + " hop=" + hop);
+ return 0;
+ }
+
+ final boolean newParentInMultiWindow = newParent.inMultiWindowMode();
+ final WindowContainer finalCurrentParent = currentParent;
+ Slog.i(TAG, "reparentChildrenTasksHierarchyOp"
+ + " currentParent=" + currentParent + " newParent=" + newParent + " hop=" + hop);
+
+ // We want to collect the tasks first before re-parenting to avoid array shifting on us.
+ final ArrayList<Task> tasksToReparent = new ArrayList<>();
+
+ currentParent.forAllTasks((Consumer<Task>) (task) -> {
+ Slog.i(TAG, " Processing task=" + task);
+ if (task.mCreatedByOrganizer
+ || task.getParent() != finalCurrentParent) {
+ // We only care about non-organized task that are direct children of the thing we
+ // are reparenting from.
+ return;
+ }
+
+ if (newParentInMultiWindow && !task.isResizeable()) {
+ Slog.e(TAG, "reparentChildrenTasksHierarchyOp non-resizeable task=" + task);
+ }
+
+ if (!ArrayUtils.contains(hop.getActivityTypes(), task.getActivityType())) return;
+ if (!ArrayUtils.contains(hop.getWindowingModes(), task.getWindowingMode())) return;
+
+ tasksToReparent.add(task);
+ }, !hop.getToTop());
+
+ final int count = tasksToReparent.size();
+ for (int i = 0; i < count; ++i) {
+ final Task task = tasksToReparent.get(i);
+ if (syncId >= 0) {
+ addToSyncSet(syncId, task);
+ }
+ if (transition != null) transition.collect(task);
+
+ if (newParent instanceof TaskDisplayArea) {
+ // For now, reparenting to display area is different from other reparents...
+ task.reparent((TaskDisplayArea) newParent, hop.getToTop());
+ } else {
+ task.reparent((Task) newParent,
+ hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
+ false /*moveParents*/, "processChildrenTaskReparentHierarchyOp");
+ }
+ }
+
+ if (transition != null) transition.collect(newParent);
+
+ return TRANSACT_EFFECTS_LIFECYCLE;
+ }
+
private void sanitizeWindowContainer(WindowContainer wc) {
if (!(wc instanceof Task) && !(wc instanceof DisplayArea)) {
throw new RuntimeException("Invalid token in task or displayArea transaction");
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 8aa154bb9dd1..663d91ecc82a 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -23,6 +23,7 @@ import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
import static android.view.Display.INVALID_DISPLAY;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
+import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.server.am.ActivityManagerService.MY_PID;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
@@ -161,6 +162,8 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
private volatile boolean mDebugging;
// Active instrumentation running in process?
private volatile boolean mInstrumenting;
+ // If there is active instrumentation, this is the source
+ private volatile int mInstrumentationSourceUid = -1;
// Active instrumentation with background activity starts privilege running in process?
private volatile boolean mInstrumentingWithBackgroundActivityStartPrivileges;
// This process it perceptible by the user.
@@ -623,9 +626,16 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
mBoundClientUids = boundClientUids;
}
- public void setInstrumenting(boolean instrumenting,
+ /**
+ * Set instrumentation-related info.
+ *
+ * If {@code instrumenting} is {@code false}, {@code sourceUid} has to be -1.
+ */
+ public void setInstrumenting(boolean instrumenting, int sourceUid,
boolean hasBackgroundActivityStartPrivileges) {
+ checkArgument(instrumenting || sourceUid == -1);
mInstrumenting = instrumenting;
+ mInstrumentationSourceUid = sourceUid;
mInstrumentingWithBackgroundActivityStartPrivileges = hasBackgroundActivityStartPrivileges;
}
@@ -633,6 +643,11 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
return mInstrumenting;
}
+ /** Returns the uid of the active instrumentation source if there is one, otherwise -1. */
+ int getInstrumentationSourceUid() {
+ return mInstrumentationSourceUid;
+ }
+
public void setPerceptible(boolean perceptible) {
mPerceptible = perceptible;
}
diff --git a/services/core/xsd/platform-compat-config.xsd b/services/core/xsd/platform-compat-config.xsd
index 992470816068..a62e2c385766 100644
--- a/services/core/xsd/platform-compat-config.xsd
+++ b/services/core/xsd/platform-compat-config.xsd
@@ -31,6 +31,7 @@
<xs:attribute type="xs:int" name="enableAfterTargetSdk"/>
<xs:attribute type="xs:int" name="enableSinceTargetSdk"/>
<xs:attribute type="xs:string" name="description"/>
+ <xs:attribute type="xs:boolean" name="overridable"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
@@ -48,7 +49,3 @@
</xs:unique>
</xs:element>
</xs:schema>
-
-
-
-
diff --git a/services/core/xsd/platform-compat-schema/current.txt b/services/core/xsd/platform-compat-schema/current.txt
index e3640edd0201..fb8bbefd8374 100644
--- a/services/core/xsd/platform-compat-schema/current.txt
+++ b/services/core/xsd/platform-compat-schema/current.txt
@@ -10,6 +10,7 @@ package com.android.server.compat.config {
method public long getId();
method public boolean getLoggingOnly();
method public String getName();
+ method public boolean getOverridable();
method public String getValue();
method public void setDescription(String);
method public void setDisabled(boolean);
@@ -18,6 +19,7 @@ package com.android.server.compat.config {
method public void setId(long);
method public void setLoggingOnly(boolean);
method public void setName(String);
+ method public void setOverridable(boolean);
method public void setValue(String);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 22e9725f49ab..8b7f83f4edb5 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -55,6 +55,12 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub {
*/
abstract void handleUnlockUser(int userId);
/**
+ * To be called by {@link DevicePolicyManagerService#Lifecycle} after a user is being unlocked.
+ *
+ * @see {@link SystemService#onUserUnlocked}
+ */
+ abstract void handleOnUserUnlocked(int userId);
+ /**
* To be called by {@link DevicePolicyManagerService#Lifecycle} when a user is being stopped.
*
* @see {@link SystemService#onUserStopping}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
index 7ec5ff0b3492..5e7f984d6726 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
@@ -78,6 +78,12 @@ class DevicePolicyData {
private static final String ATTR_DEVICE_PROVISIONING_CONFIG_APPLIED =
"device-provisioning-config-applied";
private static final String ATTR_DEVICE_PAIRED = "device-paired";
+ private static final String ATTR_NEW_USER_DISCLAIMER = "new-user-disclaimer";
+
+ // Values of ATTR_NEW_USER_DISCLAIMER
+ static final String NEW_USER_DISCLAIMER_SHOWN = "shown";
+ static final String NEW_USER_DISCLAIMER_NOT_NEEDED = "not_needed";
+ static final String NEW_USER_DISCLAIMER_NEEDED = "needed";
private static final String TAG = DevicePolicyManagerService.LOG_TAG;
private static final boolean VERBOSE_LOG = false; // DO NOT SUBMIT WITH TRUE
@@ -146,6 +152,10 @@ class DevicePolicyData {
// apps were suspended or unsuspended.
boolean mAppsSuspended = false;
+ // Whether it's necessary to show a disclaimer (that the device is managed) after the user
+ // starts.
+ String mNewUserDisclaimer = NEW_USER_DISCLAIMER_NOT_NEEDED;
+
DevicePolicyData(@UserIdInt int userId) {
mUserId = userId;
}
@@ -186,6 +196,9 @@ class DevicePolicyData {
if (policyData.mPermissionPolicy != DevicePolicyManager.PERMISSION_POLICY_PROMPT) {
out.attributeInt(null, ATTR_PERMISSION_POLICY, policyData.mPermissionPolicy);
}
+ if (NEW_USER_DISCLAIMER_NEEDED.equals(policyData.mNewUserDisclaimer)) {
+ out.attribute(null, ATTR_NEW_USER_DISCLAIMER, policyData.mNewUserDisclaimer);
+ }
// Serialize delegations.
for (int i = 0; i < policyData.mDelegationMap.size(); ++i) {
@@ -412,6 +425,7 @@ class DevicePolicyData {
if (permissionPolicy != -1) {
policy.mPermissionPolicy = permissionPolicy;
}
+ policy.mNewUserDisclaimer = parser.getAttributeValue(null, ATTR_NEW_USER_DISCLAIMER);
int outerDepth = parser.getDepth();
policy.mLockTaskPackages.clear();
@@ -588,6 +602,7 @@ class DevicePolicyData {
pw.print("mAppsSuspended="); pw.println(mAppsSuspended);
pw.print("mUserSetupComplete="); pw.println(mUserSetupComplete);
pw.print("mAffiliationIds="); pw.println(mAffiliationIds);
+ pw.print("mNewUserDisclaimer="); pw.println(mNewUserDisclaimer);
pw.decreaseIndent();
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 4654fca250a3..8b575fb16a2a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -707,6 +707,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public void onUserStopping(@NonNull TargetUser user) {
mService.handleStopUser(user.getUserIdentifier());
}
+
+ @Override
+ public void onUserUnlocked(@NonNull TargetUser user) {
+ mService.handleOnUserUnlocked(user.getUserIdentifier());
+ }
}
@GuardedBy("getLockObject()")
@@ -886,6 +891,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
+ private final class UserLifecycleListener implements UserManagerInternal.UserLifecycleListener {
+
+ @Override
+ public void onUserCreated(UserInfo user) {
+ mHandler.post(() -> handleNewUserCreated(user));
+ }
+ }
+
private void handlePackagesChanged(@Nullable String packageName, int userHandle) {
boolean removedAdmin = false;
if (VERBOSE_LOG) {
@@ -1561,6 +1574,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
mSetupContentObserver = new SetupContentObserver(mHandler);
mUserManagerInternal.addUserRestrictionsListener(new RestrictionsListener(mContext));
+ mUserManagerInternal.addUserLifecycleListener(new UserLifecycleListener());
loadOwners();
}
@@ -2902,6 +2916,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
@Override
+ void handleOnUserUnlocked(int userId) {
+ showNewUserDisclaimerIfNecessary(userId);
+ }
+
+ @Override
void handleStopUser(int userId) {
stopOwnerService(userId, "stop-user");
}
@@ -3432,6 +3451,19 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
synchronized (getLockObject()) {
ActiveAdmin ap = getActiveAdminForCallerLocked(
who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
+
+ // If setPasswordQuality is called on the parent, ensure that
+ // the primary admin does not have password complexity state (this is an
+ // unsupported state).
+ if (parent) {
+ final ActiveAdmin primaryAdmin = getActiveAdminForCallerLocked(
+ who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, false);
+ final boolean hasComplexitySet =
+ primaryAdmin.mPasswordComplexity != PASSWORD_COMPLEXITY_NONE;
+ Preconditions.checkState(!hasComplexitySet,
+ "Cannot set password quality when complexity is set on the primary admin."
+ + " Set the primary admin's complexity to NONE first.");
+ }
mInjector.binderWithCleanCallingIdentity(() -> {
final PasswordPolicy passwordPolicy = ap.mPasswordPolicy;
if (passwordPolicy.quality != quality) {
@@ -4397,6 +4429,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final ActiveAdmin admin = getParentOfAdminIfRequired(
getProfileOwnerOrDeviceOwnerLocked(caller), calledOnParent);
if (admin.mPasswordComplexity != passwordComplexity) {
+ // We require the caller to explicitly clear any password quality requirements set
+ // on the parent DPM instance, to avoid the case where password requirements are
+ // specified in the form of quality on the parent but complexity on the profile
+ // itself.
+ if (!calledOnParent) {
+ final boolean hasQualityRequirementsOnParent = admin.hasParentActiveAdmin()
+ && admin.getParentActiveAdmin().mPasswordPolicy.quality
+ != PASSWORD_QUALITY_UNSPECIFIED;
+ Preconditions.checkState(!hasQualityRequirementsOnParent,
+ "Password quality is set on the parent when attempting to set password"
+ + "complexity. Clear the quality by setting the password quality "
+ + "on the parent to PASSWORD_QUALITY_UNSPECIFIED first");
+ }
+
mInjector.binderWithCleanCallingIdentity(() -> {
admin.mPasswordComplexity = passwordComplexity;
// Reset the password policy.
@@ -7660,8 +7706,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// Sets profile owner on current foreground user since
// the human user will complete the DO setup workflow from there.
manageUserUnchecked(/* deviceOwner= */ admin, /* profileOwner= */ admin,
- /* managedUser= */ currentForegroundUser,
- /* adminExtras= */ null);
+ /* managedUser= */ currentForegroundUser, /* adminExtras= */ null,
+ /* showDisclaimer= */ false);
}
return true;
}
@@ -9713,7 +9759,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final long id = mInjector.binderClearCallingIdentity();
try {
- manageUserUnchecked(admin, profileOwner, userHandle, adminExtras);
+ manageUserUnchecked(admin, profileOwner, userHandle, adminExtras,
+ /* showDisclaimer= */ true);
if ((flags & DevicePolicyManager.SKIP_SETUP_WIZARD) != 0) {
Settings.Secure.putIntForUser(mContext.getContentResolver(),
@@ -9735,7 +9782,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
private void manageUserUnchecked(ComponentName admin, ComponentName profileOwner,
- @UserIdInt int userId, PersistableBundle adminExtras) {
+ @UserIdInt int userId, PersistableBundle adminExtras, boolean showDisclaimer) {
final String adminPkg = admin.getPackageName();
try {
// Install the profile owner if not present.
@@ -9761,11 +9808,60 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
DevicePolicyData policyData = getUserData(userId);
policyData.mInitBundle = adminExtras;
policyData.mAdminBroadcastPending = true;
+ policyData.mNewUserDisclaimer = showDisclaimer
+ ? DevicePolicyData.NEW_USER_DISCLAIMER_NEEDED
+ : DevicePolicyData.NEW_USER_DISCLAIMER_NOT_NEEDED;
+ saveSettingsLocked(userId);
+ }
+ }
+
+ private void handleNewUserCreated(UserInfo user) {
+ if (!mOwners.hasDeviceOwner()) return;
+ final int userId = user.id;
+ Log.i(LOG_TAG, "User " + userId + " added on DO mode; setting ShowNewUserDisclaimer");
+
+ setShowNewUserDisclaimer(userId, DevicePolicyData.NEW_USER_DISCLAIMER_NEEDED);
+ }
+
+ @Override
+ public void resetNewUserDisclaimer() {
+ CallerIdentity callerIdentity = getCallerIdentity();
+ canManageUsers(callerIdentity);
+
+ setShowNewUserDisclaimer(callerIdentity.getUserId(),
+ DevicePolicyData.NEW_USER_DISCLAIMER_SHOWN);
+ }
+
+ private void setShowNewUserDisclaimer(@UserIdInt int userId, String value) {
+ Slog.i(LOG_TAG, "Setting new user disclaimer for user " + userId + " as " + value);
+ synchronized (getLockObject()) {
+ DevicePolicyData policyData = getUserData(userId);
+ policyData.mNewUserDisclaimer = value;
saveSettingsLocked(userId);
}
}
+ private void showNewUserDisclaimerIfNecessary(@UserIdInt int userId) {
+ boolean mustShow;
+ synchronized (getLockObject()) {
+ DevicePolicyData policyData = getUserData(userId);
+ if (VERBOSE_LOG) {
+ Slog.v(LOG_TAG, "showNewUserDisclaimerIfNecessary(" + userId + "): "
+ + policyData.mNewUserDisclaimer + ")");
+ }
+ mustShow = DevicePolicyData.NEW_USER_DISCLAIMER_NEEDED
+ .equals(policyData.mNewUserDisclaimer);
+ }
+ if (!mustShow) return;
+
+ Intent intent = new Intent(DevicePolicyManager.ACTION_SHOW_NEW_USER_DISCLAIMER);
+
+ // TODO(b/172691310): add CTS tests to make sure disclaimer is shown
+ Slog.i(LOG_TAG, "Dispatching ACTION_SHOW_NEW_USER_DISCLAIMER intent");
+ mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
+ }
+
@Override
public boolean removeUser(ComponentName who, UserHandle userHandle) {
Objects.requireNonNull(who, "ComponentName is null");
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 79a82b8e7175..809afe01da2d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -31,6 +31,7 @@ import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.IndentingPrintWriter;
import android.util.Log;
@@ -203,6 +204,7 @@ class Owners {
}
pushToPackageManagerLocked();
pushToActivityTaskManagerLocked();
+ pushToActivityManagerLocked();
pushToAppOpsLocked();
}
}
@@ -218,12 +220,34 @@ class Owners {
}
private void pushToActivityTaskManagerLocked() {
- final int uid = mDeviceOwner != null ? mPackageManagerInternal.getPackageUid(
- mDeviceOwner.packageName,
- PackageManager.MATCH_ALL | PackageManager.MATCH_KNOWN_PACKAGES, mDeviceOwnerUserId)
- : Process.INVALID_UID;
- mActivityTaskManagerInternal.setDeviceOwnerUid(uid);
- mActivityManagerInternal.setDeviceOwnerUid(uid);
+ mActivityTaskManagerInternal.setDeviceOwnerUid(getDeviceOwnerUidLocked());
+ }
+
+ private void pushToActivityManagerLocked() {
+ mActivityManagerInternal.setDeviceOwnerUid(getDeviceOwnerUidLocked());
+
+ final ArraySet<Integer> profileOwners = new ArraySet<>();
+ for (int poi = mProfileOwners.size() - 1; poi >= 0; poi--) {
+ final int userId = mProfileOwners.keyAt(poi);
+ final int profileOwnerUid = mPackageManagerInternal.getPackageUid(
+ mProfileOwners.valueAt(poi).packageName,
+ PackageManager.MATCH_ALL | PackageManager.MATCH_KNOWN_PACKAGES,
+ userId);
+ if (profileOwnerUid >= 0) {
+ profileOwners.add(profileOwnerUid);
+ }
+ }
+ mActivityManagerInternal.setProfileOwnerUid(profileOwners);
+ }
+
+ int getDeviceOwnerUidLocked() {
+ if (mDeviceOwner != null) {
+ return mPackageManagerInternal.getPackageUid(mDeviceOwner.packageName,
+ PackageManager.MATCH_ALL | PackageManager.MATCH_KNOWN_PACKAGES,
+ mDeviceOwnerUserId);
+ } else {
+ return Process.INVALID_UID;
+ }
}
String getDeviceOwnerPackageName() {
@@ -301,6 +325,7 @@ class Owners {
mUserManagerInternal.setDeviceManaged(true);
pushToPackageManagerLocked();
pushToActivityTaskManagerLocked();
+ pushToActivityManagerLocked();
pushToAppOpsLocked();
}
}
@@ -313,6 +338,7 @@ class Owners {
mUserManagerInternal.setDeviceManaged(false);
pushToPackageManagerLocked();
pushToActivityTaskManagerLocked();
+ pushToActivityManagerLocked();
pushToAppOpsLocked();
}
}
@@ -325,6 +351,7 @@ class Owners {
/* remoteBugreportHash =*/ null, /* isOrganizationOwnedDevice =*/ false));
mUserManagerInternal.setUserManaged(userId, true);
pushToPackageManagerLocked();
+ pushToActivityManagerLocked();
pushToAppOpsLocked();
}
}
@@ -334,6 +361,7 @@ class Owners {
mProfileOwners.remove(userId);
mUserManagerInternal.setUserManaged(userId, false);
pushToPackageManagerLocked();
+ pushToActivityManagerLocked();
pushToAppOpsLocked();
}
}
@@ -347,6 +375,7 @@ class Owners {
ownerInfo.isOrganizationOwnedDevice);
mProfileOwners.put(userId, newOwnerInfo);
pushToPackageManagerLocked();
+ pushToActivityManagerLocked();
pushToAppOpsLocked();
}
}
@@ -361,6 +390,7 @@ class Owners {
mDeviceOwner.isOrganizationOwnedDevice);
pushToPackageManagerLocked();
pushToActivityTaskManagerLocked();
+ pushToActivityManagerLocked();
pushToAppOpsLocked();
}
}
@@ -665,9 +695,7 @@ class Owners {
try {
final SparseIntArray owners = new SparseIntArray();
if (mDeviceOwner != null) {
- final int uid = mPackageManagerInternal.getPackageUid(mDeviceOwner.packageName,
- PackageManager.MATCH_ALL | PackageManager.MATCH_KNOWN_PACKAGES,
- mDeviceOwnerUserId);
+ final int uid = getDeviceOwnerUidLocked();
if (uid >= 0) {
owners.put(mDeviceOwnerUserId, uid);
}
@@ -695,6 +723,7 @@ class Owners {
public void systemReady() {
synchronized (mLock) {
mSystemReady = true;
+ pushToActivityManagerLocked();
pushToAppOpsLocked();
}
}
diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp
index a31aac96eb48..d2244286450b 100644
--- a/services/incremental/BinderIncrementalService.cpp
+++ b/services/incremental/BinderIncrementalService.cpp
@@ -122,13 +122,14 @@ binder::Status BinderIncrementalService::createStorage(
const ::android::sp<::android::content::pm::IDataLoaderStatusListener>& statusListener,
const ::android::os::incremental::StorageHealthCheckParams& healthCheckParams,
const ::android::sp<::android::os::incremental::IStorageHealthListener>& healthListener,
+ const ::std::vector<::android::os::incremental::PerUidReadTimeouts>& perUidReadTimeouts,
int32_t* _aidl_return) {
*_aidl_return =
mImpl.createStorage(path, const_cast<content::pm::DataLoaderParamsParcel&&>(params),
android::incremental::IncrementalService::CreateOptions(createMode),
statusListener,
const_cast<StorageHealthCheckParams&&>(healthCheckParams),
- healthListener);
+ healthListener, perUidReadTimeouts);
return ok();
}
@@ -164,8 +165,8 @@ binder::Status BinderIncrementalService::deleteStorage(int32_t storageId) {
return ok();
}
-binder::Status BinderIncrementalService::disableReadLogs(int32_t storageId) {
- mImpl.disableReadLogs(storageId);
+binder::Status BinderIncrementalService::disallowReadLogs(int32_t storageId) {
+ mImpl.disallowReadLogs(storageId);
return ok();
}
@@ -254,7 +255,7 @@ binder::Status BinderIncrementalService::isFileFullyLoaded(int32_t storageId,
binder::Status BinderIncrementalService::getLoadingProgress(int32_t storageId,
float* _aidl_return) {
- *_aidl_return = mImpl.getLoadingProgress(storageId);
+ *_aidl_return = mImpl.getLoadingProgress(storageId).getProgress();
return ok();
}
diff --git a/services/incremental/BinderIncrementalService.h b/services/incremental/BinderIncrementalService.h
index 8afa0f7bb117..9a4537a15f31 100644
--- a/services/incremental/BinderIncrementalService.h
+++ b/services/incremental/BinderIncrementalService.h
@@ -45,6 +45,7 @@ public:
const ::android::sp<::android::content::pm::IDataLoaderStatusListener>& statusListener,
const ::android::os::incremental::StorageHealthCheckParams& healthCheckParams,
const ::android::sp<IStorageHealthListener>& healthListener,
+ const ::std::vector<::android::os::incremental::PerUidReadTimeouts>& perUidReadTimeouts,
int32_t* _aidl_return) final;
binder::Status createLinkedStorage(const std::string& path, int32_t otherStorageId,
int32_t createMode, int32_t* _aidl_return) final;
@@ -77,7 +78,7 @@ public:
std::vector<uint8_t>* _aidl_return) final;
binder::Status startLoading(int32_t storageId, bool* _aidl_return) final;
binder::Status deleteStorage(int32_t storageId) final;
- binder::Status disableReadLogs(int32_t storageId) final;
+ binder::Status disallowReadLogs(int32_t storageId) final;
binder::Status configureNativeBinaries(int32_t storageId, const std::string& apkFullPath,
const std::string& libDirRelativePath,
const std::string& abi, bool extractNativeLibs,
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index eb6b325050eb..dde70caa797f 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -38,9 +38,11 @@
using namespace std::literals;
-constexpr const char* kDataUsageStats = "android.permission.LOADER_USAGE_STATS";
+constexpr const char* kLoaderUsageStats = "android.permission.LOADER_USAGE_STATS";
constexpr const char* kOpUsage = "android:loader_usage_stats";
+constexpr const char* kInteractAcrossUsers = "android.permission.INTERACT_ACROSS_USERS";
+
namespace android::incremental {
using content::pm::DataLoaderParamsParcel;
@@ -63,6 +65,10 @@ struct Constants {
static constexpr auto libSuffix = ".so"sv;
static constexpr auto blockSize = 4096;
static constexpr auto systemPackage = "android"sv;
+
+ static constexpr auto progressUpdateInterval = 1000ms;
+ static constexpr auto perUidTimeoutOffset = progressUpdateInterval * 2;
+ static constexpr auto minPerUidTimeout = progressUpdateInterval * 3;
};
static const Constants& constants() {
@@ -350,7 +356,8 @@ void IncrementalService::onDump(int fd) {
dprintf(fd, " storages (%d): {\n", int(mnt.storages.size()));
for (auto&& [storageId, storage] : mnt.storages) {
dprintf(fd, " [%d] -> [%s] (%d %% loaded) \n", storageId, storage.name.c_str(),
- (int)(getLoadingProgressFromPath(mnt, storage.name.c_str()) * 100));
+ (int)(getLoadingProgressFromPath(mnt, storage.name.c_str()).getProgress() *
+ 100));
}
dprintf(fd, " }\n");
@@ -419,12 +426,11 @@ auto IncrementalService::getStorageSlotLocked() -> MountMap::iterator {
}
}
-StorageId IncrementalService::createStorage(std::string_view mountPoint,
- content::pm::DataLoaderParamsParcel&& dataLoaderParams,
- CreateOptions options,
- const DataLoaderStatusListener& statusListener,
- StorageHealthCheckParams&& healthCheckParams,
- const StorageHealthListener& healthListener) {
+StorageId IncrementalService::createStorage(
+ std::string_view mountPoint, content::pm::DataLoaderParamsParcel&& dataLoaderParams,
+ CreateOptions options, const DataLoaderStatusListener& statusListener,
+ StorageHealthCheckParams&& healthCheckParams, const StorageHealthListener& healthListener,
+ const std::vector<PerUidReadTimeouts>& perUidReadTimeouts) {
LOG(INFO) << "createStorage: " << mountPoint << " | " << int(options);
if (!path::isAbsolute(mountPoint)) {
LOG(ERROR) << "path is not absolute: " << mountPoint;
@@ -553,13 +559,14 @@ StorageId IncrementalService::createStorage(std::string_view mountPoint,
if (auto err = addBindMount(*ifs, storageIt->first, storageIt->second.name,
std::string(storageIt->second.name), std::move(mountNorm), bk, l);
err < 0) {
- LOG(ERROR) << "adding bind mount failed: " << -err;
+ LOG(ERROR) << "Adding bind mount failed: " << -err;
return kInvalidStorageId;
}
// Done here as well, all data structures are in good state.
secondCleanupOnFailure.release();
+ // DataLoader.
auto dataLoaderStub = prepareDataLoader(*ifs, std::move(dataLoaderParams), &statusListener,
std::move(healthCheckParams), &healthListener);
CHECK(dataLoaderStub);
@@ -567,6 +574,11 @@ StorageId IncrementalService::createStorage(std::string_view mountPoint,
mountIt->second = std::move(ifs);
l.unlock();
+ // Per Uid timeouts.
+ if (!perUidReadTimeouts.empty()) {
+ setUidReadTimeouts(mountId, perUidReadTimeouts);
+ }
+
if (mSystemReady.load(std::memory_order_relaxed) && !dataLoaderStub->requestCreate()) {
// failed to create data loader
LOG(ERROR) << "initializeDataLoader() failed";
@@ -634,17 +646,17 @@ StorageId IncrementalService::findStorageId(std::string_view path) const {
return it->second->second.storage;
}
-void IncrementalService::disableReadLogs(StorageId storageId) {
+void IncrementalService::disallowReadLogs(StorageId storageId) {
std::unique_lock l(mLock);
const auto ifs = getIfsLocked(storageId);
if (!ifs) {
- LOG(ERROR) << "disableReadLogs failed, invalid storageId: " << storageId;
+ LOG(ERROR) << "disallowReadLogs failed, invalid storageId: " << storageId;
return;
}
- if (!ifs->readLogsEnabled()) {
+ if (!ifs->readLogsAllowed()) {
return;
}
- ifs->disableReadLogs();
+ ifs->disallowReadLogs();
l.unlock();
const auto metadata = constants().readLogsDisabledMarkerName;
@@ -669,15 +681,26 @@ int IncrementalService::setStorageParams(StorageId storageId, bool enableReadLog
const auto& params = ifs->dataLoaderStub->params();
if (enableReadLogs) {
- if (!ifs->readLogsEnabled()) {
+ if (!ifs->readLogsAllowed()) {
LOG(ERROR) << "setStorageParams failed, readlogs disabled for storageId: " << storageId;
return -EPERM;
}
- if (auto status = mAppOpsManager->checkPermission(kDataUsageStats, kOpUsage,
+ // Check loader usage stats permission and apop.
+ if (auto status = mAppOpsManager->checkPermission(kLoaderUsageStats, kOpUsage,
params.packageName.c_str());
!status.isOk()) {
- LOG(ERROR) << "checkPermission failed: " << status.toString8();
+ LOG(ERROR) << " Permission: " << kLoaderUsageStats
+ << " check failed: " << status.toString8();
+ return fromBinderStatus(status);
+ }
+
+ // Check multiuser permission.
+ if (auto status = mAppOpsManager->checkPermission(kInteractAcrossUsers, nullptr,
+ params.packageName.c_str());
+ !status.isOk()) {
+ LOG(ERROR) << " Permission: " << kInteractAcrossUsers
+ << " check failed: " << status.toString8();
return fromBinderStatus(status);
}
}
@@ -704,7 +727,12 @@ binder::Status IncrementalService::applyStorageParams(IncFsMount& ifs, bool enab
}
std::lock_guard l(mMountOperationLock);
- return mVold->setIncFsMountOptions(control, enableReadLogs);
+ const auto status = mVold->setIncFsMountOptions(control, enableReadLogs);
+ if (status.isOk()) {
+ // Store enabled state.
+ ifs.setReadLogsEnabled(enableReadLogs);
+ }
+ return status;
}
void IncrementalService::deleteStorage(StorageId storageId) {
@@ -1052,6 +1080,74 @@ bool IncrementalService::startLoading(StorageId storage) const {
return true;
}
+void IncrementalService::setUidReadTimeouts(
+ StorageId storage, const std::vector<PerUidReadTimeouts>& perUidReadTimeouts) {
+ using microseconds = std::chrono::microseconds;
+ using milliseconds = std::chrono::milliseconds;
+
+ auto maxPendingTimeUs = microseconds(0);
+ for (const auto& timeouts : perUidReadTimeouts) {
+ maxPendingTimeUs = std::max(maxPendingTimeUs, microseconds(timeouts.maxPendingTimeUs));
+ }
+ if (maxPendingTimeUs < Constants::minPerUidTimeout) {
+ return;
+ }
+
+ const auto ifs = getIfs(storage);
+ if (!ifs) {
+ return;
+ }
+
+ if (auto err = mIncFs->setUidReadTimeouts(ifs->control, perUidReadTimeouts); err < 0) {
+ LOG(ERROR) << "Setting read timeouts failed: " << -err;
+ return;
+ }
+
+ const auto timeout = std::chrono::duration_cast<milliseconds>(maxPendingTimeUs) -
+ Constants::perUidTimeoutOffset;
+ updateUidReadTimeouts(storage, Clock::now() + timeout);
+}
+
+void IncrementalService::clearUidReadTimeouts(StorageId storage) {
+ const auto ifs = getIfs(storage);
+ if (!ifs) {
+ return;
+ }
+
+ mIncFs->setUidReadTimeouts(ifs->control, {});
+}
+
+void IncrementalService::updateUidReadTimeouts(StorageId storage, Clock::time_point timeLimit) {
+ // Reached maximum timeout.
+ if (Clock::now() >= timeLimit) {
+ return clearUidReadTimeouts(storage);
+ }
+
+ // Still loading?
+ const auto progress = getLoadingProgress(storage);
+ if (progress.isError()) {
+ // Something is wrong, abort.
+ return clearUidReadTimeouts(storage);
+ }
+
+ if (progress.started() && progress.fullyLoaded()) {
+ // Fully loaded, check readLogs collection.
+ const auto ifs = getIfs(storage);
+ if (!ifs->readLogsEnabled()) {
+ return clearUidReadTimeouts(storage);
+ }
+ }
+
+ const auto timeLeft = timeLimit - Clock::now();
+ if (timeLeft < Constants::progressUpdateInterval) {
+ // Don't bother.
+ return clearUidReadTimeouts(storage);
+ }
+
+ addTimedJob(*mTimedQueue, storage, Constants::progressUpdateInterval,
+ [this, storage, timeLimit]() { updateUidReadTimeouts(storage, timeLimit); });
+}
+
std::unordered_set<std::string_view> IncrementalService::adoptMountedInstances() {
std::unordered_set<std::string_view> mountedRootNames;
mIncFs->listExistingMounts([this, &mountedRootNames](auto root, auto backingDir, auto binds) {
@@ -1125,7 +1221,7 @@ std::unordered_set<std::string_view> IncrementalService::adoptMountedInstances()
// Check if marker file present.
if (checkReadLogsDisabledMarker(root)) {
- ifs->disableReadLogs();
+ ifs->disallowReadLogs();
}
std::vector<std::pair<std::string, metadata::BindPoint>> permanentBindPoints;
@@ -1301,7 +1397,7 @@ bool IncrementalService::mountExistingImage(std::string_view root) {
// Check if marker file present.
if (checkReadLogsDisabledMarker(mountTarget)) {
- ifs->disableReadLogs();
+ ifs->disallowReadLogs();
}
// DataLoader params
@@ -1705,7 +1801,7 @@ int IncrementalService::setFileContent(const IfsMountPtr& ifs, const incfs::File
return 0;
}
-int IncrementalService::isFileFullyLoaded(StorageId storage, const std::string& path) const {
+int IncrementalService::isFileFullyLoaded(StorageId storage, std::string_view filePath) const {
std::unique_lock l(mLock);
const auto ifs = getIfsLocked(storage);
if (!ifs) {
@@ -1718,7 +1814,7 @@ int IncrementalService::isFileFullyLoaded(StorageId storage, const std::string&
return -EINVAL;
}
l.unlock();
- return isFileFullyLoadedFromPath(*ifs, path);
+ return isFileFullyLoadedFromPath(*ifs, filePath);
}
int IncrementalService::isFileFullyLoadedFromPath(const IncFsMount& ifs,
@@ -1736,25 +1832,26 @@ int IncrementalService::isFileFullyLoadedFromPath(const IncFsMount& ifs,
return totalBlocks - filledBlocks;
}
-float IncrementalService::getLoadingProgress(StorageId storage) const {
+IncrementalService::LoadingProgress IncrementalService::getLoadingProgress(
+ StorageId storage) const {
std::unique_lock l(mLock);
const auto ifs = getIfsLocked(storage);
if (!ifs) {
LOG(ERROR) << "getLoadingProgress failed, invalid storageId: " << storage;
- return -EINVAL;
+ return {-EINVAL, -EINVAL};
}
const auto storageInfo = ifs->storages.find(storage);
if (storageInfo == ifs->storages.end()) {
LOG(ERROR) << "getLoadingProgress failed, no storage: " << storage;
- return -EINVAL;
+ return {-EINVAL, -EINVAL};
}
l.unlock();
return getLoadingProgressFromPath(*ifs, storageInfo->second.name);
}
-float IncrementalService::getLoadingProgressFromPath(const IncFsMount& ifs,
- std::string_view storagePath) const {
- size_t totalBlocks = 0, filledBlocks = 0;
+IncrementalService::LoadingProgress IncrementalService::getLoadingProgressFromPath(
+ const IncFsMount& ifs, std::string_view storagePath) const {
+ ssize_t totalBlocks = 0, filledBlocks = 0;
const auto filePaths = mFs->listFilesRecursive(storagePath);
for (const auto& filePath : filePaths) {
const auto [filledBlocksCount, totalBlocksCount] =
@@ -1762,33 +1859,29 @@ float IncrementalService::getLoadingProgressFromPath(const IncFsMount& ifs,
if (filledBlocksCount < 0) {
LOG(ERROR) << "getLoadingProgress failed to get filled blocks count for: " << filePath
<< " errno: " << filledBlocksCount;
- return filledBlocksCount;
+ return {filledBlocksCount, filledBlocksCount};
}
totalBlocks += totalBlocksCount;
filledBlocks += filledBlocksCount;
}
- if (totalBlocks == 0) {
- // No file in the storage or files are empty; regarded as fully loaded
- return 1;
- }
- return (float)filledBlocks / (float)totalBlocks;
+ return {filledBlocks, totalBlocks};
}
bool IncrementalService::updateLoadingProgress(
StorageId storage, const StorageLoadingProgressListener& progressListener) {
const auto progress = getLoadingProgress(storage);
- if (progress < 0) {
+ if (progress.isError()) {
// Failed to get progress from incfs, abort.
return false;
}
- progressListener->onStorageLoadingProgressChanged(storage, progress);
- if (progress > 1 - 0.001f) {
+ progressListener->onStorageLoadingProgressChanged(storage, progress.getProgress());
+ if (progress.fullyLoaded()) {
// Stop updating progress once it is fully loaded
return true;
}
- static constexpr auto kProgressUpdateInterval = 1000ms;
- addTimedJob(*mProgressUpdateJobQueue, storage, kProgressUpdateInterval /* repeat after 1s */,
+ addTimedJob(*mProgressUpdateJobQueue, storage,
+ Constants::progressUpdateInterval /* repeat after 1s */,
[storage, progressListener, this]() {
updateLoadingProgress(storage, progressListener);
});
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index eb69470c97a7..306612159412 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -23,6 +23,7 @@
#include <android/os/incremental/BnIncrementalServiceConnector.h>
#include <android/os/incremental/BnStorageHealthListener.h>
#include <android/os/incremental/BnStorageLoadingProgressListener.h>
+#include <android/os/incremental/PerUidReadTimeouts.h>
#include <android/os/incremental/StorageHealthCheckParams.h>
#include <binder/IAppOpsCallback.h>
#include <utils/String16.h>
@@ -69,6 +70,8 @@ using StorageHealthListener = ::android::sp<IStorageHealthListener>;
using IStorageLoadingProgressListener = ::android::os::incremental::IStorageLoadingProgressListener;
using StorageLoadingProgressListener = ::android::sp<IStorageLoadingProgressListener>;
+using PerUidReadTimeouts = ::android::os::incremental::PerUidReadTimeouts;
+
class IncrementalService final {
public:
explicit IncrementalService(ServiceManagerWrapper&& sm, std::string_view rootDir);
@@ -98,7 +101,23 @@ public:
};
enum StorageFlags {
- ReadLogsEnabled = 1,
+ ReadLogsAllowed = 1 << 0,
+ ReadLogsEnabled = 1 << 1,
+ };
+
+ struct LoadingProgress {
+ ssize_t filledBlocks;
+ ssize_t totalBlocks;
+
+ bool isError() const { return totalBlocks < 0; }
+ bool started() const { return totalBlocks > 0; }
+ bool fullyLoaded() const { return !isError() && (totalBlocks == filledBlocks); }
+
+ float getProgress() const {
+ return totalBlocks < 0
+ ? totalBlocks
+ : totalBlocks > 0 ? double(filledBlocks) / double(totalBlocks) : 1.f;
+ }
};
static FileId idFromMetadata(std::span<const uint8_t> metadata);
@@ -114,7 +133,8 @@ public:
content::pm::DataLoaderParamsParcel&& dataLoaderParams,
CreateOptions options, const DataLoaderStatusListener& statusListener,
StorageHealthCheckParams&& healthCheckParams,
- const StorageHealthListener& healthListener);
+ const StorageHealthListener& healthListener,
+ const std::vector<PerUidReadTimeouts>& perUidReadTimeouts);
StorageId createLinkedStorage(std::string_view mountPoint, StorageId linkedStorage,
CreateOptions options = CreateOptions::Default);
StorageId openStorage(std::string_view path);
@@ -123,7 +143,7 @@ public:
int unbind(StorageId storage, std::string_view target);
void deleteStorage(StorageId storage);
- void disableReadLogs(StorageId storage);
+ void disallowReadLogs(StorageId storage);
int setStorageParams(StorageId storage, bool enableReadLogs);
int makeFile(StorageId storage, std::string_view path, int mode, FileId id,
@@ -135,8 +155,8 @@ public:
std::string_view newPath);
int unlink(StorageId storage, std::string_view path);
- int isFileFullyLoaded(StorageId storage, const std::string& path) const;
- float getLoadingProgress(StorageId storage) const;
+ int isFileFullyLoaded(StorageId storage, std::string_view filePath) const;
+ LoadingProgress getLoadingProgress(StorageId storage) const;
bool registerLoadingProgressListener(StorageId storage,
const StorageLoadingProgressListener& progressListener);
bool unregisterLoadingProgressListener(StorageId storage);
@@ -282,7 +302,7 @@ private:
const std::string root;
Control control;
/*const*/ MountId mountId;
- int32_t flags = StorageFlags::ReadLogsEnabled;
+ int32_t flags = StorageFlags::ReadLogsAllowed;
StorageMap storages;
BindMap bindPoints;
DataLoaderStubPtr dataLoaderStub;
@@ -301,7 +321,15 @@ private:
StorageMap::iterator makeStorage(StorageId id);
- void disableReadLogs() { flags &= ~StorageFlags::ReadLogsEnabled; }
+ void disallowReadLogs() { flags &= ~StorageFlags::ReadLogsAllowed; }
+ int32_t readLogsAllowed() const { return (flags & StorageFlags::ReadLogsAllowed); }
+
+ void setReadLogsEnabled(bool value) {
+ if (value)
+ flags |= StorageFlags::ReadLogsEnabled;
+ else
+ flags &= ~StorageFlags::ReadLogsEnabled;
+ }
int32_t readLogsEnabled() const { return (flags & StorageFlags::ReadLogsEnabled); }
static void cleanupFilesystem(std::string_view root);
@@ -313,6 +341,11 @@ private:
static bool perfLoggingEnabled();
+ void setUidReadTimeouts(StorageId storage,
+ const std::vector<PerUidReadTimeouts>& perUidReadTimeouts);
+ void clearUidReadTimeouts(StorageId storage);
+ void updateUidReadTimeouts(StorageId storage, Clock::time_point timeLimit);
+
std::unordered_set<std::string_view> adoptMountedInstances();
void mountExistingImages(const std::unordered_set<std::string_view>& mountedRootNames);
bool mountExistingImage(std::string_view root);
@@ -355,7 +388,7 @@ private:
binder::Status applyStorageParams(IncFsMount& ifs, bool enableReadLogs);
int isFileFullyLoadedFromPath(const IncFsMount& ifs, std::string_view filePath) const;
- float getLoadingProgressFromPath(const IncFsMount& ifs, std::string_view path) const;
+ LoadingProgress getLoadingProgressFromPath(const IncFsMount& ifs, std::string_view path) const;
int setFileContent(const IfsMountPtr& ifs, const incfs::FileId& fileId,
std::string_view debugFilePath, std::span<const uint8_t> data) const;
diff --git a/services/incremental/IncrementalServiceValidation.cpp b/services/incremental/IncrementalServiceValidation.cpp
index abadbbf10742..9f2639a81666 100644
--- a/services/incremental/IncrementalServiceValidation.cpp
+++ b/services/incremental/IncrementalServiceValidation.cpp
@@ -56,13 +56,18 @@ binder::Status CheckPermissionForDataDelivery(const char* permission, const char
String16 packageName{package};
- // Caller must also have op granted.
PermissionController pc;
if (auto packageUid = pc.getPackageUid(packageName, 0); packageUid != uid) {
return Exception(binder::Status::EX_SECURITY,
StringPrintf("UID %d / PID %d does not own package %s", uid, pid,
package));
}
+
+ if (!operation) {
+ return binder::Status::ok();
+ }
+
+ // Caller must also have op granted.
switch (auto result = pc.noteOp(String16(operation), uid, packageName); result) {
case PermissionController::MODE_ALLOWED:
case PermissionController::MODE_DEFAULT:
diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp
index dfe9684779fe..b1521b0d4e27 100644
--- a/services/incremental/ServiceWrappers.cpp
+++ b/services/incremental/ServiceWrappers.cpp
@@ -206,6 +206,11 @@ public:
std::vector<incfs::ReadInfo>* pendingReadsBuffer) const final {
return incfs::waitForPendingReads(control, timeout, pendingReadsBuffer);
}
+ ErrorCode setUidReadTimeouts(const Control& control,
+ const std::vector<android::os::incremental::PerUidReadTimeouts>&
+ perUidReadTimeouts) const final {
+ return -ENOTSUP;
+ }
};
static JNIEnv* getOrAttachJniEnv(JavaVM* jvm);
diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h
index f2d00735bc44..fad8d67e0da7 100644
--- a/services/incremental/ServiceWrappers.h
+++ b/services/incremental/ServiceWrappers.h
@@ -21,6 +21,7 @@
#include <android/content/pm/FileSystemControlParcel.h>
#include <android/content/pm/IDataLoader.h>
#include <android/content/pm/IDataLoaderStatusListener.h>
+#include <android/os/incremental/PerUidReadTimeouts.h>
#include <binder/IAppOpsCallback.h>
#include <binder/IServiceManager.h>
#include <binder/Status.h>
@@ -103,6 +104,10 @@ public:
virtual WaitResult waitForPendingReads(
const Control& control, std::chrono::milliseconds timeout,
std::vector<incfs::ReadInfo>* pendingReadsBuffer) const = 0;
+ virtual ErrorCode setUidReadTimeouts(
+ const Control& control,
+ const std::vector<::android::os::incremental::PerUidReadTimeouts>& perUidReadTimeouts)
+ const = 0;
};
class AppOpsManagerWrapper {
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index 9b8cf4084bf1..47b9051970e4 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -42,6 +42,7 @@ using testing::NiceMock;
using namespace android::incfs;
using namespace android::content::pm;
+using PerUidReadTimeouts = android::os::incremental::PerUidReadTimeouts;
namespace android::os::incremental {
@@ -307,6 +308,9 @@ public:
MOCK_CONST_METHOD3(waitForPendingReads,
WaitResult(const Control& control, std::chrono::milliseconds timeout,
std::vector<incfs::ReadInfo>* pendingReadsBuffer));
+ MOCK_CONST_METHOD2(setUidReadTimeouts,
+ ErrorCode(const Control& control,
+ const std::vector<PerUidReadTimeouts>& perUidReadTimeouts));
MockIncFs() { ON_CALL(*this, listExistingMounts(_)).WillByDefault(Return()); }
@@ -393,6 +397,15 @@ public:
void checkPermissionSuccess() {
ON_CALL(*this, checkPermission(_, _, _)).WillByDefault(Return(android::incremental::Ok()));
}
+ void checkPermissionNoCrossUsers() {
+ ON_CALL(*this,
+ checkPermission("android.permission.LOADER_USAGE_STATS",
+ "android:loader_usage_stats", _))
+ .WillByDefault(Return(android::incremental::Ok()));
+ ON_CALL(*this, checkPermission("android.permission.INTERACT_ACROSS_USERS", nullptr, _))
+ .WillByDefault(
+ Return(android::incremental::Exception(binder::Status::EX_SECURITY, {})));
+ }
void checkPermissionFails() {
ON_CALL(*this, checkPermission(_, _, _))
.WillByDefault(
@@ -665,7 +678,7 @@ TEST_F(IncrementalServiceTest, testCreateStorageMountIncFsFails) {
TemporaryDir tempDir;
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew,
- {}, {}, {});
+ {}, {}, {}, {});
ASSERT_LT(storageId, 0);
}
@@ -676,7 +689,7 @@ TEST_F(IncrementalServiceTest, testCreateStorageMountIncFsInvalidControlParcel)
TemporaryDir tempDir;
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew,
- {}, {}, {});
+ {}, {}, {}, {});
ASSERT_LT(storageId, 0);
}
@@ -689,7 +702,7 @@ TEST_F(IncrementalServiceTest, testCreateStorageMakeFileFails) {
TemporaryDir tempDir;
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew,
- {}, {}, {});
+ {}, {}, {}, {});
ASSERT_LT(storageId, 0);
}
@@ -703,7 +716,7 @@ TEST_F(IncrementalServiceTest, testCreateStorageBindMountFails) {
TemporaryDir tempDir;
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew,
- {}, {}, {});
+ {}, {}, {}, {});
ASSERT_LT(storageId, 0);
}
@@ -721,7 +734,7 @@ TEST_F(IncrementalServiceTest, testCreateStoragePrepareDataLoaderFails) {
TemporaryDir tempDir;
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew,
- {}, {}, {});
+ {}, {}, {}, {});
ASSERT_LT(storageId, 0);
}
@@ -735,7 +748,7 @@ TEST_F(IncrementalServiceTest, testDeleteStorageSuccess) {
TemporaryDir tempDir;
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew,
- {}, {}, {});
+ {}, {}, {}, {});
ASSERT_GE(storageId, 0);
mIncrementalService->deleteStorage(storageId);
}
@@ -750,7 +763,7 @@ TEST_F(IncrementalServiceTest, testDataLoaderDestroyed) {
TemporaryDir tempDir;
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew,
- {}, {}, {});
+ {}, {}, {}, {});
ASSERT_GE(storageId, 0);
// Simulated crash/other connection breakage.
mDataLoaderManager->setDataLoaderStatusDestroyed();
@@ -767,7 +780,7 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderCreate) {
TemporaryDir tempDir;
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew,
- {}, {}, {});
+ {}, {}, {}, {});
ASSERT_GE(storageId, 0);
mDataLoaderManager->setDataLoaderStatusCreated();
ASSERT_TRUE(mIncrementalService->startLoading(storageId));
@@ -785,7 +798,7 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderPendingStart) {
TemporaryDir tempDir;
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew,
- {}, {}, {});
+ {}, {}, {}, {});
ASSERT_GE(storageId, 0);
ASSERT_TRUE(mIncrementalService->startLoading(storageId));
mDataLoaderManager->setDataLoaderStatusCreated();
@@ -802,7 +815,7 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderCreateUnavailable) {
TemporaryDir tempDir;
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew,
- {}, {}, {});
+ {}, {}, {}, {});
ASSERT_GE(storageId, 0);
mDataLoaderManager->setDataLoaderStatusUnavailable();
}
@@ -823,7 +836,7 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderRecreateOnPendingReads) {
TemporaryDir tempDir;
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew,
- {}, {}, {});
+ {}, {}, {}, {});
ASSERT_GE(storageId, 0);
mDataLoaderManager->setDataLoaderStatusUnavailable();
ASSERT_NE(nullptr, mLooper->mCallback);
@@ -877,7 +890,7 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderUnhealthyStorage) {
TemporaryDir tempDir;
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew,
- {}, std::move(params), listener);
+ {}, std::move(params), listener, {});
ASSERT_GE(storageId, 0);
// Healthy state, registered for pending reads.
@@ -972,7 +985,7 @@ TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccess) {
TemporaryDir tempDir;
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew,
- {}, {}, {});
+ {}, {}, {}, {});
ASSERT_GE(storageId, 0);
ASSERT_GE(mDataLoader->setStorageParams(true), 0);
}
@@ -993,11 +1006,11 @@ TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndDisabled) {
TemporaryDir tempDir;
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew,
- {}, {}, {});
+ {}, {}, {}, {});
ASSERT_GE(storageId, 0);
ASSERT_GE(mDataLoader->setStorageParams(true), 0);
// Now disable.
- mIncrementalService->disableReadLogs(storageId);
+ mIncrementalService->disallowReadLogs(storageId);
ASSERT_EQ(mDataLoader->setStorageParams(true), -EPERM);
}
@@ -1019,7 +1032,7 @@ TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndPermissionChang
TemporaryDir tempDir;
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew,
- {}, {}, {});
+ {}, {}, {}, {});
ASSERT_GE(storageId, 0);
ASSERT_GE(mDataLoader->setStorageParams(true), 0);
ASSERT_NE(nullptr, mAppOpsManager->mStoredCallback.get());
@@ -1038,7 +1051,24 @@ TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsCheckPermissionFails) {
TemporaryDir tempDir;
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew,
- {}, {}, {});
+ {}, {}, {}, {});
+ ASSERT_GE(storageId, 0);
+ ASSERT_LT(mDataLoader->setStorageParams(true), 0);
+}
+
+TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsCheckPermissionNoCrossUsers) {
+ mAppOpsManager->checkPermissionNoCrossUsers();
+
+ EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
+ EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
+ // checkPermission fails, no calls to set opitions, start or stop WatchingMode.
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(0);
+ EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(0);
+ EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
+ TemporaryDir tempDir;
+ int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+ IncrementalService::CreateOptions::CreateNew,
+ {}, {}, {}, {});
ASSERT_GE(storageId, 0);
ASSERT_LT(mDataLoader->setStorageParams(true), 0);
}
@@ -1057,7 +1087,7 @@ TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsFails) {
TemporaryDir tempDir;
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew,
- {}, {}, {});
+ {}, {}, {}, {});
ASSERT_GE(storageId, 0);
ASSERT_LT(mDataLoader->setStorageParams(true), 0);
}
@@ -1066,7 +1096,7 @@ TEST_F(IncrementalServiceTest, testMakeDirectory) {
TemporaryDir tempDir;
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew,
- {}, {}, {});
+ {}, {}, {}, {});
std::string dir_path("test");
// Expecting incfs to call makeDir on a path like:
@@ -1085,7 +1115,7 @@ TEST_F(IncrementalServiceTest, testMakeDirectories) {
TemporaryDir tempDir;
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew,
- {}, {}, {});
+ {}, {}, {}, {});
auto first = "first"sv;
auto second = "second"sv;
auto third = "third"sv;
@@ -1108,7 +1138,7 @@ TEST_F(IncrementalServiceTest, testIsFileFullyLoadedFailsWithNoFile) {
TemporaryDir tempDir;
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew,
- {}, {}, {});
+ {}, {}, {}, {});
ASSERT_EQ(-1, mIncrementalService->isFileFullyLoaded(storageId, "base.apk"));
}
@@ -1119,7 +1149,7 @@ TEST_F(IncrementalServiceTest, testIsFileFullyLoadedFailsWithFailedRanges) {
TemporaryDir tempDir;
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew,
- {}, {}, {});
+ {}, {}, {}, {});
EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1);
ASSERT_EQ(-1, mIncrementalService->isFileFullyLoaded(storageId, "base.apk"));
}
@@ -1131,7 +1161,7 @@ TEST_F(IncrementalServiceTest, testIsFileFullyLoadedSuccessWithEmptyRanges) {
TemporaryDir tempDir;
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew,
- {}, {}, {});
+ {}, {}, {}, {});
EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1);
ASSERT_EQ(0, mIncrementalService->isFileFullyLoaded(storageId, "base.apk"));
}
@@ -1143,7 +1173,7 @@ TEST_F(IncrementalServiceTest, testIsFileFullyLoadedSuccess) {
TemporaryDir tempDir;
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew,
- {}, {}, {});
+ {}, {}, {}, {});
EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1);
ASSERT_EQ(0, mIncrementalService->isFileFullyLoaded(storageId, "base.apk"));
}
@@ -1155,8 +1185,8 @@ TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccessWithNoFile) {
TemporaryDir tempDir;
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew,
- {}, {}, {});
- ASSERT_EQ(1, mIncrementalService->getLoadingProgress(storageId));
+ {}, {}, {}, {});
+ ASSERT_EQ(1, mIncrementalService->getLoadingProgress(storageId).getProgress());
}
TEST_F(IncrementalServiceTest, testGetLoadingProgressFailsWithFailedRanges) {
@@ -1166,9 +1196,9 @@ TEST_F(IncrementalServiceTest, testGetLoadingProgressFailsWithFailedRanges) {
TemporaryDir tempDir;
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew,
- {}, {}, {});
+ {}, {}, {}, {});
EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1);
- ASSERT_EQ(-1, mIncrementalService->getLoadingProgress(storageId));
+ ASSERT_EQ(-1, mIncrementalService->getLoadingProgress(storageId).getProgress());
}
TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccessWithEmptyRanges) {
@@ -1178,9 +1208,9 @@ TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccessWithEmptyRanges) {
TemporaryDir tempDir;
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew,
- {}, {}, {});
+ {}, {}, {}, {});
EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(3);
- ASSERT_EQ(1, mIncrementalService->getLoadingProgress(storageId));
+ ASSERT_EQ(1, mIncrementalService->getLoadingProgress(storageId).getProgress());
}
TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccess) {
@@ -1190,9 +1220,9 @@ TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccess) {
TemporaryDir tempDir;
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew,
- {}, {}, {});
+ {}, {}, {}, {});
EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(3);
- ASSERT_EQ(0.5, mIncrementalService->getLoadingProgress(storageId));
+ ASSERT_EQ(0.5, mIncrementalService->getLoadingProgress(storageId).getProgress());
}
TEST_F(IncrementalServiceTest, testRegisterLoadingProgressListenerSuccess) {
@@ -1202,7 +1232,7 @@ TEST_F(IncrementalServiceTest, testRegisterLoadingProgressListenerSuccess) {
TemporaryDir tempDir;
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew,
- {}, {}, {});
+ {}, {}, {}, {});
sp<NiceMock<MockStorageLoadingProgressListener>> listener{
new NiceMock<MockStorageLoadingProgressListener>};
NiceMock<MockStorageLoadingProgressListener>* listenerMock = listener.get();
@@ -1227,7 +1257,7 @@ TEST_F(IncrementalServiceTest, testRegisterLoadingProgressListenerFailsToGetProg
TemporaryDir tempDir;
int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew,
- {}, {}, {});
+ {}, {}, {}, {});
sp<NiceMock<MockStorageLoadingProgressListener>> listener{
new NiceMock<MockStorageLoadingProgressListener>};
NiceMock<MockStorageLoadingProgressListener>* listenerMock = listener.get();
@@ -1242,9 +1272,10 @@ TEST_F(IncrementalServiceTest, testRegisterStorageHealthListenerSuccess) {
NiceMock<MockStorageHealthListener>* newListenerMock = newListener.get();
TemporaryDir tempDir;
- int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
- IncrementalService::CreateOptions::CreateNew,
- {}, StorageHealthCheckParams{}, listener);
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+ IncrementalService::CreateOptions::CreateNew, {},
+ StorageHealthCheckParams{}, listener, {});
ASSERT_GE(storageId, 0);
StorageHealthCheckParams newParams;
newParams.blockedTimeoutMs = 10000;
@@ -1313,4 +1344,123 @@ TEST_F(IncrementalServiceTest, testRegisterStorageHealthListenerSuccess) {
mTimedQueue->clearJob(storageId);
}
+static std::vector<PerUidReadTimeouts> createPerUidTimeouts(
+ std::initializer_list<std::tuple<int, int, int, int>> tuples) {
+ std::vector<PerUidReadTimeouts> result;
+ for (auto&& tuple : tuples) {
+ result.emplace_back();
+ auto& timeouts = result.back();
+ timeouts.uid = std::get<0>(tuple);
+ timeouts.minTimeUs = std::get<1>(tuple);
+ timeouts.minPendingTimeUs = std::get<2>(tuple);
+ timeouts.maxPendingTimeUs = std::get<3>(tuple);
+ }
+ return result;
+}
+
+static ErrorCode checkPerUidTimeouts(const Control& control,
+ const std::vector<PerUidReadTimeouts>& perUidReadTimeouts) {
+ std::vector<PerUidReadTimeouts> expected =
+ createPerUidTimeouts({{0, 1, 2, 3}, {1, 2, 3, 4}, {2, 3, 4, 100000000}});
+ EXPECT_EQ(expected, perUidReadTimeouts);
+ return 0;
+}
+
+static ErrorCode checkPerUidTimeoutsEmpty(
+ const Control& control, const std::vector<PerUidReadTimeouts>& perUidReadTimeouts) {
+ EXPECT_EQ(0u, perUidReadTimeouts.size());
+ return 0;
+}
+
+TEST_F(IncrementalServiceTest, testPerUidTimeoutsTooShort) {
+ EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(1);
+ EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
+ EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(1);
+ EXPECT_CALL(*mDataLoader, start(_)).Times(0);
+ EXPECT_CALL(*mDataLoader, destroy(_)).Times(1);
+ EXPECT_CALL(*mIncFs, setUidReadTimeouts(_, _)).Times(0);
+ EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(0);
+ EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
+ TemporaryDir tempDir;
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+ IncrementalService::CreateOptions::CreateNew, {}, {},
+ {},
+ createPerUidTimeouts(
+ {{0, 1, 2, 3}, {1, 2, 3, 4}, {2, 3, 4, 5}}));
+ ASSERT_GE(storageId, 0);
+}
+
+TEST_F(IncrementalServiceTest, testPerUidTimeoutsSuccess) {
+ mVold->setIncFsMountOptionsSuccess();
+ mAppOpsManager->checkPermissionSuccess();
+ mFs->hasFiles();
+
+ EXPECT_CALL(*mIncFs, setUidReadTimeouts(_, _))
+ // First call.
+ .WillOnce(Invoke(&checkPerUidTimeouts))
+ // Fully loaded and no readlogs.
+ .WillOnce(Invoke(&checkPerUidTimeoutsEmpty));
+ EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(3);
+
+ // Empty storage.
+ mIncFs->countFilledBlocksEmpty();
+
+ TemporaryDir tempDir;
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+ IncrementalService::CreateOptions::CreateNew, {}, {},
+ {},
+ createPerUidTimeouts({{0, 1, 2, 3},
+ {1, 2, 3, 4},
+ {2, 3, 4, 100000000}}));
+ ASSERT_GE(storageId, 0);
+
+ {
+ // Timed callback present -> 0 progress.
+ ASSERT_EQ(storageId, mTimedQueue->mId);
+ ASSERT_GE(mTimedQueue->mAfter, std::chrono::seconds(1));
+ const auto timedCallback = mTimedQueue->mWhat;
+ mTimedQueue->clearJob(storageId);
+
+ // Still loading.
+ mIncFs->countFilledBlocksSuccess();
+
+ // Call it again.
+ timedCallback();
+ }
+
+ {
+ // Still present -> 0.5 progress.
+ ASSERT_EQ(storageId, mTimedQueue->mId);
+ ASSERT_GE(mTimedQueue->mAfter, std::chrono::seconds(1));
+ const auto timedCallback = mTimedQueue->mWhat;
+ mTimedQueue->clearJob(storageId);
+
+ // Fully loaded but readlogs collection enabled.
+ mIncFs->countFilledBlocksFullyLoaded();
+ ASSERT_GE(mDataLoader->setStorageParams(true), 0);
+
+ // Call it again.
+ timedCallback();
+ }
+
+ {
+ // Still present -> fully loaded + readlogs.
+ ASSERT_EQ(storageId, mTimedQueue->mId);
+ ASSERT_GE(mTimedQueue->mAfter, std::chrono::seconds(1));
+ const auto timedCallback = mTimedQueue->mWhat;
+ mTimedQueue->clearJob(storageId);
+
+ // Now disable readlogs.
+ ASSERT_GE(mDataLoader->setStorageParams(false), 0);
+
+ // Call it again.
+ timedCallback();
+ }
+
+ // No callbacks anymore -> fully loaded and no readlogs.
+ ASSERT_EQ(mTimedQueue->mAfter, Milliseconds());
+}
+
} // namespace android::os::incremental
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index d9350f39ee58..f8cbc355a18c 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -299,6 +299,8 @@ public final class SystemServer implements Dumpable {
"com.android.server.autofill.AutofillManagerService";
private static final String CONTENT_CAPTURE_MANAGER_SERVICE_CLASS =
"com.android.server.contentcapture.ContentCaptureManagerService";
+ private static final String TRANSLATION_MANAGER_SERVICE_CLASS =
+ "com.android.server.translation.TranslationManagerService";
private static final String MUSIC_RECOGNITION_MANAGER_SERVICE_CLASS =
"com.android.server.musicrecognition.MusicRecognitionManagerService";
private static final String SYSTEM_CAPTIONS_MANAGER_SERVICE_CLASS =
@@ -1358,8 +1360,7 @@ public final class SystemServer implements Dumpable {
}
t.traceBegin("IpConnectivityMetrics");
- mSystemServiceManager.startServiceFromJar(IP_CONNECTIVITY_METRICS_CLASS,
- CONNECTIVITY_SERVICE_APEX_PATH);
+ mSystemServiceManager.startService(IP_CONNECTIVITY_METRICS_CLASS);
t.traceEnd();
t.traceBegin("NetworkWatchlistService");
@@ -2291,6 +2292,13 @@ public final class SystemServer implements Dumpable {
t.traceEnd();
}
+ // Translation manager service
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_TRANSLATION)) {
+ t.traceBegin("StartTranslationManagerService");
+ mSystemServiceManager.startService(TRANSLATION_MANAGER_SERVICE_CLASS);
+ t.traceEnd();
+ }
+
// NOTE: ClipboardService depends on ContentCapture and Autofill
t.traceBegin("StartClipboardService");
mSystemServiceManager.startService(ClipboardService.class);
diff --git a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
index 3220dff553d3..a691a8d44e48 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
@@ -313,29 +313,30 @@ public class AppStateTrackerTest {
}
}
- private static final int NONE = 0;
- private static final int ALARMS_ONLY = 1 << 0;
- private static final int JOBS_ONLY = 1 << 1;
- private static final int JOBS_AND_ALARMS = ALARMS_ONLY | JOBS_ONLY;
-
- private void areRestricted(AppStateTrackerTestable instance, int uid, String packageName,
- int restrictionTypes, boolean exemptFromBatterySaver) {
- assertEquals(((restrictionTypes & JOBS_ONLY) != 0),
- instance.areJobsRestricted(uid, packageName, exemptFromBatterySaver));
- assertEquals(((restrictionTypes & ALARMS_ONLY) != 0),
- instance.areAlarmsRestricted(uid, packageName, exemptFromBatterySaver));
+ private void areJobsRestricted(AppStateTrackerTestable instance, int[] uids, String[] packages,
+ boolean[] restricted, boolean exemption) {
+ assertTrue(uids.length == packages.length && uids.length == restricted.length);
+ for (int i = 0; i < uids.length; i++) {
+ assertEquals(restricted[i],
+ instance.areJobsRestricted(uids[i], packages[i], exemption));
+ }
}
- private void areRestricted(AppStateTrackerTestable instance, int uid, String packageName,
- int restrictionTypes) {
- areRestricted(instance, uid, packageName, restrictionTypes,
- /*exemptFromBatterySaver=*/ false);
+ private void areAlarmsRestrictedByFAS(AppStateTrackerTestable instance, int[] uids,
+ String[] packages, boolean[] restricted) {
+ assertTrue(uids.length == packages.length && uids.length == restricted.length);
+ for (int i = 0; i < uids.length; i++) {
+ assertEquals(restricted[i], instance.areAlarmsRestricted(uids[i], packages[i]));
+ }
}
- private void areRestrictedWithExemption(AppStateTrackerTestable instance,
- int uid, String packageName, int restrictionTypes) {
- areRestricted(instance, uid, packageName, restrictionTypes,
- /*exemptFromBatterySaver=*/ true);
+ private void areAlarmsRestrictedByBatterySaver(AppStateTrackerTestable instance, int[] uids,
+ String[] packages, boolean[] restricted) {
+ assertTrue(uids.length == packages.length && uids.length == restricted.length);
+ for (int i = 0; i < uids.length; i++) {
+ assertEquals(restricted[i],
+ instance.areAlarmsRestrictedByBatterySaver(uids[i], packages[i]));
+ }
}
@Test
@@ -344,30 +345,42 @@ public class AppStateTrackerTest {
callStart(instance);
assertFalse(instance.isForceAllAppsStandbyEnabled());
- areRestricted(instance, UID_1, PACKAGE_1, NONE);
- areRestricted(instance, UID_2, PACKAGE_2, NONE);
- areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
-
- areRestrictedWithExemption(instance, UID_1, PACKAGE_1, NONE);
- areRestrictedWithExemption(instance, UID_2, PACKAGE_2, NONE);
- areRestrictedWithExemption(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
+ areJobsRestricted(instance,
+ new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {false, false, false, false},
+ false);
+ areJobsRestricted(instance,
+ new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {false, false, false, false},
+ true);
+ areAlarmsRestrictedByBatterySaver(instance,
+ new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {false, false, false, false});
mPowerSaveMode = true;
mPowerSaveObserver.accept(getPowerSaveState());
assertTrue(instance.isForceAllAppsStandbyEnabled());
- areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS);
- areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
- areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
-
- areRestrictedWithExemption(instance, UID_1, PACKAGE_1, NONE);
- areRestrictedWithExemption(instance, UID_2, PACKAGE_2, NONE);
- areRestrictedWithExemption(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
+ areJobsRestricted(instance,
+ new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {true, true, true, false},
+ false);
+ areJobsRestricted(instance,
+ new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {false, false, false, false},
+ true);
+ areAlarmsRestrictedByBatterySaver(instance,
+ new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {true, true, true, false});
// Toggle the foreground state.
- mPowerSaveMode = true;
- mPowerSaveObserver.accept(getPowerSaveState());
assertFalse(instance.isUidActive(UID_1));
assertFalse(instance.isUidActive(UID_2));
@@ -376,34 +389,65 @@ public class AppStateTrackerTest {
mIUidObserver.onUidActive(UID_1);
waitUntilMainHandlerDrain();
waitUntilMainHandlerDrain();
- areRestricted(instance, UID_1, PACKAGE_1, NONE);
- areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
- areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
+
+ areJobsRestricted(instance,
+ new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {false, true, true, false},
+ false);
+ areAlarmsRestrictedByBatterySaver(instance,
+ new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {false, true, true, false});
+
assertTrue(instance.isUidActive(UID_1));
assertFalse(instance.isUidActive(UID_2));
mIUidObserver.onUidGone(UID_1, /*disable=*/ false);
waitUntilMainHandlerDrain();
waitUntilMainHandlerDrain();
- areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS);
- areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
- areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
+
+ areJobsRestricted(instance,
+ new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {true, true, true, false},
+ false);
+ areAlarmsRestrictedByBatterySaver(instance,
+ new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {true, true, true, false});
+
assertFalse(instance.isUidActive(UID_1));
assertFalse(instance.isUidActive(UID_2));
mIUidObserver.onUidActive(UID_1);
waitUntilMainHandlerDrain();
waitUntilMainHandlerDrain();
- areRestricted(instance, UID_1, PACKAGE_1, NONE);
- areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
- areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
+
+ areJobsRestricted(instance,
+ new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {false, true, true, false},
+ false);
+ areAlarmsRestrictedByBatterySaver(instance,
+ new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {false, true, true, false});
mIUidObserver.onUidIdle(UID_1, /*disable=*/ false);
waitUntilMainHandlerDrain();
waitUntilMainHandlerDrain();
- areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS);
- areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
- areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
+
+ areJobsRestricted(instance,
+ new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {true, true, true, false},
+ false);
+ areAlarmsRestrictedByBatterySaver(instance,
+ new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {true, true, true, false});
+
assertFalse(instance.isUidActive(UID_1));
assertFalse(instance.isUidActive(UID_2));
@@ -416,11 +460,19 @@ public class AppStateTrackerTest {
assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_2, PACKAGE_2));
assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_10_2, PACKAGE_2));
- areRestricted(instance, UID_1, PACKAGE_1, NONE);
- areRestricted(instance, UID_10_1, PACKAGE_1, NONE);
- areRestricted(instance, UID_2, PACKAGE_2, NONE);
- areRestricted(instance, UID_10_2, PACKAGE_2, NONE);
- areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
+ areJobsRestricted(instance,
+ new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {false, false, false, false, false},
+ false);
+ areAlarmsRestrictedByBatterySaver(instance,
+ new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {false, false, false, false, false});
+ areAlarmsRestrictedByFAS(instance,
+ new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {false, false, false, false, false});
setAppOps(UID_1, PACKAGE_1, true);
setAppOps(UID_10_2, PACKAGE_2, true);
@@ -429,24 +481,72 @@ public class AppStateTrackerTest {
assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_2, PACKAGE_2));
assertFalse(instance.isRunAnyInBackgroundAppOpsAllowed(UID_10_2, PACKAGE_2));
- areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS);
- areRestricted(instance, UID_10_1, PACKAGE_1, NONE);
- areRestricted(instance, UID_2, PACKAGE_2, NONE);
- areRestricted(instance, UID_10_2, PACKAGE_2, JOBS_AND_ALARMS);
- areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
+ areJobsRestricted(instance,
+ new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {true, false, false, true, false},
+ false);
+ areJobsRestricted(instance,
+ new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {true, false, false, true, false},
+ true);
+
+ areAlarmsRestrictedByBatterySaver(instance,
+ new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {false, false, false, false, false});
+ areAlarmsRestrictedByFAS(instance,
+ new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {true, false, false, true, false});
// Toggle power saver, should still be the same.
mPowerSaveMode = true;
mPowerSaveObserver.accept(getPowerSaveState());
+ areJobsRestricted(instance,
+ new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {true, true, true, true, false},
+ false);
+ areJobsRestricted(instance,
+ new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {true, false, false, true, false},
+ true);
+
+ areAlarmsRestrictedByBatterySaver(instance,
+ new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {true, true, true, true, false});
+ areAlarmsRestrictedByFAS(instance,
+ new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {true, false, false, true, false});
+
mPowerSaveMode = false;
mPowerSaveObserver.accept(getPowerSaveState());
- areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS);
- areRestricted(instance, UID_10_1, PACKAGE_1, NONE);
- areRestricted(instance, UID_2, PACKAGE_2, NONE);
- areRestricted(instance, UID_10_2, PACKAGE_2, JOBS_AND_ALARMS);
- areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
+ areJobsRestricted(instance,
+ new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {true, false, false, true, false},
+ false);
+ areJobsRestricted(instance,
+ new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {true, false, false, true, false},
+ true);
+
+ areAlarmsRestrictedByBatterySaver(instance,
+ new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {false, false, false, false, false});
+ areAlarmsRestrictedByFAS(instance,
+ new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {true, false, false, true, false});
// Clear the app ops and update the exemption list.
setAppOps(UID_1, PACKAGE_1, false);
@@ -455,24 +555,41 @@ public class AppStateTrackerTest {
mPowerSaveMode = true;
mPowerSaveObserver.accept(getPowerSaveState());
- areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS);
- areRestricted(instance, UID_10_1, PACKAGE_1, JOBS_AND_ALARMS);
- areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
- areRestricted(instance, UID_10_2, PACKAGE_2, JOBS_AND_ALARMS);
- areRestricted(instance, UID_3, PACKAGE_3, JOBS_AND_ALARMS);
- areRestricted(instance, UID_10_3, PACKAGE_3, JOBS_AND_ALARMS);
- areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
+ areJobsRestricted(instance,
+ new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {true, true, true, true, false},
+ false);
+ areJobsRestricted(instance,
+ new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {false, false, false, false, false},
+ true);
+
+ areAlarmsRestrictedByBatterySaver(instance,
+ new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {true, true, true, true, false});
+ areAlarmsRestrictedByFAS(instance,
+ new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {false, false, false, false, false});
instance.setPowerSaveExemptionListAppIds(new int[] {UID_1}, new int[] {},
new int[] {UID_2});
- areRestricted(instance, UID_1, PACKAGE_1, NONE);
- areRestricted(instance, UID_10_1, PACKAGE_1, NONE);
- areRestricted(instance, UID_2, PACKAGE_2, ALARMS_ONLY);
- areRestricted(instance, UID_10_2, PACKAGE_2, ALARMS_ONLY);
- areRestricted(instance, UID_3, PACKAGE_3, JOBS_AND_ALARMS);
- areRestricted(instance, UID_10_3, PACKAGE_3, JOBS_AND_ALARMS);
- areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
+ areJobsRestricted(instance,
+ new int[] {UID_1, UID_10_1, UID_2, UID_10_2, UID_3, UID_10_3, Process.SYSTEM_UID},
+ new String[]{PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_3, PACKAGE_3,
+ PACKAGE_SYSTEM},
+ new boolean[] {false, false, false, false, true, true, false},
+ false);
+
+ areAlarmsRestrictedByBatterySaver(instance,
+ new int[] {UID_1, UID_10_1, UID_2, UID_10_2, UID_3, UID_10_3, Process.SYSTEM_UID},
+ new String[]{PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_3, PACKAGE_3,
+ PACKAGE_SYSTEM},
+ new boolean[] {false, false, true, true, true, true, false});
// Again, make sure toggling the global state doesn't change it.
mPowerSaveMode = false;
@@ -481,13 +598,18 @@ public class AppStateTrackerTest {
mPowerSaveMode = true;
mPowerSaveObserver.accept(getPowerSaveState());
- areRestricted(instance, UID_1, PACKAGE_1, NONE);
- areRestricted(instance, UID_10_1, PACKAGE_1, NONE);
- areRestricted(instance, UID_2, PACKAGE_2, ALARMS_ONLY);
- areRestricted(instance, UID_10_2, PACKAGE_2, ALARMS_ONLY);
- areRestricted(instance, UID_3, PACKAGE_3, JOBS_AND_ALARMS);
- areRestricted(instance, UID_10_3, PACKAGE_3, JOBS_AND_ALARMS);
- areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
+ areJobsRestricted(instance,
+ new int[] {UID_1, UID_10_1, UID_2, UID_10_2, UID_3, UID_10_3, Process.SYSTEM_UID},
+ new String[]{PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_3, PACKAGE_3,
+ PACKAGE_SYSTEM},
+ new boolean[] {false, false, false, false, true, true, false},
+ false);
+
+ areAlarmsRestrictedByBatterySaver(instance,
+ new int[] {UID_1, UID_10_1, UID_2, UID_10_2, UID_3, UID_10_3, Process.SYSTEM_UID},
+ new String[]{PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_3, PACKAGE_3,
+ PACKAGE_SYSTEM},
+ new boolean[] {false, false, true, true, true, true, false});
assertTrue(instance.isUidPowerSaveExempt(UID_1));
assertTrue(instance.isUidPowerSaveExempt(UID_10_1));
@@ -646,52 +768,98 @@ public class AppStateTrackerTest {
}
@Test
- public void testExempt() throws Exception {
+ public void testExemptedBucket() throws Exception {
final AppStateTrackerTestable instance = newInstance();
callStart(instance);
assertFalse(instance.isForceAllAppsStandbyEnabled());
- areRestricted(instance, UID_1, PACKAGE_1, NONE);
- areRestricted(instance, UID_2, PACKAGE_2, NONE);
- areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
+
+ areJobsRestricted(instance,
+ new int[] {UID_1, UID_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {false, false, false},
+ false);
+ areAlarmsRestrictedByBatterySaver(instance,
+ new int[] {UID_1, UID_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {false, false, false});
mPowerSaveMode = true;
mPowerSaveObserver.accept(getPowerSaveState());
assertTrue(instance.isForceAllAppsStandbyEnabled());
- areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS);
- areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
- areRestricted(instance, UID_10_2, PACKAGE_2, JOBS_AND_ALARMS);
- areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE);
+ areJobsRestricted(instance,
+ new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {true, true, true, false},
+ false);
+ areJobsRestricted(instance,
+ new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {false, false, false, false},
+ true);
+ areAlarmsRestrictedByBatterySaver(instance,
+ new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID},
+ new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM},
+ new boolean[] {true, true, true, false});
// Exempt package 2 on user-10.
mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_2, /*user=*/ 10, false,
UsageStatsManager.STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT);
- areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS);
- areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
- areRestricted(instance, UID_10_2, PACKAGE_2, NONE);
-
- areRestrictedWithExemption(instance, UID_1, PACKAGE_1, NONE);
- areRestrictedWithExemption(instance, UID_2, PACKAGE_2, NONE);
- areRestrictedWithExemption(instance, UID_10_2, PACKAGE_2, NONE);
+ areJobsRestricted(instance,
+ new int[] {UID_1, UID_2, UID_10_2},
+ new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2},
+ new boolean[] {true, true, false},
+ false);
+ areJobsRestricted(instance,
+ new int[] {UID_1, UID_2, UID_10_2},
+ new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2},
+ new boolean[] {false, false, false},
+ true);
+ areAlarmsRestrictedByBatterySaver(instance,
+ new int[] {UID_1, UID_2, UID_10_2},
+ new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2},
+ new boolean[] {true, true, false});
// Exempt package 1 on user-0.
mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_1, /*user=*/ 0, false,
UsageStatsManager.STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT);
- areRestricted(instance, UID_1, PACKAGE_1, NONE);
- areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
- areRestricted(instance, UID_10_2, PACKAGE_2, NONE);
+ areJobsRestricted(instance,
+ new int[] {UID_1, UID_2, UID_10_2},
+ new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2},
+ new boolean[] {false, true, false},
+ false);
+ areJobsRestricted(instance,
+ new int[] {UID_1, UID_2, UID_10_2},
+ new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2},
+ new boolean[] {false, false, false},
+ true);
+ areAlarmsRestrictedByBatterySaver(instance,
+ new int[] {UID_1, UID_2, UID_10_2},
+ new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2},
+ new boolean[] {false, true, false});
// Unexempt package 2 on user-10.
mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_2, /*user=*/ 10, false,
UsageStatsManager.STANDBY_BUCKET_ACTIVE, REASON_MAIN_USAGE);
- areRestricted(instance, UID_1, PACKAGE_1, NONE);
- areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
- areRestricted(instance, UID_10_2, PACKAGE_2, JOBS_AND_ALARMS);
+ areJobsRestricted(instance,
+ new int[] {UID_1, UID_2, UID_10_2},
+ new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2},
+ new boolean[] {false, true, true},
+ false);
+ areJobsRestricted(instance,
+ new int[] {UID_1, UID_2, UID_10_2},
+ new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2},
+ new boolean[] {false, false, false},
+ true);
+ areAlarmsRestrictedByBatterySaver(instance,
+ new int[] {UID_1, UID_2, UID_10_2},
+ new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2},
+ new boolean[] {false, true, true});
// Check force-app-standby.
// EXEMPT doesn't exempt from force-app-standby.
@@ -703,13 +871,28 @@ public class AppStateTrackerTest {
mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_2, /*user=*/ 0, false,
UsageStatsManager.STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT);
+ // All 3 packages (u0:p1, u0:p2, u10:p2) are now in the exempted bucket.
setAppOps(UID_1, PACKAGE_1, true);
- areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS);
- areRestricted(instance, UID_2, PACKAGE_2, NONE);
-
- areRestrictedWithExemption(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS);
- areRestrictedWithExemption(instance, UID_2, PACKAGE_2, NONE);
+ areJobsRestricted(instance,
+ new int[] {UID_1, UID_2, UID_10_2},
+ new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2},
+ new boolean[] {true, false, false},
+ false);
+ areJobsRestricted(instance,
+ new int[] {UID_1, UID_2, UID_10_2},
+ new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2},
+ new boolean[] {true, false, false},
+ true);
+
+ areAlarmsRestrictedByBatterySaver(instance,
+ new int[] {UID_1, UID_2, UID_10_2},
+ new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2},
+ new boolean[] {false, false, false});
+ areAlarmsRestrictedByFAS(instance,
+ new int[] {UID_1, UID_2, UID_10_2},
+ new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2},
+ new boolean[] {true, false, false});
}
@Test
@@ -809,6 +992,8 @@ public class AppStateTrackerTest {
verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
+ verify(l, times(1)).updateAllAlarms();
+ verify(l, times(0)).updateAlarmsForUid(anyInt());
verify(l, times(0)).unblockAllUnrestrictedAlarms();
verify(l, times(0)).unblockAlarmsForUid(anyInt());
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
@@ -823,7 +1008,9 @@ public class AppStateTrackerTest {
verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
- verify(l, times(1)).unblockAllUnrestrictedAlarms();
+ verify(l, times(1)).updateAllAlarms();
+ verify(l, times(0)).updateAlarmsForUid(anyInt());
+ verify(l, times(0)).unblockAllUnrestrictedAlarms();
verify(l, times(0)).unblockAlarmsForUid(anyInt());
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
reset(l);
@@ -853,6 +1040,8 @@ public class AppStateTrackerTest {
verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
verify(l, times(1)).updateJobsForUidPackage(eq(UID_10_2), eq(PACKAGE_2), anyBoolean());
+ verify(l, times(0)).updateAllAlarms();
+ verify(l, times(0)).updateAlarmsForUid(anyInt());
verify(l, times(0)).unblockAllUnrestrictedAlarms();
verify(l, times(0)).unblockAlarmsForUid(anyInt());
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
@@ -865,6 +1054,8 @@ public class AppStateTrackerTest {
verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
verify(l, times(1)).updateJobsForUidPackage(eq(UID_10_2), eq(PACKAGE_2), anyBoolean());
+ verify(l, times(0)).updateAllAlarms();
+ verify(l, times(0)).updateAlarmsForUid(anyInt());
verify(l, times(0)).unblockAllUnrestrictedAlarms();
verify(l, times(0)).unblockAlarmsForUid(anyInt());
verify(l, times(1)).unblockAlarmsForUidPackage(eq(UID_10_2), eq(PACKAGE_2));
@@ -876,15 +1067,16 @@ public class AppStateTrackerTest {
verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
+ verify(l, times(0)).updateAllAlarms();
+ verify(l, times(0)).updateAlarmsForUid(anyInt());
verify(l, times(0)).unblockAllUnrestrictedAlarms();
verify(l, times(0)).unblockAlarmsForUid(anyInt());
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
- // Unrestrict while battery saver is on. Shouldn't fire.
+ // Test overlap with battery saver
mPowerSaveMode = true;
mPowerSaveObserver.accept(getPowerSaveState());
- // Note toggling appops while BS is on will suppress unblockAlarmsForUidPackage().
setAppOps(UID_10_2, PACKAGE_2, true);
waitUntilMainHandlerDrain();
@@ -892,6 +1084,8 @@ public class AppStateTrackerTest {
verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
verify(l, times(1)).updateJobsForUidPackage(eq(UID_10_2), eq(PACKAGE_2), anyBoolean());
+ verify(l, times(1)).updateAllAlarms();
+ verify(l, times(0)).updateAlarmsForUid(anyInt());
verify(l, times(0)).unblockAllUnrestrictedAlarms();
verify(l, times(0)).unblockAlarmsForUid(anyInt());
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
@@ -906,7 +1100,9 @@ public class AppStateTrackerTest {
verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
- verify(l, times(1)).unblockAllUnrestrictedAlarms();
+ verify(l, times(1)).updateAllAlarms();
+ verify(l, times(0)).updateAlarmsForUid(anyInt());
+ verify(l, times(0)).unblockAllUnrestrictedAlarms();
verify(l, times(0)).unblockAlarmsForUid(anyInt());
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
reset(l);
@@ -922,7 +1118,9 @@ public class AppStateTrackerTest {
verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
- verify(l, times(0)).unblockAllUnrestrictedAlarms();
+ verify(l, times(1)).updateAllAlarms();
+ verify(l, times(0)).updateAlarmsForUid(anyInt());
+ verify(l, times(1)).unblockAllUnrestrictedAlarms();
verify(l, times(0)).unblockAlarmsForUid(anyInt());
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
reset(l);
@@ -934,7 +1132,9 @@ public class AppStateTrackerTest {
verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
- verify(l, times(1)).unblockAllUnrestrictedAlarms();
+ verify(l, times(1)).updateAllAlarms();
+ verify(l, times(0)).updateAlarmsForUid(anyInt());
+ verify(l, times(0)).unblockAllUnrestrictedAlarms();
verify(l, times(0)).unblockAlarmsForUid(anyInt());
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
reset(l);
@@ -948,6 +1148,8 @@ public class AppStateTrackerTest {
verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
+ verify(l, times(0)).updateAllAlarms();
+ verify(l, times(0)).updateAlarmsForUid(anyInt());
verify(l, times(0)).unblockAllUnrestrictedAlarms();
verify(l, times(0)).unblockAlarmsForUid(anyInt());
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
@@ -961,12 +1163,14 @@ public class AppStateTrackerTest {
verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
+ verify(l, times(0)).updateAllAlarms();
+ verify(l, times(0)).updateAlarmsForUid(anyInt());
verify(l, times(0)).unblockAllUnrestrictedAlarms();
verify(l, times(0)).unblockAlarmsForUid(anyInt());
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
reset(l);
- // Do the same thing with battery saver on. (Currently same callbacks are called.)
+ // Do the same thing with battery saver on.
mPowerSaveMode = true;
mPowerSaveObserver.accept(getPowerSaveState());
@@ -975,6 +1179,8 @@ public class AppStateTrackerTest {
verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
+ verify(l, times(1)).updateAllAlarms();
+ verify(l, times(0)).updateAlarmsForUid(anyInt());
verify(l, times(0)).unblockAllUnrestrictedAlarms();
verify(l, times(0)).unblockAlarmsForUid(anyInt());
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
@@ -989,7 +1195,9 @@ public class AppStateTrackerTest {
verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
- verify(l, times(0)).unblockAllUnrestrictedAlarms();
+ verify(l, times(1)).updateAllAlarms();
+ verify(l, times(0)).updateAlarmsForUid(anyInt());
+ verify(l, times(1)).unblockAllUnrestrictedAlarms();
verify(l, times(0)).unblockAlarmsForUid(anyInt());
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
reset(l);
@@ -1001,7 +1209,9 @@ public class AppStateTrackerTest {
verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
- verify(l, times(1)).unblockAllUnrestrictedAlarms();
+ verify(l, times(1)).updateAllAlarms();
+ verify(l, times(0)).updateAlarmsForUid(anyInt());
+ verify(l, times(0)).unblockAllUnrestrictedAlarms();
verify(l, times(0)).unblockAlarmsForUid(anyInt());
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
reset(l);
@@ -1015,6 +1225,8 @@ public class AppStateTrackerTest {
verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
+ verify(l, times(0)).updateAllAlarms();
+ verify(l, times(0)).updateAlarmsForUid(anyInt());
verify(l, times(0)).unblockAllUnrestrictedAlarms();
verify(l, times(0)).unblockAlarmsForUid(anyInt());
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
@@ -1028,6 +1240,8 @@ public class AppStateTrackerTest {
verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
+ verify(l, times(0)).updateAllAlarms();
+ verify(l, times(0)).updateAlarmsForUid(anyInt());
verify(l, times(0)).unblockAllUnrestrictedAlarms();
verify(l, times(0)).unblockAlarmsForUid(anyInt());
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
@@ -1037,9 +1251,8 @@ public class AppStateTrackerTest {
// -------------------------------------------------------------------------
// Tests with proc state changes.
- // With battery save.
- mPowerSaveMode = true;
- mPowerSaveObserver.accept(getPowerSaveState());
+ // With battery saver.
+ // Battery saver is already on.
mIUidObserver.onUidActive(UID_10_1);
@@ -1049,6 +1262,8 @@ public class AppStateTrackerTest {
verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
+ verify(l, times(0)).updateAllAlarms();
+ verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1));
verify(l, times(0)).unblockAllUnrestrictedAlarms();
verify(l, times(1)).unblockAlarmsForUid(eq(UID_10_1));
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
@@ -1062,6 +1277,8 @@ public class AppStateTrackerTest {
verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
+ verify(l, times(0)).updateAllAlarms();
+ verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1));
verify(l, times(0)).unblockAllUnrestrictedAlarms();
verify(l, times(0)).unblockAlarmsForUid(anyInt());
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
@@ -1075,6 +1292,8 @@ public class AppStateTrackerTest {
verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
+ verify(l, times(0)).updateAllAlarms();
+ verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1));
verify(l, times(0)).unblockAllUnrestrictedAlarms();
verify(l, times(1)).unblockAlarmsForUid(eq(UID_10_1));
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
@@ -1088,12 +1307,14 @@ public class AppStateTrackerTest {
verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
+ verify(l, times(0)).updateAllAlarms();
+ verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1));
verify(l, times(0)).unblockAllUnrestrictedAlarms();
verify(l, times(0)).unblockAlarmsForUid(anyInt());
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
reset(l);
- // Without battery save.
+ // Without battery saver.
mPowerSaveMode = false;
mPowerSaveObserver.accept(getPowerSaveState());
@@ -1102,7 +1323,9 @@ public class AppStateTrackerTest {
verify(l, times(0)).updateJobsForUid(eq(UID_10_1), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
- verify(l, times(1)).unblockAllUnrestrictedAlarms();
+ verify(l, times(1)).updateAllAlarms();
+ verify(l, times(0)).updateAlarmsForUid(eq(UID_10_1));
+ verify(l, times(0)).unblockAllUnrestrictedAlarms();
verify(l, times(0)).unblockAlarmsForUid(anyInt());
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
reset(l);
@@ -1115,6 +1338,8 @@ public class AppStateTrackerTest {
verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
+ verify(l, times(0)).updateAllAlarms();
+ verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1));
verify(l, times(0)).unblockAllUnrestrictedAlarms();
verify(l, times(1)).unblockAlarmsForUid(eq(UID_10_1));
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
@@ -1128,6 +1353,8 @@ public class AppStateTrackerTest {
verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
+ verify(l, times(0)).updateAllAlarms();
+ verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1));
verify(l, times(0)).unblockAllUnrestrictedAlarms();
verify(l, times(0)).unblockAlarmsForUid(anyInt());
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
@@ -1141,6 +1368,8 @@ public class AppStateTrackerTest {
verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
+ verify(l, times(0)).updateAllAlarms();
+ verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1));
verify(l, times(0)).unblockAllUnrestrictedAlarms();
verify(l, times(1)).unblockAlarmsForUid(eq(UID_10_1));
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
@@ -1154,6 +1383,8 @@ public class AppStateTrackerTest {
verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
+ verify(l, times(0)).updateAllAlarms();
+ verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1));
verify(l, times(0)).unblockAllUnrestrictedAlarms();
verify(l, times(0)).unblockAlarmsForUid(anyInt());
verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 8edac4f8ce58..7a970a1c3d46 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -52,6 +52,7 @@ import static com.android.server.alarm.AlarmManagerService.Constants.KEY_LISTENE
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MAX_INTERVAL;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_FUTURITY;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_INTERVAL;
+import static com.android.server.alarm.AlarmManagerService.INDEFINITE_DELAY;
import static com.android.server.alarm.AlarmManagerService.IS_WAKEUP_MASK;
import static com.android.server.alarm.AlarmManagerService.TIME_CHANGED_MASK;
import static com.android.server.alarm.AlarmManagerService.WORKING_INDEX;
@@ -71,6 +72,7 @@ import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.never;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
@@ -817,14 +819,14 @@ public class AlarmManagerServiceTest {
}
@Test
- public void testAlarmRestrictedInBatterySaver() throws Exception {
+ public void testAlarmRestrictedByFAS() throws Exception {
final ArgumentCaptor<AppStateTrackerImpl.Listener> listenerArgumentCaptor =
ArgumentCaptor.forClass(AppStateTrackerImpl.Listener.class);
verify(mAppStateTracker).addListener(listenerArgumentCaptor.capture());
final PendingIntent alarmPi = getNewMockPendingIntent();
- when(mAppStateTracker.areAlarmsRestricted(TEST_CALLING_UID, TEST_CALLING_PACKAGE,
- false)).thenReturn(true);
+ when(mAppStateTracker.areAlarmsRestricted(TEST_CALLING_UID,
+ TEST_CALLING_PACKAGE)).thenReturn(true);
setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 2, alarmPi);
assertEquals(mNowElapsedTest + 2, mTestTimer.getElapsed());
@@ -1301,7 +1303,6 @@ public class AlarmManagerServiceTest {
final long awiDelayForTest = 23;
setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_LONG_TIME, awiDelayForTest);
- setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_SHORT_TIME, 0);
setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 1000,
getNewMockPendingIntent());
@@ -1336,7 +1337,7 @@ public class AlarmManagerServiceTest {
}
@Test
- public void allowWhileIdleUnrestricted() throws Exception {
+ public void allowWhileIdleUnrestrictedInIdle() throws Exception {
doReturn(0).when(mService).fuzzForDuration(anyLong());
final long awiDelayForTest = 127;
@@ -1361,7 +1362,7 @@ public class AlarmManagerServiceTest {
}
@Test
- public void deviceIdleThrottling() throws Exception {
+ public void deviceIdleDeferralOnSet() throws Exception {
doReturn(0).when(mService).fuzzForDuration(anyLong());
final long deviceIdleUntil = mNowElapsedTest + 1234;
@@ -1386,6 +1387,123 @@ public class AlarmManagerServiceTest {
}
@Test
+ public void deviceIdleStateChanges() throws Exception {
+ doReturn(0).when(mService).fuzzForDuration(anyLong());
+
+ final int numAlarms = 10;
+ final PendingIntent[] pis = new PendingIntent[numAlarms];
+ for (int i = 0; i < numAlarms; i++) {
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + i + 1,
+ pis[i] = getNewMockPendingIntent());
+ assertEquals(mNowElapsedTest + 1, mTestTimer.getElapsed());
+ }
+
+ final PendingIntent idleUntil = getNewMockPendingIntent();
+ setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 1234, idleUntil);
+
+ assertEquals(mNowElapsedTest + 1234, mTestTimer.getElapsed());
+
+ mNowElapsedTest += 5;
+ mTestTimer.expire();
+ // Nothing should happen.
+ verify(pis[0], never()).send(eq(mMockContext), eq(0), any(Intent.class), any(),
+ any(Handler.class), isNull(), any());
+
+ mService.removeLocked(idleUntil, null);
+ mTestTimer.expire();
+ // Now, the first 5 alarms (upto i = 4) should expire.
+ for (int i = 0; i < 5; i++) {
+ verify(pis[i]).send(eq(mMockContext), eq(0), any(Intent.class), any(),
+ any(Handler.class), isNull(), any());
+ }
+ // Rest should be restored, so the timer should reflect the next alarm.
+ assertEquals(mNowElapsedTest + 1, mTestTimer.getElapsed());
+ }
+
+ @Test
+ public void batterySaverThrottling() {
+ final ArgumentCaptor<AppStateTrackerImpl.Listener> listenerArgumentCaptor =
+ ArgumentCaptor.forClass(AppStateTrackerImpl.Listener.class);
+ verify(mAppStateTracker).addListener(listenerArgumentCaptor.capture());
+ final AppStateTrackerImpl.Listener listener = listenerArgumentCaptor.getValue();
+
+ final PendingIntent alarmPi = getNewMockPendingIntent();
+ when(mAppStateTracker.areAlarmsRestrictedByBatterySaver(TEST_CALLING_UID,
+ TEST_CALLING_PACKAGE)).thenReturn(true);
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 7, alarmPi);
+ assertEquals(mNowElapsedTest + INDEFINITE_DELAY, mTestTimer.getElapsed());
+
+ when(mAppStateTracker.areAlarmsRestrictedByBatterySaver(TEST_CALLING_UID,
+ TEST_CALLING_PACKAGE)).thenReturn(false);
+ listener.updateAllAlarms();
+ assertEquals(mNowElapsedTest + 7, mTestTimer.getElapsed());
+
+ when(mAppStateTracker.areAlarmsRestrictedByBatterySaver(TEST_CALLING_UID,
+ TEST_CALLING_PACKAGE)).thenReturn(true);
+ listener.updateAlarmsForUid(TEST_CALLING_UID);
+ assertEquals(mNowElapsedTest + INDEFINITE_DELAY, mTestTimer.getElapsed());
+ }
+
+ @Test
+ public void allowWhileIdleAlarmsInBatterySaver() throws Exception {
+ final ArgumentCaptor<AppStateTrackerImpl.Listener> listenerArgumentCaptor =
+ ArgumentCaptor.forClass(AppStateTrackerImpl.Listener.class);
+ verify(mAppStateTracker).addListener(listenerArgumentCaptor.capture());
+ final AppStateTrackerImpl.Listener listener = listenerArgumentCaptor.getValue();
+
+ final long longDelay = 23;
+ final long shortDelay = 7;
+ setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_LONG_TIME, longDelay);
+ setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_SHORT_TIME, shortDelay);
+
+ when(mAppStateTracker.areAlarmsRestrictedByBatterySaver(TEST_CALLING_UID,
+ TEST_CALLING_PACKAGE)).thenReturn(true);
+ setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 1,
+ getNewMockPendingIntent(), false);
+ setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 2,
+ getNewMockPendingIntent(), false);
+
+ assertEquals(mNowElapsedTest + 1, mTestTimer.getElapsed());
+
+ mNowElapsedTest += 1;
+ mTestTimer.expire();
+
+ assertEquals(mNowElapsedTest + longDelay, mTestTimer.getElapsed());
+ listener.onUidForeground(TEST_CALLING_UID, true);
+ // The next alarm should be deferred by shortDelay.
+ assertEquals(mNowElapsedTest + shortDelay, mTestTimer.getElapsed());
+
+ mNowElapsedTest = mTestTimer.getElapsed();
+ setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 1,
+ getNewMockPendingIntent(), false);
+
+ when(mAppStateTracker.isUidInForeground(TEST_CALLING_UID)).thenReturn(true);
+ mTestTimer.expire();
+ // The next alarm should be deferred by shortDelay again.
+ assertEquals(mNowElapsedTest + shortDelay, mTestTimer.getElapsed());
+
+ mNowElapsedTest = mTestTimer.getElapsed();
+ setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 1,
+ getNewMockPendingIntent(), true);
+ when(mAppStateTracker.isUidInForeground(TEST_CALLING_UID)).thenReturn(false);
+ mTestTimer.expire();
+ final long lastAwiDispatch = mNowElapsedTest;
+ // Unrestricted, so should not be changed.
+ assertEquals(mNowElapsedTest + 1, mTestTimer.getElapsed());
+
+ mNowElapsedTest = mTestTimer.getElapsed();
+ // AWI_unrestricted should not affect normal AWI bookkeeping.
+ // The next alarm is after the short delay but before the long delay.
+ setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, lastAwiDispatch + shortDelay + 1,
+ getNewMockPendingIntent(), false);
+ mTestTimer.expire();
+ assertEquals(lastAwiDispatch + longDelay, mTestTimer.getElapsed());
+
+ listener.onUidForeground(TEST_CALLING_UID, true);
+ assertEquals(lastAwiDispatch + shortDelay + 1, mTestTimer.getElapsed());
+ }
+
+ @Test
public void dispatchOrder() throws Exception {
doReturn(0).when(mService).fuzzForDuration(anyLong());
diff --git a/services/tests/servicestests/src/com/android/server/DropBoxTest.java b/services/tests/servicestests/src/com/android/server/DropBoxTest.java
index 56773e831902..a25f4920030c 100644
--- a/services/tests/servicestests/src/com/android/server/DropBoxTest.java
+++ b/services/tests/servicestests/src/com/android/server/DropBoxTest.java
@@ -31,15 +31,20 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.test.AndroidTestCase;
+import com.android.server.DropBoxManagerInternal.EntrySource;
import com.android.server.DropBoxManagerService.EntryFile;
+import libcore.io.Streams;
+
import java.io.BufferedReader;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
import java.util.Random;
import java.util.zip.GZIPOutputStream;
@@ -56,6 +61,8 @@ public class DropBoxTest extends AndroidTestCase {
protected void setUp() throws Exception {
super.setUp();
+ LocalServices.removeServiceForTest(DropBoxManagerInternal.class);
+
mContext = new ContextWrapper(super.getContext()) {
@Override
public void sendBroadcastAsUser(Intent intent,
@@ -212,6 +219,67 @@ public class DropBoxTest extends AndroidTestCase {
e3.close();
}
+ public void testAddEntry_Success() throws Exception {
+ File dir = getEmptyDir("testAddEntry");
+ long before = System.currentTimeMillis();
+
+ DropBoxManagerService service = new DropBoxManagerService(getContext(), dir,
+ Looper.getMainLooper());
+ DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
+
+ LocalServices.getService(DropBoxManagerInternal.class).addEntry("DropBoxTest",
+ new EntrySource() {
+ @Override
+ public void writeTo(FileDescriptor fd) throws IOException {
+ try (FileOutputStream out = new FileOutputStream(fd)) {
+ out.write("test".getBytes(StandardCharsets.UTF_8));
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ }
+
+ @Override
+ public long length() {
+ return 0;
+ }
+ }, DropBoxManager.IS_TEXT);
+
+ DropBoxManager.Entry entry = dropbox.getNextEntry("DropBoxTest", before);
+ assertEquals(DropBoxManager.IS_TEXT, entry.getFlags());
+ assertEquals("test", new String(Streams.readFully(entry.getInputStream())));
+ }
+
+ public void testAddEntry_Failure() throws Exception {
+ File dir = getEmptyDir("testAddEntry");
+ long before = System.currentTimeMillis();
+
+ DropBoxManagerService service = new DropBoxManagerService(getContext(), dir,
+ Looper.getMainLooper());
+ DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
+
+ LocalServices.getService(DropBoxManagerInternal.class).addEntry("DropBoxTest",
+ new EntrySource() {
+ @Override
+ public void writeTo(FileDescriptor fd) throws IOException {
+ throw new IOException();
+ }
+
+ @Override
+ public void close() throws IOException {
+ }
+
+ @Override
+ public long length() {
+ return 0;
+ }
+ }, DropBoxManager.IS_TEXT);
+
+ DropBoxManager.Entry entry = dropbox.getNextEntry("DropBoxTest", before);
+ assertNull(entry);
+ }
+
public void testAddEntriesInTheFuture() throws Exception {
File dir = getEmptyDir("testAddEntriesInTheFuture");
long before = System.currentTimeMillis();
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/InvalidationTrackerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/InvalidationTrackerTest.java
new file mode 100644
index 000000000000..340a1d9bdda0
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/InvalidationTrackerTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricManager.Authenticators;
+import android.hardware.biometrics.IBiometricAuthenticator;
+import android.hardware.biometrics.IInvalidationCallback;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.biometrics.BiometricService.InvalidationTracker;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+
+@Presubmit
+@SmallTest
+public class InvalidationTrackerTest {
+
+ @Test
+ public void testCallbackReceived_whenAllStrongSensorsInvalidated() throws Exception {
+ final IBiometricAuthenticator authenticator1 = mock(IBiometricAuthenticator.class);
+ final TestSensor sensor1 = new TestSensor(0 /* id */,
+ BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG,
+ authenticator1);
+
+ final IBiometricAuthenticator authenticator2 = mock(IBiometricAuthenticator.class);
+ final TestSensor sensor2 = new TestSensor(1 /* id */,
+ BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG,
+ authenticator2);
+
+ final IBiometricAuthenticator authenticator3 = mock(IBiometricAuthenticator.class);
+ final TestSensor sensor3 = new TestSensor(2 /* id */,
+ BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG,
+ authenticator3);
+
+ final IBiometricAuthenticator authenticator4 = mock(IBiometricAuthenticator.class);
+ final TestSensor sensor4 = new TestSensor(3 /* id */,
+ BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_WEAK,
+ authenticator4);
+
+ final ArrayList<BiometricSensor> sensors = new ArrayList<>();
+ sensors.add(sensor1);
+ sensors.add(sensor2);
+ sensors.add(sensor3);
+ sensors.add(sensor4);
+
+ final IInvalidationCallback callback = mock(IInvalidationCallback.class);
+ final InvalidationTracker tracker =
+ InvalidationTracker.start(sensors, 0 /* userId */, 0 /* fromSensorId */, callback);
+
+ // The sensor which the request originated from should not be requested to invalidate
+ // its authenticatorId.
+ verify(authenticator1, never()).invalidateAuthenticatorId(anyInt(), any());
+
+ // All other strong sensors should be requested to invalidate authenticatorId
+ verify(authenticator2).invalidateAuthenticatorId(eq(0) /* userId */, any());
+ verify(authenticator3).invalidateAuthenticatorId(eq(0) /* userId */, any());
+
+ // Weak sensors are not requested to invalidate authenticatorId
+ verify(authenticator4, never()).invalidateAuthenticatorId(anyInt(), any());
+
+ // Client is not notified until invalidation for all required sensors have completed
+ verify(callback, never()).onCompleted();
+ tracker.onInvalidated(1);
+ verify(callback, never()).onCompleted();
+ tracker.onInvalidated(2);
+ verify(callback).onCompleted();
+ }
+
+ private static class TestSensor extends BiometricSensor {
+
+ TestSensor(int id, int modality, int strength, IBiometricAuthenticator impl) {
+ super(id, modality, strength, impl);
+ }
+
+ @Override
+ boolean confirmationAlwaysRequired(int userId) {
+ return false;
+ }
+
+ @Override
+ boolean confirmationSupported() {
+ return false;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
index 4f4aa3f16f09..f00edcc85404 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
@@ -40,78 +40,83 @@ class CompatConfigBuilder {
}
CompatConfigBuilder addEnableAfterSdkChangeWithId(int sdk, long id) {
- mChanges.add(new CompatChange(id, "", sdk, -1, false, false, ""));
+ mChanges.add(new CompatChange(id, "", sdk, -1, false, false, "", false));
return this;
}
CompatConfigBuilder addEnableAfterSdkChangeWithIdAndName(int sdk, long id, String name) {
- mChanges.add(new CompatChange(id, name, sdk, -1, false, false, ""));
+ mChanges.add(new CompatChange(id, name, sdk, -1, false, false, "", false));
return this;
}
CompatConfigBuilder addEnableAfterSdkChangeWithIdDefaultDisabled(int sdk, long id) {
- mChanges.add(new CompatChange(id, "", sdk, -1, true, false, ""));
+ mChanges.add(new CompatChange(id, "", sdk, -1, true, false, "", false));
return this;
}
CompatConfigBuilder addEnableAfterSdkChangeWithIdAndDescription(int sdk, long id,
String description) {
- mChanges.add(new CompatChange(id, "", sdk, -1, false, false, description));
+ mChanges.add(new CompatChange(id, "", sdk, -1, false, false, description, false));
return this;
}
CompatConfigBuilder addEnableSinceSdkChangeWithId(int sdk, long id) {
- mChanges.add(new CompatChange(id, "", -1, sdk, false, false, ""));
+ mChanges.add(new CompatChange(id, "", -1, sdk, false, false, "", false));
return this;
}
CompatConfigBuilder addEnableSinceSdkChangeWithIdAndName(int sdk, long id, String name) {
- mChanges.add(new CompatChange(id, name, -1, sdk, false, false, ""));
+ mChanges.add(new CompatChange(id, name, -1, sdk, false, false, "", false));
return this;
}
CompatConfigBuilder addEnableSinceSdkChangeWithIdDefaultDisabled(int sdk, long id) {
- mChanges.add(new CompatChange(id, "", -1, sdk, true, false, ""));
+ mChanges.add(new CompatChange(id, "", -1, sdk, true, false, "", false));
return this;
}
CompatConfigBuilder addEnableSinceSdkChangeWithIdAndDescription(int sdk, long id,
String description) {
- mChanges.add(new CompatChange(id, "", -1, sdk, false, false, description));
+ mChanges.add(new CompatChange(id, "", -1, sdk, false, false, description, false));
return this;
}
CompatConfigBuilder addEnabledChangeWithId(long id) {
- mChanges.add(new CompatChange(id, "", -1, -1, false, false, ""));
+ mChanges.add(new CompatChange(id, "", -1, -1, false, false, "", false));
return this;
}
CompatConfigBuilder addEnabledChangeWithIdAndName(long id, String name) {
- mChanges.add(new CompatChange(id, name, -1, -1, false, false, ""));
+ mChanges.add(new CompatChange(id, name, -1, -1, false, false, "", false));
return this;
}
CompatConfigBuilder addEnabledChangeWithIdAndDescription(long id, String description) {
- mChanges.add(new CompatChange(id, "", -1, -1, false, false, description));
+ mChanges.add(new CompatChange(id, "", -1, -1, false, false, description, false));
return this;
}
CompatConfigBuilder addDisabledChangeWithId(long id) {
- mChanges.add(new CompatChange(id, "", -1, -1, true, false, ""));
+ mChanges.add(new CompatChange(id, "", -1, -1, true, false, "", false));
return this;
}
CompatConfigBuilder addDisabledChangeWithIdAndName(long id, String name) {
- mChanges.add(new CompatChange(id, name, -1, -1, true, false, ""));
+ mChanges.add(new CompatChange(id, name, -1, -1, true, false, "", false));
return this;
}
CompatConfigBuilder addDisabledChangeWithIdAndDescription(long id, String description) {
- mChanges.add(new CompatChange(id, "", -1, -1, true, false, description));
+ mChanges.add(new CompatChange(id, "", -1, -1, true, false, description, false));
return this;
}
CompatConfigBuilder addLoggingOnlyChangeWithId(long id) {
- mChanges.add(new CompatChange(id, "", -1, -1, false, true, ""));
+ mChanges.add(new CompatChange(id, "", -1, -1, false, true, "", false));
+ return this;
+ }
+
+ CompatConfigBuilder addOverridableChangeWithId(long id) {
+ mChanges.add(new CompatChange(id, "", -1, -1, false, true, "", true));
return this;
}
diff --git a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
index a70c51045340..a1b2dc8bd82d 100644
--- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
@@ -97,17 +97,22 @@ public class PlatformCompatTest {
.addEnableAfterSdkChangeWithId(Build.VERSION_CODES.Q, 5L)
.addEnableAfterSdkChangeWithId(Build.VERSION_CODES.R, 6L)
.addLoggingOnlyChangeWithId(7L)
+ .addOverridableChangeWithId(8L)
.build();
mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
assertThat(mPlatformCompat.listAllChanges()).asList().containsExactly(
- new CompatibilityChangeInfo(1L, "", -1, -1, false, false, ""),
- new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, ""),
+ new CompatibilityChangeInfo(1L, "", -1, -1, false, false, "", false),
+ new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, "", false),
new CompatibilityChangeInfo(3L, "", Build.VERSION_CODES.O, -1, false, false,
- "desc"),
- new CompatibilityChangeInfo(4L, "", Build.VERSION_CODES.P, -1, false, false, ""),
- new CompatibilityChangeInfo(5L, "", Build.VERSION_CODES.Q, -1, false, false, ""),
- new CompatibilityChangeInfo(6L, "", Build.VERSION_CODES.R, -1, false, false, ""),
- new CompatibilityChangeInfo(7L, "", -1, -1, false, true, ""));
+ "desc", false),
+ new CompatibilityChangeInfo(
+ 4L, "", Build.VERSION_CODES.P, -1, false, false, "", false),
+ new CompatibilityChangeInfo(
+ 5L, "", Build.VERSION_CODES.Q, -1, false, false, "", false),
+ new CompatibilityChangeInfo(
+ 6L, "", Build.VERSION_CODES.R, -1, false, false, "", false),
+ new CompatibilityChangeInfo(7L, "", -1, -1, false, true, "", false),
+ new CompatibilityChangeInfo(8L, "", -1, -1, false, true, "", true));
}
@Test
@@ -123,12 +128,12 @@ public class PlatformCompatTest {
.build();
mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
assertThat(mPlatformCompat.listUIChanges()).asList().containsExactly(
- new CompatibilityChangeInfo(1L, "", -1, -1, false, false, ""),
- new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, ""),
- new CompatibilityChangeInfo(5L, "", /*enableAfter*/ -1,
- /*enableSince*/ Build.VERSION_CODES.Q, false, false, ""),
- new CompatibilityChangeInfo(6L, "", /*enableAfter*/ -1,
- /*enableSince*/ Build.VERSION_CODES.R, false, false, ""));
+ new CompatibilityChangeInfo(1L, "", -1, -1, false, false, "", false),
+ new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, "", false),
+ new CompatibilityChangeInfo(
+ 5L, "", Build.VERSION_CODES.P, -1, false, false, "", false),
+ new CompatibilityChangeInfo(
+ 6L, "", Build.VERSION_CODES.Q, -1, false, false, "", false));
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 39fa20e4153f..a455ba90ccfe 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -6918,6 +6918,35 @@ public class DevicePolicyManagerTest extends DpmTestBase {
DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
}
+ @Test
+ public void testSetRequiredPasswordComplexityFailsWithQualityOnParent() throws Exception {
+ final int managedProfileUserId = CALLER_USER_HANDLE;
+ final int managedProfileAdminUid =
+ UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID);
+ mContext.binder.callingUid = managedProfileAdminUid;
+ addManagedProfile(admin1, managedProfileAdminUid, admin1, VERSION_CODES.R);
+
+ parentDpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_COMPLEX);
+
+ assertThrows(IllegalStateException.class,
+ () -> dpm.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_HIGH));
+ }
+
+ @Test
+ public void testSetQualityOnParentFailsWithComplexityOnProfile() throws Exception {
+ final int managedProfileUserId = CALLER_USER_HANDLE;
+ final int managedProfileAdminUid =
+ UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID);
+ mContext.binder.callingUid = managedProfileAdminUid;
+ addManagedProfile(admin1, managedProfileAdminUid, admin1, VERSION_CODES.R);
+
+ dpm.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_HIGH);
+
+ assertThrows(IllegalStateException.class,
+ () -> parentDpm.setPasswordQuality(admin1,
+ DevicePolicyManager.PASSWORD_QUALITY_COMPLEX));
+ }
+
private void setUserUnlocked(int userHandle, boolean unlocked) {
when(getServices().userManager.isUserUnlocked(eq(userHandle))).thenReturn(unlocked);
}
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index 603608b23172..26c304f763d6 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -34,6 +34,7 @@ import static org.mockito.Mockito.any;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static org.mockito.internal.verification.VerificationModeFactory.times;
import android.annotation.NonNull;
import android.content.ContentResolver;
@@ -505,7 +506,7 @@ public class DisplayModeDirectorTest {
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
setPeakRefreshRate(90);
director.getSettingsObserver().setDefaultRefreshRate(90);
- director.getBrightnessObserver().setDefaultDisplayState(true);
+ director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
final FakeDeviceConfig config = mInjector.getDeviceConfig();
config.setRefreshRateInLowZone(90);
@@ -548,7 +549,7 @@ public class DisplayModeDirectorTest {
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
setPeakRefreshRate(90 /*fps*/);
director.getSettingsObserver().setDefaultRefreshRate(90);
- director.getBrightnessObserver().setDefaultDisplayState(true);
+ director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
final FakeDeviceConfig config = mInjector.getDeviceConfig();
config.setRefreshRateInHighZone(60);
@@ -585,6 +586,43 @@ public class DisplayModeDirectorTest {
assertVoteForRefreshRateLocked(vote, 60 /*fps*/);
}
+ @Test
+ public void testSensorRegistration() {
+ DisplayModeDirector director =
+ createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
+ setPeakRefreshRate(90 /*fps*/);
+ director.getSettingsObserver().setDefaultRefreshRate(90);
+ director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
+
+ Sensor lightSensor = createLightSensor();
+ SensorManager sensorManager = createMockSensorManager(lightSensor);
+
+ director.start(sensorManager);
+ ArgumentCaptor<SensorEventListener> listenerCaptor =
+ ArgumentCaptor.forClass(SensorEventListener.class);
+ Mockito.verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
+ .registerListener(
+ listenerCaptor.capture(),
+ eq(lightSensor),
+ anyInt(),
+ any(Handler.class));
+
+ // Dispaly state changed from On to Doze
+ director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_DOZE);
+ Mockito.verify(sensorManager)
+ .unregisterListener(listenerCaptor.capture());
+
+ // Dispaly state changed from Doze to On
+ director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
+ Mockito.verify(sensorManager, times(2))
+ .registerListener(
+ listenerCaptor.capture(),
+ eq(lightSensor),
+ anyInt(),
+ any(Handler.class));
+
+ }
+
private void assertVoteForRefreshRateLocked(Vote vote, float refreshRate) {
assertThat(vote).isNotNull();
final DisplayModeDirector.RefreshRateRange expectedRange =
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
index d54a40e58af1..c010e1995446 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
@@ -29,6 +29,10 @@ import android.util.SparseArray;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.util.HexDump;
+import com.android.server.pm.PerPackageReadTimeouts.Timeouts;
+import com.android.server.pm.PerPackageReadTimeouts.VersionCodes;
+
import com.google.android.collect.Lists;
import org.junit.After;
@@ -45,6 +49,7 @@ import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;
+// atest PackageManagerServiceTest
// runtest -c com.android.server.pm.PackageManagerServiceTest frameworks-services
// bit FrameworksServicesTests:com.android.server.pm.PackageManagerServiceTest
@RunWith(AndroidJUnit4.class)
@@ -182,6 +187,219 @@ public class PackageManagerServiceTest {
}
}
+ @Test
+ public void testTimeouts() {
+ Timeouts defaults = Timeouts.parse("3600000001:3600000002:3600000003");
+ Assert.assertEquals(3600000001L, defaults.minTimeUs);
+ Assert.assertEquals(3600000002L, defaults.minPendingTimeUs);
+ Assert.assertEquals(3600000003L, defaults.maxPendingTimeUs);
+
+ Timeouts empty = Timeouts.parse("");
+ Assert.assertEquals(3600000000L, empty.minTimeUs);
+ Assert.assertEquals(3600000000L, empty.minPendingTimeUs);
+ Assert.assertEquals(3600000000L, empty.maxPendingTimeUs);
+
+ Timeouts partial0 = Timeouts.parse("10000::");
+ Assert.assertEquals(10000L, partial0.minTimeUs);
+ Assert.assertEquals(3600000000L, partial0.minPendingTimeUs);
+ Assert.assertEquals(3600000000L, partial0.maxPendingTimeUs);
+
+ Timeouts partial1 = Timeouts.parse("10000:10001:");
+ Assert.assertEquals(10000L, partial1.minTimeUs);
+ Assert.assertEquals(10001L, partial1.minPendingTimeUs);
+ Assert.assertEquals(3600000000L, partial1.maxPendingTimeUs);
+
+ Timeouts fullDefault = Timeouts.parse("3600000000:3600000000:3600000000");
+ Assert.assertEquals(3600000000L, fullDefault.minTimeUs);
+ Assert.assertEquals(3600000000L, fullDefault.minPendingTimeUs);
+ Assert.assertEquals(3600000000L, fullDefault.maxPendingTimeUs);
+
+ Timeouts full = Timeouts.parse("10000:10001:10002");
+ Assert.assertEquals(10000L, full.minTimeUs);
+ Assert.assertEquals(10001L, full.minPendingTimeUs);
+ Assert.assertEquals(10002L, full.maxPendingTimeUs);
+
+ Timeouts invalid0 = Timeouts.parse(":10000");
+ Assert.assertEquals(3600000000L, invalid0.minTimeUs);
+ Assert.assertEquals(3600000000L, invalid0.minPendingTimeUs);
+ Assert.assertEquals(3600000000L, invalid0.maxPendingTimeUs);
+
+ Timeouts invalid1 = Timeouts.parse(":10000::");
+ Assert.assertEquals(3600000000L, invalid1.minTimeUs);
+ Assert.assertEquals(3600000000L, invalid1.minPendingTimeUs);
+ Assert.assertEquals(3600000000L, invalid1.maxPendingTimeUs);
+
+ Timeouts invalid2 = Timeouts.parse("10000:10001:abcd");
+ Assert.assertEquals(10000L, invalid2.minTimeUs);
+ Assert.assertEquals(10001L, invalid2.minPendingTimeUs);
+ Assert.assertEquals(3600000000L, invalid2.maxPendingTimeUs);
+
+ Timeouts invalid3 = Timeouts.parse(":10000:");
+ Assert.assertEquals(3600000000L, invalid3.minTimeUs);
+ Assert.assertEquals(3600000000L, invalid3.minPendingTimeUs);
+ Assert.assertEquals(3600000000L, invalid3.maxPendingTimeUs);
+
+ Timeouts invalid4 = Timeouts.parse("abcd:10001:10002");
+ Assert.assertEquals(3600000000L, invalid4.minTimeUs);
+ Assert.assertEquals(3600000000L, invalid4.minPendingTimeUs);
+ Assert.assertEquals(3600000000L, invalid4.maxPendingTimeUs);
+
+ Timeouts invalid5 = Timeouts.parse("::1000000000000000000000000");
+ Assert.assertEquals(3600000000L, invalid5.minTimeUs);
+ Assert.assertEquals(3600000000L, invalid5.minPendingTimeUs);
+ Assert.assertEquals(3600000000L, invalid5.maxPendingTimeUs);
+
+ Timeouts invalid6 = Timeouts.parse("-10000:10001:10002");
+ Assert.assertEquals(3600000000L, invalid6.minTimeUs);
+ Assert.assertEquals(3600000000L, invalid6.minPendingTimeUs);
+ Assert.assertEquals(3600000000L, invalid6.maxPendingTimeUs);
+ }
+
+ @Test
+ public void testVersionCodes() {
+ final VersionCodes defaults = VersionCodes.parse("");
+ Assert.assertEquals(Long.MIN_VALUE, defaults.minVersionCode);
+ Assert.assertEquals(Long.MAX_VALUE, defaults.maxVersionCode);
+
+ VersionCodes single = VersionCodes.parse("191000070");
+ Assert.assertEquals(191000070, single.minVersionCode);
+ Assert.assertEquals(191000070, single.maxVersionCode);
+
+ VersionCodes single2 = VersionCodes.parse("191000070-191000070");
+ Assert.assertEquals(191000070, single2.minVersionCode);
+ Assert.assertEquals(191000070, single2.maxVersionCode);
+
+ VersionCodes upto = VersionCodes.parse("-191000070");
+ Assert.assertEquals(Long.MIN_VALUE, upto.minVersionCode);
+ Assert.assertEquals(191000070, upto.maxVersionCode);
+
+ VersionCodes andabove = VersionCodes.parse("191000070-");
+ Assert.assertEquals(191000070, andabove.minVersionCode);
+ Assert.assertEquals(Long.MAX_VALUE, andabove.maxVersionCode);
+
+ VersionCodes range = VersionCodes.parse("191000070-201000070");
+ Assert.assertEquals(191000070, range.minVersionCode);
+ Assert.assertEquals(201000070, range.maxVersionCode);
+
+ VersionCodes invalid0 = VersionCodes.parse("201000070-191000070");
+ Assert.assertEquals(Long.MIN_VALUE, invalid0.minVersionCode);
+ Assert.assertEquals(Long.MAX_VALUE, invalid0.maxVersionCode);
+
+ VersionCodes invalid1 = VersionCodes.parse("abcd-191000070");
+ Assert.assertEquals(Long.MIN_VALUE, invalid1.minVersionCode);
+ Assert.assertEquals(191000070, invalid1.maxVersionCode);
+
+ VersionCodes invalid2 = VersionCodes.parse("abcd");
+ Assert.assertEquals(Long.MIN_VALUE, invalid2.minVersionCode);
+ Assert.assertEquals(Long.MAX_VALUE, invalid2.maxVersionCode);
+
+ VersionCodes invalid3 = VersionCodes.parse("191000070-abcd");
+ Assert.assertEquals(191000070, invalid3.minVersionCode);
+ Assert.assertEquals(Long.MAX_VALUE, invalid3.maxVersionCode);
+ }
+
+ @Test
+ public void testPerPackageReadTimeouts() {
+ final String sha256 = "336faefc91bb2dddf9b21829106fbc607b862132fecd273e1b6b3ea55f09d4e1";
+ final VersionCodes defVCs = VersionCodes.parse("");
+ final Timeouts defTs = Timeouts.parse("3600000001:3600000002:3600000003");
+
+ PerPackageReadTimeouts empty = PerPackageReadTimeouts.parse("", defVCs, defTs);
+ Assert.assertNull(empty);
+
+ PerPackageReadTimeouts packageOnly = PerPackageReadTimeouts.parse("package.com", defVCs,
+ defTs);
+ Assert.assertEquals("package.com", packageOnly.packageName);
+ Assert.assertEquals(null, packageOnly.sha256certificate);
+ Assert.assertEquals(Long.MIN_VALUE, packageOnly.versionCodes.minVersionCode);
+ Assert.assertEquals(Long.MAX_VALUE, packageOnly.versionCodes.maxVersionCode);
+ Assert.assertEquals(3600000001L, packageOnly.timeouts.minTimeUs);
+ Assert.assertEquals(3600000002L, packageOnly.timeouts.minPendingTimeUs);
+ Assert.assertEquals(3600000003L, packageOnly.timeouts.maxPendingTimeUs);
+
+ PerPackageReadTimeouts packageHash = PerPackageReadTimeouts.parse(
+ "package.com:" + sha256, defVCs, defTs);
+ Assert.assertEquals("package.com", packageHash.packageName);
+ Assert.assertEquals(sha256, bytesToHexString(packageHash.sha256certificate));
+ Assert.assertEquals(Long.MIN_VALUE, packageHash.versionCodes.minVersionCode);
+ Assert.assertEquals(Long.MAX_VALUE, packageHash.versionCodes.maxVersionCode);
+ Assert.assertEquals(3600000001L, packageHash.timeouts.minTimeUs);
+ Assert.assertEquals(3600000002L, packageHash.timeouts.minPendingTimeUs);
+ Assert.assertEquals(3600000003L, packageHash.timeouts.maxPendingTimeUs);
+
+ PerPackageReadTimeouts packageVersionCode = PerPackageReadTimeouts.parse(
+ "package.com::191000070", defVCs, defTs);
+ Assert.assertEquals("package.com", packageVersionCode.packageName);
+ Assert.assertEquals(null, packageVersionCode.sha256certificate);
+ Assert.assertEquals(191000070, packageVersionCode.versionCodes.minVersionCode);
+ Assert.assertEquals(191000070, packageVersionCode.versionCodes.maxVersionCode);
+ Assert.assertEquals(3600000001L, packageVersionCode.timeouts.minTimeUs);
+ Assert.assertEquals(3600000002L, packageVersionCode.timeouts.minPendingTimeUs);
+ Assert.assertEquals(3600000003L, packageVersionCode.timeouts.maxPendingTimeUs);
+
+ PerPackageReadTimeouts full = PerPackageReadTimeouts.parse(
+ "package.com:" + sha256 + ":191000070-201000070:10001:10002:10003", defVCs, defTs);
+ Assert.assertEquals("package.com", full.packageName);
+ Assert.assertEquals(sha256, bytesToHexString(full.sha256certificate));
+ Assert.assertEquals(191000070, full.versionCodes.minVersionCode);
+ Assert.assertEquals(201000070, full.versionCodes.maxVersionCode);
+ Assert.assertEquals(10001L, full.timeouts.minTimeUs);
+ Assert.assertEquals(10002L, full.timeouts.minPendingTimeUs);
+ Assert.assertEquals(10003L, full.timeouts.maxPendingTimeUs);
+ }
+
+ @Test
+ public void testGetPerPackageReadTimeouts() {
+ Assert.assertEquals(0, getPerPackageReadTimeouts(null).length);
+ Assert.assertEquals(0, getPerPackageReadTimeouts("").length);
+ Assert.assertEquals(0, getPerPackageReadTimeouts(",,,,").length);
+
+ final String sha256 = "0fae93f1a7925b4c68bbea80ad3eaa41acfc9bc6f10bf1054f5d93a2bd556093";
+
+ PerPackageReadTimeouts[] singlePackage = getPerPackageReadTimeouts(
+ "package.com:" + sha256 + ":191000070-201000070:10001:10002:10003");
+ Assert.assertEquals(1, singlePackage.length);
+ Assert.assertEquals("package.com", singlePackage[0].packageName);
+ Assert.assertEquals(sha256, bytesToHexString(singlePackage[0].sha256certificate));
+ Assert.assertEquals(191000070, singlePackage[0].versionCodes.minVersionCode);
+ Assert.assertEquals(201000070, singlePackage[0].versionCodes.maxVersionCode);
+ Assert.assertEquals(10001L, singlePackage[0].timeouts.minTimeUs);
+ Assert.assertEquals(10002L, singlePackage[0].timeouts.minPendingTimeUs);
+ Assert.assertEquals(10003L, singlePackage[0].timeouts.maxPendingTimeUs);
+
+ PerPackageReadTimeouts[] multiPackage = getPerPackageReadTimeouts("package.com:" + sha256
+ + ":191000070-201000070:10001:10002:10003,package1.com::123456");
+ Assert.assertEquals(2, multiPackage.length);
+ Assert.assertEquals("package.com", multiPackage[0].packageName);
+ Assert.assertEquals(sha256, bytesToHexString(multiPackage[0].sha256certificate));
+ Assert.assertEquals(191000070, multiPackage[0].versionCodes.minVersionCode);
+ Assert.assertEquals(201000070, multiPackage[0].versionCodes.maxVersionCode);
+ Assert.assertEquals(10001L, multiPackage[0].timeouts.minTimeUs);
+ Assert.assertEquals(10002L, multiPackage[0].timeouts.minPendingTimeUs);
+ Assert.assertEquals(10003L, multiPackage[0].timeouts.maxPendingTimeUs);
+ Assert.assertEquals("package1.com", multiPackage[1].packageName);
+ Assert.assertEquals(null, multiPackage[1].sha256certificate);
+ Assert.assertEquals(123456, multiPackage[1].versionCodes.minVersionCode);
+ Assert.assertEquals(123456, multiPackage[1].versionCodes.maxVersionCode);
+ Assert.assertEquals(3600000001L, multiPackage[1].timeouts.minTimeUs);
+ Assert.assertEquals(3600000002L, multiPackage[1].timeouts.minPendingTimeUs);
+ Assert.assertEquals(3600000003L, multiPackage[1].timeouts.maxPendingTimeUs);
+ }
+
+ private static PerPackageReadTimeouts[] getPerPackageReadTimeouts(String knownDigestersList) {
+ final String defaultTimeouts = "3600000001:3600000002:3600000003";
+ List<PerPackageReadTimeouts> result = PerPackageReadTimeouts.parseDigestersList(
+ defaultTimeouts, knownDigestersList);
+ if (result == null) {
+ return null;
+ }
+ return result.toArray(new PerPackageReadTimeouts[result.size()]);
+ }
+
+ private static String bytesToHexString(byte[] bytes) {
+ return HexDump.toHexString(bytes, 0, bytes.length, /*upperCase=*/ false);
+ }
+
private List<Integer> getKnownPackageIdsList() throws IllegalAccessException {
final ArrayList<Integer> knownPackageIds = new ArrayList<>();
final Field[] allFields = PackageManagerInternal.class.getDeclaredFields();
diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
index 62be98c15a2e..385837009b2f 100644
--- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
@@ -29,6 +29,7 @@ import android.media.tv.tuner.frontend.FrontendSettings;
import android.media.tv.tunerresourcemanager.CasSessionRequest;
import android.media.tv.tunerresourcemanager.IResourcesReclaimListener;
import android.media.tv.tunerresourcemanager.ResourceClientProfile;
+import android.media.tv.tunerresourcemanager.TunerCiCamRequest;
import android.media.tv.tunerresourcemanager.TunerDemuxRequest;
import android.media.tv.tunerresourcemanager.TunerDescramblerRequest;
import android.media.tv.tunerresourcemanager.TunerFrontendInfo;
@@ -86,9 +87,9 @@ public class TunerResourceManagerServiceTest {
return (actual == null) && (expected == null);
}
- return actual.getHandle() == expected.getHandle()
- && actual.getType() == expected.getFrontendType()
- && actual.getExclusiveGroupId() == expected.getExclusiveGroupId();
+ return actual.getHandle() == expected.handle
+ && actual.getType() == expected.frontendType
+ && actual.getExclusiveGroupId() == expected.exclusiveGroupId;
}, "is correctly configured from ");
@Before
@@ -99,7 +100,7 @@ public class TunerResourceManagerServiceTest {
when(mContextSpy.getSystemService(Context.TV_INPUT_SERVICE)).thenReturn(tvInputManager);
mTunerResourceManagerService = new TunerResourceManagerService(mContextSpy) {
@Override
- protected boolean isForeground(int pid) {
+ protected boolean checkIsForeground(int pid) {
return mIsForeground;
}
};
@@ -111,19 +112,19 @@ public class TunerResourceManagerServiceTest {
// Init frontend resources.
TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
infos[0] =
- new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/);
+ tunerFrontendInfo(0 /*handle*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/);
infos[1] =
- new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+ tunerFrontendInfo(1 /*handle*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
mTunerResourceManagerService.setFrontendInfoListInternal(infos);
Map<Integer, FrontendResource> resources =
mTunerResourceManagerService.getFrontendResources();
for (int id = 0; id < infos.length; id++) {
- assertThat(resources.get(infos[id].getHandle())
+ assertThat(resources.get(infos[id].handle)
.getExclusiveGroupMemberFeHandles().size()).isEqualTo(0);
}
for (int id = 0; id < infos.length; id++) {
- assertThat(resources.get(infos[id].getHandle())
+ assertThat(resources.get(infos[id].handle)
.getExclusiveGroupMemberFeHandles().size()).isEqualTo(0);
}
assertThat(resources.values()).comparingElementsUsing(FR_TFI_COMPARE)
@@ -135,13 +136,13 @@ public class TunerResourceManagerServiceTest {
// Init frontend resources.
TunerFrontendInfo[] infos = new TunerFrontendInfo[4];
infos[0] =
- new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/);
+ tunerFrontendInfo(0 /*handle*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/);
infos[1] =
- new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+ tunerFrontendInfo(1 /*handle*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
infos[2] =
- new TunerFrontendInfo(2 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
+ tunerFrontendInfo(2 /*handle*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
infos[3] =
- new TunerFrontendInfo(3 /*id*/, FrontendSettings.TYPE_ATSC, 1 /*exclusiveGroupId*/);
+ tunerFrontendInfo(3 /*handle*/, FrontendSettings.TYPE_ATSC, 1 /*exclusiveGroupId*/);
mTunerResourceManagerService.setFrontendInfoListInternal(infos);
Map<Integer, FrontendResource> resources =
@@ -160,9 +161,9 @@ public class TunerResourceManagerServiceTest {
// Init frontend resources.
TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
infos[0] =
- new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+ tunerFrontendInfo(0 /*handle*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
infos[1] =
- new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
+ tunerFrontendInfo(1 /*handle*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
mTunerResourceManagerService.setFrontendInfoListInternal(infos);
Map<Integer, FrontendResource> resources0 =
@@ -180,22 +181,22 @@ public class TunerResourceManagerServiceTest {
// Init frontend resources.
TunerFrontendInfo[] infos0 = new TunerFrontendInfo[3];
infos0[0] =
- new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/);
+ tunerFrontendInfo(0 /*handle*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/);
infos0[1] =
- new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+ tunerFrontendInfo(1 /*handle*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
infos0[2] =
- new TunerFrontendInfo(2 /*id*/, FrontendSettings.TYPE_DVBS, 2 /*exclusiveGroupId*/);
+ tunerFrontendInfo(2 /*handle*/, FrontendSettings.TYPE_DVBS, 2 /*exclusiveGroupId*/);
mTunerResourceManagerService.setFrontendInfoListInternal(infos0);
TunerFrontendInfo[] infos1 = new TunerFrontendInfo[1];
infos1[0] =
- new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+ tunerFrontendInfo(1 /*handle*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
mTunerResourceManagerService.setFrontendInfoListInternal(infos1);
Map<Integer, FrontendResource> resources =
mTunerResourceManagerService.getFrontendResources();
for (int id = 0; id < infos1.length; id++) {
- assertThat(resources.get(infos1[id].getHandle())
+ assertThat(resources.get(infos1[id].handle)
.getExclusiveGroupMemberFeHandles().size()).isEqualTo(0);
}
assertThat(resources.values()).comparingElementsUsing(FR_TFI_COMPARE)
@@ -207,22 +208,22 @@ public class TunerResourceManagerServiceTest {
// Init frontend resources.
TunerFrontendInfo[] infos0 = new TunerFrontendInfo[3];
infos0[0] =
- new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/);
+ tunerFrontendInfo(0 /*handle*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/);
infos0[1] =
- new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+ tunerFrontendInfo(1 /*handle*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
infos0[2] =
- new TunerFrontendInfo(2 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
+ tunerFrontendInfo(2 /*handle*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
mTunerResourceManagerService.setFrontendInfoListInternal(infos0);
TunerFrontendInfo[] infos1 = new TunerFrontendInfo[1];
infos1[0] =
- new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+ tunerFrontendInfo(1 /*handle*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
mTunerResourceManagerService.setFrontendInfoListInternal(infos1);
Map<Integer, FrontendResource> resources =
mTunerResourceManagerService.getFrontendResources();
for (int id = 0; id < infos1.length; id++) {
- assertThat(resources.get(infos1[id].getHandle())
+ assertThat(resources.get(infos1[id].handle)
.getExclusiveGroupMemberFeHandles().size()).isEqualTo(0);
}
assertThat(resources.values()).comparingElementsUsing(FR_TFI_COMPARE)
@@ -231,8 +232,12 @@ public class TunerResourceManagerServiceTest {
@Test
public void requestFrontendTest_ClientNotRegistered() {
+ TunerFrontendInfo[] infos0 = new TunerFrontendInfo[1];
+ infos0[0] =
+ tunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/);
+ mTunerResourceManagerService.setFrontendInfoListInternal(infos0);
TunerFrontendRequest request =
- new TunerFrontendRequest(0 /*clientId*/, FrontendSettings.TYPE_DVBT);
+ tunerFrontendRequest(0 /*clientId*/, FrontendSettings.TYPE_DVBT);
int[] frontendHandle = new int[1];
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isFalse();
@@ -241,7 +246,7 @@ public class TunerResourceManagerServiceTest {
@Test
public void requestFrontendTest_NoFrontendWithGiveTypeAvailable() {
- ResourceClientProfile profile = new ResourceClientProfile("0" /*sessionId*/,
+ ResourceClientProfile profile = resourceClientProfile("0" /*sessionId*/,
TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
int[] clientId = new int[1];
mTunerResourceManagerService.registerClientProfileInternal(
@@ -251,11 +256,11 @@ public class TunerResourceManagerServiceTest {
// Init frontend resources.
TunerFrontendInfo[] infos = new TunerFrontendInfo[1];
infos[0] =
- new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBS, 0 /*exclusiveGroupId*/);
+ tunerFrontendInfo(0 /*handle*/, FrontendSettings.TYPE_DVBS, 0 /*exclusiveGroupId*/);
mTunerResourceManagerService.setFrontendInfoListInternal(infos);
TunerFrontendRequest request =
- new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ tunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
int[] frontendHandle = new int[1];
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isFalse();
@@ -264,7 +269,7 @@ public class TunerResourceManagerServiceTest {
@Test
public void requestFrontendTest_FrontendWithNoExclusiveGroupAvailable() {
- ResourceClientProfile profile = new ResourceClientProfile("0" /*sessionId*/,
+ ResourceClientProfile profile = resourceClientProfile("0" /*sessionId*/,
TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
int[] clientId = new int[1];
mTunerResourceManagerService.registerClientProfileInternal(
@@ -273,22 +278,22 @@ public class TunerResourceManagerServiceTest {
// Init frontend resources.
TunerFrontendInfo[] infos = new TunerFrontendInfo[3];
- infos[0] = new TunerFrontendInfo(
+ infos[0] = tunerFrontendInfo(
0 /*handle*/,
FrontendSettings.TYPE_DVBT,
0 /*exclusiveGroupId*/);
- infos[1] = new TunerFrontendInfo(
+ infos[1] = tunerFrontendInfo(
1 /*handle*/,
FrontendSettings.TYPE_DVBT,
1 /*exclusiveGroupId*/);
- infos[2] = new TunerFrontendInfo(
+ infos[2] = tunerFrontendInfo(
2 /*handle*/,
FrontendSettings.TYPE_DVBS,
1 /*exclusiveGroupId*/);
mTunerResourceManagerService.setFrontendInfoListInternal(infos);
TunerFrontendRequest request =
- new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ tunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
int[] frontendHandle = new int[1];
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isTrue();
@@ -297,9 +302,9 @@ public class TunerResourceManagerServiceTest {
@Test
public void requestFrontendTest_FrontendWithExclusiveGroupAvailable() {
- ResourceClientProfile profile0 = new ResourceClientProfile("0" /*sessionId*/,
+ ResourceClientProfile profile0 = resourceClientProfile("0" /*sessionId*/,
TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- ResourceClientProfile profile1 = new ResourceClientProfile("1" /*sessionId*/,
+ ResourceClientProfile profile1 = resourceClientProfile("1" /*sessionId*/,
TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
int[] clientId0 = new int[1];
int[] clientId1 = new int[1];
@@ -312,15 +317,15 @@ public class TunerResourceManagerServiceTest {
// Init frontend resources.
TunerFrontendInfo[] infos = new TunerFrontendInfo[3];
- infos[0] = new TunerFrontendInfo(
+ infos[0] = tunerFrontendInfo(
0 /*handle*/,
FrontendSettings.TYPE_DVBT,
0 /*exclusiveGroupId*/);
- infos[1] = new TunerFrontendInfo(
+ infos[1] = tunerFrontendInfo(
1 /*handle*/,
FrontendSettings.TYPE_DVBT,
1 /*exclusiveGroupId*/);
- infos[2] = new TunerFrontendInfo(
+ infos[2] = tunerFrontendInfo(
2 /*handle*/,
FrontendSettings.TYPE_DVBS,
1 /*exclusiveGroupId*/);
@@ -328,19 +333,19 @@ public class TunerResourceManagerServiceTest {
int[] frontendHandle = new int[1];
TunerFrontendRequest request =
- new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ tunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isTrue();
- assertThat(frontendHandle[0]).isEqualTo(infos[0].getHandle());
+ assertThat(frontendHandle[0]).isEqualTo(infos[0].handle);
request =
- new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ tunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isTrue();
- assertThat(frontendHandle[0]).isEqualTo(infos[1].getHandle());
- assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getHandle()).isInUse())
+ assertThat(frontendHandle[0]).isEqualTo(infos[1].handle);
+ assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle).isInUse())
.isTrue();
- assertThat(mTunerResourceManagerService.getFrontendResource(infos[2].getHandle()).isInUse())
+ assertThat(mTunerResourceManagerService.getFrontendResource(infos[2].handle).isInUse())
.isTrue();
}
@@ -348,9 +353,9 @@ public class TunerResourceManagerServiceTest {
public void requestFrontendTest_NoFrontendAvailable_RequestWithLowerPriority() {
// Register clients
ResourceClientProfile[] profiles = new ResourceClientProfile[2];
- profiles[0] = new ResourceClientProfile("0" /*sessionId*/,
+ profiles[0] = resourceClientProfile("0" /*sessionId*/,
TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- profiles[1] = new ResourceClientProfile("1" /*sessionId*/,
+ profiles[1] = resourceClientProfile("1" /*sessionId*/,
TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
int[] clientPriorities = {100, 50};
int[] clientId0 = new int[1];
@@ -371,25 +376,25 @@ public class TunerResourceManagerServiceTest {
// Init frontend resources.
TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
infos[0] =
- new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+ tunerFrontendInfo(0 /*handle*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
infos[1] =
- new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
+ tunerFrontendInfo(1 /*handle*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
mTunerResourceManagerService.setFrontendInfoListInternal(infos);
TunerFrontendRequest request =
- new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ tunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
int[] frontendHandle = new int[1];
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isTrue();
request =
- new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ tunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isFalse();
assertThat(listener.isReclaimed()).isFalse();
request =
- new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS);
+ tunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS);
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isFalse();
assertThat(listener.isReclaimed()).isFalse();
@@ -399,9 +404,9 @@ public class TunerResourceManagerServiceTest {
public void requestFrontendTest_NoFrontendAvailable_RequestWithHigherPriority() {
// Register clients
ResourceClientProfile[] profiles = new ResourceClientProfile[2];
- profiles[0] = new ResourceClientProfile("0" /*sessionId*/,
+ profiles[0] = resourceClientProfile("0" /*sessionId*/,
TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- profiles[1] = new ResourceClientProfile("1" /*sessionId*/,
+ profiles[1] = resourceClientProfile("1" /*sessionId*/,
TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
int[] clientPriorities = {100, 500};
int[] clientId0 = new int[1];
@@ -421,33 +426,33 @@ public class TunerResourceManagerServiceTest {
// Init frontend resources.
TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
infos[0] =
- new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+ tunerFrontendInfo(0 /*handle*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
infos[1] =
- new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
+ tunerFrontendInfo(1 /*handle*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
mTunerResourceManagerService.setFrontendInfoListInternal(infos);
TunerFrontendRequest request =
- new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ tunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
int[] frontendHandle = new int[1];
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isTrue();
- assertThat(frontendHandle[0]).isEqualTo(infos[0].getHandle());
+ assertThat(frontendHandle[0]).isEqualTo(infos[0].handle);
assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0])
.getInUseFrontendHandles()).isEqualTo(new HashSet<Integer>(Arrays.asList(
- infos[0].getHandle(), infos[1].getHandle())));
+ infos[0].handle, infos[1].handle)));
request =
- new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS);
+ tunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS);
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isTrue();
- assertThat(frontendHandle[0]).isEqualTo(infos[1].getHandle());
- assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getHandle())
+ assertThat(frontendHandle[0]).isEqualTo(infos[1].handle);
+ assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle)
.isInUse()).isTrue();
- assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getHandle())
+ assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle)
.isInUse()).isTrue();
- assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getHandle())
+ assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle)
.getOwnerClientId()).isEqualTo(clientId1[0]);
- assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getHandle())
+ assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle)
.getOwnerClientId()).isEqualTo(clientId1[0]);
assertThat(listener.isReclaimed()).isTrue();
}
@@ -456,7 +461,7 @@ public class TunerResourceManagerServiceTest {
public void releaseFrontendTest_UnderTheSameExclusiveGroup() {
// Register clients
ResourceClientProfile[] profiles = new ResourceClientProfile[1];
- profiles[0] = new ResourceClientProfile("0" /*sessionId*/,
+ profiles[0] = resourceClientProfile("0" /*sessionId*/,
TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
int[] clientId = new int[1];
TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
@@ -466,19 +471,19 @@ public class TunerResourceManagerServiceTest {
// Init frontend resources.
TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
infos[0] =
- new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+ tunerFrontendInfo(0 /*handle*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
infos[1] =
- new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
+ tunerFrontendInfo(1 /*handle*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
mTunerResourceManagerService.setFrontendInfoListInternal(infos);
TunerFrontendRequest request =
- new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ tunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
int[] frontendHandle = new int[1];
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isTrue();
- assertThat(frontendHandle[0]).isEqualTo(infos[0].getHandle());
+ assertThat(frontendHandle[0]).isEqualTo(infos[0].handle);
assertThat(mTunerResourceManagerService
- .getFrontendResource(infos[1].getHandle()).isInUse()).isTrue();
+ .getFrontendResource(infos[1].handle).isInUse()).isTrue();
// Release frontend
mTunerResourceManagerService.releaseFrontendInternal(mTunerResourceManagerService
@@ -486,7 +491,7 @@ public class TunerResourceManagerServiceTest {
assertThat(mTunerResourceManagerService
.getFrontendResource(frontendHandle[0]).isInUse()).isFalse();
assertThat(mTunerResourceManagerService
- .getFrontendResource(infos[1].getHandle()).isInUse()).isFalse();
+ .getFrontendResource(infos[1].handle).isInUse()).isFalse();
assertThat(mTunerResourceManagerService
.getClientProfile(clientId[0]).getInUseFrontendHandles().size()).isEqualTo(0);
}
@@ -495,9 +500,9 @@ public class TunerResourceManagerServiceTest {
public void requestCasTest_NoCasAvailable_RequestWithHigherPriority() {
// Register clients
ResourceClientProfile[] profiles = new ResourceClientProfile[2];
- profiles[0] = new ResourceClientProfile("0" /*sessionId*/,
+ profiles[0] = resourceClientProfile("0" /*sessionId*/,
TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- profiles[1] = new ResourceClientProfile("1" /*sessionId*/,
+ profiles[1] = resourceClientProfile("1" /*sessionId*/,
TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
int[] clientPriorities = {100, 500};
int[] clientId0 = new int[1];
@@ -517,7 +522,7 @@ public class TunerResourceManagerServiceTest {
// Init cas resources.
mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
- CasSessionRequest request = new CasSessionRequest(clientId0[0], 1 /*casSystemId*/);
+ CasSessionRequest request = casSessionRequest(clientId0[0], 1 /*casSystemId*/);
int[] casSessionHandle = new int[1];
// Request for 2 cas sessions.
assertThat(mTunerResourceManagerService
@@ -532,7 +537,7 @@ public class TunerResourceManagerServiceTest {
.getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId0[0])));
assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isTrue();
- request = new CasSessionRequest(clientId1[0], 1);
+ request = casSessionRequest(clientId1[0], 1);
assertThat(mTunerResourceManagerService
.requestCasSessionInternal(request, casSessionHandle)).isTrue();
assertThat(mTunerResourceManagerService.getResourceIdFromHandle(casSessionHandle[0]))
@@ -548,10 +553,66 @@ public class TunerResourceManagerServiceTest {
}
@Test
+ public void requestCiCamTest_NoCiCamAvailable_RequestWithHigherPriority() {
+ // Register clients
+ ResourceClientProfile[] profiles = new ResourceClientProfile[2];
+ profiles[0] = resourceClientProfile("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ profiles[1] = resourceClientProfile("1" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ int[] clientPriorities = {100, 500};
+ int[] clientId0 = new int[1];
+ int[] clientId1 = new int[1];
+ TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
+ mTunerResourceManagerService.registerClientProfileInternal(
+ profiles[0], listener, clientId0);
+ assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ mTunerResourceManagerService.getClientProfile(clientId0[0])
+ .setPriority(clientPriorities[0]);
+ mTunerResourceManagerService.registerClientProfileInternal(
+ profiles[1], new TestResourcesReclaimListener(), clientId1);
+ assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ mTunerResourceManagerService.getClientProfile(clientId1[0])
+ .setPriority(clientPriorities[1]);
+
+ // Init cicam/cas resources.
+ mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
+
+ TunerCiCamRequest request = tunerCiCamRequest(clientId0[0], 1 /*ciCamId*/);
+ int[] ciCamHandle = new int[1];
+ // Request for 2 ciCam sessions.
+ assertThat(mTunerResourceManagerService
+ .requestCiCamInternal(request, ciCamHandle)).isTrue();
+ assertThat(mTunerResourceManagerService
+ .requestCiCamInternal(request, ciCamHandle)).isTrue();
+ assertThat(mTunerResourceManagerService.getResourceIdFromHandle(ciCamHandle[0]))
+ .isEqualTo(1);
+ assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0])
+ .getInUseCiCamId()).isEqualTo(1);
+ assertThat(mTunerResourceManagerService.getCiCamResource(1)
+ .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId0[0])));
+ assertThat(mTunerResourceManagerService.getCiCamResource(1).isFullyUsed()).isTrue();
+
+ request = tunerCiCamRequest(clientId1[0], 1);
+ assertThat(mTunerResourceManagerService
+ .requestCiCamInternal(request, ciCamHandle)).isTrue();
+ assertThat(mTunerResourceManagerService.getResourceIdFromHandle(ciCamHandle[0]))
+ .isEqualTo(1);
+ assertThat(mTunerResourceManagerService.getClientProfile(clientId1[0])
+ .getInUseCiCamId()).isEqualTo(1);
+ assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0])
+ .getInUseCiCamId()).isEqualTo(ClientProfile.INVALID_RESOURCE_ID);
+ assertThat(mTunerResourceManagerService.getCiCamResource(1)
+ .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId1[0])));
+ assertThat(mTunerResourceManagerService.getCiCamResource(1).isFullyUsed()).isFalse();
+ assertThat(listener.isReclaimed()).isTrue();
+ }
+
+ @Test
public void releaseCasTest() {
// Register clients
ResourceClientProfile[] profiles = new ResourceClientProfile[1];
- profiles[0] = new ResourceClientProfile("0" /*sessionId*/,
+ profiles[0] = resourceClientProfile("0" /*sessionId*/,
TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
int[] clientId = new int[1];
TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
@@ -561,7 +622,7 @@ public class TunerResourceManagerServiceTest {
// Init cas resources.
mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
- CasSessionRequest request = new CasSessionRequest(clientId[0], 1 /*casSystemId*/);
+ CasSessionRequest request = casSessionRequest(clientId[0], 1 /*casSystemId*/);
int[] casSessionHandle = new int[1];
// Request for 1 cas sessions.
assertThat(mTunerResourceManagerService
@@ -585,12 +646,49 @@ public class TunerResourceManagerServiceTest {
}
@Test
+ public void releaseCiCamTest() {
+ // Register clients
+ ResourceClientProfile[] profiles = new ResourceClientProfile[1];
+ profiles[0] = resourceClientProfile("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ int[] clientId = new int[1];
+ TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
+ mTunerResourceManagerService.registerClientProfileInternal(profiles[0], listener, clientId);
+ assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+
+ // Init cas resources.
+ mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
+
+ TunerCiCamRequest request = tunerCiCamRequest(clientId[0], 1 /*ciCamId*/);
+ int[] ciCamHandle = new int[1];
+ // Request for 1 ciCam sessions.
+ assertThat(mTunerResourceManagerService
+ .requestCiCamInternal(request, ciCamHandle)).isTrue();
+ assertThat(mTunerResourceManagerService.getResourceIdFromHandle(ciCamHandle[0]))
+ .isEqualTo(1);
+ assertThat(mTunerResourceManagerService.getClientProfile(clientId[0])
+ .getInUseCiCamId()).isEqualTo(1);
+ assertThat(mTunerResourceManagerService.getCiCamResource(1)
+ .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId[0])));
+ assertThat(mTunerResourceManagerService.getCiCamResource(1).isFullyUsed()).isFalse();
+
+ // Release ciCam
+ mTunerResourceManagerService.releaseCiCamInternal(mTunerResourceManagerService
+ .getCiCamResource(1), clientId[0]);
+ assertThat(mTunerResourceManagerService.getClientProfile(clientId[0])
+ .getInUseCiCamId()).isEqualTo(ClientProfile.INVALID_RESOURCE_ID);
+ assertThat(mTunerResourceManagerService.getCiCamResource(1).isFullyUsed()).isFalse();
+ assertThat(mTunerResourceManagerService.getCiCamResource(1)
+ .getOwnerClientIds()).isEmpty();
+ }
+
+ @Test
public void requestLnbTest_NoLnbAvailable_RequestWithHigherPriority() {
// Register clients
ResourceClientProfile[] profiles = new ResourceClientProfile[2];
- profiles[0] = new ResourceClientProfile("0" /*sessionId*/,
+ profiles[0] = resourceClientProfile("0" /*sessionId*/,
TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- profiles[1] = new ResourceClientProfile("1" /*sessionId*/,
+ profiles[1] = resourceClientProfile("1" /*sessionId*/,
TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
int[] clientPriorities = {100, 500};
int[] clientId0 = new int[1];
@@ -611,7 +709,8 @@ public class TunerResourceManagerServiceTest {
int[] lnbHandles = {1};
mTunerResourceManagerService.setLnbInfoListInternal(lnbHandles);
- TunerLnbRequest request = new TunerLnbRequest(clientId0[0]);
+ TunerLnbRequest request = new TunerLnbRequest();
+ request.clientId = clientId0[0];
int[] lnbHandle = new int[1];
assertThat(mTunerResourceManagerService
.requestLnbInternal(request, lnbHandle)).isTrue();
@@ -619,7 +718,9 @@ public class TunerResourceManagerServiceTest {
assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0]).getInUseLnbHandles())
.isEqualTo(new HashSet<Integer>(Arrays.asList(lnbHandles[0])));
- request = new TunerLnbRequest(clientId1[0]);
+ request = new TunerLnbRequest();
+ request.clientId = clientId1[0];
+
assertThat(mTunerResourceManagerService
.requestLnbInternal(request, lnbHandle)).isTrue();
assertThat(lnbHandle[0]).isEqualTo(lnbHandles[0]);
@@ -636,7 +737,7 @@ public class TunerResourceManagerServiceTest {
public void releaseLnbTest() {
// Register clients
ResourceClientProfile[] profiles = new ResourceClientProfile[1];
- profiles[0] = new ResourceClientProfile("0" /*sessionId*/,
+ profiles[0] = resourceClientProfile("0" /*sessionId*/,
TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
int[] clientId = new int[1];
TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
@@ -647,7 +748,8 @@ public class TunerResourceManagerServiceTest {
int[] lnbHandles = {0};
mTunerResourceManagerService.setLnbInfoListInternal(lnbHandles);
- TunerLnbRequest request = new TunerLnbRequest(clientId[0]);
+ TunerLnbRequest request = new TunerLnbRequest();
+ request.clientId = clientId[0];
int[] lnbHandle = new int[1];
assertThat(mTunerResourceManagerService
.requestLnbInternal(request, lnbHandle)).isTrue();
@@ -665,7 +767,7 @@ public class TunerResourceManagerServiceTest {
@Test
public void unregisterClientTest_usingFrontend() {
// Register client
- ResourceClientProfile profile = new ResourceClientProfile("0" /*sessionId*/,
+ ResourceClientProfile profile = resourceClientProfile("0" /*sessionId*/,
TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
int[] clientId = new int[1];
mTunerResourceManagerService.registerClientProfileInternal(
@@ -675,27 +777,27 @@ public class TunerResourceManagerServiceTest {
// Init frontend resources.
TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
infos[0] =
- new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+ tunerFrontendInfo(0 /*handle*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
infos[1] =
- new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
+ tunerFrontendInfo(1 /*handle*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
mTunerResourceManagerService.setFrontendInfoListInternal(infos);
TunerFrontendRequest request =
- new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ tunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
int[] frontendHandle = new int[1];
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isTrue();
- assertThat(frontendHandle[0]).isEqualTo(infos[0].getHandle());
- assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getHandle())
+ assertThat(frontendHandle[0]).isEqualTo(infos[0].handle);
+ assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle)
.isInUse()).isTrue();
- assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getHandle())
+ assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle)
.isInUse()).isTrue();
// Unregister client when using frontend
mTunerResourceManagerService.unregisterClientProfileInternal(clientId[0]);
- assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getHandle())
+ assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle)
.isInUse()).isFalse();
- assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getHandle())
+ assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle)
.isInUse()).isFalse();
assertThat(mTunerResourceManagerService.checkClientExists(clientId[0])).isFalse();
@@ -704,7 +806,7 @@ public class TunerResourceManagerServiceTest {
@Test
public void requestDemuxTest() {
// Register client
- ResourceClientProfile profile = new ResourceClientProfile("0" /*sessionId*/,
+ ResourceClientProfile profile = resourceClientProfile("0" /*sessionId*/,
TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
int[] clientId = new int[1];
mTunerResourceManagerService.registerClientProfileInternal(
@@ -712,7 +814,8 @@ public class TunerResourceManagerServiceTest {
assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
int[] demuxHandle = new int[1];
- TunerDemuxRequest request = new TunerDemuxRequest(clientId[0]);
+ TunerDemuxRequest request = new TunerDemuxRequest();
+ request.clientId = clientId[0];
assertThat(mTunerResourceManagerService.requestDemuxInternal(request, demuxHandle))
.isTrue();
assertThat(mTunerResourceManagerService.getResourceIdFromHandle(demuxHandle[0]))
@@ -722,7 +825,7 @@ public class TunerResourceManagerServiceTest {
@Test
public void requestDescramblerTest() {
// Register client
- ResourceClientProfile profile = new ResourceClientProfile("0" /*sessionId*/,
+ ResourceClientProfile profile = resourceClientProfile("0" /*sessionId*/,
TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
int[] clientId = new int[1];
mTunerResourceManagerService.registerClientProfileInternal(
@@ -730,7 +833,8 @@ public class TunerResourceManagerServiceTest {
assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
int[] desHandle = new int[1];
- TunerDescramblerRequest request = new TunerDescramblerRequest(clientId[0]);
+ TunerDescramblerRequest request = new TunerDescramblerRequest();
+ request.clientId = clientId[0];
assertThat(mTunerResourceManagerService.requestDescramblerInternal(request, desHandle))
.isTrue();
assertThat(mTunerResourceManagerService.getResourceIdFromHandle(desHandle[0])).isEqualTo(0);
@@ -740,15 +844,15 @@ public class TunerResourceManagerServiceTest {
public void isHigherPriorityTest() {
mIsForeground = false;
ResourceClientProfile backgroundPlaybackProfile =
- new ResourceClientProfile(null /*sessionId*/,
+ resourceClientProfile(null /*sessionId*/,
TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
ResourceClientProfile backgroundRecordProfile =
- new ResourceClientProfile(null /*sessionId*/,
+ resourceClientProfile(null /*sessionId*/,
TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD);
int backgroundPlaybackPriority = mTunerResourceManagerService.getClientPriority(
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 0);
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, mIsForeground);
int backgroundRecordPriority = mTunerResourceManagerService.getClientPriority(
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD, 0);
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD, mIsForeground);
assertThat(mTunerResourceManagerService.isHigherPriorityInternal(backgroundPlaybackProfile,
backgroundRecordProfile)).isEqualTo(
(backgroundPlaybackPriority > backgroundRecordPriority));
@@ -767,16 +871,16 @@ public class TunerResourceManagerServiceTest {
// Predefined client profiles
ResourceClientProfile[] ownerProfiles = new ResourceClientProfile[2];
ResourceClientProfile[] shareProfiles = new ResourceClientProfile[2];
- ownerProfiles[0] = new ResourceClientProfile(
+ ownerProfiles[0] = resourceClientProfile(
"0" /*sessionId*/,
TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE);
- ownerProfiles[1] = new ResourceClientProfile(
+ ownerProfiles[1] = resourceClientProfile(
"1" /*sessionId*/,
TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE);
- shareProfiles[0] = new ResourceClientProfile(
+ shareProfiles[0] = resourceClientProfile(
"2" /*sessionId*/,
TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD);
- shareProfiles[1] = new ResourceClientProfile(
+ shareProfiles[1] = resourceClientProfile(
"3" /*sessionId*/,
TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD);
@@ -828,12 +932,12 @@ public class TunerResourceManagerServiceTest {
// Predefined frontend info
TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
- infos[0] = new TunerFrontendInfo(
- 0 /*id*/,
+ infos[0] = tunerFrontendInfo(
+ 0 /*handle*/,
FrontendSettings.TYPE_DVBT,
1 /*exclusiveGroupId*/);
- infos[1] = new TunerFrontendInfo(
- 1 /*id*/,
+ infos[1] = tunerFrontendInfo(
+ 1 /*handle*/,
FrontendSettings.TYPE_DVBS,
1 /*exclusiveGroupId*/);
@@ -848,7 +952,7 @@ public class TunerResourceManagerServiceTest {
// Predefined frontend request and array to save returned frontend handle
int[] frontendHandle = new int[1];
- TunerFrontendRequest request = new TunerFrontendRequest(
+ TunerFrontendRequest request = tunerFrontendRequest(
ownerClientId0[0] /*clientId*/,
FrontendSettings.TYPE_DVBT);
@@ -856,13 +960,13 @@ public class TunerResourceManagerServiceTest {
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle))
.isTrue();
- assertThat(frontendHandle[0]).isEqualTo(infos[0].getHandle());
+ assertThat(frontendHandle[0]).isEqualTo(infos[0].handle);
assertThat(mTunerResourceManagerService
.getClientProfile(ownerClientId0[0])
.getInUseFrontendHandles())
.isEqualTo(new HashSet<Integer>(Arrays.asList(
- infos[0].getHandle(),
- infos[1].getHandle())));
+ infos[0].handle,
+ infos[1].handle)));
/**** Share Frontend ****/
@@ -874,14 +978,14 @@ public class TunerResourceManagerServiceTest {
shareClientId1[0]/*selfClientId*/,
ownerClientId0[0]/*targetClientId*/);
// Verify fe in use status
- assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getHandle())
+ assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle)
.isInUse()).isTrue();
- assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getHandle())
+ assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle)
.isInUse()).isTrue();
// Verify fe owner status
- assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getHandle())
+ assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle)
.getOwnerClientId()).isEqualTo(ownerClientId0[0]);
- assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getHandle())
+ assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle)
.getOwnerClientId()).isEqualTo(ownerClientId0[0]);
// Verify share fe client status in the primary owner client
assertThat(mTunerResourceManagerService.getClientProfile(ownerClientId0[0])
@@ -894,20 +998,20 @@ public class TunerResourceManagerServiceTest {
.getClientProfile(ownerClientId0[0])
.getInUseFrontendHandles())
.isEqualTo(new HashSet<Integer>(Arrays.asList(
- infos[0].getHandle(),
- infos[1].getHandle())));
+ infos[0].handle,
+ infos[1].handle)));
assertThat(mTunerResourceManagerService
.getClientProfile(shareClientId0[0])
.getInUseFrontendHandles())
.isEqualTo(new HashSet<Integer>(Arrays.asList(
- infos[0].getHandle(),
- infos[1].getHandle())));
+ infos[0].handle,
+ infos[1].handle)));
assertThat(mTunerResourceManagerService
.getClientProfile(shareClientId1[0])
.getInUseFrontendHandles())
.isEqualTo(new HashSet<Integer>(Arrays.asList(
- infos[0].getHandle(),
- infos[1].getHandle())));
+ infos[0].handle,
+ infos[1].handle)));
/**** Remove Frontend Share Owner ****/
@@ -923,19 +1027,19 @@ public class TunerResourceManagerServiceTest {
.getClientProfile(ownerClientId0[0])
.getInUseFrontendHandles())
.isEqualTo(new HashSet<Integer>(Arrays.asList(
- infos[0].getHandle(),
- infos[1].getHandle())));
+ infos[0].handle,
+ infos[1].handle)));
assertThat(mTunerResourceManagerService
.getClientProfile(shareClientId0[0])
.getInUseFrontendHandles())
.isEqualTo(new HashSet<Integer>(Arrays.asList(
- infos[0].getHandle(),
- infos[1].getHandle())));
+ infos[0].handle,
+ infos[1].handle)));
/**** Request Shared Frontend with Higher Priority Client ****/
// Predefined second frontend request
- request = new TunerFrontendRequest(
+ request = tunerFrontendRequest(
ownerClientId1[0] /*clientId*/,
FrontendSettings.TYPE_DVBT);
@@ -945,17 +1049,17 @@ public class TunerResourceManagerServiceTest {
.isTrue();
// Validate granted resource and internal mapping
- assertThat(frontendHandle[0]).isEqualTo(infos[0].getHandle());
- assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getHandle())
+ assertThat(frontendHandle[0]).isEqualTo(infos[0].handle);
+ assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle)
.getOwnerClientId()).isEqualTo(ownerClientId1[0]);
- assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getHandle())
+ assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle)
.getOwnerClientId()).isEqualTo(ownerClientId1[0]);
assertThat(mTunerResourceManagerService
.getClientProfile(ownerClientId1[0])
.getInUseFrontendHandles())
.isEqualTo(new HashSet<Integer>(Arrays.asList(
- infos[0].getHandle(),
- infos[1].getHandle())));
+ infos[0].handle,
+ infos[1].handle)));
assertThat(mTunerResourceManagerService
.getClientProfile(ownerClientId0[0])
.getInUseFrontendHandles()
@@ -983,12 +1087,12 @@ public class TunerResourceManagerServiceTest {
// Release the frontend resource from the primary owner
mTunerResourceManagerService.releaseFrontendInternal(mTunerResourceManagerService
- .getFrontendResource(infos[0].getHandle()), ownerClientId1[0]);
+ .getFrontendResource(infos[0].handle), ownerClientId1[0]);
// Validate the internal mapping
- assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getHandle())
+ assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle)
.isInUse()).isFalse();
- assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getHandle())
+ assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle)
.isInUse()).isFalse();
// Verify client status
assertThat(mTunerResourceManagerService
@@ -1010,7 +1114,8 @@ public class TunerResourceManagerServiceTest {
/**** Unregister Primary Owner when the Share owner owns an Lnb ****/
// Predefined Lnb request and handle array
- TunerLnbRequest requestLnb = new TunerLnbRequest(shareClientId0[0]);
+ TunerLnbRequest requestLnb = new TunerLnbRequest();
+ requestLnb.clientId = shareClientId0[0];
int[] lnbHandle = new int[1];
// Request for an Lnb
@@ -1030,9 +1135,9 @@ public class TunerResourceManagerServiceTest {
mTunerResourceManagerService.unregisterClientProfileInternal(ownerClientId1[0]);
// Validate the internal mapping
- assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getHandle())
+ assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle)
.isInUse()).isFalse();
- assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getHandle())
+ assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle)
.isInUse()).isFalse();
// Verify client status
assertThat(mTunerResourceManagerService
@@ -1046,4 +1151,41 @@ public class TunerResourceManagerServiceTest {
.isEqualTo(new HashSet<Integer>(Arrays.asList(
lnbHandles[0])));
}
+
+ private TunerFrontendInfo tunerFrontendInfo(
+ int handle, int frontendType, int exclusiveGroupId) {
+ TunerFrontendInfo info = new TunerFrontendInfo();
+ info.handle = handle;
+ info.frontendType = frontendType;
+ info.exclusiveGroupId = exclusiveGroupId;
+ return info;
+ }
+
+ private TunerFrontendRequest tunerFrontendRequest(int clientId, int frontendType) {
+ TunerFrontendRequest request = new TunerFrontendRequest();
+ request.clientId = clientId;
+ request.frontendType = frontendType;
+ return request;
+ }
+
+ private ResourceClientProfile resourceClientProfile(String sessionId, int useCase) {
+ ResourceClientProfile profile = new ResourceClientProfile();
+ profile.tvInputSessionId = sessionId;
+ profile.useCase = useCase;
+ return profile;
+ }
+
+ private CasSessionRequest casSessionRequest(int clientId, int casSystemId) {
+ CasSessionRequest request = new CasSessionRequest();
+ request.clientId = clientId;
+ request.casSystemId = casSystemId;
+ return request;
+ }
+
+ private TunerCiCamRequest tunerCiCamRequest(int clientId, int ciCamId) {
+ TunerCiCamRequest request = new TunerCiCamRequest();
+ request.clientId = clientId;
+ request.ciCamId = ciCamId;
+ return request;
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index 21aa6bf8750a..fc96b69a568b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -156,8 +156,10 @@ public class ActivityStackTests extends WindowTestsBase {
organizer.setMoveToSecondaryOnEnter(false);
// Create primary splitscreen stack.
- final Task primarySplitScreen = mDefaultTaskDisplayArea.createRootTask(
- WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final Task primarySplitScreen = new TaskBuilder(mAtm.mTaskSupervisor)
+ .setParentTask(organizer.mPrimary)
+ .setOnTop(true)
+ .build();
// Assert windowing mode.
assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, primarySplitScreen.getWindowingMode());
@@ -205,14 +207,15 @@ public class ActivityStackTests extends WindowTestsBase {
@Test
public void testSplitScreenMoveToBack() {
TestSplitOrganizer organizer = new TestSplitOrganizer(mAtm);
- // Set up split-screen with primary on top and secondary containing the home task below
- // another stack.
+ // Explicitly reparent task to primary split root to enter split mode, in which implies
+ // primary on top and secondary containing the home task below another stack.
final Task primaryTask = mDefaultTaskDisplayArea.createRootTask(
- WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final Task secondaryTask = mDefaultTaskDisplayArea.createRootTask(
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
final Task homeRoot = mDefaultTaskDisplayArea.getRootTask(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
- final Task secondaryTask = mDefaultTaskDisplayArea.createRootTask(
- WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ primaryTask.reparent(organizer.mPrimary, POSITION_TOP);
mDefaultTaskDisplayArea.positionChildAt(POSITION_TOP, organizer.mPrimary,
false /* includingParents */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index e8045e06d9ff..4bfc83742c04 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -485,8 +485,10 @@ public class ActivityStarterTests extends WindowTestsBase {
final ActivityRecord splitSecondActivity =
new ActivityBuilder(mAtm).setCreateTask(true).build();
final ActivityRecord splitPrimaryActivity = new TaskBuilder(mSupervisor)
- .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).setCreateActivity(true)
- .build().getTopMostActivity();
+ .setParentTask(splitOrg.mPrimary)
+ .setCreateActivity(true)
+ .build()
+ .getTopMostActivity();
splitPrimaryActivity.mVisibleRequested = splitSecondActivity.mVisibleRequested = true;
assertEquals(splitOrg.mPrimary, splitPrimaryActivity.getRootTask());
@@ -720,6 +722,7 @@ public class ActivityStarterTests extends WindowTestsBase {
}
// caller is instrumenting with background activity starts privileges
callerApp.setInstrumenting(callerIsInstrumentingWithBackgroundActivityStartPrivileges,
+ callerIsInstrumentingWithBackgroundActivityStartPrivileges ? Process.SHELL_UID : -1,
callerIsInstrumentingWithBackgroundActivityStartPrivileges);
// callingUid is the device owner
doReturn(isCallingUidDeviceOwner).when(mAtm).isDeviceOwner(callingUid);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index fe7bdd8f620a..d8be2c1a21a7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -80,6 +80,8 @@ import android.window.WindowContainerTransaction;
import androidx.test.filters.SmallTest;
+import com.android.server.wm.TaskOrganizerController.PendingTaskEvent;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -150,10 +152,14 @@ public class WindowOrganizerTests extends WindowTestsBase {
final ITaskOrganizer organizer = registerMockOrganizer();
final Task stack = createStack();
final Task task = createTask(stack);
+ // Ensure events dispatch to organizer.
+ mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
stack.removeImmediately();
+ // Ensure events dispatch to organizer.
+ mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer).onTaskVanished(any());
}
@@ -162,15 +168,21 @@ public class WindowOrganizerTests extends WindowTestsBase {
final ITaskOrganizer organizer = registerMockOrganizer();
final Task stack = createStack();
final Task task = createTask(stack, false);
+ // Ensure events dispatch to organizer.
+ mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer, never())
.onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
stack.setHasBeenVisible(true);
+ // Ensure events dispatch to organizer.
+ mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
assertTrue(stack.getHasBeenVisible());
verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
stack.removeImmediately();
+ // Ensure events dispatch to organizer.
+ mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer).onTaskVanished(any());
}
@@ -195,12 +207,16 @@ public class WindowOrganizerTests extends WindowTestsBase {
final ITaskOrganizer organizer = registerMockOrganizer();
final Task stack = createStack();
final Task task = createTask(stack, false /* fakeDraw */);
+ // Ensure events dispatch to organizer.
+ mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer, never())
.onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
assertTrue(stack.isOrganized());
mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer);
+ // Ensure events dispatch to organizer.
+ mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
assertTaskVanished(organizer, false /* expectVanished */, stack);
assertFalse(stack.isOrganized());
}
@@ -210,11 +226,16 @@ public class WindowOrganizerTests extends WindowTestsBase {
final ITaskOrganizer organizer = registerMockOrganizer();
final Task stack = createStack();
final Task task = createTask(stack);
+ // Ensure events dispatch to organizer.
+ mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
assertTrue(stack.isOrganized());
stack.setTaskOrganizer(null);
+ // Ensure events dispatch to organizer.
+ mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
+
verify(organizer).onTaskVanished(any());
assertFalse(stack.isOrganized());
}
@@ -224,11 +245,16 @@ public class WindowOrganizerTests extends WindowTestsBase {
final ITaskOrganizer organizer = registerMockOrganizer();
final Task stack = createStack();
final Task task = createTask(stack);
+ // Ensure events dispatch to organizer.
+ mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
assertTrue(stack.isOrganized());
mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer);
+ // Ensure events dispatch to organizer.
+ mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
+
assertTaskVanished(organizer, true /* expectVanished */, stack);
assertFalse(stack.isOrganized());
}
@@ -243,6 +269,8 @@ public class WindowOrganizerTests extends WindowTestsBase {
final Task task3 = createTask(stack3);
final ArrayList<TaskAppearedInfo> existingTasks = new ArrayList<>();
final ITaskOrganizer organizer = registerMockOrganizer(existingTasks);
+ // Ensure events dispatch to organizer.
+ mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
// verify that tasks are returned and taskAppeared is not called
assertContainsTasks(existingTasks, stack, stack2, stack3);
@@ -254,6 +282,8 @@ public class WindowOrganizerTests extends WindowTestsBase {
// Now we replace the registration and verify the new organizer receives existing tasks
final ArrayList<TaskAppearedInfo> existingTasks2 = new ArrayList<>();
final ITaskOrganizer organizer2 = registerMockOrganizer(existingTasks2);
+ // Ensure events dispatch to organizer.
+ mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
assertContainsTasks(existingTasks2, stack, stack2, stack3);
verify(organizer2, times(0)).onTaskAppeared(any(RunningTaskInfo.class),
any(SurfaceControl.class));
@@ -265,6 +295,8 @@ public class WindowOrganizerTests extends WindowTestsBase {
// Now we unregister the second one, the first one should automatically be reregistered
// so we verify that it's now seeing changes.
mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer2);
+ // Ensure events dispatch to organizer.
+ mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer, times(3))
.onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
assertTaskVanished(organizer2, true /* expectVanished */, stack, stack2, stack3);
@@ -599,6 +631,8 @@ public class WindowOrganizerTests extends WindowTestsBase {
Task task = mWm.mAtmService.mTaskOrganizerController.createRootTask(
mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null);
RunningTaskInfo info1 = task.getTaskInfo();
+ // Ensure events dispatch to organizer.
+ mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
lastReportedTiles.clear();
called[0] = false;
@@ -673,6 +707,8 @@ public class WindowOrganizerTests extends WindowTestsBase {
Task task2 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null);
RunningTaskInfo info2 = task2.getTaskInfo();
+ // Ensure events dispatch to organizer.
+ mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
final int initialRootTaskCount = mWm.mAtmService.mTaskOrganizerController.getRootTasks(
mDisplayContent.mDisplayId, null /* activityTypes */).size();
@@ -856,6 +892,8 @@ public class WindowOrganizerTests extends WindowTestsBase {
.setAspectRatio(new Rational(3, 4)).build();
mWm.mAtmService.mActivityClientController.setPictureInPictureParams(record.token, p2);
waitUntilHandlersIdle();
+ // Ensure events dispatch to organizer.
+ mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
assertNotNull(o.mChangedInfo);
assertNotNull(o.mChangedInfo.pictureInPictureParams);
final Rational ratio = o.mChangedInfo.pictureInPictureParams.getAspectRatioRational();
@@ -895,16 +933,24 @@ public class WindowOrganizerTests extends WindowTestsBase {
stack.setTaskOrganizer(organizer);
// setHasBeenVisible was already called once by the set-up code.
stack.setHasBeenVisible(true);
+ // Ensure events dispatch to organizer.
+ mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer, times(1))
.onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
stack.setTaskOrganizer(null);
+ // Ensure events dispatch to organizer.
+ mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer, times(1)).onTaskVanished(any());
stack.setTaskOrganizer(organizer);
+ // Ensure events dispatch to organizer.
+ mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer, times(2))
.onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
stack.removeImmediately();
+ // Ensure events dispatch to organizer.
+ mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer, times(2)).onTaskVanished(any());
}
@@ -923,6 +969,8 @@ public class WindowOrganizerTests extends WindowTestsBase {
// Verify a back pressed does not call the organizer
mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token);
+ // Ensure events dispatch to organizer.
+ mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer, never()).onBackPressedOnTaskRoot(any());
// Enable intercepting back
@@ -931,6 +979,8 @@ public class WindowOrganizerTests extends WindowTestsBase {
// Verify now that the back press does call the organizer
mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token);
+ // Ensure events dispatch to organizer.
+ mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer, times(1)).onBackPressedOnTaskRoot(any());
// Disable intercepting back
@@ -939,6 +989,8 @@ public class WindowOrganizerTests extends WindowTestsBase {
// Verify now that the back press no longer calls the organizer
mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token);
+ // Ensure events dispatch to organizer.
+ mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer, times(1)).onBackPressedOnTaskRoot(any());
}
@@ -1019,6 +1071,151 @@ public class WindowOrganizerTests extends WindowTestsBase {
assertTrue(task2.isOrganized());
}
+ @Test
+ public void testAppearDeferThenInfoChange() {
+ final ITaskOrganizer organizer = registerMockOrganizer();
+ final Task stack = createStack();
+
+ // Assume layout defer
+ mWm.mWindowPlacerLocked.deferLayout();
+
+ final Task task = createTask(stack);
+ final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task);
+
+ stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription"));
+ waitUntilHandlersIdle();
+
+ ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack);
+ assertEquals(1, pendingEvents.size());
+ assertEquals(PendingTaskEvent.EVENT_APPEARED, pendingEvents.get(0).mEventType);
+ assertEquals("TestDescription",
+ pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel());
+ }
+
+ @Test
+ public void testAppearDeferThenVanish() {
+ final ITaskOrganizer organizer = registerMockOrganizer();
+ final Task stack = createStack();
+
+ // Assume layout defer
+ mWm.mWindowPlacerLocked.deferLayout();
+
+ final Task task = createTask(stack);
+
+ stack.removeImmediately();
+ waitUntilHandlersIdle();
+
+ ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack);
+ assertEquals(0, pendingEvents.size());
+ }
+
+ @Test
+ public void testInfoChangeDeferMultiple() {
+ final ITaskOrganizer organizer = registerMockOrganizer();
+ final Task stack = createStack();
+ final Task task = createTask(stack);
+ final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task);
+
+ // Assume layout defer
+ mWm.mWindowPlacerLocked.deferLayout();
+
+ stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription"));
+ waitUntilHandlersIdle();
+
+ ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack);
+ assertEquals(1, pendingEvents.size());
+ assertEquals(PendingTaskEvent.EVENT_INFO_CHANGED, pendingEvents.get(0).mEventType);
+ assertEquals("TestDescription",
+ pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel());
+
+ record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription2"));
+ waitUntilHandlersIdle();
+
+ pendingEvents = getTaskPendingEvent(stack);
+ assertEquals(1, pendingEvents.size());
+ assertEquals(PendingTaskEvent.EVENT_INFO_CHANGED, pendingEvents.get(0).mEventType);
+ assertEquals("TestDescription2",
+ pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel());
+ }
+
+ @Test
+ public void testInfoChangDeferThenVanish() {
+ final ITaskOrganizer organizer = registerMockOrganizer();
+ final Task stack = createStack();
+ final Task task = createTask(stack);
+ final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task);
+
+ // Assume layout defer
+ mWm.mWindowPlacerLocked.deferLayout();
+
+ stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription"));
+
+ stack.removeImmediately();
+ waitUntilHandlersIdle();
+
+ ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack);
+ assertEquals(1, pendingEvents.size());
+ assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType);
+ assertEquals("TestDescription",
+ pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel());
+ }
+
+ @Test
+ public void testVanishDeferThenInfoChange() {
+ final ITaskOrganizer organizer = registerMockOrganizer();
+ final Task stack = createStack();
+ final Task task = createTask(stack);
+ final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task);
+
+ // Assume layout defer
+ mWm.mWindowPlacerLocked.deferLayout();
+
+ stack.removeImmediately();
+ stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ waitUntilHandlersIdle();
+
+ ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack);
+ assertEquals(1, pendingEvents.size());
+ assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType);
+ }
+
+ @Test
+ public void testVanishDeferThenBackOnRoot() {
+ final ITaskOrganizer organizer = registerMockOrganizer();
+ final Task stack = createStack();
+ final Task task = createTask(stack);
+ final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task);
+
+ // Assume layout defer
+ mWm.mWindowPlacerLocked.deferLayout();
+
+ stack.removeImmediately();
+ mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(record.token);
+ waitUntilHandlersIdle();
+
+ ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack);
+ assertEquals(1, pendingEvents.size());
+ assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType);
+ }
+
+ private ArrayList<PendingTaskEvent> getTaskPendingEvent(Task task) {
+ ArrayList<PendingTaskEvent> total =
+ mWm.mAtmService.mTaskOrganizerController.getPendingEventList();
+ ArrayList<PendingTaskEvent> result = new ArrayList();
+
+ for (int i = 0; i < total.size(); i++) {
+ PendingTaskEvent entry = total.get(i);
+ if (entry.mTask.mTaskId == task.mTaskId) {
+ result.add(entry);
+ }
+ }
+
+ return result;
+ }
+
/**
* Verifies that task vanished is called for a specific task.
*/
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 31e2dce008de..8b93372e5a76 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -17,6 +17,8 @@
package com.android.server.wm;
import static android.app.AppOpsManager.OP_NONE;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -1107,6 +1109,17 @@ class WindowTestsBase extends SystemServiceTestsBase {
// moves everything to secondary. Most tests expect this since sysui usually does it.
boolean mMoveToSecondaryOnEnter = true;
int mDisplayId;
+ private static final int[] CONTROLLED_ACTIVITY_TYPES = {
+ ACTIVITY_TYPE_STANDARD,
+ ACTIVITY_TYPE_HOME,
+ ACTIVITY_TYPE_RECENTS,
+ ACTIVITY_TYPE_UNDEFINED
+ };
+ private static final int[] CONTROLLED_WINDOWING_MODES = {
+ WINDOWING_MODE_FULLSCREEN,
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
+ WINDOWING_MODE_UNDEFINED
+ };
TestSplitOrganizer(ActivityTaskManagerService service, DisplayContent display) {
mService = service;
mDisplayId = display.mDisplayId;
@@ -1151,9 +1164,9 @@ class WindowTestsBase extends SystemServiceTestsBase {
if (!mMoveToSecondaryOnEnter) {
return;
}
- mService.mTaskOrganizerController.setLaunchRoot(mDisplayId,
- mSecondary.mRemoteToken.toWindowContainerToken());
DisplayContent dc = mService.mRootWindowContainer.getDisplayContent(mDisplayId);
+ dc.getDefaultTaskDisplayArea().setLaunchRootTask(
+ mSecondary, CONTROLLED_WINDOWING_MODES, CONTROLLED_ACTIVITY_TYPES);
dc.forAllRootTasks(rootTask -> {
if (!WindowConfiguration.isSplitScreenWindowingMode(rootTask.getWindowingMode())) {
rootTask.reparent(mSecondary, POSITION_BOTTOM);
diff --git a/services/translation/Android.bp b/services/translation/Android.bp
new file mode 100644
index 000000000000..804a6177e94e
--- /dev/null
+++ b/services/translation/Android.bp
@@ -0,0 +1,13 @@
+filegroup {
+ name: "services.translation-sources",
+ srcs: ["java/**/*.java"],
+ path: "java",
+ visibility: ["//frameworks/base/services"],
+}
+
+java_library_static {
+ name: "services.translation",
+ defaults: ["platform_service_defaults"],
+ srcs: [":services.translation-sources"],
+ libs: ["services.core"],
+} \ No newline at end of file
diff --git a/services/translation/OWNERS b/services/translation/OWNERS
new file mode 100644
index 000000000000..a1e663aa8ff7
--- /dev/null
+++ b/services/translation/OWNERS
@@ -0,0 +1,8 @@
+# Bug component: 994311
+
+adamhe@google.com
+augale@google.com
+joannechung@google.com
+lpeter@google.com
+svetoslavganov@google.com
+tymtsai@google.com
diff --git a/services/translation/java/com/android/server/translation/RemoteTranslationService.java b/services/translation/java/com/android/server/translation/RemoteTranslationService.java
new file mode 100644
index 000000000000..0c7e61765501
--- /dev/null
+++ b/services/translation/java/com/android/server/translation/RemoteTranslationService.java
@@ -0,0 +1,87 @@
+/*
+ * 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.translation;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.service.translation.ITranslationService;
+import android.service.translation.TranslationService;
+import android.util.Slog;
+import android.view.translation.TranslationSpec;
+
+import com.android.internal.infra.AbstractRemoteService;
+import com.android.internal.infra.ServiceConnector;
+import com.android.internal.os.IResultReceiver;
+
+final class RemoteTranslationService extends ServiceConnector.Impl<ITranslationService> {
+
+ private static final String TAG = RemoteTranslationService.class.getSimpleName();
+
+ // TODO(b/176590870): Make PERMANENT now.
+ private static final long TIMEOUT_IDLE_UNBIND_MS =
+ AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS;
+ private static final int TIMEOUT_REQUEST_MS = 5_000;
+
+ private final long mIdleUnbindTimeoutMs;
+ private final int mRequestTimeoutMs;
+ private final ComponentName mComponentName;
+
+ RemoteTranslationService(Context context, ComponentName serviceName,
+ int userId, boolean bindInstantServiceAllowed) {
+ super(context,
+ new Intent(TranslationService.SERVICE_INTERFACE).setComponent(serviceName),
+ bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0,
+ userId, ITranslationService.Stub::asInterface);
+ mIdleUnbindTimeoutMs = TIMEOUT_IDLE_UNBIND_MS;
+ mRequestTimeoutMs = TIMEOUT_REQUEST_MS;
+ mComponentName = serviceName;
+
+ // Bind right away.
+ connect();
+ }
+
+ public ComponentName getComponentName() {
+ return mComponentName;
+ }
+
+ @Override // from ServiceConnector.Impl
+ protected void onServiceConnectionStatusChanged(ITranslationService service,
+ boolean connected) {
+ try {
+ if (connected) {
+ service.onConnected();
+ } else {
+ service.onDisconnected();
+ }
+ } catch (Exception e) {
+ Slog.w(TAG,
+ "Exception calling onServiceConnectionStatusChanged(" + connected + "): ", e);
+ }
+ }
+
+ @Override // from AbstractRemoteService
+ protected long getAutoDisconnectTimeoutMs() {
+ return mIdleUnbindTimeoutMs;
+ }
+
+ public void onSessionCreated(@NonNull TranslationSpec sourceSpec,
+ @NonNull TranslationSpec destSpec, int sessionId, IResultReceiver resultReceiver) {
+ run((s) -> s.onCreateTranslationSession(sourceSpec, destSpec, sessionId, resultReceiver));
+ }
+}
diff --git a/services/translation/java/com/android/server/translation/TranslationManagerService.java b/services/translation/java/com/android/server/translation/TranslationManagerService.java
new file mode 100644
index 000000000000..e2aabe6a89ea
--- /dev/null
+++ b/services/translation/java/com/android/server/translation/TranslationManagerService.java
@@ -0,0 +1,92 @@
+/*
+ * 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.translation;
+
+import static android.content.Context.TRANSLATION_MANAGER_SERVICE;
+import static android.view.translation.TranslationManager.STATUS_SYNC_CALL_FAIL;
+
+import android.content.Context;
+import android.os.RemoteException;
+import android.util.Slog;
+import android.view.translation.ITranslationManager;
+import android.view.translation.TranslationSpec;
+
+import com.android.internal.os.IResultReceiver;
+import com.android.server.infra.AbstractMasterSystemService;
+import com.android.server.infra.FrameworkResourcesServiceNameResolver;
+
+/**
+ * Entry point service for translation management.
+ *
+ * <p>This service provides the {@link ITranslationManager} implementation and keeps a list of
+ * {@link TranslationManagerServiceImpl} per user; the real work is done by
+ * {@link TranslationManagerServiceImpl} itself.
+ */
+public final class TranslationManagerService
+ extends AbstractMasterSystemService<TranslationManagerService,
+ TranslationManagerServiceImpl> {
+
+ private static final String TAG = "TranslationManagerService";
+
+ public TranslationManagerService(Context context) {
+ // TODO: Discuss the disallow policy
+ super(context, new FrameworkResourcesServiceNameResolver(context,
+ com.android.internal.R.string.config_defaultTranslationService),
+ /* disallowProperty */ null, PACKAGE_UPDATE_POLICY_REFRESH_EAGER);
+ }
+
+ @Override
+ protected TranslationManagerServiceImpl newServiceLocked(int resolvedUserId, boolean disabled) {
+ return new TranslationManagerServiceImpl(this, mLock, resolvedUserId, disabled);
+ }
+
+ final class TranslationManagerServiceStub extends ITranslationManager.Stub {
+ @Override
+ public void getSupportedLocales(IResultReceiver receiver, int userId)
+ throws RemoteException {
+ synchronized (mLock) {
+ final TranslationManagerServiceImpl service = getServiceForUserLocked(userId);
+ if (service != null) {
+ service.getSupportedLocalesLocked(receiver);
+ } else {
+ Slog.v(TAG, "getSupportedLocales(): no service for " + userId);
+ receiver.send(STATUS_SYNC_CALL_FAIL, null);
+ }
+ }
+ }
+
+ @Override
+ public void onSessionCreated(TranslationSpec sourceSpec, TranslationSpec destSpec,
+ int sessionId, IResultReceiver receiver, int userId) throws RemoteException {
+ synchronized (mLock) {
+ final TranslationManagerServiceImpl service = getServiceForUserLocked(userId);
+ if (service != null) {
+ service.onSessionCreatedLocked(sourceSpec, destSpec, sessionId, receiver);
+ } else {
+ Slog.v(TAG, "onSessionCreated(): no service for " + userId);
+ receiver.send(STATUS_SYNC_CALL_FAIL, null);
+ }
+ }
+ }
+ }
+
+ @Override // from SystemService
+ public void onStart() {
+ publishBinderService(TRANSLATION_MANAGER_SERVICE,
+ new TranslationManagerService.TranslationManagerServiceStub());
+ }
+}
diff --git a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
new file mode 100644
index 000000000000..b1f6f80d4158
--- /dev/null
+++ b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
@@ -0,0 +1,125 @@
+/*
+ * 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.translation;
+
+import static android.view.translation.TranslationManager.STATUS_SYNC_CALL_SUCCESS;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.os.RemoteException;
+import android.service.translation.TranslationServiceInfo;
+import android.util.Slog;
+import android.view.translation.TranslationSpec;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.IResultReceiver;
+import com.android.internal.util.SyncResultReceiver;
+import com.android.server.infra.AbstractPerUserSystemService;
+
+import java.util.ArrayList;
+
+final class TranslationManagerServiceImpl extends
+ AbstractPerUserSystemService<TranslationManagerServiceImpl, TranslationManagerService> {
+
+ private static final String TAG = "TranslationManagerServiceImpl";
+
+ @GuardedBy("mLock")
+ @Nullable
+ private RemoteTranslationService mRemoteTranslationService;
+
+ @GuardedBy("mLock")
+ @Nullable
+ private ServiceInfo mRemoteTranslationServiceInfo;
+
+ protected TranslationManagerServiceImpl(
+ @NonNull TranslationManagerService master,
+ @NonNull Object lock, int userId, boolean disabled) {
+ super(master, lock, userId);
+ updateRemoteServiceLocked();
+ }
+
+ @GuardedBy("mLock")
+ @Override // from PerUserSystemService
+ protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
+ throws PackageManager.NameNotFoundException {
+ final TranslationServiceInfo info = new TranslationServiceInfo(getContext(),
+ serviceComponent, isTemporaryServiceSetLocked(), mUserId);
+ mRemoteTranslationServiceInfo = info.getServiceInfo();
+ return info.getServiceInfo();
+ }
+
+ @GuardedBy("mLock")
+ @Override // from PerUserSystemService
+ protected boolean updateLocked(boolean disabled) {
+ final boolean enabledChanged = super.updateLocked(disabled);
+ updateRemoteServiceLocked();
+ return enabledChanged;
+ }
+
+ /**
+ * Updates the reference to the remote service.
+ */
+ @GuardedBy("mLock")
+ private void updateRemoteServiceLocked() {
+ if (mRemoteTranslationService != null) {
+ if (mMaster.debug) Slog.d(TAG, "updateRemoteService(): destroying old remote service");
+ mRemoteTranslationService.unbind();
+ mRemoteTranslationService = null;
+ }
+ }
+
+ @GuardedBy("mLock")
+ @Nullable
+ private RemoteTranslationService ensureRemoteServiceLocked() {
+ if (mRemoteTranslationService == null) {
+ final String serviceName = getComponentNameLocked();
+ if (serviceName == null) {
+ if (mMaster.verbose) {
+ Slog.v(TAG, "ensureRemoteServiceLocked(): no service component name.");
+ }
+ return null;
+ }
+ final ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
+ mRemoteTranslationService = new RemoteTranslationService(getContext(),
+ serviceComponent, mUserId, /* isInstantAllowed= */ false);
+ }
+ return mRemoteTranslationService;
+ }
+
+ @GuardedBy("mLock")
+ void getSupportedLocalesLocked(@NonNull IResultReceiver resultReceiver) {
+ // TODO: implement this
+ try {
+ resultReceiver.send(STATUS_SYNC_CALL_SUCCESS,
+ SyncResultReceiver.bundleFor(new ArrayList<>()));
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException returning supported locales: " + e);
+ }
+ }
+
+ @GuardedBy("mLock")
+ void onSessionCreatedLocked(@NonNull TranslationSpec sourceSpec,
+ @NonNull TranslationSpec destSpec, int sessionId, IResultReceiver resultReceiver) {
+ final RemoteTranslationService remoteService = ensureRemoteServiceLocked();
+ if (remoteService != null) {
+ remoteService.onSessionCreated(sourceSpec, destSpec, sessionId, resultReceiver);
+ }
+ }
+}
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 1238e7b69a87..044ea80cba4b 100755
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -607,6 +607,11 @@ public final class Call {
*/
public static final int PROPERTY_IS_ADHOC_CONFERENCE = 0x00002000;
+ /**
+ * Connection is using Cross SIM Calling.
+ */
+ public static final int PROPERTY_CROSS_SIM = 0x00004000;
+
//******************************************************************************************
// Next PROPERTY value: 0x00004000
//******************************************************************************************
@@ -798,6 +803,9 @@ public final class Call {
if (hasProperty(properties, PROPERTY_IS_ADHOC_CONFERENCE)) {
builder.append(" PROPERTY_IS_ADHOC_CONFERENCE");
}
+ if (hasProperty(properties, PROPERTY_CROSS_SIM)) {
+ builder.append(" PROPERTY_CROSS_SIM");
+ }
builder.append("]");
return builder.toString();
}
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 724a9e477b95..7a21b3adf654 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -527,9 +527,17 @@ public abstract class Connection extends Conferenceable {
*/
public static final int PROPERTY_IS_ADHOC_CONFERENCE = 1 << 12;
+ /**
+ * Connection is using cross sim technology.
+ * <p>
+ * Indicates that the {@link Connection} is using a cross sim technology which would
+ * register IMS over internet APN of default data subscription.
+ * <p>
+ */
+ public static final int PROPERTY_CROSS_SIM = 1 << 13;
//**********************************************************************************************
- // Next PROPERTY value: 1<<13
+ // Next PROPERTY value: 1<<14
//**********************************************************************************************
/**
diff --git a/telephony/java/android/telephony/CarrierBandwidth.java b/telephony/java/android/telephony/CarrierBandwidth.java
index 17747a3919ee..b153fefce6e3 100644
--- a/telephony/java/android/telephony/CarrierBandwidth.java
+++ b/telephony/java/android/telephony/CarrierBandwidth.java
@@ -18,6 +18,7 @@ package android.telephony;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -102,7 +103,7 @@ public final class CarrierBandwidth implements Parcelable {
/**
* Retrieves the upstream bandwidth for the primary network in Kbps. This always only refers to
* the estimated first hop transport bandwidth.
- * This will be INVALID if the network is not connected
+ * This will be {@link #INVALID} if the network is not connected
*
* @return The estimated first hop upstream (device to network) bandwidth.
*/
@@ -113,7 +114,7 @@ public final class CarrierBandwidth implements Parcelable {
/**
* Retrieves the downstream bandwidth for the primary network in Kbps. This always only refers
* to the estimated first hop transport bandwidth.
- * This will be INVALID if the network is not connected
+ * This will be {@link #INVALID} if the network is not connected
*
* @return The estimated first hop downstream (network to device) bandwidth.
*/
@@ -124,10 +125,19 @@ public final class CarrierBandwidth implements Parcelable {
/**
* Retrieves the upstream bandwidth for the secondary network in Kbps. This always only refers
* to the estimated first hop transport bandwidth.
- * This will be INVALID if the network is not connected
+ * <p/>
+ * This will be {@link #INVALID} if either are the case:
+ * <ol>
+ * <li>The network is not connected</li>
+ * <li>The device does not support
+ * {@link android.telephony.TelephonyManager#CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE}.</li>
+ * </ol>
*
* @return The estimated first hop upstream (device to network) bandwidth.
*/
+ @RequiresFeature(
+ enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
+ value = TelephonyManager.CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE)
public int getSecondaryDownlinkCapacityKbps() {
return mSecondaryDownlinkCapacityKbps;
}
@@ -135,10 +145,18 @@ public final class CarrierBandwidth implements Parcelable {
/**
* Retrieves the downstream bandwidth for the secondary network in Kbps. This always only
* refers to the estimated first hop transport bandwidth.
- * This will be INVALID if the network is not connected
- *
+ * <p/>
+ * This will be {@link #INVALID} if either are the case:
+ * <ol>
+ * <li>The network is not connected</li>
+ * <li>The device does not support
+ * {@link android.telephony.TelephonyManager#CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE}.</li>
+ * </ol>
* @return The estimated first hop downstream (network to device) bandwidth.
*/
+ @RequiresFeature(
+ enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
+ value = TelephonyManager.CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE)
public int getSecondaryUplinkCapacityKbps() {
return mSecondaryUplinkCapacityKbps;
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 904232b54b8f..d4c2bc9d9b8b 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -373,6 +373,26 @@ public class SubscriptionManager {
CONTENT_URI, "wfc_roaming_enabled");
/**
+ * A content {@link Uri} used to receive updates on cross sim enabled user setting.
+ * <p>
+ * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the
+ * subscription cross sim calling enabled
+ * {@link ImsMmTelManager#isCrossSimCallingEnabledByUser()}
+ * while your app is running. You can also use a {@link android.app.job.JobService}
+ * to ensure your app
+ * is notified of changes to the {@link Uri} even when it is not running.
+ * Note, however, that using a {@link android.app.job.JobService} does not guarantee timely
+ * delivery of updates to the {@link Uri}.
+ * To be notified of changes to a specific subId, append subId to the URI
+ * {@link Uri#withAppendedPath(Uri, String)}.
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public static final Uri CROSS_SIM_ENABLED_CONTENT_URI = Uri.withAppendedPath(CONTENT_URI,
+ SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED);
+
+ /**
* TelephonyProvider unique key column name is the subscription id.
* <P>Type: TEXT (String)</P>
*/
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index a6e870fe7a16..bfd37cd60806 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -32,6 +32,7 @@ import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.StringDef;
import android.annotation.SuppressAutoDoc;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
@@ -56,8 +57,10 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.OutcomeReceiver;
import android.os.ParcelFileDescriptor;
+import android.os.ParcelUuid;
import android.os.Parcelable;
import android.os.PersistableBundle;
import android.os.Process;
@@ -118,8 +121,13 @@ import com.android.internal.telephony.RILConstants;
import com.android.internal.telephony.SmsApplication;
import com.android.telephony.Rlog;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -553,12 +561,12 @@ public class TelephonyManager {
private static final int MAXIMUM_CALL_COMPOSER_PICTURE_SIZE = 80000;
- // TODO(hallliu): link to upload method in docs
/**
* Indicates the maximum size of the call composure picture.
*
- * Pictures sent via uploadCallComposerPicture must not exceed this size, or an
- * {@link IllegalArgumentException} will be thrown.
+ * Pictures sent via {@link #uploadCallComposerPicture(InputStream, Executor, OutcomeReceiver)}
+ * or {@link #uploadCallComposerPicture(Path, Executor, OutcomeReceiver)} must not exceed this
+ * size, or an error will be returned via the callback in those methods.
*
* @return Maximum file size in bytes.
*/
@@ -4242,6 +4250,342 @@ public class TelephonyManager {
}
/**
+ * Exception that may be supplied to the callback in {@link #uploadCallComposerPicture} if
+ * something goes awry.
+ */
+ public static class CallComposerException extends Exception {
+ /**
+ * Used internally only, signals success of the upload to the carrier.
+ * @hide
+ */
+ public static final int SUCCESS = -1;
+ /**
+ * Indicates that an unknown error was encountered when uploading the call composer picture.
+ *
+ * Clients that encounter this error should retry the upload.
+ */
+ public static final int ERROR_UNKNOWN = 0;
+
+ /**
+ * Indicates that the phone process died or otherwise became unavailable while uploading the
+ * call composer picture.
+ *
+ * Clients that encounter this error should retry the upload.
+ */
+ public static final int ERROR_REMOTE_END_CLOSED = 1;
+
+ /**
+ * Indicates that the file or stream supplied exceeds the size limit defined in
+ * {@link #getMaximumCallComposerPictureSize()}.
+ *
+ * Clients that encounter this error should retry the upload after reducing the size of the
+ * picture.
+ */
+ public static final int ERROR_FILE_TOO_LARGE = 2;
+
+ /**
+ * Indicates that the device failed to authenticate with the carrier when uploading the
+ * picture.
+ *
+ * Clients that encounter this error should not retry the upload unless a reboot or radio
+ * reset has been performed in the interim.
+ */
+ public static final int ERROR_AUTHENTICATION_FAILED = 3;
+
+ /**
+ * Indicates that the {@link InputStream} passed to {@link #uploadCallComposerPicture}
+ * was closed.
+ *
+ * The caller should retry if this error is encountered, and be sure to not close the stream
+ * before the callback is called this time.
+ */
+ public static final int ERROR_INPUT_CLOSED = 4;
+
+ /**
+ * Indicates that an {@link IOException} was encountered while reading the picture.
+ *
+ * The offending {@link IOException} will be available via {@link #getIOException()}.
+ * Clients should use the contents of the exception to determine whether a retry is
+ * warranted.
+ */
+ public static final int ERROR_IO_EXCEPTION = 5;
+
+ /** @hide */
+ @IntDef(prefix = {"ERROR_"}, value = {
+ ERROR_UNKNOWN,
+ ERROR_REMOTE_END_CLOSED,
+ ERROR_FILE_TOO_LARGE,
+ ERROR_AUTHENTICATION_FAILED,
+ ERROR_INPUT_CLOSED,
+ ERROR_IO_EXCEPTION,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CallComposerError {}
+
+ private final int mErrorCode;
+ private final IOException mIOException;
+
+ public CallComposerException(@CallComposerError int errorCode,
+ @Nullable IOException ioException) {
+ mErrorCode = errorCode;
+ mIOException = ioException;
+ }
+
+ /**
+ * Fetches the error code associated with this exception.
+ * @return An error code.
+ */
+ public @CallComposerError int getErrorCode() {
+ return mErrorCode;
+ }
+
+ /**
+ * Fetches the {@link IOException} that caused the error.
+ */
+ // Follows the naming of IOException
+ @SuppressLint("AcronymName")
+ public @Nullable IOException getIOException() {
+ return mIOException;
+ }
+ }
+
+ /** @hide */
+ public static final String KEY_CALL_COMPOSER_PICTURE_HANDLE = "call_composer_picture_handle";
+
+ /**
+ * Uploads a picture to the carrier network for use with call composer.
+ *
+ * @see #uploadCallComposerPicture(InputStream, Executor, OutcomeReceiver)
+ * @param pictureToUpload Path to a local file containing the picture to upload.
+ * @param executor The {@link Executor} on which the {@code pictureToUpload} file will be read
+ * from disk, as well as on which {@code callback} will be called.
+ * @param callback A callback called when the upload operation terminates, either in success
+ * or in error.
+ */
+ public void uploadCallComposerPicture(@NonNull Path pictureToUpload,
+ @CallbackExecutor @NonNull Executor executor,
+ @NonNull OutcomeReceiver<ParcelUuid, CallComposerException> callback) {
+ Objects.requireNonNull(pictureToUpload);
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+
+ // Do the role check now so that we can quit early if needed -- there's an additional
+ // permission check on the other side of the binder call as well.
+ RoleManager rm = mContext.getSystemService(RoleManager.class);
+ if (!rm.isRoleHeld(RoleManager.ROLE_DIALER)) {
+ throw new SecurityException("You must hold RoleManager.ROLE_DIALER to do this");
+ }
+
+ executor.execute(() -> {
+ try {
+ if (Looper.getMainLooper().isCurrentThread()) {
+ Log.w(TAG, "Uploading call composer picture on main thread!"
+ + " hic sunt dracones!");
+ }
+ long size = Files.size(pictureToUpload);
+ if (size > getMaximumCallComposerPictureSize()) {
+ callback.onError(new CallComposerException(
+ CallComposerException.ERROR_FILE_TOO_LARGE, null));
+ return;
+ }
+ InputStream fileStream = Files.newInputStream(pictureToUpload);
+ try {
+ uploadCallComposerPicture(fileStream, executor,
+ new OutcomeReceiver<ParcelUuid, CallComposerException>() {
+ @Override
+ public void onResult(ParcelUuid result) {
+ try {
+ fileStream.close();
+ } catch (IOException e) {
+ // ignore
+ Log.e(TAG, "Error closing file input stream when"
+ + " uploading call composer pic");
+ }
+ callback.onResult(result);
+ }
+
+ @Override
+ public void onError(CallComposerException error) {
+ try {
+ fileStream.close();
+ } catch (IOException e) {
+ // ignore
+ Log.e(TAG, "Error closing file input stream when"
+ + " uploading call composer pic");
+ }
+ callback.onError(error);
+ }
+ });
+ } catch (Exception e) {
+ Log.e(TAG, "Got exception calling into stream-version of"
+ + " uploadCallComposerPicture: " + e);
+ try {
+ fileStream.close();
+ } catch (IOException e1) {
+ // ignore
+ Log.e(TAG, "Error closing file input stream when uploading"
+ + " call composer pic");
+ }
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "IOException when uploading call composer pic:" + e);
+ callback.onError(
+ new CallComposerException(CallComposerException.ERROR_IO_EXCEPTION, e));
+ }
+ });
+
+ }
+
+ /**
+ * Uploads a picture to the carrier network for use with call composer.
+ *
+ * This method allows a dialer app to upload a picture to the carrier network that can then
+ * later be attached to an outgoing call. In order to attach the picture to a call, use the
+ * {@link ParcelUuid} returned from {@code callback} upon successful upload as the value to
+ * {@link TelecomManager#EXTRA_OUTGOING_PICTURE}.
+ *
+ * This functionality is only available to the app filling the {@link RoleManager#ROLE_DIALER}
+ * role on the device.
+ *
+ * @param pictureToUpload An {@link InputStream} that supplies the bytes representing the
+ * picture to upload. The client bears responsibility for closing this
+ * stream after {@code callback} is called with success or failure.
+ *
+ * Additionally, if the stream supplies more bytes than the return value
+ * of {@link #getMaximumCallComposerPictureSize()}, the upload will be
+ * aborted and the callback will be called with an exception containing
+ * {@link CallComposerException#ERROR_FILE_TOO_LARGE}.
+ * @param executor The {@link Executor} on which the {@code pictureToUpload} stream will be
+ * read, as well as on which the callback will be called.
+ * @param callback A callback called when the upload operation terminates, either in success
+ * or in error.
+ */
+ public void uploadCallComposerPicture(@NonNull InputStream pictureToUpload,
+ @CallbackExecutor @NonNull Executor executor,
+ @NonNull OutcomeReceiver<ParcelUuid, CallComposerException> callback) {
+ Objects.requireNonNull(pictureToUpload);
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+
+ ITelephony telephony = getITelephony();
+ if (telephony == null) {
+ throw new IllegalStateException("Telephony service not available.");
+ }
+
+ ParcelFileDescriptor writeFd;
+ ParcelFileDescriptor readFd;
+ try {
+ ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createReliablePipe();
+ writeFd = pipe[1];
+ readFd = pipe[0];
+ } catch (IOException e) {
+ executor.execute(() -> callback.onError(
+ new CallComposerException(CallComposerException.ERROR_IO_EXCEPTION, e)));
+ return;
+ }
+
+ OutputStream output = new ParcelFileDescriptor.AutoCloseOutputStream(writeFd);
+
+ try {
+ telephony.uploadCallComposerPicture(getSubId(), mContext.getOpPackageName(),
+ readFd, new ResultReceiver(null) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle result) {
+ if (resultCode != CallComposerException.SUCCESS) {
+ executor.execute(() -> callback.onError(
+ new CallComposerException(resultCode, null)));
+ return;
+ }
+ ParcelUuid resultUuid =
+ result.getParcelable(KEY_CALL_COMPOSER_PICTURE_HANDLE);
+ if (resultUuid == null) {
+ Log.e(TAG, "Got null uuid without an error"
+ + " while uploading call composer pic");
+ executor.execute(() -> callback.onError(
+ new CallComposerException(
+ CallComposerException.ERROR_UNKNOWN, null)));
+ return;
+ }
+ executor.execute(() -> callback.onResult(resultUuid));
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Remote exception uploading call composer pic:" + e);
+ e.rethrowAsRuntimeException();
+ }
+
+ executor.execute(() -> {
+ if (Looper.getMainLooper().isCurrentThread()) {
+ Log.w(TAG, "Uploading call composer picture on main thread!"
+ + " hic sunt dracones!");
+ }
+
+ int totalBytesRead = 0;
+ byte[] buffer = new byte[16 * 1024];
+ try {
+ while (true) {
+ int numRead;
+ try {
+ numRead = pictureToUpload.read(buffer);
+ } catch (IOException e) {
+ Log.e(TAG, "IOException reading from input while uploading pic: " + e);
+ // Most likely, this was because the stream was closed. We have no way to
+ // tell though.
+ callback.onError(new CallComposerException(
+ CallComposerException.ERROR_INPUT_CLOSED, e));
+ try {
+ writeFd.closeWithError("input closed");
+ } catch (IOException e1) {
+ // log and ignore
+ Log.e(TAG, "Error closing fd pipe: " + e1);
+ }
+ break;
+ }
+
+ if (numRead < 0) {
+ break;
+ }
+
+ totalBytesRead += numRead;
+ if (totalBytesRead > getMaximumCallComposerPictureSize()) {
+ Log.e(TAG, "Read too many bytes from call composer pic stream: "
+ + totalBytesRead);
+ try {
+ callback.onError(new CallComposerException(
+ CallComposerException.ERROR_FILE_TOO_LARGE, null));
+ writeFd.closeWithError("too large");
+ } catch (IOException e1) {
+ // log and ignore
+ Log.e(TAG, "Error closing fd pipe: " + e1);
+ }
+ break;
+ }
+
+ try {
+ output.write(buffer, 0, numRead);
+ } catch (IOException e) {
+ callback.onError(new CallComposerException(
+ CallComposerException.ERROR_REMOTE_END_CLOSED, e));
+ try {
+ writeFd.closeWithError("remote end closed");
+ } catch (IOException e1) {
+ // log and ignore
+ Log.e(TAG, "Error closing fd pipe: " + e1);
+ }
+ break;
+ }
+ }
+ } finally {
+ try {
+ output.close();
+ } catch (IOException e) {
+ // Ignore -- we might've already closed it.
+ }
+ }
+ });
+ }
+
+ /**
* Returns the Group Identifier Level1 for a GSM phone.
* Return null if it is unavailable.
*
@@ -9953,6 +10297,8 @@ public class TelephonyManager {
* Valid return results are:
* - {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} for LTE registration,
* - {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} for IWLAN registration, or
+ * - {@link ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM} for registration over
+ * other sim's internet, or
* - {@link ImsRegistrationImplBase#REGISTRATION_TECH_NONE} if we are not registered or the
* result is unavailable.
* Use {@link ImsMmTelManager.RegistrationCallback} instead.
@@ -14430,10 +14776,22 @@ public class TelephonyManager {
return Collections.emptyList();
}
+ /**
+ * Indicates whether {@link CarrierBandwidth#getSecondaryDownlinkCapacityKbps()} and
+ * {@link CarrierBandwidth#getSecondaryUplinkCapacityKbps()} are visible. See comments
+ * on respective methods for more information.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE =
+ "CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE";
+
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = {"RADIO_INTERFACE_CAPABILITY_"},
- value = {})
+ @StringDef(prefix = "CAPABILITY_", value = {
+ CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE,
+ })
public @interface RadioInterfaceCapability {}
/**
@@ -14446,6 +14804,7 @@ public class TelephonyManager {
*
* @hide
*/
+ @SystemApi
public boolean isRadioInterfaceCapabilitySupported(
@NonNull @RadioInterfaceCapability String capability) {
try {
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index b15f4edeb79f..1faae42f054b 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -445,6 +445,15 @@ public final class ImsCallProfile implements Parcelable {
public static final String EXTRA_FORWARDED_NUMBER =
"android.telephony.ims.extra.FORWARDED_NUMBER";
+ /**
+ * Extra key with an {@code boolean} value which can be set in
+ * {@link #setCallExtraBoolean(String, boolean)} to indicate whether call is a cross sim call.
+ * <p>
+ * Valid values are true if call is cross sim call else false.
+ */
+ public static final String EXTRA_IS_CROSS_SIM_CALL =
+ "android.telephony.ims.extra.IS_CROSS_SIM_CALL";
+
/** @hide */
public int mServiceType;
/** @hide */
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index 218875e347c7..fcb4782c7f62 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -716,6 +716,7 @@ public class ImsMmTelManager implements RegistrationManager {
*
* @param imsRegTech The IMS registration technology, can be one of the following:
* {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE},
+ * {@link ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM},
* {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
* @param capability The IMS MmTel capability to query, can be one of the following:
* {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE},
@@ -750,6 +751,7 @@ public class ImsMmTelManager implements RegistrationManager {
*
* @param imsRegTech The IMS registration technology, can be one of the following:
* {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE},
+ * {@link ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM},
* {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
* @param capability The IMS MmTel capability to query, can be one of the following:
* {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE},
@@ -979,6 +981,105 @@ public class ImsMmTelManager implements RegistrationManager {
}
/**
+ * This configuration is meaningful only on dual sim device.
+ * If enabled, this will result in the device setting up IMS of all other
+ * active subscriptions over the INTERNET APN of the primary default data subscription
+ * when any of those subscriptions are roaming or out of service and if wifi is not available
+ * for VoWifi. This feature will be disabled if
+ * {@link CarrierConfigManager#KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL} is set to false.
+ * <p>Following are the conditions in which system will try to register IMS over
+ * cross sim
+ * <ul>
+ * <li>Wifi is not available, one SIM is roaming and the default data
+ * SIM is in home network. Then roaming SIM IMS will be registered over INTERNET APN of the
+ * default data subscription </li>
+ * <li>Wifi is not available, one SIM is out of service and the default data
+ * SIM is in home network. Then out of service SIM IMS will be registered over INTERNET
+ * APN of the default data subscription </li>
+ * </ul>
+ * <p>This API requires one of the following:
+ * <ul>
+ * <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
+ * <li>If the caller is the device or profile owner, the caller holds the
+ * {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
+ * <li>The caller has carrier privileges (see
+ * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
+ * active subscription.</li>
+ * </ul>
+ * <p>The profile owner is an app that owns a managed profile on the device; for more details
+ * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
+ * Access by profile owners is deprecated and will be removed in a future release.
+ *
+ * @throws ImsException if the IMS service associated with this subscription is not available or
+ * the IMS service is not available.
+ * @return true if the user's setting for Voice over Cross SIM is enabled and false if it is not
+ */
+ @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ android.Manifest.permission.READ_PRECISE_PHONE_STATE})
+ public boolean isCrossSimCallingEnabledByUser() throws ImsException {
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new ImsException("Could not find Telephony Service.",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+
+ try {
+ return iTelephony.isCrossSimCallingEnabledByUser(mSubId);
+ } catch (ServiceSpecificException sse) {
+ throw new ImsException(sse.getMessage(), sse.errorCode);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ // Not reachable. Adding return to make compiler happy.
+ return false;
+ }
+
+ /**
+ * Sets the user's setting for whether or not Voice over Cross SIM is enabled.
+ * If enabled, this will result in the device setting up IMS of all other
+ * active subscriptions over the INTERNET APN of the primary default data subscription
+ * when any of those subscriptions are roaming or out of service and if wifi is not available
+ * for VoWifi. This feature will be disabled if
+ * {@link CarrierConfigManager#KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL} is set to false.
+ *
+ * <p>Following are the conditions in which system will try to register IMS over
+ * cross sim
+ * <ul>
+ * <li>Wifi is not available, one SIM is roaming and the default data
+ * SIM is in home network. Then roaming SIM IMS will be registered over INTERNET APN of the
+ * default data subscription </li>
+ * <li>Wifi is not available, one SIM is out of service and the default data
+ * SIM is in home network. Then out of service SIM IMS will be registered over INTERNET
+ * APN of the default data subscription </li>
+ * </ul>
+ * @throws ImsException if the IMS service associated with this subscription is not available or
+ * the IMS service is not available.
+ * @param isEnabled true if the user's setting for Voice over Cross SIM is enabled,
+ * false otherwise
+ * @see #isCrossSimCallingEnabledByUser()
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void setCrossSimCallingEnabled(boolean isEnabled) throws ImsException {
+ ITelephony iTelephony = getITelephony();
+ if (iTelephony == null) {
+ throw new ImsException("Could not find Telephony Service.",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+
+ try {
+ iTelephony.setCrossSimCallingEnabled(mSubId, isEnabled);
+ } catch (ServiceSpecificException sse) {
+ throw new ImsException(sse.getMessage(), sse.errorCode);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
* Returns the user's voice over WiFi roaming setting associated with the current subscription.
*
* <p>This API requires one of the following:
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index ad461c091493..f39e30b6d61f 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -390,6 +390,7 @@ public class ImsRcsManager {
* @param capability The RCS capability to query.
* @param radioTech The radio tech that this capability failed for, defined as
* {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or
+ * {@link ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM} or
* {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}.
* @return true if the RCS capability is capable for this subscription, false otherwise. This
* does not necessarily mean that we are registered for IMS and the capability is available, but
diff --git a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
index e4d20e965c49..519d0164b0d6 100644
--- a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
+++ b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
@@ -34,7 +34,7 @@ import java.util.List;
* network during a SUBSCRIBE request. See RFC3863 for more information.
* @hide
*/
-public class RcsContactPresenceTuple implements Parcelable {
+public final class RcsContactPresenceTuple implements Parcelable {
/** The service id of the MMTEL */
public static final String SERVICE_ID_MMTEL = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.mmtel";
@@ -61,7 +61,7 @@ public class RcsContactPresenceTuple implements Parcelable {
* An optional addition to the PIDF Presence Tuple containing service capabilities, which is
* defined in the servcaps element. See RFC5196, section 3.2.1.
*/
- public static class ServiceCapabilities implements Parcelable {
+ public static final class ServiceCapabilities implements Parcelable {
/** The service can simultaneously send and receive data. */
public static final String DUPLEX_MODE_FULL = "full";
@@ -88,7 +88,7 @@ public class RcsContactPresenceTuple implements Parcelable {
/**
* Builder to help construct {@link ServiceCapabilities} instances.
*/
- public static class Builder {
+ public static final class Builder {
private ServiceCapabilities mCapabilities;
@@ -106,7 +106,7 @@ public class RcsContactPresenceTuple implements Parcelable {
* Add the supported duplex mode.
* @param mode The supported duplex mode
*/
- public Builder addSupportedDuplexMode(@NonNull @DuplexMode String mode) {
+ public @NonNull Builder addSupportedDuplexMode(@NonNull @DuplexMode String mode) {
mCapabilities.mSupportedDuplexModeList.add(mode);
return this;
}
@@ -115,7 +115,7 @@ public class RcsContactPresenceTuple implements Parcelable {
* Add the unsupported duplex mode.
* @param mode The unsupported duplex mode
*/
- public Builder addUnsupportedDuplexMode(@NonNull @DuplexMode String mode) {
+ public @NonNull Builder addUnsupportedDuplexMode(@NonNull @DuplexMode String mode) {
mCapabilities.mUnsupportedDuplexModeList.add(mode);
return this;
}
@@ -123,7 +123,7 @@ public class RcsContactPresenceTuple implements Parcelable {
/**
* @return the ServiceCapabilities instance.
*/
- public ServiceCapabilities build() {
+ public @NonNull ServiceCapabilities build() {
return mCapabilities;
}
}
@@ -211,9 +211,9 @@ public class RcsContactPresenceTuple implements Parcelable {
/**
* Builder to help construct {@link RcsContactPresenceTuple} instances.
*/
- public static class Builder {
+ public static final class Builder {
- private RcsContactPresenceTuple mPresenceTuple;
+ private final RcsContactPresenceTuple mPresenceTuple;
/**
* Builds a RcsContactPresenceTuple instance.
@@ -230,7 +230,7 @@ public class RcsContactPresenceTuple implements Parcelable {
/**
* The optional SIP Contact URI associated with the PIDF tuple element.
*/
- public Builder addContactUri(@NonNull Uri contactUri) {
+ public @NonNull Builder addContactUri(@NonNull Uri contactUri) {
mPresenceTuple.mContactUri = contactUri;
return this;
}
@@ -239,7 +239,7 @@ public class RcsContactPresenceTuple implements Parcelable {
* The optional timestamp indicating the data and time of the status change of this tuple.
* See RFC3863, section 4.1.7 for more information on the expected format.
*/
- public Builder addTimeStamp(@NonNull String timestamp) {
+ public @NonNull Builder addTimeStamp(@NonNull String timestamp) {
mPresenceTuple.mTimestamp = timestamp;
return this;
}
@@ -248,7 +248,7 @@ public class RcsContactPresenceTuple implements Parcelable {
* An optional parameter containing the description element of the service-description. See
* OMA Presence SIMPLE specification v1.1
*/
- public Builder addDescription(@NonNull String description) {
+ public @NonNull Builder addDescription(@NonNull String description) {
mPresenceTuple.mServiceDescription = description;
return this;
}
@@ -257,7 +257,7 @@ public class RcsContactPresenceTuple implements Parcelable {
* An optional parameter containing the service capabilities of the presence tuple if they
* are present in the servcaps element.
*/
- public Builder addServiceCapabilities(@NonNull ServiceCapabilities caps) {
+ public @NonNull Builder addServiceCapabilities(@NonNull ServiceCapabilities caps) {
mPresenceTuple.mServiceCapabilities = caps;
return this;
}
@@ -265,7 +265,7 @@ public class RcsContactPresenceTuple implements Parcelable {
/**
* @return the constructed instance.
*/
- public RcsContactPresenceTuple build() {
+ public @NonNull RcsContactPresenceTuple build() {
return mPresenceTuple;
}
}
diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
index 5848be8b0bf2..d4715bfeeb3e 100644
--- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java
+++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
@@ -144,7 +144,7 @@ public final class RcsContactUceCapability implements Parcelable {
* @param tag the supported feature tag
* @return this OptionBuilder
*/
- public @NonNull OptionsBuilder addFeatureTag(String tag) {
+ public @NonNull OptionsBuilder addFeatureTag(@NonNull String tag) {
mCapabilities.mFeatureTags.add(tag);
return this;
}
@@ -154,7 +154,7 @@ public final class RcsContactUceCapability implements Parcelable {
* @param tags the list of the supported feature tags
* @return this OptionBuilder
*/
- public @NonNull OptionsBuilder addFeatureTags(List<String> tags) {
+ public @NonNull OptionsBuilder addFeatureTags(@NonNull List<String> tags) {
mCapabilities.mFeatureTags.addAll(tags);
return this;
}
@@ -195,7 +195,7 @@ public final class RcsContactUceCapability implements Parcelable {
* @param tuple The {@link RcsContactPresenceTuple} to be added into.
* @return this PresenceBuilder
*/
- public @NonNull PresenceBuilder addCapabilityTuple(RcsContactPresenceTuple tuple) {
+ public @NonNull PresenceBuilder addCapabilityTuple(@NonNull RcsContactPresenceTuple tuple) {
mCapabilities.mPresenceTuples.add(tuple);
return this;
}
@@ -205,7 +205,8 @@ public final class RcsContactUceCapability implements Parcelable {
* @param tuples The list of the {@link RcsContactPresenceTuple} to be added into.
* @return this PresenceBuilder
*/
- public @NonNull PresenceBuilder addCapabilityTuples(List<RcsContactPresenceTuple> tuples) {
+ public @NonNull PresenceBuilder addCapabilityTuples(
+ @NonNull List<RcsContactPresenceTuple> tuples) {
mCapabilities.mPresenceTuples.addAll(tuples);
return this;
}
@@ -282,7 +283,7 @@ public final class RcsContactUceCapability implements Parcelable {
* @return The feature tags present in the OPTIONS response from the network.
* <p>
* Note: this is only populated if {@link #getCapabilityMechanism} is
- * {@link CAPABILITY_MECHANISM_OPTIONS}
+ * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_OPTIONS}
*/
public @NonNull List<String> getOptionsFeatureTags() {
if (mCapabilityMechanism != CAPABILITY_MECHANISM_OPTIONS) {
@@ -296,7 +297,7 @@ public final class RcsContactUceCapability implements Parcelable {
* contained in the NOTIFY response from the network.
* <p>
* Note: this is only populated if {@link #getCapabilityMechanism} is
- * {@link CAPABILITY_MECHANISM_PRESENCE}
+ * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_PRESENCE}
*/
public @NonNull List<RcsContactPresenceTuple> getPresenceTuples() {
if (mCapabilityMechanism != CAPABILITY_MECHANISM_PRESENCE) {
@@ -312,9 +313,9 @@ public final class RcsContactUceCapability implements Parcelable {
*
* <p>
* Note: this is only populated if {@link #getCapabilityMechanism} is
- * {@link CAPABILITY_MECHANISM_PRESENCE}
+ * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_PRESENCE}
*/
- public @Nullable RcsContactPresenceTuple getPresenceTuple(String serviceId) {
+ public @Nullable RcsContactPresenceTuple getPresenceTuple(@NonNull String serviceId) {
if (mCapabilityMechanism != CAPABILITY_MECHANISM_PRESENCE) {
return null;
}
diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
index 8d7742b7510b..6c31466c2a89 100644
--- a/telephony/java/android/telephony/ims/RcsUceAdapter.java
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -36,7 +36,9 @@ import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.Executor;
/**
@@ -110,7 +112,7 @@ public class RcsUceAdapter {
public static final int ERROR_FORBIDDEN = 6;
/**
- * The contact URI requested is not provisioned for VoLTE or it is not known as an IMS
+ * The contact URI requested is not provisioned for voice or it is not known as an IMS
* subscriber to the carrier network.
* @hide
*/
@@ -128,26 +130,26 @@ public class RcsUceAdapter {
* The network did not respond to the capabilities request before the request timed out.
* @hide
*/
- public static final int ERROR_REQUEST_TIMEOUT = 10;
+ public static final int ERROR_REQUEST_TIMEOUT = 9;
/**
* The request failed due to the service having insufficient memory.
* @hide
*/
- public static final int ERROR_INSUFFICIENT_MEMORY = 11;
+ public static final int ERROR_INSUFFICIENT_MEMORY = 10;
/**
* The network was lost while trying to complete the request.
* @hide
*/
- public static final int ERROR_LOST_NETWORK = 12;
+ public static final int ERROR_LOST_NETWORK = 11;
/**
* The network is temporarily unavailable or busy. Retries should only be done after the retry
* time returned in {@link CapabilitiesCallback#onError} has elapsed.
* @hide
*/
- public static final int ERROR_SERVER_UNAVAILABLE = 13;
+ public static final int ERROR_SERVER_UNAVAILABLE = 12;
/**@hide*/
@Retention(RetentionPolicy.SOURCE)
@@ -168,69 +170,93 @@ public class RcsUceAdapter {
public @interface ErrorCode {}
/**
+ * A capability update has been requested but the reason is unknown.
+ * @hide
+ */
+ @SystemApi
+ public static final int CAPABILITY_UPDATE_TRIGGER_UNKNOWN = 0;
+
+ /**
* A capability update has been requested due to the Entity Tag (ETag) expiring.
* @hide
*/
- public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 0;
+ @SystemApi
+ public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 1;
+
/**
* A capability update has been requested due to moving to LTE with VoPS disabled.
* @hide
*/
- public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED = 1;
+ @SystemApi
+ public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED = 2;
+
/**
* A capability update has been requested due to moving to LTE with VoPS enabled.
* @hide
*/
- public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED = 2;
+ @SystemApi
+ public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED = 3;
+
/**
* A capability update has been requested due to moving to eHRPD.
* @hide
*/
- public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD = 3;
+ @SystemApi
+ public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD = 4;
+
/**
* A capability update has been requested due to moving to HSPA+.
* @hide
*/
- public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS = 4;
+ @SystemApi
+ public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS = 5;
+
/**
* A capability update has been requested due to moving to 3G.
* @hide
*/
- public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 5;
+ @SystemApi
+ public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 6;
+
/**
* A capability update has been requested due to moving to 2G.
* @hide
*/
- public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 6;
+ @SystemApi
+ public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 7;
+
/**
* A capability update has been requested due to moving to WLAN
* @hide
*/
- public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN = 7;
+ @SystemApi
+ public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN = 8;
+
/**
* A capability update has been requested due to moving to IWLAN
* @hide
*/
- public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN = 8;
- /**
- * A capability update has been requested but the reason is unknown.
- * @hide
- */
- public static final int CAPABILITY_UPDATE_TRIGGER_UNKNOWN = 9;
+ @SystemApi
+ public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN = 9;
+
/**
* A capability update has been requested due to moving to 5G NR with VoPS disabled.
* @hide
*/
+ @SystemApi
public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED = 10;
+
/**
* A capability update has been requested due to moving to 5G NR with VoPS enabled.
* @hide
*/
+ @SystemApi
public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED = 11;
/**@hide*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = "ERROR_", value = {
+ CAPABILITY_UPDATE_TRIGGER_UNKNOWN,
CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED,
CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED,
CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED,
@@ -240,7 +266,6 @@ public class RcsUceAdapter {
CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G,
CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN,
CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN,
- CAPABILITY_UPDATE_TRIGGER_UNKNOWN,
CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED,
CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED
})
@@ -251,32 +276,37 @@ public class RcsUceAdapter {
* UCE.
* @hide
*/
+ @SystemApi
public static final int PUBLISH_STATE_OK = 1;
/**
* The hasn't published its capabilities since boot or hasn't gotten any publish response yet.
* @hide
*/
+ @SystemApi
public static final int PUBLISH_STATE_NOT_PUBLISHED = 2;
/**
* The device has tried to publish its capabilities, which has resulted in an error. This error
- * is related to the fact that the device is not VoLTE provisioned.
+ * is related to the fact that the device is not provisioned for voice.
* @hide
*/
- public static final int PUBLISH_STATE_VOLTE_PROVISION_ERROR = 3;
+ @SystemApi
+ public static final int PUBLISH_STATE_VOICE_PROVISION_ERROR = 3;
/**
* The device has tried to publish its capabilities, which has resulted in an error. This error
* is related to the fact that the device is not RCS or UCE provisioned.
* @hide
*/
+ @SystemApi
public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4;
/**
* The last publish resulted in a "408 Request Timeout" response.
* @hide
*/
+ @SystemApi
public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5;
/**
@@ -286,6 +316,7 @@ public class RcsUceAdapter {
* Device shall retry with exponential back-off.
* @hide
*/
+ @SystemApi
public static final int PUBLISH_STATE_OTHER_ERROR = 6;
/**@hide*/
@@ -293,7 +324,7 @@ public class RcsUceAdapter {
@IntDef(prefix = "PUBLISH_STATE_", value = {
PUBLISH_STATE_OK,
PUBLISH_STATE_NOT_PUBLISHED,
- PUBLISH_STATE_VOLTE_PROVISION_ERROR,
+ PUBLISH_STATE_VOICE_PROVISION_ERROR,
PUBLISH_STATE_RCS_PROVISION_ERROR,
PUBLISH_STATE_REQUEST_TIMEOUT,
PUBLISH_STATE_OTHER_ERROR
@@ -301,56 +332,62 @@ public class RcsUceAdapter {
public @interface PublishState {}
/**
- * An application can use {@link #registerPublishStateCallback} to register a
- * {@link PublishStateCallback), which will notify the user when the publish state to the
- * network changes.
+ * An application can use {@link #addOnPublishStateChangedListener} to register a
+ * {@link OnPublishStateChangedListener ), which will notify the user when the publish state to
+ * the network changes.
* @hide
*/
- public static class PublishStateCallback {
+ @SystemApi
+ public interface OnPublishStateChangedListener {
+ /**
+ * Notifies the callback when the publish state has changed.
+ * @param publishState The latest update to the publish state.
+ */
+ void onPublishStateChange(@PublishState int publishState);
+ }
- private static class PublishStateBinder extends IRcsUcePublishStateCallback.Stub {
+ /**
+ * An application can use {@link #addOnPublishStateChangedListener} to register a
+ * {@link OnPublishStateChangedListener ), which will notify the user when the publish state to
+ * the network changes.
+ * @hide
+ */
+ public static class PublishStateCallbackAdapter {
- private final PublishStateCallback mLocalCallback;
- private Executor mExecutor;
+ private static class PublishStateBinder extends IRcsUcePublishStateCallback.Stub {
+ private final OnPublishStateChangedListener mPublishStateChangeListener;
+ private final Executor mExecutor;
- PublishStateBinder(PublishStateCallback c) {
- mLocalCallback = c;
+ PublishStateBinder(Executor executor, OnPublishStateChangedListener listener) {
+ mExecutor = executor;
+ mPublishStateChangeListener = listener;
}
@Override
public void onPublishStateChanged(int publishState) {
- if (mLocalCallback == null) return;
+ if (mPublishStateChangeListener == null) return;
final long callingIdentity = Binder.clearCallingIdentity();
try {
- mExecutor.execute(() -> mLocalCallback.onChanged(publishState));
+ mExecutor.execute(() ->
+ mPublishStateChangeListener.onPublishStateChange(publishState));
} finally {
restoreCallingIdentity(callingIdentity);
}
}
-
- private void setExecutor(Executor executor) {
- mExecutor = executor;
- }
}
- private final PublishStateBinder mBinder = new PublishStateBinder(this);
+ private final PublishStateBinder mBinder;
+
+ public PublishStateCallbackAdapter(@NonNull Executor executor,
+ @NonNull OnPublishStateChangedListener listener) {
+ mBinder = new PublishStateBinder(executor, listener);
+ }
/**@hide*/
public final IRcsUcePublishStateCallback getBinder() {
return mBinder;
}
-
- private void setExecutor(Executor executor) {
- mBinder.setExecutor(executor);
- }
-
- /**
- * Notifies the callback when the publish state has changed.
- * @param publishState The latest update to the publish state.
- */
- public void onChanged(@PublishState int publishState) {
- }
}
/**
@@ -395,6 +432,8 @@ public class RcsUceAdapter {
private final Context mContext;
private final int mSubId;
+ private final Map<OnPublishStateChangedListener, PublishStateCallbackAdapter>
+ mPublishStateCallbacks;
/**
* Not to be instantiated directly, use {@link ImsRcsManager#getUceAdapter()} to instantiate
@@ -404,6 +443,7 @@ public class RcsUceAdapter {
RcsUceAdapter(Context context, int subId) {
mContext = context;
mSubId = subId;
+ mPublishStateCallbacks = new HashMap<>();
}
/**
@@ -588,6 +628,7 @@ public class RcsUceAdapter {
* becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public @PublishState int getUcePublishState() throws ImsException {
IImsRcsController imsRcsController = getIImsRcsController();
@@ -609,81 +650,90 @@ public class RcsUceAdapter {
}
/**
- * Registers a {@link PublishStateCallback} with the system, which will provide publish state
- * updates for the subscription specified in {@link ImsManager@getRcsManager(subid)}.
+ * Registers a {@link OnPublishStateChangedListener} with the system, which will provide publish
+ * state updates for the subscription specified in {@link ImsManager@getRcsManager(subid)}.
* <p>
* Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to subscription
* changed events and call {@link #unregisterPublishStateCallback} to clean up.
* <p>
- * The registered {@link PublishStateCallback} will also receive a callback when it is
+ * The registered {@link OnPublishStateChangedListener} will also receive a callback when it is
* registered with the current publish state.
*
* @param executor The executor the listener callback events should be run on.
- * @param c The {@link PublishStateCallback} to be added.
+ * @param listener The {@link OnPublishStateChangedListener} to be added.
* @throws ImsException if the subscription associated with this callback is valid, but
* the {@link ImsService} associated with the subscription is not available. This can happen if
* the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
* reason.
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- public void registerPublishStateCallback(@NonNull @CallbackExecutor Executor executor,
- @NonNull PublishStateCallback c) throws ImsException {
- if (c == null) {
- throw new IllegalArgumentException("Must include a non-null PublishStateCallback.");
- }
+ public void addOnPublishStateChangedListener(@NonNull @CallbackExecutor Executor executor,
+ @NonNull OnPublishStateChangedListener listener) throws ImsException {
if (executor == null) {
throw new IllegalArgumentException("Must include a non-null Executor.");
}
+ if (listener == null) {
+ throw new IllegalArgumentException(
+ "Must include a non-null OnPublishStateChangedListener.");
+ }
IImsRcsController imsRcsController = getIImsRcsController();
if (imsRcsController == null) {
- Log.e(TAG, "registerPublishStateCallback : IImsRcsController is null");
+ Log.e(TAG, "addOnPublishStateChangedListener : IImsRcsController is null");
throw new ImsException("Cannot find remote IMS service",
- ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
- c.setExecutor(executor);
+ PublishStateCallbackAdapter stateCallback = addPublishStateCallback(executor, listener);
try {
- imsRcsController.registerUcePublishStateCallback(mSubId, c.getBinder());
+ imsRcsController.registerUcePublishStateCallback(mSubId, stateCallback.getBinder());
} catch (ServiceSpecificException e) {
throw new ImsException(e.getMessage(), e.errorCode);
} catch (RemoteException e) {
Log.e(TAG, "Error calling IImsRcsController#registerUcePublishStateCallback", e);
throw new ImsException("Remote IMS Service is not available",
- ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
}
/**
- * Removes an existing {@link PublishStateCallback}.
+ * Removes an existing {@link OnPublishStateChangedListener}.
* <p>
* When the subscription associated with this callback is removed
* (SIM removed, ESIM swap,etc...), this callback will automatically be removed. If this method
* is called for an inactive subscription, it will result in a no-op.
*
- * @param c The callback to be unregistered.
+ * @param listener The callback to be unregistered.
* @throws ImsException if the subscription associated with this callback is valid, but
* the {@link ImsService} associated with the subscription is not available. This can happen if
* the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
* reason.
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- public void unregisterPublishStateCallback(@NonNull PublishStateCallback c)
- throws ImsException {
- if (c == null) {
- throw new IllegalArgumentException("Must include a non-null PublishStateCallback.");
+ public void removeOnPublishStateChangedListener(
+ @NonNull OnPublishStateChangedListener listener) throws ImsException {
+ if (listener == null) {
+ throw new IllegalArgumentException(
+ "Must include a non-null OnPublishStateChangedListener.");
}
IImsRcsController imsRcsController = getIImsRcsController();
if (imsRcsController == null) {
- Log.e(TAG, "unregisterPublishStateCallback: IImsRcsController is null");
+ Log.e(TAG, "removeOnPublishStateChangedListener: IImsRcsController is null");
throw new ImsException("Cannot find remote IMS service",
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
+ PublishStateCallbackAdapter callback = removePublishStateCallback(listener);
+ if (callback == null) {
+ return;
+ }
+
try {
- imsRcsController.unregisterUcePublishStateCallback(mSubId, c.getBinder());
+ imsRcsController.unregisterUcePublishStateCallback(mSubId, callback.getBinder());
} catch (android.os.ServiceSpecificException e) {
throw new ImsException(e.getMessage(), e.errorCode);
} catch (RemoteException e) {
@@ -763,6 +813,36 @@ public class RcsUceAdapter {
}
}
+ /**
+ * Add the {@link OnPublishStateChangedListener} to collection for tracking.
+ * @param executor The executor that will be used when the publish state is changed and the
+ * {@link OnPublishStateChangedListener} is called.
+ * @param listener The {@link OnPublishStateChangedListener} to call the publish state changed.
+ * @return The {@link PublishStateCallbackAdapter} to wrapper the
+ * {@link OnPublishStateChangedListener}
+ */
+ private PublishStateCallbackAdapter addPublishStateCallback(@NonNull Executor executor,
+ @NonNull OnPublishStateChangedListener listener) {
+ PublishStateCallbackAdapter adapter = new PublishStateCallbackAdapter(executor, listener);
+ synchronized (mPublishStateCallbacks) {
+ mPublishStateCallbacks.put(listener, adapter);
+ }
+ return adapter;
+ }
+
+ /**
+ * Remove the existing {@link OnPublishStateChangedListener}.
+ * @param listener The {@link OnPublishStateChangedListener} to remove from the collection.
+ * @return The wrapper class {@link PublishStateCallbackAdapter} associated with the
+ * {@link OnPublishStateChangedListener}.
+ */
+ private PublishStateCallbackAdapter removePublishStateCallback(
+ @NonNull OnPublishStateChangedListener listener) {
+ synchronized (mPublishStateCallbacks) {
+ return mPublishStateCallbacks.remove(listener);
+ }
+ }
+
private IImsRcsController getIImsRcsController() {
IBinder binder = TelephonyFrameworkInitializer
.getTelephonyServiceManager()
diff --git a/telephony/java/android/telephony/ims/RegistrationManager.java b/telephony/java/android/telephony/ims/RegistrationManager.java
index 1a78e166932a..8ed48388d352 100644
--- a/telephony/java/android/telephony/ims/RegistrationManager.java
+++ b/telephony/java/android/telephony/ims/RegistrationManager.java
@@ -24,6 +24,7 @@ import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.net.Uri;
import android.os.Binder;
+import android.os.Bundle;
import android.telephony.AccessNetworkConstants;
import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.telephony.ims.feature.ImsFeature;
@@ -70,6 +71,29 @@ public interface RegistrationManager {
*/
int REGISTRATION_STATE_REGISTERED = 2;
+ /**
+ * @hide
+ */
+ // Defines the underlying radio technology type that we have registered for IMS over.
+ @IntDef(prefix = "ATTR_",
+ value = {
+ ATTR_EPDG_OVER_CELL_INTERNET,
+ },
+ flag = true)
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ImsAttributes {}
+
+ /**
+ * Attribute to specify if EPDG tunnel is setup over cellular internet.
+ * if EPDG tunnel is setup over cellular internet then this bit will be set else the same will
+ * not be set.
+ */
+ int ATTR_EPDG_OVER_CELL_INTERNET = 0x00000001;
+
+ //******************************************************************************************
+ // Next attribute value: 0x00000002
+ //******************************************************************************************
+
/**@hide*/
// Translate ImsRegistrationImplBase API to new AccessNetworkConstant because WLAN
@@ -83,6 +107,11 @@ public interface RegistrationManager {
AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
put(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
+ /* As the cross sim will be using ePDG tunnel over internet, it behaves
+ like IWLAN in most cases. Hence setting the access type as IWLAN
+ */
+ put(ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM,
+ AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
}};
/**
@@ -96,6 +125,7 @@ public interface RegistrationManager {
private final RegistrationCallback mLocalCallback;
private Executor mExecutor;
+ private Bundle mBundle = new Bundle();
RegistrationBinder(RegistrationCallback localCallback) {
mLocalCallback = localCallback;
@@ -107,8 +137,18 @@ public interface RegistrationManager {
final long callingIdentity = Binder.clearCallingIdentity();
try {
+ mExecutor.execute(() -> {
+ mLocalCallback.onRegistered(getAccessType(imsRadioTech));
+ });
+ int attributes = 0;
+ if (imsRadioTech == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM) {
+ attributes = changeBitmask(attributes, ATTR_EPDG_OVER_CELL_INTERNET,
+ true);
+ }
+ final int finalattributes = attributes;
mExecutor.execute(() ->
- mLocalCallback.onRegistered(getAccessType(imsRadioTech)));
+ mLocalCallback.onRegistered(getAccessType(imsRadioTech),
+ finalattributes));
} finally {
restoreCallingIdentity(callingIdentity);
}
@@ -122,6 +162,15 @@ public interface RegistrationManager {
try {
mExecutor.execute(() ->
mLocalCallback.onRegistering(getAccessType(imsRadioTech)));
+ int attributes = 0;
+ if (imsRadioTech == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM) {
+ attributes = changeBitmask(attributes, ATTR_EPDG_OVER_CELL_INTERNET,
+ true);
+ }
+ final int finalattributes = attributes;
+ mExecutor.execute(() ->
+ mLocalCallback.onRegistering(getAccessType(imsRadioTech),
+ finalattributes));
} finally {
restoreCallingIdentity(callingIdentity);
}
@@ -175,6 +224,22 @@ public interface RegistrationManager {
}
return RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.get(regType);
}
+
+ /**
+ * Changes a attribute bit-mask to add or remove an attribute.
+ *
+ * @param bitmask The bit-mask.
+ * @param bitfield The bit-field to change.
+ * @param enabled Whether the bit-field should be set or removed.
+ * @return The bit-mask with the bit-field changed.
+ */
+ private int changeBitmask(int bitmask, int bitfield, boolean enabled) {
+ if (enabled) {
+ return bitmask | bitfield;
+ } else {
+ return bitmask & ~bitfield;
+ }
+ }
}
private final RegistrationBinder mBinder = new RegistrationBinder(this);
@@ -183,19 +248,49 @@ public interface RegistrationManager {
* Notifies the framework when the IMS Provider is registered to the IMS network.
*
* @param imsTransportType the radio access technology.
+ * @deprecated Use {@link #onRegistered(int, int)} instead.
*/
+ @Deprecated
public void onRegistered(@AccessNetworkConstants.TransportType int imsTransportType) {
}
/**
+ * Notifies the framework when the IMS Provider is registered to the IMS network
+ * with corresponding attributes
+ *
+ * @param imsTransportType the radio access technology.
+ * @param registrationAttributes IMS registration attributes as a bitmap of attributes.
+ * Possible attributes are following
+ * <ul>
+ * <li>{@link #ATTR_EPDG_OVER_CELL_INTERNET}</li>
+ * </ul>
+ *
+ */
+ public void onRegistered(@AccessNetworkConstants.TransportType int imsTransportType,
+ @ImsAttributes int registrationAttributes) {
+ }
+
+ /**
* Notifies the framework when the IMS Provider is trying to register the IMS network.
*
* @param imsTransportType the radio access technology.
+ * @deprecated Use {@link #onRegistering(int, int)} instead.
*/
public void onRegistering(@AccessNetworkConstants.TransportType int imsTransportType) {
}
/**
+ * Notifies the framework when the IMS Provider is trying to register the IMS network.
+ *
+ * @param imsTransportType the radio access technology.
+ * @param registrationAttributes IMS registration attributes as a bitmap of attributes.
+ * Possible attributes are following
+ */
+ public void onRegistering(@AccessNetworkConstants.TransportType int imsTransportType,
+ @ImsAttributes int registrationAttributes) {
+ }
+
+ /**
* Notifies the framework when the IMS Provider is unregistered from the IMS network.
*
* @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
diff --git a/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java
new file mode 100644
index 000000000000..4435640e008c
--- /dev/null
+++ b/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.aidl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.stub.CapabilityExchangeEventListener;
+import android.util.Log;
+
+import java.util.List;
+
+/**
+ * The ICapabilityExchangeEventListener wrapper class to store the listener which is registered by
+ * the framework. This wrapper class also delivers the request to the framework when receive the
+ * request from the network.
+ * @hide
+ */
+public class CapabilityExchangeAidlWrapper implements CapabilityExchangeEventListener {
+
+ private static final String LOG_TAG = "CapExchangeListener";
+
+ private final ICapabilityExchangeEventListener mListenerBinder;
+
+ public CapabilityExchangeAidlWrapper(@Nullable ICapabilityExchangeEventListener listener) {
+ mListenerBinder = listener;
+ }
+
+ /**
+ * Receives the request of publishing capabilities from the network and deliver this request
+ * to the framework via the registered capability exchange event listener.
+ */
+ public void onRequestPublishCapabilities(int publishTriggerType) {
+ ICapabilityExchangeEventListener listener = mListenerBinder;
+ if (listener == null) {
+ return;
+ }
+ try {
+ listener.onRequestPublishCapabilities(publishTriggerType);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "request publish capabilities exception: " + e);
+ }
+ }
+
+ /**
+ * Receives the unpublish notification and deliver this callback to the framework.
+ */
+ public void onUnpublish() {
+ ICapabilityExchangeEventListener listener = mListenerBinder;
+ if (listener == null) {
+ return;
+ }
+ try {
+ listener.onUnpublish();
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Unpublish exception: " + e);
+ }
+ }
+
+ /**
+ * Receives the callback of the remote capability request from the network and deliver this
+ * request to the framework.
+ */
+ public void onRemoteCapabilityRequest(@NonNull Uri contactUri,
+ @NonNull List<String> remoteCapabilities, @NonNull OptionsRequestCallback callback) {
+ ICapabilityExchangeEventListener listener = mListenerBinder;
+ if (listener == null) {
+ return;
+ }
+
+ IOptionsRequestCallback internalCallback = new IOptionsRequestCallback.Stub() {
+ @Override
+ public void respondToCapabilityRequest(RcsContactUceCapability ownCapabilities) {
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ callback.onRespondToCapabilityRequest(ownCapabilities);
+ } finally {
+ restoreCallingIdentity(callingIdentity);
+ }
+ }
+ @Override
+ public void respondToCapabilityRequestWithError(int code, String reason) {
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ callback.onRespondToCapabilityRequestWithError(code, reason);
+ } finally {
+ restoreCallingIdentity(callingIdentity);
+ }
+ }
+ };
+
+ try {
+ listener.onRemoteCapabilityRequest(contactUri, remoteCapabilities, internalCallback);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Remote capability request exception: " + e);
+ }
+ }
+}
diff --git a/telephony/java/android/telephony/ims/aidl/ICapabilityExchangeEventListener.aidl b/telephony/java/android/telephony/ims/aidl/ICapabilityExchangeEventListener.aidl
index a4ffbef9fa84..078ac919b75e 100644
--- a/telephony/java/android/telephony/ims/aidl/ICapabilityExchangeEventListener.aidl
+++ b/telephony/java/android/telephony/ims/aidl/ICapabilityExchangeEventListener.aidl
@@ -22,54 +22,15 @@ import android.telephony.ims.aidl.IOptionsRequestCallback;
import java.util.List;
/**
- * Listener interface for the ImsService to use to notify the framework of UCE events.
+ * Listener interface for the ImsService to use to notify the framework of UCE
+ * events.
+ *
+ * See CapabilityExchangeEventListener for more information.
* {@hide}
*/
oneway interface ICapabilityExchangeEventListener {
- /**
- * Trigger the framework to provide a capability update using
- * {@link RcsCapabilityExchangeImplBase#publishCapabilities}.
- * <p>
- * This is typically used when trying to generate an initial PUBLISH for a new
- * subscription to the network. The device will cache all presence publications
- * after boot until this method is called the first time.
- * @param publishTriggerType {@link StackPublishTriggerType} The reason for the
- * capability update request.
- * @throws ImsException If this {@link RcsPresenceExchangeImplBase} instance is
- * not currently connected to the framework. This can happen if the
- * {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the
- * {@link RcsFeature} has not received the
- * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare
- * cases when the Telephony stack has crashed.
- */
void onRequestPublishCapabilities(int publishTriggerType);
-
- /**
- * Notify the framework that the device's capabilities have been unpublished from the network.
- *
- * @throws ImsException If this {@link RcsPresenceExchangeImplBase} instance is not currently
- * connected to the framework. This can happen if the {@link RcsFeature} is not
- * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
- * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
- * Telephony stack has crashed.
- */
void onUnpublish();
-
- /**
- * Inform the framework of a query for this device's UCE capabilities.
- * <p>
- * The framework will respond via the
- * {@link IOptionsRequestCallback#respondToCapabilityRequest} or
- * {@link IOptionsRequestCallback#respondToCapabilityRequestWithError} method.
- * @param contactUri The URI associated with the remote contact that is requesting capabilities.
- * @param remoteCapabilities The remote contact's capability information.
- * @throws ImsException If this {@link RcsSipOptionsImplBase} instance is not currently
- * connected to the framework. This can happen if the {@link RcsFeature} is not
- * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
- * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when
- * the Telephony stack has crashed.
- */
void onRemoteCapabilityRequest(in Uri contactUri,
- in List<String> remoteCapabilities,
- IOptionsRequestCallback cb);
+ in List<String> remoteCapabilities, IOptionsRequestCallback cb);
}
diff --git a/telephony/java/android/telephony/ims/aidl/IOptionsRequestCallback.aidl b/telephony/java/android/telephony/ims/aidl/IOptionsRequestCallback.aidl
index d55670dd313b..d4d5301f38fa 100644
--- a/telephony/java/android/telephony/ims/aidl/IOptionsRequestCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IOptionsRequestCallback.aidl
@@ -33,7 +33,6 @@ oneway interface IOptionsRequestCallback {
/**
* Respond to a remote capability request from the contact specified with the
* specified error.
- * @param contactUri A URI containing the remote contact.
* @param code The SIP response code to respond with.
* @param reason A non-null String containing the reason associated with the SIP code.
*/
diff --git a/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java b/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java
index 87a6873d00b2..c5b1c90ced12 100644
--- a/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java
+++ b/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java
@@ -44,6 +44,7 @@ public final class CapabilityChangeRequest implements Parcelable {
* along with an associated technology, defined as
* {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or
* {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
+ * {@link ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM}
*/
public static class CapabilityPair {
private final int mCapability;
@@ -92,8 +93,9 @@ public final class CapabilityChangeRequest implements Parcelable {
/**
* @return the stored radio technology, defined as
- * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or
- * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
+ * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE},
+ * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} or
+ * {@link ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM}
*/
public @ImsRegistrationImplBase.ImsRegistrationTech int getRadioTech() {
return radioTech;
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index 96ca0225040f..b56aa9687aee 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -199,8 +199,9 @@ public abstract class ImsFeature {
* {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, or
* {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS}.
* @param radioTech The radio tech that this capability failed for, defined as
- * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or
- * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}.
+ * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE},
+ * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} or
+ * {@link ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM}.
* @param reason The reason this capability was unable to be changed, defined as
* {@link #CAPABILITY_ERROR_GENERIC} or {@link #CAPABILITY_SUCCESS}.
*/
@@ -336,7 +337,7 @@ public abstract class ImsFeature {
/**
* @hide
*/
- public final void initialize(Context context, int slotId) {
+ public void initialize(Context context, int slotId) {
mContext = context;
mSlotId = slotId;
}
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index cde7067e8bf3..22df921c4214 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -21,9 +21,11 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.content.Context;
import android.net.Uri;
import android.os.RemoteException;
import android.telephony.ims.RcsUceAdapter;
+import android.telephony.ims.aidl.CapabilityExchangeAidlWrapper;
import android.telephony.ims.aidl.ICapabilityExchangeEventListener;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsRcsFeature;
@@ -33,6 +35,7 @@ import android.telephony.ims.aidl.ISubscribeResponseCallback;
import android.telephony.ims.aidl.RcsOptionsResponseAidlWrapper;
import android.telephony.ims.aidl.RcsPublishResponseAidlWrapper;
import android.telephony.ims.aidl.RcsSubscribeResponseAidlWrapper;
+import android.telephony.ims.stub.CapabilityExchangeEventListener;
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.telephony.ims.stub.RcsCapabilityExchangeImplBase;
import android.telephony.ims.stub.RcsCapabilityExchangeImplBase.OptionsResponseCallback;
@@ -114,8 +117,10 @@ public class RcsFeature extends ImsFeature {
@Override
public void setCapabilityExchangeEventListener(
@Nullable ICapabilityExchangeEventListener listener) throws RemoteException {
- executeMethodAsync(() -> mReference.setCapabilityExchangeEventListener(listener),
- "setCapabilityExchangeEventListener");
+ CapabilityExchangeEventListener listenerWrapper =
+ new CapabilityExchangeAidlWrapper(listener);
+ executeMethodAsync(() -> mReference.setCapabilityExchangeEventListener(
+ mExecutor, listenerWrapper), "setCapabilityExchangeEventListener");
}
@Override
@@ -245,9 +250,10 @@ public class RcsFeature extends ImsFeature {
}
}
+ private final Executor mExecutor;
private final RcsFeatureBinder mImsRcsBinder;
private RcsCapabilityExchangeImplBase mCapabilityExchangeImpl;
- private ICapabilityExchangeEventListener mCapExchangeEventListener;
+ private CapabilityExchangeEventListener mCapExchangeEventListener;
/**
* Create a new RcsFeature.
@@ -255,26 +261,45 @@ public class RcsFeature extends ImsFeature {
* Method stubs called from the framework will be called asynchronously. To specify the
* {@link Executor} that the methods stubs will be called, use
* {@link RcsFeature#RcsFeature(Executor)} instead.
+ *
+ * @deprecated Use {@link #RcsFeature(Executor)} to create the RcsFeature.
*/
+ @Deprecated
public RcsFeature() {
super();
+ mExecutor = Runnable::run;
// Run on the Binder threads that call them.
- mImsRcsBinder = new RcsFeatureBinder(this, Runnable::run);
+ mImsRcsBinder = new RcsFeatureBinder(this, mExecutor);
}
/**
* Create a new RcsFeature using the Executor specified for methods being called by the
* framework.
- * @param executor The executor for the framework to use when making calls to this service.
- * @hide
+ * @param executor The executor for the framework to use when executing the methods overridden
+ * by the implementation of RcsFeature.
*/
public RcsFeature(@NonNull Executor executor) {
super();
if (executor == null) {
throw new IllegalArgumentException("executor can not be null.");
}
+ mExecutor = executor;
// Run on the Binder thread by default.
- mImsRcsBinder = new RcsFeatureBinder(this, executor);
+ mImsRcsBinder = new RcsFeatureBinder(this, mExecutor);
+ }
+
+ /**
+ * Called when the RcsFeature is initialized.
+ *
+ * @param context The context that is used in the ImsService.
+ * @param slotId The slot ID associated with the RcsFeature.
+ * @hide
+ */
+ @Override
+ public void initialize(Context context, int slotId) {
+ super.initialize(context, slotId);
+ // Notify that the RcsFeature is ready.
+ mExecutor.execute(() -> onFeatureReady());
}
/**
@@ -348,13 +373,26 @@ public class RcsFeature extends ImsFeature {
* operation and the RcsFeature sets the status of the capability to true using
* {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}.
*
- * @return An instance of {@link RcsCapabilityExchangeImplBase} that implements presence
+ * @param executor The executor for the framework to use when request RCS resquests to this
+ * service.
+ * @param listener A {@link CapabilityExchangeEventListener} to send the capability exchange
+ * event to the framework.
+ * @return An instance of {@link RcsCapabilityExchangeImplBase} that implements capability
* exchange if it is supported by the device.
- * @hide
*/
- public @NonNull RcsCapabilityExchangeImplBase createCapabilityExchangeImpl() {
+ public @NonNull RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(
+ @NonNull Executor executor, @NonNull CapabilityExchangeEventListener listener) {
// Base Implementation, override to implement functionality
- return new RcsCapabilityExchangeImplBase();
+ return new RcsCapabilityExchangeImplBase(executor);
+ }
+
+ /**
+ * Remove the given CapabilityExchangeImplBase instance.
+ * @param capExchangeImpl The {@link RcsCapabilityExchangeImplBase} instance to be removed.
+ */
+ public void removeCapabilityExchangeImpl(
+ @NonNull RcsCapabilityExchangeImplBase capExchangeImpl) {
+ // Override to implement the process of removing RcsCapabilityExchangeImplBase instance.
}
/**{@inheritDoc}*/
@@ -377,18 +415,58 @@ public class RcsFeature extends ImsFeature {
return mImsRcsBinder;
}
- private void setCapabilityExchangeEventListener(ICapabilityExchangeEventListener listener) {
- mCapExchangeEventListener = listener;
- if (mCapExchangeEventListener != null) {
- onFeatureReady();
+ /**
+ * Set the capability exchange listener.
+ * @param executor The executor for the framework to use when request RCS requests to this
+ * service.
+ * @param listener A {@link CapabilityExchangeEventListener} to send the capability exchange
+ * event to the framework.
+ */
+ private void setCapabilityExchangeEventListener(@NonNull Executor executor,
+ @Nullable CapabilityExchangeEventListener listener) {
+ synchronized (mLock) {
+ mCapExchangeEventListener = listener;
+ if (mCapExchangeEventListener != null) {
+ initRcsCapabilityExchangeImplBase(executor, mCapExchangeEventListener);
+ } else {
+ // Remove the RcsCapabilityExchangeImplBase instance when the capability exchange
+ // instance has been removed in the framework.
+ if (mCapabilityExchangeImpl != null) {
+ removeCapabilityExchangeImpl(mCapabilityExchangeImpl);
+ }
+ mCapabilityExchangeImpl = null;
+ }
}
}
- private RcsCapabilityExchangeImplBase getCapabilityExchangeImplBaseInternal() {
+ /**
+ * Initialize the RcsCapabilityExchangeImplBase instance if the capability exchange instance
+ * has already been created in the framework.
+ * @param executor The executor for the framework to use when request RCS requests to this
+ * service.
+ * @param listener A {@link CapabilityExchangeEventListener} to send the capability exchange
+ * event to the framework.
+ */
+ private void initRcsCapabilityExchangeImplBase(@NonNull Executor executor,
+ @NonNull CapabilityExchangeEventListener listener) {
+ synchronized (mLock) {
+ // Remove the original instance
+ if (mCapabilityExchangeImpl != null) {
+ removeCapabilityExchangeImpl(mCapabilityExchangeImpl);
+ }
+ mCapabilityExchangeImpl = createCapabilityExchangeImpl(executor, listener);
+ }
+ }
+
+ /**
+ * @return the {@link RcsCapabilityExchangeImplBase} associated with the RcsFeature.
+ */
+ private @NonNull RcsCapabilityExchangeImplBase getCapabilityExchangeImplBaseInternal() {
synchronized (mLock) {
+ // The method should not be called if the instance of RcsCapabilityExchangeImplBase has
+ // not been created yet.
if (mCapabilityExchangeImpl == null) {
- mCapabilityExchangeImpl = createCapabilityExchangeImpl();
- mCapabilityExchangeImpl.setEventListener(mCapExchangeEventListener);
+ throw new IllegalStateException("Session is not available.");
}
return mCapabilityExchangeImpl;
}
diff --git a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
new file mode 100644
index 000000000000..d2cb9761a028
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.stub;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.net.Uri;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.RcsUceAdapter;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.RcsFeature;
+
+import java.util.List;
+
+/**
+ * The interface of the capabilities event listener for ImsService to notify the framework of the
+ * UCE request and status updated.
+ * @hide
+ */
+@SystemApi
+public interface CapabilityExchangeEventListener {
+ /**
+ * Interface used by the framework to respond to OPTIONS requests.
+ * @hide
+ */
+ interface OptionsRequestCallback {
+ /**
+ * Respond to a remote capability request from the contact specified with the
+ * capabilities of this device.
+ * @param ownCapabilities The capabilities of this device.
+ */
+ void onRespondToCapabilityRequest(@NonNull RcsContactUceCapability ownCapabilities);
+
+ /**
+ * Respond to a remote capability request from the contact specified with the
+ * specified error.
+ * @param code The SIP response code to respond with.
+ * @param reason A non-null String containing the reason associated with the SIP code.
+ */
+ void onRespondToCapabilityRequestWithError(int code, @NonNull String reason);
+ }
+
+ /**
+ * Trigger the framework to provide a capability update using
+ * {@link RcsCapabilityExchangeImplBase#publishCapabilities}.
+ * <p>
+ * This is typically used when trying to generate an initial PUBLISH for a new subscription to
+ * the network. The device will cache all presence publications after boot until this method is
+ * called the first time.
+ * @param publishTriggerType {@link RcsUceAdapter#StackPublishTriggerType} The reason for the
+ * capability update request.
+ * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not currently
+ * connected to the framework. This can happen if the {@link RcsFeature} is not
+ * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
+ * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
+ * Telephony stack has crashed.
+ */
+ void onRequestPublishCapabilities(
+ @RcsUceAdapter.StackPublishTriggerType int publishTriggerType) throws ImsException;
+
+ /**
+ * Notify the framework that the device's capabilities have been unpublished
+ * from the network.
+ *
+ * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not currently
+ * connected to the framework. This can happen if the {@link RcsFeature} is not
+ * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
+ * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
+ * Telephony stack has crashed.
+ */
+ void onUnpublish() throws ImsException;
+
+ /**
+ * Inform the framework of a query for this device's UCE capabilities.
+ * <p>
+ * The framework will respond via the
+ * {@link OptionsRequestCallback#onRespondToCapabilityRequest} or
+ * {@link OptionsRequestCallback#onRespondToCapabilityRequestWithError}
+ * @param contactUri The URI associated with the remote contact that is
+ * requesting capabilities.
+ * @param remoteCapabilities The remote contact's capability information.
+ * @param callback The callback of this request which is sent from the remote user.
+ * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not
+ * currently connected to the framework. This can happen if the {@link RcsFeature} is not
+ * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
+ * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare
+ * cases when the Telephony stack has crashed.
+ * @hide
+ */
+ void onRemoteCapabilityRequest(@NonNull Uri contactUri,
+ @NonNull List<String> remoteCapabilities,
+ @NonNull OptionsRequestCallback callback) throws ImsException;
+}
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index 088a7e26a9d0..4f753c308f7e 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -55,7 +55,8 @@ public class ImsRegistrationImplBase {
@IntDef(value = {
REGISTRATION_TECH_NONE,
REGISTRATION_TECH_LTE,
- REGISTRATION_TECH_IWLAN
+ REGISTRATION_TECH_IWLAN,
+ REGISTRATION_TECH_CROSS_SIM
})
@Retention(RetentionPolicy.SOURCE)
public @interface ImsRegistrationTech {}
@@ -72,6 +73,11 @@ public class ImsRegistrationImplBase {
*/
public static final int REGISTRATION_TECH_IWLAN = 1;
+ /**
+ * IMS is registered to IMS via internet over second subscription.
+ */
+ public static final int REGISTRATION_TECH_CROSS_SIM = 2;
+
// Registration states, used to notify new ImsRegistrationImplBase#Callbacks of the current
// state.
// The unknown state is set as the initialization state. This is so that we do not call back
@@ -196,7 +202,8 @@ public class ImsRegistrationImplBase {
* Notify the framework that the device is connected to the IMS network.
*
* @param imsRadioTech the radio access technology. Valid values are defined as
- * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}.
+ * {@link #REGISTRATION_TECH_LTE}, {@link #REGISTRATION_TECH_IWLAN} and
+ * {@link #REGISTRATION_TECH_CROSS_SIM}.
*/
public final void onRegistered(@ImsRegistrationTech int imsRadioTech) {
updateToState(imsRadioTech, RegistrationManager.REGISTRATION_STATE_REGISTERED);
@@ -214,7 +221,8 @@ public class ImsRegistrationImplBase {
* Notify the framework that the device is trying to connect the IMS network.
*
* @param imsRadioTech the radio access technology. Valid values are defined as
- * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}.
+ * {@link #REGISTRATION_TECH_LTE}, {@link #REGISTRATION_TECH_IWLAN} and
+ * {@link #REGISTRATION_TECH_CROSS_SIM}.
*/
public final void onRegistering(@ImsRegistrationTech int imsRadioTech) {
updateToState(imsRadioTech, RegistrationManager.REGISTRATION_STATE_REGISTERING);
@@ -262,7 +270,8 @@ public class ImsRegistrationImplBase {
* Notify the framework that the handover from the current radio technology to the technology
* defined in {@code imsRadioTech} has failed.
* @param imsRadioTech The technology that has failed to be changed. Valid values are
- * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}.
+ * {@link #REGISTRATION_TECH_LTE}, {@link #REGISTRATION_TECH_IWLAN} and
+ * {@link #REGISTRATION_TECH_CROSS_SIM}.
* @param info The {@link ImsReasonInfo} for the failure to change technology.
*/
public final void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
@@ -329,7 +338,8 @@ public class ImsRegistrationImplBase {
/**
* @return the current registration connection type. Valid values are
- * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}
+ * {@link #REGISTRATION_TECH_LTE}, {@link #REGISTRATION_TECH_IWLAN} and
+ * {@link #REGISTRATION_TECH_CROSS_SIM}.
* @hide
*/
@VisibleForTesting
diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
index 3a0fb6edb2fb..c84e23c38e97 100644
--- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
@@ -20,20 +20,28 @@ import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
import android.net.Uri;
import android.telephony.ims.ImsException;
-import android.telephony.ims.aidl.ICapabilityExchangeEventListener;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.RcsFeature;
import android.util.Log;
import android.util.Pair;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
+import java.util.concurrent.Executor;
/**
- * Base class for different types of Capability exchange.
+ * Extend this base class to implement RCS User Capability Exchange (UCE) for the AOSP platform
+ * using the vendor ImsService.
+ * <p>
+ * See RCC.07 for more details on UCE as well as how UCE should be implemented.
* @hide
*/
+@SystemApi
public class RcsCapabilityExchangeImplBase {
private static final String LOG_TAG = "RcsCapExchangeImplBase";
@@ -70,13 +78,11 @@ public class RcsCapabilityExchangeImplBase {
/**
* Network connection is lost.
- * @hide
*/
public static final int COMMAND_CODE_LOST_NETWORK_CONNECTION = 6;
/**
* Requested feature/resource is not supported.
- * @hide
*/
public static final int COMMAND_CODE_NOT_SUPPORTED = 7;
@@ -117,7 +123,8 @@ public class RcsCapabilityExchangeImplBase {
*/
public interface PublishResponseCallback {
/**
- * Notify the framework that the command associated with this callback has failed.
+ * Notify the framework that the command associated with the
+ * {@link #publishCapabilities(String, PublishResponseCallback)} has failed.
*
* @param code The reason why the associated command has failed.
* @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
@@ -128,15 +135,15 @@ public class RcsCapabilityExchangeImplBase {
*/
void onCommandError(@CommandCode int code) throws ImsException;
-
/**
* Provide the framework with a subsequent network response update to
* {@link #publishCapabilities(String, PublishResponseCallback)}.
*
* @param code The SIP response code sent from the network for the operation
* token specified.
- * @param reason The optional reason response from the network. If the network
- * provided no reason with the code, the string should be empty.
+ * @param reason The optional reason response from the network. If there is a reason header
+ * included in the response, that should take precedence over the reason provided in the
+ * status line. If the network provided no reason with the code, the string should be empty.
* @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
* not currently connected to the framework. This can happen if the {@link RcsFeature}
* is not {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
@@ -149,6 +156,7 @@ public class RcsCapabilityExchangeImplBase {
/**
* Interface used by the framework to respond to OPTIONS requests.
+ * @hide
*/
public interface OptionsResponseCallback {
/**
@@ -171,7 +179,7 @@ public class RcsCapabilityExchangeImplBase {
* If none was sent, this should be an empty string.
* @param theirCaps the contact's UCE capabilities associated with the
* capability request.
- * @throws ImsException If this {@link RcsSipOptionsImplBase} instance is not
+ * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not
* currently connected to the framework. This can happen if the
* {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the
* {@link RcsFeature} has not received the
@@ -184,6 +192,7 @@ public class RcsCapabilityExchangeImplBase {
/**
* Interface used by the framework to receive the response of the subscribe request.
+ * @hide
*/
public interface SubscribeResponseCallback {
/**
@@ -219,17 +228,16 @@ public class RcsCapabilityExchangeImplBase {
/**
* Provides the framework with latest XML PIDF documents included in the
* network response for the requested contacts' capabilities requested by the
- * Framework using {@link #requestCapabilities(List, int)}. This should be
+ * Framework using {@link #requestCapabilities(List, int)}. This should be
* called every time a new NOTIFY event is received with new capability
* information.
*
* @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
- * not currently
- * connected to the framework. This can happen if the {@link RcsFeature} is not
- * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
- * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in
- * rare cases when the
- * Telephony stack has crashed.
+ * not currently connected to the framework.
+ * This can happen if the {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the
+ * {@link RcsFeature} {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not
+ * received the {@link ImsFeature#onFeatureReady()} callback. This may also happen in
+ * rare cases when the Telephony stack has crashed.
*/
void onNotifyCapabilitiesUpdate(@NonNull List<String> pidfXmls) throws ImsException;
@@ -250,24 +258,21 @@ public class RcsCapabilityExchangeImplBase {
* This allows the framework to know that there will no longer be any
* capability updates for the requested operationToken.
*/
- void onTerminated(String reason, long retryAfterMilliseconds) throws ImsException;
+ void onTerminated(@NonNull String reason, long retryAfterMilliseconds) throws ImsException;
}
-
- private ICapabilityExchangeEventListener mListener;
+ private final Executor mBinderExecutor;
/**
- * Set the event listener to send the request to Framework.
+ * Create a new RcsCapabilityExchangeImplBase instance.
+ *
+ * @param executor The executor that remote calls from the framework will be called on.
*/
- public void setEventListener(ICapabilityExchangeEventListener listener) {
- mListener = listener;
- }
-
- /**
- * Get the event listener.
- */
- public ICapabilityExchangeEventListener getEventListener() {
- return mListener;
+ public RcsCapabilityExchangeImplBase(@NonNull Executor executor) {
+ if (executor == null) {
+ throw new IllegalArgumentException("executor must not be null");
+ }
+ mBinderExecutor = executor;
}
/**
@@ -284,7 +289,10 @@ public class RcsCapabilityExchangeImplBase {
* @param uris A {@link List} of the {@link Uri}s that the framework is requesting the UCE
* capabilities for.
* @param cb The callback of the subscribe request.
+ * @hide
*/
+ // executor used is defined in the constructor.
+ @SuppressLint("ExecutorRegistration")
public void subscribeForCapabilities(@NonNull List<Uri> uris,
@NonNull SubscribeResponseCallback cb) {
// Stub - to be implemented by service
@@ -300,11 +308,13 @@ public class RcsCapabilityExchangeImplBase {
* The capabilities of this device have been updated and should be published to the network.
* <p>
* If this operation succeeds, network response updates should be sent to the framework using
- * {@link #onNetworkResponse(int, String)}.
+ * {@link PublishResponseCallback#onNetworkResponse(int, String)}.
* @param pidfXml The XML PIDF document containing the capabilities of this device to be sent
* to the carrier’s presence server.
* @param cb The callback of the publish request
*/
+ // executor used is defined in the constructor.
+ @SuppressLint("ExecutorRegistration")
public void publishCapabilities(@NonNull String pidfXml, @NonNull PublishResponseCallback cb) {
// Stub - to be implemented by service
Log.w(LOG_TAG, "publishCapabilities called with no implementation.");
@@ -324,7 +334,10 @@ public class RcsCapabilityExchangeImplBase {
* @param contactUri The URI of the remote user that we wish to get the capabilities of.
* @param myCapabilities The capabilities of this device to send to the remote user.
* @param callback The callback of this request which is sent from the remote user.
+ * @hide
*/
+ // executor used is defined in the constructor.
+ @SuppressLint("ExecutorRegistration")
public void sendOptionsCapabilityRequest(@NonNull Uri contactUri,
@NonNull List<String> myCapabilities, @NonNull OptionsResponseCallback callback) {
// Stub - to be implemented by service
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 74753ca1560a..83fb38be820d 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -629,7 +629,7 @@ interface ITelephony {
* successful iccOpenLogicalChannel.
* @return true if the channel was closed successfully.
*/
- @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
+ @UnsupportedAppUsage(trackingBug = 171933273)
boolean iccCloseLogicalChannel(int subId, int channel);
/**
@@ -671,7 +671,7 @@ interface ITelephony {
* @return The APDU response from the ICC card with the status appended at
* the end.
*/
- @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
+ @UnsupportedAppUsage(trackingBug = 171933273)
String iccTransmitApduLogicalChannel(int subId, int channel, int cla, int instruction,
int p1, int p2, int p3, String data);
@@ -1261,6 +1261,9 @@ interface ITelephony {
*/
int getRadioAccessFamily(in int phoneId, String callingPackage);
+ void uploadCallComposerPicture(int subscriptionId, String callingPackage,
+ in ParcelFileDescriptor fd, in ResultReceiver callback);
+
/**
* Enables or disables video calling.
*
@@ -1964,6 +1967,16 @@ interface ITelephony {
void setVoWiFiSettingEnabled(int subId, boolean isEnabled);
/**
+ * return true if the user's setting for Voice over Cross SIM is enabled and false if it is not
+ */
+ boolean isCrossSimCallingEnabledByUser(int subId);
+
+ /**
+ * Sets the user's setting for whether or not Voice over Cross SIM is enabled.
+ */
+ void setCrossSimCallingEnabled(int subId, boolean isEnabled);
+
+ /**
* return true if the user's setting for Voice over WiFi while roaming is enabled.
*/
boolean isVoWiFiRoamingSettingEnabled(int subId);
diff --git a/tests/net/common/Android.bp b/tests/net/common/Android.bp
index 373aac604b2a..c271f49ee537 100644
--- a/tests/net/common/Android.bp
+++ b/tests/net/common/Android.bp
@@ -24,6 +24,7 @@ java_library {
"androidx.test.rules",
"junit",
"mockito-target-minus-junit4",
+ "modules-utils-build",
"net-tests-utils",
"net-utils-framework-common",
"platform-test-annotations",
diff --git a/tests/net/common/java/android/net/CaptivePortalDataTest.kt b/tests/net/common/java/android/net/CaptivePortalDataTest.kt
index bd1847b7c440..8710d23730b6 100644
--- a/tests/net/common/java/android/net/CaptivePortalDataTest.kt
+++ b/tests/net/common/java/android/net/CaptivePortalDataTest.kt
@@ -41,13 +41,14 @@ class CaptivePortalDataTest {
.setBytesRemaining(456L)
.setExpiryTime(789L)
.setCaptive(true)
+ .setVenueFriendlyName("venue friendly name")
.build()
private fun makeBuilder() = CaptivePortalData.Builder(data)
@Test
fun testParcelUnparcel() {
- assertParcelSane(data, fieldCount = 7)
+ assertParcelSane(data, fieldCount = 8)
assertParcelingIsLossless(makeBuilder().setUserPortalUrl(null).build())
assertParcelingIsLossless(makeBuilder().setVenueInfoUrl(null).build())
@@ -66,6 +67,8 @@ class CaptivePortalDataTest {
assertNotEqualsAfterChange { it.setBytesRemaining(789L) }
assertNotEqualsAfterChange { it.setExpiryTime(12L) }
assertNotEqualsAfterChange { it.setCaptive(false) }
+ assertNotEqualsAfterChange { it.setVenueFriendlyName("another friendly name") }
+ assertNotEqualsAfterChange { it.setVenueFriendlyName(null) }
}
@Test
@@ -108,6 +111,11 @@ class CaptivePortalDataTest {
assertFalse(makeBuilder().setCaptive(false).build().isCaptive)
}
+ @Test
+ fun testVenueFriendlyName() {
+ assertEquals("venue friendly name", data.venueFriendlyName)
+ }
+
private fun CaptivePortalData.mutate(mutator: (CaptivePortalData.Builder) -> Unit) =
CaptivePortalData.Builder(this).apply { mutator(this) }.build()
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index 6b7ea66df233..5d0e016d50fa 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -42,9 +42,11 @@ import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
import static android.net.NetworkCapabilities.UNRESTRICTED_CAPABILITIES;
+import static android.os.Process.INVALID_UID;
import static com.android.testutils.ParcelUtils.assertParcelSane;
import static com.android.testutils.ParcelUtils.assertParcelingIsLossless;
+import static com.android.testutils.ParcelUtils.parcelingRoundTrip;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -53,18 +55,19 @@ import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+import android.net.wifi.WifiInfo;
import android.net.wifi.aware.DiscoverySession;
import android.net.wifi.aware.PeerHandle;
import android.net.wifi.aware.WifiAwareNetworkSpecifier;
import android.os.Build;
-import android.os.Process;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.ArraySet;
-import androidx.core.os.BuildCompat;
import androidx.test.runner.AndroidJUnit4;
+import com.android.modules.utils.build.SdkLevel;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
@@ -89,10 +92,11 @@ public class NetworkCapabilitiesTest {
private PeerHandle mPeerHandle = Mockito.mock(PeerHandle.class);
private boolean isAtLeastR() {
- // BuildCompat.isAtLeastR() is used to check the Android version before releasing Android R.
- // Build.VERSION.SDK_INT > Build.VERSION_CODES.Q is used to check the Android version after
- // releasing Android R.
- return BuildCompat.isAtLeastR() || Build.VERSION.SDK_INT > Build.VERSION_CODES.Q;
+ return SdkLevel.isAtLeastR();
+ }
+
+ private boolean isAtLeastS() {
+ return SdkLevel.isAtLeastS();
}
@Test
@@ -324,8 +328,59 @@ public class NetworkCapabilitiesTest {
testParcelSane(netCap);
}
+ private NetworkCapabilities createNetworkCapabilitiesWithWifiInfo() {
+ // uses a real WifiInfo to test parceling of sensitive data.
+ final WifiInfo wifiInfo = new WifiInfo.Builder()
+ .setSsid("sssid1234".getBytes())
+ .setBssid("00:11:22:33:44:55")
+ .build();
+ return new NetworkCapabilities()
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .addCapability(NET_CAPABILITY_EIMS)
+ .addCapability(NET_CAPABILITY_NOT_METERED)
+ .setSSID(TEST_SSID)
+ .setTransportInfo(wifiInfo)
+ .setRequestorPackageName("com.android.test")
+ .setRequestorUid(9304);
+ }
+
+ @Test
+ public void testParcelNetworkCapabilitiesWithLocationSensitiveFields() {
+ assumeTrue(isAtLeastS());
+
+ final NetworkCapabilities netCap = createNetworkCapabilitiesWithWifiInfo();
+ final NetworkCapabilities netCapWithLocationSensitiveFields =
+ new NetworkCapabilities(netCap, true);
+
+ assertParcelingIsLossless(netCapWithLocationSensitiveFields);
+ testParcelSane(netCapWithLocationSensitiveFields);
+
+ assertEquals(netCapWithLocationSensitiveFields,
+ parcelingRoundTrip(netCapWithLocationSensitiveFields));
+ }
+
+ @Test
+ public void testParcelNetworkCapabilitiesWithoutLocationSensitiveFields() {
+ assumeTrue(isAtLeastS());
+
+ final NetworkCapabilities netCap = createNetworkCapabilitiesWithWifiInfo();
+ final NetworkCapabilities netCapWithoutLocationSensitiveFields =
+ new NetworkCapabilities(netCap, false);
+
+ final NetworkCapabilities sanitizedNetCap =
+ new NetworkCapabilities(netCapWithoutLocationSensitiveFields);
+ final WifiInfo sanitizedWifiInfo = new WifiInfo.Builder()
+ .setSsid(new byte[0])
+ .setBssid(WifiInfo.DEFAULT_MAC_ADDRESS)
+ .build();
+ sanitizedNetCap.setTransportInfo(sanitizedWifiInfo);
+ assertEquals(sanitizedNetCap, parcelingRoundTrip(netCapWithoutLocationSensitiveFields));
+ }
+
private void testParcelSane(NetworkCapabilities cap) {
- if (isAtLeastR()) {
+ if (isAtLeastS()) {
+ assertParcelSane(cap, 16);
+ } else if (isAtLeastR()) {
assertParcelSane(cap, 15);
} else {
assertParcelSane(cap, 11);
@@ -639,26 +694,23 @@ public class NetworkCapabilitiesTest {
// Sequence 1: Transport + Transport + TransportInfo
NetworkCapabilities nc1 = new NetworkCapabilities();
nc1.addTransportType(TRANSPORT_CELLULAR).addTransportType(TRANSPORT_WIFI)
- .setTransportInfo(new TransportInfo() {});
+ .setTransportInfo(new TestTransportInfo());
// Sequence 2: Transport + NetworkSpecifier + Transport
NetworkCapabilities nc2 = new NetworkCapabilities();
- nc2.addTransportType(TRANSPORT_CELLULAR).setTransportInfo(new TransportInfo() {})
+ nc2.addTransportType(TRANSPORT_CELLULAR).setTransportInfo(new TestTransportInfo())
.addTransportType(TRANSPORT_WIFI);
}
@Test
public void testCombineTransportInfo() {
NetworkCapabilities nc1 = new NetworkCapabilities();
- nc1.setTransportInfo(new TransportInfo() {
- // empty
- });
+ nc1.setTransportInfo(new TestTransportInfo());
+
NetworkCapabilities nc2 = new NetworkCapabilities();
// new TransportInfo so that object is not #equals to nc1's TransportInfo (that's where
// combine fails)
- nc2.setTransportInfo(new TransportInfo() {
- // empty
- });
+ nc2.setTransportInfo(new TestTransportInfo());
try {
nc1.combineCapabilities(nc2);
@@ -761,7 +813,7 @@ public class NetworkCapabilitiesTest {
// Test default owner uid.
// If the owner uid is not set, the default value should be Process.INVALID_UID.
final NetworkCapabilities nc1 = new NetworkCapabilities.Builder().build();
- assertEquals(Process.INVALID_UID, nc1.getOwnerUid());
+ assertEquals(INVALID_UID, nc1.getOwnerUid());
// Test setAdministratorUids and getAdministratorUids.
final int[] administratorUids = {1001, 10001};
final NetworkCapabilities nc2 = new NetworkCapabilities.Builder()
@@ -906,6 +958,16 @@ public class NetworkCapabilitiesTest {
private class TestTransportInfo implements TransportInfo {
TestTransportInfo() {
}
+
+ @Override
+ public TransportInfo makeCopy(boolean parcelLocationSensitiveFields) {
+ return this;
+ }
+
+ @Override
+ public boolean hasLocationSensitiveFields() {
+ return false;
+ }
}
@Test @IgnoreUpTo(Build.VERSION_CODES.Q)
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index e318207cdc75..31d566d4bd47 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -63,6 +63,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
import static android.net.NetworkCapabilities.NET_CAPABILITY_RCS;
@@ -161,7 +162,6 @@ import android.net.DataStallReportParcelable;
import android.net.EthernetManager;
import android.net.IConnectivityDiagnosticsCallback;
import android.net.IDnsResolver;
-import android.net.IIpConnectivityMetrics;
import android.net.INetd;
import android.net.INetworkMonitor;
import android.net.INetworkMonitorCallbacks;
@@ -202,6 +202,7 @@ import android.net.metrics.IpConnectivityLog;
import android.net.shared.NetworkMonitorUtils;
import android.net.shared.PrivateDnsConfig;
import android.net.util.MultinetworkPolicyTracker;
+import android.net.wifi.WifiInfo;
import android.os.BadParcelableException;
import android.os.Binder;
import android.os.Build;
@@ -344,6 +345,11 @@ public class ConnectivityServiceTest {
private static final String INTERFACE_NAME = "interface";
+ private static final String TEST_VENUE_URL_NA = "https://android.com/";
+ private static final String TEST_VENUE_URL_CAPPORT = "https://android.com/capport/";
+ private static final String TEST_FRIENDLY_NAME = "Network friendly name";
+ private static final String TEST_REDIRECT_URL = "http://example.com/firstPath";
+
private MockContext mServiceContext;
private HandlerThread mCsHandlerThread;
private ConnectivityService.Dependencies mDeps;
@@ -359,7 +365,6 @@ public class ConnectivityServiceTest {
private HandlerThread mAlarmManagerThread;
private TestNetIdManager mNetIdManager;
- @Mock IIpConnectivityMetrics mIpConnectivityMetrics;
@Mock IpConnectivityMetrics.Logger mMetricsService;
@Mock DefaultNetworkMetrics mDefaultNetworkMetrics;
@Mock DeviceIdleInternal mDeviceIdleInternal;
@@ -869,7 +874,7 @@ public class ConnectivityServiceTest {
mProbesSucceeded = probesSucceeded;
}
- void notifyCaptivePortalDataChanged(CaptivePortalData data) {
+ void notifyCapportApiDataChanged(CaptivePortalData data) {
try {
mNmCallbacks.notifyCaptivePortalDataChanged(data);
} catch (RemoteException e) {
@@ -1373,7 +1378,6 @@ public class ConnectivityServiceTest {
doReturn(mock(ProxyTracker.class)).when(deps).makeProxyTracker(any(), any());
doReturn(mMetricsService).when(deps).getMetricsLogger();
doReturn(true).when(deps).queryUserAccess(anyInt(), anyInt());
- doReturn(mIpConnectivityMetrics).when(deps).getIpConnectivityMetrics();
doReturn(mBatteryStatsService).when(deps).getBatteryStatsService();
doAnswer(inv -> {
mPolicyTracker = new WrappedMultinetworkPolicyTracker(
@@ -2007,7 +2011,7 @@ public class ConnectivityServiceTest {
Objects.equals(expectedCapportUrl, lp.getCaptivePortalApiUrl()));
final CaptivePortalData expectedCapportData = sanitized ? null : capportData;
- mWiFiNetworkAgent.notifyCaptivePortalDataChanged(capportData);
+ mWiFiNetworkAgent.notifyCapportApiDataChanged(capportData);
callback.expectLinkPropertiesThat(mWiFiNetworkAgent, lp ->
Objects.equals(expectedCapportData, lp.getCaptivePortalData()));
defaultCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, lp ->
@@ -3045,7 +3049,7 @@ public class ConnectivityServiceTest {
.setBytesRemaining(12345L)
.build();
- mWiFiNetworkAgent.notifyCaptivePortalDataChanged(testData);
+ mWiFiNetworkAgent.notifyCapportApiDataChanged(testData);
captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
lp -> testData.equals(lp.getCaptivePortalData()));
@@ -3058,6 +3062,136 @@ public class ConnectivityServiceTest {
lp -> testData.equals(lp.getCaptivePortalData()) && lp.getMtu() == 1234);
}
+ private TestNetworkCallback setupNetworkCallbackAndConnectToWifi() throws Exception {
+ // Grant NETWORK_SETTINGS permission to be able to receive LinkProperties change callbacks
+ // with sensitive (captive portal) data
+ mServiceContext.setPermission(
+ android.Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED);
+
+ final TestNetworkCallback captivePortalCallback = new TestNetworkCallback();
+ final NetworkRequest captivePortalRequest = new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_CAPTIVE_PORTAL).build();
+ mCm.registerNetworkCallback(captivePortalRequest, captivePortalCallback);
+
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+
+ mWiFiNetworkAgent.connectWithCaptivePortal(TEST_REDIRECT_URL, false /* isStrictMode */);
+ captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ return captivePortalCallback;
+ }
+
+ private class CaptivePortalTestData {
+ CaptivePortalTestData(CaptivePortalData naData, CaptivePortalData capportData,
+ CaptivePortalData expectedMergedData) {
+ mNaData = naData;
+ mCapportData = capportData;
+ mExpectedMergedData = expectedMergedData;
+ }
+
+ public final CaptivePortalData mNaData;
+ public final CaptivePortalData mCapportData;
+ public final CaptivePortalData mExpectedMergedData;
+ }
+
+ private CaptivePortalTestData setupCaptivePortalData() {
+ final CaptivePortalData capportData = new CaptivePortalData.Builder()
+ .setUserPortalUrl(Uri.parse(TEST_REDIRECT_URL))
+ .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_CAPPORT))
+ .setExpiryTime(1000000L)
+ .setBytesRemaining(12345L)
+ .build();
+
+ final CaptivePortalData naData = new CaptivePortalData.Builder()
+ .setBytesRemaining(80802L)
+ .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_NA))
+ .setVenueFriendlyName(TEST_FRIENDLY_NAME).build();
+
+ final CaptivePortalData expectedMergedData = new CaptivePortalData.Builder()
+ .setUserPortalUrl(Uri.parse(TEST_REDIRECT_URL))
+ .setBytesRemaining(12345L)
+ .setExpiryTime(1000000L)
+ .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_NA))
+ .setVenueFriendlyName(TEST_FRIENDLY_NAME).build();
+
+ return new CaptivePortalTestData(naData, capportData, expectedMergedData);
+ }
+
+ @Test
+ public void testMergeCaptivePortalApiWithFriendlyNameAndVenueUrl() throws Exception {
+ final TestNetworkCallback captivePortalCallback = setupNetworkCallbackAndConnectToWifi();
+ final CaptivePortalTestData captivePortalTestData = setupCaptivePortalData();
+
+ // Baseline capport data
+ mWiFiNetworkAgent.notifyCapportApiDataChanged(captivePortalTestData.mCapportData);
+
+ captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
+ lp -> captivePortalTestData.mCapportData.equals(lp.getCaptivePortalData()));
+
+ // Venue URL and friendly name from Network agent, confirm that API data gets precedence
+ // on the bytes remaining.
+ final LinkProperties linkProperties = new LinkProperties();
+ linkProperties.setCaptivePortalData(captivePortalTestData.mNaData);
+ mWiFiNetworkAgent.sendLinkProperties(linkProperties);
+
+ // Make sure that the capport data is merged
+ captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
+ lp -> captivePortalTestData.mExpectedMergedData.equals(lp.getCaptivePortalData()));
+
+ // Create a new LP with no Network agent capport data
+ final LinkProperties newLps = new LinkProperties();
+ newLps.setMtu(1234);
+ mWiFiNetworkAgent.sendLinkProperties(newLps);
+ // CaptivePortalData is not lost and has the original values when LPs are received from the
+ // NetworkAgent
+ captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
+ lp -> captivePortalTestData.mCapportData.equals(lp.getCaptivePortalData())
+ && lp.getMtu() == 1234);
+
+ // Now send capport data only from the Network agent
+ mWiFiNetworkAgent.notifyCapportApiDataChanged(null);
+ captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
+ lp -> lp.getCaptivePortalData() == null);
+
+ newLps.setCaptivePortalData(captivePortalTestData.mNaData);
+ mWiFiNetworkAgent.sendLinkProperties(newLps);
+
+ // Make sure that only the network agent capport data is available
+ captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
+ lp -> captivePortalTestData.mNaData.equals(lp.getCaptivePortalData()));
+ }
+
+ @Test
+ public void testMergeCaptivePortalDataFromNetworkAgentFirstThenCapport() throws Exception {
+ final TestNetworkCallback captivePortalCallback = setupNetworkCallbackAndConnectToWifi();
+ final CaptivePortalTestData captivePortalTestData = setupCaptivePortalData();
+
+ // Venue URL and friendly name from Network agent, confirm that API data gets precedence
+ // on the bytes remaining.
+ final LinkProperties linkProperties = new LinkProperties();
+ linkProperties.setCaptivePortalData(captivePortalTestData.mNaData);
+ mWiFiNetworkAgent.sendLinkProperties(linkProperties);
+
+ // Make sure that the data is saved correctly
+ captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
+ lp -> captivePortalTestData.mNaData.equals(lp.getCaptivePortalData()));
+
+ // Expected merged data: Network agent data is preferred, and values that are not used by
+ // it are merged from capport data
+ mWiFiNetworkAgent.notifyCapportApiDataChanged(captivePortalTestData.mCapportData);
+
+ // Make sure that the Capport data is merged correctly
+ captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
+ lp -> captivePortalTestData.mExpectedMergedData.equals(lp.getCaptivePortalData()));
+
+ // Now set the naData to null
+ linkProperties.setCaptivePortalData(null);
+ mWiFiNetworkAgent.sendLinkProperties(linkProperties);
+
+ // Make sure that the Capport data is retained correctly
+ captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
+ lp -> captivePortalTestData.mCapportData.equals(lp.getCaptivePortalData()));
+ }
+
private NetworkRequest.Builder newWifiRequestBuilder() {
return new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI);
}
@@ -3363,6 +3497,7 @@ public class ConnectivityServiceTest {
assertEquals(null, mCm.getActiveNetwork());
mMockVpn.establishForMyUid();
+ assertUidRangesUpdatedForMyUid(true);
defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
@@ -5053,6 +5188,7 @@ public class ConnectivityServiceTest {
lp.setInterfaceName(VPN_IFNAME);
mMockVpn.establishForMyUid(lp);
+ assertUidRangesUpdatedForMyUid(true);
final Network[] cellAndVpn = new Network[] {
mCellNetworkAgent.getNetwork(), mMockVpn.getNetwork()};
@@ -5638,6 +5774,7 @@ public class ConnectivityServiceTest {
// (and doing so is difficult without using reflection) but it's good to test that the code
// behaves approximately correctly.
mMockVpn.establishForMyUid(false, true, false);
+ assertUidRangesUpdatedForMyUid(true);
final Network wifiNetwork = new Network(mNetIdManager.peekNextNetId());
mService.setUnderlyingNetworksForVpn(new Network[]{wifiNetwork});
callback.expectAvailableCallbacksUnvalidated(mMockVpn);
@@ -5795,6 +5932,7 @@ public class ConnectivityServiceTest {
mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */,
false /* isStrictMode */);
+ assertUidRangesUpdatedForMyUid(true);
defaultCallback.assertNoCallback();
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
@@ -5820,6 +5958,7 @@ public class ConnectivityServiceTest {
mMockVpn.establishForMyUid(true /* validated */, true /* hasInternet */,
false /* isStrictMode */);
+ assertUidRangesUpdatedForMyUid(true);
defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
@@ -5845,6 +5984,7 @@ public class ConnectivityServiceTest {
// Bring up a VPN that has the INTERNET capability, initially unvalidated.
mMockVpn.establishForMyUid(false /* validated */, true /* hasInternet */,
false /* isStrictMode */);
+ assertUidRangesUpdatedForMyUid(true);
// Even though the VPN is unvalidated, it becomes the default network for our app.
callback.expectAvailableCallbacksUnvalidated(mMockVpn);
@@ -5896,6 +6036,7 @@ public class ConnectivityServiceTest {
mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */,
false /* isStrictMode */);
+ assertUidRangesUpdatedForMyUid(true);
vpnNetworkCallback.expectAvailableCallbacks(mMockVpn.getNetwork(),
false /* suspended */, false /* validated */, false /* blocked */, TIMEOUT_MS);
@@ -5937,6 +6078,7 @@ public class ConnectivityServiceTest {
mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */,
false /* isStrictMode */);
+ assertUidRangesUpdatedForMyUid(true);
vpnNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
@@ -6104,6 +6246,7 @@ public class ConnectivityServiceTest {
mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */,
false /* isStrictMode */);
+ assertUidRangesUpdatedForMyUid(true);
vpnNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
@@ -6162,6 +6305,7 @@ public class ConnectivityServiceTest {
// Bring up a VPN
mMockVpn.establishForMyUid();
+ assertUidRangesUpdatedForMyUid(true);
callback.expectAvailableThenValidatedCallbacks(mMockVpn);
callback.assertNoCallback();
@@ -6316,6 +6460,7 @@ public class ConnectivityServiceTest {
// Connect VPN network. By default it is using current default network (Cell).
mMockVpn.establishForMyUid();
+ assertUidRangesUpdatedForMyUid(true);
// Ensure VPN is now the active network.
assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
@@ -6368,6 +6513,7 @@ public class ConnectivityServiceTest {
// Connect VPN network.
mMockVpn.establishForMyUid();
+ assertUidRangesUpdatedForMyUid(true);
// Ensure VPN is now the active network.
assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
@@ -6742,6 +6888,7 @@ public class ConnectivityServiceTest {
assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
mMockVpn.establishForMyUid();
+ assertUidRangesUpdatedForMyUid(true);
defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
vpnUidCallback.assertNoCallback(); // vpnUidCallback has NOT_VPN capability.
assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
@@ -6761,36 +6908,58 @@ public class ConnectivityServiceTest {
mCm.unregisterNetworkCallback(vpnUidCallback);
}
+ /**
+ * Test mutable and requestable network capabilities such as
+ * {@link NetworkCapabilities#NET_CAPABILITY_TRUSTED} and
+ * {@link NetworkCapabilities#NET_CAPABILITY_NOT_VCN_MANAGED}. Verify that the
+ * {@code ConnectivityService} re-assign the networks accordingly.
+ */
@Test
- public final void testLoseTrusted() throws Exception {
- final NetworkRequest trustedRequest = new NetworkRequest.Builder()
- .addCapability(NET_CAPABILITY_TRUSTED)
- .build();
- final TestNetworkCallback trustedCallback = new TestNetworkCallback();
- mCm.requestNetwork(trustedRequest, trustedCallback);
-
- mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
- mCellNetworkAgent.connect(true);
- trustedCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
- verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
- reset(mMockNetd);
+ public final void testLoseMutableAndRequestableCaps() throws Exception {
+ final int[] testCaps = new int [] {
+ NET_CAPABILITY_TRUSTED,
+ NET_CAPABILITY_NOT_VCN_MANAGED
+ };
+ for (final int testCap : testCaps) {
+ // Create requests with and without the testing capability.
+ final TestNetworkCallback callbackWithCap = new TestNetworkCallback();
+ final TestNetworkCallback callbackWithoutCap = new TestNetworkCallback();
+ mCm.requestNetwork(new NetworkRequest.Builder().addCapability(testCap).build(),
+ callbackWithCap);
+ mCm.requestNetwork(new NetworkRequest.Builder().removeCapability(testCap).build(),
+ callbackWithoutCap);
+
+ // Setup networks with testing capability and verify the default network changes.
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+ callbackWithCap.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ callbackWithoutCap.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
+ reset(mMockNetd);
- mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
- mWiFiNetworkAgent.connect(true);
- trustedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
- verify(mMockNetd).networkSetDefault(eq(mWiFiNetworkAgent.getNetwork().netId));
- reset(mMockNetd);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(true);
+ callbackWithCap.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+ callbackWithoutCap.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+ verify(mMockNetd).networkSetDefault(eq(mWiFiNetworkAgent.getNetwork().netId));
+ reset(mMockNetd);
- mWiFiNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED);
- trustedCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
- verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
- reset(mMockNetd);
+ // Remove the testing capability on wifi, verify the callback and default network
+ // changes back to cellular.
+ mWiFiNetworkAgent.removeCapability(testCap);
+ callbackWithCap.expectAvailableCallbacksValidated(mCellNetworkAgent);
+ callbackWithoutCap.expectCapabilitiesWithout(testCap, mWiFiNetworkAgent);
+ verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
+ reset(mMockNetd);
- mCellNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED);
- trustedCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
- verify(mMockNetd).networkClearDefault();
+ mCellNetworkAgent.removeCapability(testCap);
+ callbackWithCap.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ callbackWithoutCap.assertNoCallback();
+ verify(mMockNetd).networkClearDefault();
- mCm.unregisterNetworkCallback(trustedCallback);
+ mCm.unregisterNetworkCallback(callbackWithCap);
+ mCm.unregisterNetworkCallback(callbackWithoutCap);
+ }
}
@Ignore // 40%+ flakiness : figure out why and re-enable.
@@ -7260,7 +7429,7 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.connect(true);
networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
verify(mNetworkManagementService, times(1)).addIdleTimer(eq(MOBILE_IFNAME), anyInt(),
- eq(ConnectivityManager.TYPE_MOBILE));
+ eq(NetworkCapabilities.TRANSPORT_CELLULAR));
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
final LinkProperties wifiLp = new LinkProperties();
@@ -7274,7 +7443,7 @@ public class ConnectivityServiceTest {
networkCallback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
verify(mNetworkManagementService, times(1)).addIdleTimer(eq(WIFI_IFNAME), anyInt(),
- eq(ConnectivityManager.TYPE_WIFI));
+ eq(NetworkCapabilities.TRANSPORT_WIFI));
verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(MOBILE_IFNAME));
// Disconnect wifi and switch back to cell
@@ -7284,7 +7453,7 @@ public class ConnectivityServiceTest {
assertNoCallbacks(networkCallback);
verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(WIFI_IFNAME));
verify(mNetworkManagementService, times(1)).addIdleTimer(eq(MOBILE_IFNAME), anyInt(),
- eq(ConnectivityManager.TYPE_MOBILE));
+ eq(NetworkCapabilities.TRANSPORT_CELLULAR));
// reconnect wifi
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
@@ -7399,6 +7568,7 @@ public class ConnectivityServiceTest {
LinkProperties testLinkProperties = new LinkProperties();
testLinkProperties.setHttpProxy(testProxyInfo);
mMockVpn.establishForMyUid(testLinkProperties);
+ assertUidRangesUpdatedForMyUid(true);
// Test that the VPN network returns a proxy, and the WiFi does not.
assertEquals(testProxyInfo, mService.getProxyForNetwork(mMockVpn.getNetwork()));
@@ -7436,6 +7606,7 @@ public class ConnectivityServiceTest {
// The uid range needs to cover the test app so the network is visible to it.
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
mMockVpn.establish(lp, VPN_UID, vpnRange);
+ assertVpnUidRangesUpdated(true, vpnRange, VPN_UID);
// A connected VPN should have interface rules set up. There are two expected invocations,
// one during the VPN initial connection, one during the VPN LinkProperties update.
@@ -7463,6 +7634,7 @@ public class ConnectivityServiceTest {
// The uid range needs to cover the test app so the network is visible to it.
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange);
+ assertVpnUidRangesUpdated(true, vpnRange, Process.SYSTEM_UID);
// Legacy VPN should not have interface rules set up
verify(mMockNetd, never()).firewallAddUidInterfaceRules(any(), any());
@@ -7478,6 +7650,7 @@ public class ConnectivityServiceTest {
// The uid range needs to cover the test app so the network is visible to it.
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange);
+ assertVpnUidRangesUpdated(true, vpnRange, Process.SYSTEM_UID);
// IPv6 unreachable route should not be misinterpreted as a default route
verify(mMockNetd, never()).firewallAddUidInterfaceRules(any(), any());
@@ -7492,6 +7665,7 @@ public class ConnectivityServiceTest {
// The uid range needs to cover the test app so the network is visible to it.
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
mMockVpn.establish(lp, VPN_UID, vpnRange);
+ assertVpnUidRangesUpdated(true, vpnRange, VPN_UID);
// Connected VPN should have interface rules set up. There are two expected invocations,
// one during VPN uid update, one during VPN LinkProperties update
@@ -7542,7 +7716,9 @@ public class ConnectivityServiceTest {
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
// The uid range needs to cover the test app so the network is visible to it.
final UidRange vpnRange = UidRange.createForUser(VPN_USER);
- mMockVpn.establish(lp, VPN_UID, Collections.singleton(vpnRange));
+ final Set<UidRange> vpnRanges = Collections.singleton(vpnRange);
+ mMockVpn.establish(lp, VPN_UID, vpnRanges);
+ assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID);
reset(mMockNetd);
InOrder inOrder = inOrder(mMockNetd);
@@ -7626,51 +7802,76 @@ public class ConnectivityServiceTest {
private int getOwnerUidNetCapsForCallerPermission(int ownerUid, int callerUid) {
final NetworkCapabilities netCap = new NetworkCapabilities().setOwnerUid(ownerUid);
- return mService
- .maybeSanitizeLocationInfoForCaller(netCap, callerUid, mContext.getPackageName())
- .getOwnerUid();
+ return mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled(
+ netCap, callerUid, mContext.getPackageName()).getOwnerUid();
+ }
+
+ private void verifyWifiInfoCopyNetCapsForCallerPermission(
+ int callerUid, boolean shouldMakeCopyWithLocationSensitiveFieldsParcelable) {
+ final WifiInfo wifiInfo = mock(WifiInfo.class);
+ when(wifiInfo.hasLocationSensitiveFields()).thenReturn(true);
+ final NetworkCapabilities netCap = new NetworkCapabilities().setTransportInfo(wifiInfo);
+
+ mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled(
+ netCap, callerUid, mContext.getPackageName());
+ verify(wifiInfo).makeCopy(eq(shouldMakeCopyWithLocationSensitiveFieldsParcelable));
}
@Test
- public void testMaybeSanitizeLocationInfoForCallerWithFineLocationAfterQ() throws Exception {
+ public void testCreateForCallerWithLocationInfoSanitizedWithFineLocationAfterQ()
+ throws Exception {
setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION);
final int myUid = Process.myUid();
assertEquals(myUid, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
+
+ verifyWifiInfoCopyNetCapsForCallerPermission(myUid,
+ true /* shouldMakeCopyWithLocationSensitiveFieldsParcelable */);
}
@Test
- public void testMaybeSanitizeLocationInfoForCallerWithCoarseLocationPreQ() throws Exception {
+ public void testCreateForCallerWithLocationInfoSanitizedWithCoarseLocationPreQ()
+ throws Exception {
setupLocationPermissions(Build.VERSION_CODES.P, true, AppOpsManager.OPSTR_COARSE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION);
final int myUid = Process.myUid();
assertEquals(myUid, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
+
+ verifyWifiInfoCopyNetCapsForCallerPermission(myUid,
+ true /* shouldMakeCopyWithLocationSensitiveFieldsParcelable */);
}
@Test
- public void testMaybeSanitizeLocationInfoForCallerLocationOff() throws Exception {
+ public void testCreateForCallerWithLocationInfoSanitizedLocationOff() throws Exception {
// Test that even with fine location permission, and UIDs matching, the UID is sanitized.
setupLocationPermissions(Build.VERSION_CODES.Q, false, AppOpsManager.OPSTR_FINE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION);
final int myUid = Process.myUid();
assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
+
+ verifyWifiInfoCopyNetCapsForCallerPermission(myUid,
+ false/* shouldMakeCopyWithLocationSensitiveFieldsParcelable */);
}
@Test
- public void testMaybeSanitizeLocationInfoForCallerWrongUid() throws Exception {
+ public void testCreateForCallerWithLocationInfoSanitizedWrongUid() throws Exception {
// Test that even with fine location permission, not being the owner leads to sanitization.
setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION);
final int myUid = Process.myUid();
assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid + 1, myUid));
+
+ verifyWifiInfoCopyNetCapsForCallerPermission(myUid,
+ true /* shouldMakeCopyWithLocationSensitiveFieldsParcelable */);
}
@Test
- public void testMaybeSanitizeLocationInfoForCallerWithCoarseLocationAfterQ() throws Exception {
+ public void testCreateForCallerWithLocationInfoSanitizedWithCoarseLocationAfterQ()
+ throws Exception {
// Test that not having fine location permission leads to sanitization.
setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_COARSE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION);
@@ -7678,21 +7879,29 @@ public class ConnectivityServiceTest {
// Test that without the location permission, the owner field is sanitized.
final int myUid = Process.myUid();
assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
+
+ verifyWifiInfoCopyNetCapsForCallerPermission(myUid,
+ false/* shouldMakeCopyWithLocationSensitiveFieldsParcelable */);
}
@Test
- public void testMaybeSanitizeLocationInfoForCallerWithoutLocationPermission() throws Exception {
+ public void testCreateForCallerWithLocationInfoSanitizedWithoutLocationPermission()
+ throws Exception {
setupLocationPermissions(Build.VERSION_CODES.Q, true, null /* op */, null /* perm */);
// Test that without the location permission, the owner field is sanitized.
final int myUid = Process.myUid();
assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
+
+ verifyWifiInfoCopyNetCapsForCallerPermission(myUid,
+ false/* shouldMakeCopyWithLocationSensitiveFieldsParcelable */);
}
private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
throws Exception {
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
mMockVpn.establish(new LinkProperties(), vpnOwnerUid, vpnRange);
+ assertVpnUidRangesUpdated(true, vpnRange, vpnOwnerUid);
mMockVpn.setVpnType(vpnType);
final VpnInfo vpnInfo = new VpnInfo();
@@ -7950,6 +8159,7 @@ public class ConnectivityServiceTest {
Manifest.permission.ACCESS_FINE_LOCATION);
mMockVpn.establishForMyUid();
+ assertUidRangesUpdatedForMyUid(true);
// Wait for networks to connect and broadcasts to be sent before removing permissions.
waitForIdle();
@@ -8229,4 +8439,54 @@ public class ConnectivityServiceTest {
assertTrue(isRequestIdInOrder);
}
}
+
+ private void assertUidRangesUpdatedForMyUid(boolean add) throws Exception {
+ final int uid = Process.myUid();
+ assertVpnUidRangesUpdated(add, uidRangesForUid(uid), uid);
+ }
+
+ private void assertVpnUidRangesUpdated(boolean add, Set<UidRange> vpnRanges, int exemptUid)
+ throws Exception {
+ InOrder inOrder = inOrder(mMockNetd);
+ ArgumentCaptor<int[]> exemptUidCaptor = ArgumentCaptor.forClass(int[].class);
+
+ inOrder.verify(mMockNetd, times(1)).socketDestroy(eq(toUidRangeStableParcels(vpnRanges)),
+ exemptUidCaptor.capture());
+ assertContainsExactly(exemptUidCaptor.getValue(), Process.VPN_UID, exemptUid);
+
+ if (add) {
+ inOrder.verify(mMockNetd, times(1)).networkAddUidRanges(eq(mMockVpn.getNetId()),
+ eq(toUidRangeStableParcels(vpnRanges)));
+ } else {
+ inOrder.verify(mMockNetd, times(1)).networkRemoveUidRanges(eq(mMockVpn.getNetId()),
+ eq(toUidRangeStableParcels(vpnRanges)));
+ }
+
+ inOrder.verify(mMockNetd, times(1)).socketDestroy(eq(toUidRangeStableParcels(vpnRanges)),
+ exemptUidCaptor.capture());
+ assertContainsExactly(exemptUidCaptor.getValue(), Process.VPN_UID, exemptUid);
+ }
+
+ @Test
+ public void testVpnUidRangesUpdate() throws Exception {
+ LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName("tun0");
+ lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
+ lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
+ final UidRange vpnRange = UidRange.createForUser(VPN_USER);
+ Set<UidRange> vpnRanges = Collections.singleton(vpnRange);
+ mMockVpn.establish(lp, VPN_UID, vpnRanges);
+ assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID);
+
+ reset(mMockNetd);
+ // Update to new range which is old range minus APP1, i.e. only APP2
+ final Set<UidRange> newRanges = new HashSet<>(Arrays.asList(
+ new UidRange(vpnRange.start, APP1_UID - 1),
+ new UidRange(APP1_UID + 1, vpnRange.stop)));
+ mMockVpn.setUids(newRanges);
+ waitForIdle();
+
+ assertVpnUidRangesUpdated(true, newRanges, VPN_UID);
+ assertVpnUidRangesUpdated(false, vpnRanges, VPN_UID);
+ }
}
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index 3a071667a542..8c5d1d6d05e5 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -124,6 +124,22 @@ public class IpConnectivityMetricsTest {
assertEquals("", output2);
}
+ private void logDefaultNetworkEvent(long timeMs, NetworkAgentInfo nai,
+ NetworkAgentInfo oldNai) {
+ final Network network = (nai != null) ? nai.network() : null;
+ final int score = (nai != null) ? nai.getCurrentScore() : 0;
+ final boolean validated = (nai != null) ? nai.lastValidated : false;
+ final LinkProperties lp = (nai != null) ? nai.linkProperties : null;
+ final NetworkCapabilities nc = (nai != null) ? nai.networkCapabilities : null;
+
+ final Network prevNetwork = (oldNai != null) ? oldNai.network() : null;
+ final int prevScore = (oldNai != null) ? oldNai.getCurrentScore() : 0;
+ final LinkProperties prevLp = (oldNai != null) ? oldNai.linkProperties : null;
+ final NetworkCapabilities prevNc = (oldNai != null) ? oldNai.networkCapabilities : null;
+
+ mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs, network, score, validated,
+ lp, nc, prevNetwork, prevScore, prevLp, prevNc);
+ }
@Test
public void testDefaultNetworkEvents() throws Exception {
final long cell = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
@@ -147,7 +163,7 @@ public class IpConnectivityMetricsTest {
for (NetworkAgentInfo[] pair : defaultNetworks) {
timeMs += durationMs;
durationMs += durationMs;
- mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs, pair[1], pair[0]);
+ logDefaultNetworkEvent(timeMs, pair[1], pair[0]);
}
String want = String.join("\n",
@@ -331,8 +347,8 @@ public class IpConnectivityMetricsTest {
final long wifi = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_WIFI});
NetworkAgentInfo cellNai = makeNai(100, 50, false, true, cell);
NetworkAgentInfo wifiNai = makeNai(101, 60, true, false, wifi);
- mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs + 200, cellNai, null);
- mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs + 300, wifiNai, cellNai);
+ logDefaultNetworkEvent(timeMs + 200L, cellNai, null);
+ logDefaultNetworkEvent(timeMs + 300L, wifiNai, cellNai);
String want = String.join("\n",
"dropped_events: 0",